diff --git a/README.md b/README.md
index 1748da0..7e1ce29 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,9 @@ https://youtube.com/playlist?list=PLabWm-zCaD1axcMGvf7wFxJz8FZmyHSJ7
Clone this repo into `~/.config/nvim/` or copy-paste just the parts you like.
If you already have your own configuration, check out `lua/plugins/quarto.lua`
-for the configuration of all plugins directly relevant to your Quarto experience.
+for the configuration of plugins directly relevant to your Quarto experience.
+The comments in this file will also point to to other plugins required for
+the full functionality.
This configuration can make use of a "Nerd Font" for icons and symbols.
Download one here: and set it as your terminal font.
@@ -48,10 +50,8 @@ rm -r ~/.local/state/nvim
## Screenshots
-![image](https://user-images.githubusercontent.com/17450586/210392216-a99815ac-1872-4c48-bf24-5a50df14c6d2.png)
![image](https://user-images.githubusercontent.com/17450586/210392419-3ee2b3e3-e805-4e36-99ab-6922abe3a66b.png)
![image](https://user-images.githubusercontent.com/17450586/210392573-57c0ad1c-5db0-4f2a-9119-608bd2398494.png)
-![image](https://user-images.githubusercontent.com/17450586/210392838-1c643a65-e792-4a54-bbdb-3ae959995a79.png)
Use the integrated neovim terminal to execute code chunks:
diff --git a/lua/plugins/completion.lua b/lua/plugins/completion.lua
new file mode 100644
index 0000000..86da564
--- /dev/null
+++ b/lua/plugins/completion.lua
@@ -0,0 +1,177 @@
+return {
+ {
+ 'windwp/nvim-autopairs',
+ config = function()
+ require('nvim-autopairs').setup {}
+ require('nvim-autopairs').remove_rule '`'
+ end,
+ },
+
+ { -- completion
+ 'hrsh7th/nvim-cmp',
+ event = 'InsertEnter',
+ dependencies = {
+ { 'hrsh7th/cmp-nvim-lsp' },
+ { 'hrsh7th/cmp-nvim-lsp-signature-help' },
+ { 'hrsh7th/cmp-buffer' },
+ { 'hrsh7th/cmp-path' },
+ { 'hrsh7th/cmp-calc' },
+ { 'hrsh7th/cmp-emoji' },
+ { 'saadparwaiz1/cmp_luasnip' },
+ { 'f3fora/cmp-spell' },
+ { 'ray-x/cmp-treesitter' },
+ { 'kdheepak/cmp-latex-symbols' },
+ { 'jmbuhr/cmp-pandoc-references' },
+ { 'L3MON4D3/LuaSnip' },
+ { 'rafamadriz/friendly-snippets' },
+ { 'onsails/lspkind-nvim' },
+ },
+ config = function()
+ local cmp = require 'cmp'
+ local luasnip = require 'luasnip'
+ local lspkind = require 'lspkind'
+
+ local has_words_before = function()
+ 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
+
+ cmp.setup {
+ snippet = {
+ expand = function(args)
+ luasnip.lsp_expand(args.body)
+ end,
+ },
+ completion = { completeopt = 'menu,menuone,noinsert' },
+ mapping = {
+ [''] = cmp.mapping.scroll_docs(-4),
+ [''] = cmp.mapping.scroll_docs(4),
+
+ [''] = cmp.mapping(function(fallback)
+ if luasnip.expand_or_jumpable() then
+ luasnip.expand_or_jump()
+ fallback()
+ end
+ end, { 'i', 's' }),
+ [''] = cmp.mapping(function(fallback)
+ if luasnip.jumpable(-1) then
+ luasnip.jump(-1)
+ else
+ fallback()
+ end
+ end, { 'i', 's' }),
+ [''] = cmp.mapping.abort(),
+ [''] = cmp.mapping.confirm {
+ select = true,
+ },
+ [''] = cmp.mapping.confirm {
+ select = true,
+ },
+
+ [''] = cmp.mapping(function(fallback)
+ if cmp.visible() then
+ cmp.select_next_item()
+ 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()
+ else
+ fallback()
+ end
+ end, { 'i', 's' }),
+
+ [''] = cmp.mapping(function()
+ if luasnip.expand_or_locally_jumpable() then
+ luasnip.expand_or_jump()
+ end
+ end, { 'i', 's' }),
+ [''] = cmp.mapping(function()
+ if luasnip.locally_jumpable(-1) then
+ luasnip.jump(-1)
+ end
+ end, { 'i', 's' }),
+ },
+ autocomplete = false,
+
+ ---@diagnostic disable-next-line: missing-fields
+ formatting = {
+ format = lspkind.cmp_format {
+ mode = 'symbol',
+ menu = {
+ otter = '[🦦]',
+ nvim_lsp = '[LSP]',
+ luasnip = '[snip]',
+ buffer = '[buf]',
+ path = '[path]',
+ spell = '[spell]',
+ pandoc_references = '[ref]',
+ tags = '[tag]',
+ treesitter = '[TS]',
+ calc = '[calc]',
+ latex_symbols = '[tex]',
+ emoji = '[emoji]',
+ },
+ },
+ },
+ sources = {
+ { name = 'otter' }, -- for code chunks in quarto
+ { name = 'path' },
+ { name = 'nvim_lsp' },
+ { name = 'nvim_lsp_signature_help' },
+ { name = 'luasnip', keyword_length = 3, max_item_count = 3 },
+ { name = 'pandoc_references' },
+ { name = 'buffer', keyword_length = 5, max_item_count = 3 },
+ { name = 'spell' },
+ { name = 'treesitter', keyword_length = 5, max_item_count = 3 },
+ { name = 'calc' },
+ { name = 'latex_symbols' },
+ { name = 'emoji' },
+ },
+ view = {
+ entries = 'native',
+ },
+ window = {
+ documentation = {
+ border = require('misc.style').border,
+ },
+ },
+ }
+
+ -- for friendly snippets
+ require('luasnip.loaders.from_vscode').lazy_load()
+ -- for custom snippets
+ require('luasnip.loaders.from_vscode').lazy_load { paths = { vim.fn.stdpath 'config' .. '/snips' } }
+ -- link quarto and rmarkdown to markdown snippets
+ luasnip.filetype_extend('quarto', { 'markdown' })
+ luasnip.filetype_extend('rmarkdown', { 'markdown' })
+ end,
+ },
+
+ { -- gh copilot
+ 'zbirenbaum/copilot.lua',
+ enabled = false,
+ config = function()
+ require('copilot').setup {
+ suggestion = {
+ enabled = true,
+ auto_trigger = true,
+ debounce = 75,
+ keymap = {
+ accept = '',
+ accept_word = false,
+ accept_line = false,
+ next = '',
+ prev = '',
+ dismiss = '',
+ },
+ },
+ panel = { enabled = false },
+ }
+ end,
+ },
+}
diff --git a/lua/plugins/editing.lua b/lua/plugins/editing.lua
index 4a53c44..60a7073 100644
--- a/lua/plugins/editing.lua
+++ b/lua/plugins/editing.lua
@@ -11,14 +11,6 @@ return {
opts = {},
},
- {
- 'windwp/nvim-autopairs',
- config = function()
- require('nvim-autopairs').setup {}
- require('nvim-autopairs').remove_rule '`'
- end,
- },
-
-- commenting with e.g. `gcc` or `gcip`
-- respects TS, so it works in quarto documents
{
@@ -28,6 +20,59 @@ return {
config = true,
},
+ { -- Autoformat
+ 'stevearc/conform.nvim',
+ enabled = true,
+ config = function()
+ require('conform').setup {
+ notify_on_error = false,
+ format_on_save = {
+ timeout_ms = 500,
+ lsp_fallback = true,
+ },
+ formatters_by_ft = {
+ lua = { 'mystylua' },
+ python = { 'isort', 'black' },
+ },
+ formatters = {
+ mystylua = {
+ command = 'stylua',
+ args = { '--indent-type', 'Spaces', '--indent-width', '2', '-' },
+ },
+ },
+ }
+ -- Customize the "injected" formatter
+ require('conform').formatters.injected = {
+ -- Set the options field
+ 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',
+ r = 'r',
+ typescript = 'ts',
+ },
+ -- Map of treesitter language to formatters to use
+ -- (defaults to the value from formatters_by_ft)
+ lang_to_formatters = {},
+ },
+ }
+ end,
+ },
+
{ -- generate docstrings
'danymat/neogen',
cmd = { 'Neogen' },
diff --git a/lua/plugins/lsp.lua b/lua/plugins/lsp.lua
new file mode 100644
index 0000000..f0e5509
--- /dev/null
+++ b/lua/plugins/lsp.lua
@@ -0,0 +1,249 @@
+return {
+ {
+ 'neovim/nvim-lspconfig',
+ dependencies = {
+ { 'williamboman/mason.nvim' },
+ { 'williamboman/mason-lspconfig.nvim' },
+ { 'WhoIsSethDaniel/mason-tool-installer.nvim' },
+ { -- nice loading notifications
+ -- PERF: but can slow down startup
+ 'j-hui/fidget.nvim',
+ enabled = false,
+ opts = {},
+ },
+ { 'folke/neodev.nvim', opts = {}, enabled = true },
+ { 'folke/neoconf.nvim', opts = {}, enabled = false },
+ },
+ config = function()
+ local lspconfig = require 'lspconfig'
+ local util = require 'lspconfig.util'
+
+ require('mason').setup()
+ require('mason-lspconfig').setup {
+ automatic_installation = true,
+ }
+ require('mason-tool-installer').setup {
+ ensure_installed = {
+ 'black',
+ 'stylua',
+ 'shfmt',
+ 'isort',
+ 'tree-sitter-cli',
+ },
+ }
+
+ vim.api.nvim_create_autocmd('LspAttach', {
+ group = vim.api.nvim_create_augroup('kickstart-lsp-attach', { clear = true }),
+ callback = function(event)
+ local telescope = require 'telescope.builtin'
+ local function map(keys, func, desc)
+ vim.keymap.set('n', keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc })
+ end
+
+ local client = vim.lsp.get_client_by_id(event.data.client_id)
+ assert(client, 'LSP client not found')
+
+ ---@diagnostic disable-next-line: inject-field
+ client.server_capabilities.document_formatting = true
+
+ map('gS', telescope.lsp_document_symbols, '[g]o so [S]ymbols')
+ map('gD', telescope.lsp_type_definitions, '[g]o to type [D]efinition')
+ map('gd', telescope.lsp_definitions, '[g]o to [d]efinition')
+ map('K', 'lua vim.lsp.buf.hover()', '[K] hover documentation')
+ map('gh', 'lua vim.lsp.buf.signature_help()', '[g]o to signature [h]elp')
+ map('gI', telescope.lsp_implementations, '[g]o to [I]mplementation')
+ map('gr', telescope.lsp_references, '[g]o to [r]eferences')
+ map('[d', vim.diagnostic.goto_prev, 'previous [d]iagnostic ')
+ map(']d', vim.diagnostic.goto_next, 'next [d]iagnostic ')
+ map('ll', vim.lsp.codelens.run, '[l]ens run')
+ map('lR', vim.lsp.buf.rename, '[l]sp [R]ename')
+ map('lf', vim.lsp.buf.format, '[l]sp [f]ormat')
+ map('lq', vim.diagnostic.setqflist, '[l]sp diagnostic [q]uickfix')
+ end,
+ })
+
+ local lsp_flags = {
+ allow_incremental_sync = true,
+ debounce_text_changes = 150,
+ }
+ vim.lsp.handlers['textDocument/hover'] = vim.lsp.with(vim.lsp.handlers.hover, { border = require('misc.style').border })
+ vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with(vim.lsp.handlers.signature_help, { border = require('misc.style').border })
+
+ local capabilities = vim.lsp.protocol.make_client_capabilities()
+ capabilities = vim.tbl_deep_extend('force', capabilities, require('cmp_nvim_lsp').default_capabilities())
+ capabilities.textDocument.completion.completionItem.snippetSupport = true
+
+ -- also needs:
+ -- $home/.config/marksman/config.toml :
+ -- [core]
+ -- markdown.file_extensions = ["md", "markdown", "qmd"]
+ lspconfig.marksman.setup {
+ capabilities = capabilities,
+ filetypes = { 'markdown', 'quarto' },
+ root_dir = util.root_pattern('.git', '.marksman.toml', '_quarto.yml'),
+ }
+
+ lspconfig.r_language_server.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ settings = {
+ r = {
+ lsp = {
+ rich_documentation = false,
+ },
+ },
+ },
+ }
+
+ lspconfig.cssls.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ }
+
+ lspconfig.html.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ }
+
+ lspconfig.emmet_language_server.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ }
+
+ lspconfig.yamlls.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ settings = {
+ yaml = {
+ schemaStore = {
+ enable = true,
+ url = '',
+ },
+ },
+ },
+ }
+
+ lspconfig.dotls.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ }
+
+ lspconfig.tsserver.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ filetypes = { 'js', 'javascript', 'typescript', 'ojs' },
+ }
+
+ local function get_quarto_resource_path()
+ local function strsplit(s, delimiter)
+ local result = {}
+ for match in (s .. delimiter):gmatch('(.-)' .. delimiter) do
+ table.insert(result, match)
+ end
+ return result
+ end
+
+ local f = assert(io.popen('quarto --paths', 'r'))
+ local s = assert(f:read '*a')
+ f:close()
+ return strsplit(s, '\n')[2]
+ end
+
+ local lua_library_files = vim.api.nvim_get_runtime_file('', true)
+ local lua_plugin_paths = {}
+ local resource_path = get_quarto_resource_path()
+ if resource_path == nil then
+ vim.notify_once 'quarto not found, lua library files not loaded'
+ else
+ table.insert(lua_library_files, resource_path .. '/lua-types')
+ table.insert(lua_plugin_paths, resource_path .. '/lua-plugin/plugin.lua')
+ end
+
+ lspconfig.lua_ls.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ settings = {
+ Lua = {
+ completion = {
+ callSnippet = 'Replace',
+ },
+ runtime = {
+ version = 'LuaJIT',
+ plugin = lua_plugin_paths,
+ },
+ diagnostics = {
+ globals = { 'vim', 'quarto', 'pandoc', 'io', 'string', 'print', 'require', 'table' },
+ disable = { 'trailing-space' },
+ },
+ workspace = {
+ library = lua_library_files,
+ checkThirdParty = false,
+ },
+ telemetry = {
+ enable = false,
+ },
+ },
+ },
+ }
+
+ lspconfig.julials.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ }
+
+ lspconfig.bashls.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ filetypes = { 'sh', 'bash' },
+ }
+
+ -- Add additional languages here.
+ -- See `:h lspconfig-all` for the configuration.
+ -- Like e.g. Haskell:
+ -- lspconfig.hls.setup {
+ -- on_attach = on_attach,
+ -- capabilities = capabilities,
+ -- flags = lsp_flags
+ -- }
+
+ -- lspconfig.rust_analyzer.setup{
+ -- on_attach = on_attach,
+ -- capabilities = capabilities,
+ -- settings = {
+ -- ['rust-analyzer'] = {
+ -- diagnostics = {
+ -- enable = false;
+ -- }
+ -- }
+ -- }
+ -- }
+
+ -- See https://github.com/neovim/neovim/issues/23291
+ -- disable lsp watcher.
+ -- Too lags on linux for python projects
+ -- because pyright and nvim both create too many watchers otherwise
+ if capabilities.workspace == nil then
+ capabilities.workspace = {}
+ capabilities.workspace.didChangeWatchedFiles = {}
+ end
+ capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = false
+
+ lspconfig.pyright.setup {
+ capabilities = capabilities,
+ flags = lsp_flags,
+ settings = {
+ python = {
+ analysis = {
+ autoSearchPaths = true,
+ useLibraryCodeForTypes = true,
+ diagnosticMode = 'workspace',
+ },
+ },
+ },
+ root_dir = function(fname)
+ return util.root_pattern('.git', 'setup.py', 'setup.cfg', 'pyproject.toml', 'requirements.txt')(fname) or util.path.dirname(fname)
+ end,
+ }
+ end,
+ },
+}
diff --git a/lua/plugins/quarto.lua b/lua/plugins/quarto.lua
index e09d3e3..cb5f963 100644
--- a/lua/plugins/quarto.lua
+++ b/lua/plugins/quarto.lua
@@ -1,6 +1,6 @@
return {
- {
+ { -- requires plugins in treesitter.lua and lsp.lua
'quarto-dev/quarto-nvim',
ft = { 'quarto' },
dev = false,
@@ -15,10 +15,16 @@ return {
},
dependencies = {
{
+ -- for language features in code cells
+ -- specifically needs its source added for nvim-cmp in completion.lua
'jmbuhr/otter.nvim',
dev = false,
dependencies = {
- { 'neovim/nvim-lspconfig' },
+ {
+ 'neovim/nvim-lspconfig',
+ 'nvim-treesitter/nvim-treesitter',
+ 'hrsh7th/nvim-cmp',
+ },
},
opts = {
lsp = {
@@ -35,563 +41,8 @@ return {
},
},
- {
- 'nvim-treesitter/nvim-treesitter',
- dependencies = {
- { 'nvim-treesitter/nvim-treesitter-textobjects' },
- },
- run = ':TSUpdate',
- config = function()
- ---@diagnostic disable-next-line: missing-fields
- require('nvim-treesitter.configs').setup {
- auto_install = true,
- ensure_installed = {
- 'r',
- 'python',
- 'markdown',
- 'markdown_inline',
- 'julia',
- 'bash',
- 'yaml',
- 'lua',
- 'vim',
- 'query',
- 'vimdoc',
- 'latex', -- requires tree-sitter-cli (installed automatically via Mason)
- 'html',
- 'css',
- 'dot',
- 'javascript',
- 'mermaid',
- 'norg',
- 'typescript',
- },
- highlight = {
- enable = true,
- additional_vim_regex_highlighting = false,
- },
- indent = {
- enable = true,
- },
- incremental_selection = {
- enable = true,
- keymaps = {
- init_selection = 'gnn',
- node_incremental = 'grn',
- scope_incremental = 'grc',
- node_decremental = 'grm',
- },
- },
- textobjects = {
- select = {
- enable = true,
- lookahead = true,
- keymaps = {
- -- You can use the capture groups defined in textobjects.scm
- ['af'] = '@function.outer',
- ['if'] = '@function.inner',
- ['ac'] = '@class.outer',
- ['ic'] = '@class.inner',
- },
- },
- move = {
- enable = true,
- set_jumps = true, -- whether to set jumps in the jumplist
- goto_next_start = {
- [']m'] = '@function.outer',
- [']]'] = '@class.inner',
- },
- goto_next_end = {
- [']M'] = '@function.outer',
- [']['] = '@class.outer',
- },
- goto_previous_start = {
- ['[m'] = '@function.outer',
- ['[['] = '@class.inner',
- },
- goto_previous_end = {
- ['[M'] = '@function.outer',
- ['[]'] = '@class.outer',
- },
- },
- },
- }
- end,
- },
-
- {
- 'neovim/nvim-lspconfig',
- dependencies = {
- { 'williamboman/mason.nvim' },
- { 'williamboman/mason-lspconfig.nvim' },
- { 'WhoIsSethDaniel/mason-tool-installer.nvim' },
- { -- nice loading notifications
- -- PERF: but can slow down startup
- 'j-hui/fidget.nvim',
- enabled = false,
- opts = {},
- },
- { 'folke/neodev.nvim', opts = {}, enabled = true },
- { 'folke/neoconf.nvim', opts = {}, enabled = false },
- },
- config = function()
- local lspconfig = require 'lspconfig'
- local util = require 'lspconfig.util'
-
- require('mason').setup()
- require('mason-lspconfig').setup {
- automatic_installation = true,
- }
- require('mason-tool-installer').setup {
- ensure_installed = {
- 'black',
- 'stylua',
- 'shfmt',
- 'isort',
- 'tree-sitter-cli',
- },
- }
-
- vim.api.nvim_create_autocmd('LspAttach', {
- group = vim.api.nvim_create_augroup('kickstart-lsp-attach', { clear = true }),
- callback = function(event)
- local telescope = require 'telescope.builtin'
- local function map(keys, func, desc)
- vim.keymap.set('n', keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc })
- end
-
- local client = vim.lsp.get_client_by_id(event.data.client_id)
- assert(client, 'LSP client not found')
-
- ---@diagnostic disable-next-line: inject-field
- client.server_capabilities.document_formatting = true
-
- map('gS', telescope.lsp_document_symbols, '[g]o so [S]ymbols')
- map('gD', telescope.lsp_type_definitions, '[g]o to type [D]efinition')
- map('gd', telescope.lsp_definitions, '[g]o to [d]efinition')
- map('K', 'lua vim.lsp.buf.hover()', '[K] hover documentation')
- map('gh', 'lua vim.lsp.buf.signature_help()', '[g]o to signature [h]elp')
- map('gI', telescope.lsp_implementations, '[g]o to [I]mplementation')
- map('gr', telescope.lsp_references, '[g]o to [r]eferences')
- map('[d', vim.diagnostic.goto_prev, 'previous [d]iagnostic ')
- map(']d', vim.diagnostic.goto_next, 'next [d]iagnostic ')
- map('ll', vim.lsp.codelens.run, '[l]ens run')
- map('lR', vim.lsp.buf.rename, '[l]sp [R]ename')
- map('lf', vim.lsp.buf.format, '[l]sp [f]ormat')
- map('lq', vim.diagnostic.setqflist, '[l]sp diagnostic [q]uickfix')
- end,
- })
-
- local lsp_flags = {
- allow_incremental_sync = true,
- debounce_text_changes = 150,
- }
- vim.lsp.handlers['textDocument/hover'] = vim.lsp.with(vim.lsp.handlers.hover, { border = require('misc.style').border })
- vim.lsp.handlers['textDocument/signatureHelp'] = vim.lsp.with(vim.lsp.handlers.signature_help, { border = require('misc.style').border })
-
- local capabilities = vim.lsp.protocol.make_client_capabilities()
- capabilities = vim.tbl_deep_extend('force', capabilities, require('cmp_nvim_lsp').default_capabilities())
- capabilities.textDocument.completion.completionItem.snippetSupport = true
-
- -- also needs:
- -- $home/.config/marksman/config.toml :
- -- [core]
- -- markdown.file_extensions = ["md", "markdown", "qmd"]
- lspconfig.marksman.setup {
- capabilities = capabilities,
- filetypes = { 'markdown', 'quarto' },
- root_dir = util.root_pattern('.git', '.marksman.toml', '_quarto.yml'),
- }
-
- lspconfig.r_language_server.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- settings = {
- r = {
- lsp = {
- rich_documentation = false,
- },
- },
- },
- }
-
- lspconfig.cssls.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- }
-
- lspconfig.html.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- }
-
- lspconfig.emmet_language_server.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- }
-
- lspconfig.yamlls.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- settings = {
- yaml = {
- schemaStore = {
- enable = true,
- url = '',
- },
- },
- },
- }
-
- lspconfig.dotls.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- }
-
- lspconfig.tsserver.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- filetypes = { 'js', 'javascript', 'typescript', 'ojs' },
- }
-
- local function get_quarto_resource_path()
- local function strsplit(s, delimiter)
- local result = {}
- for match in (s .. delimiter):gmatch('(.-)' .. delimiter) do
- table.insert(result, match)
- end
- return result
- end
-
- local f = assert(io.popen('quarto --paths', 'r'))
- local s = assert(f:read '*a')
- f:close()
- return strsplit(s, '\n')[2]
- end
-
- local lua_library_files = vim.api.nvim_get_runtime_file('', true)
- local lua_plugin_paths = {}
- local resource_path = get_quarto_resource_path()
- if resource_path == nil then
- vim.notify_once 'quarto not found, lua library files not loaded'
- else
- table.insert(lua_library_files, resource_path .. '/lua-types')
- table.insert(lua_plugin_paths, resource_path .. '/lua-plugin/plugin.lua')
- end
-
- lspconfig.lua_ls.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- settings = {
- Lua = {
- completion = {
- callSnippet = 'Replace',
- },
- runtime = {
- version = 'LuaJIT',
- plugin = lua_plugin_paths,
- },
- diagnostics = {
- globals = { 'vim', 'quarto', 'pandoc', 'io', 'string', 'print', 'require', 'table' },
- disable = { 'trailing-space' },
- },
- workspace = {
- library = lua_library_files,
- checkThirdParty = false,
- },
- telemetry = {
- enable = false,
- },
- },
- },
- }
-
- lspconfig.julials.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- }
-
- lspconfig.bashls.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- filetypes = { 'sh', 'bash' },
- }
-
- -- Add additional languages here.
- -- See `:h lspconfig-all` for the configuration.
- -- Like e.g. Haskell:
- -- lspconfig.hls.setup {
- -- on_attach = on_attach,
- -- capabilities = capabilities,
- -- flags = lsp_flags
- -- }
-
- -- lspconfig.rust_analyzer.setup{
- -- on_attach = on_attach,
- -- capabilities = capabilities,
- -- settings = {
- -- ['rust-analyzer'] = {
- -- diagnostics = {
- -- enable = false;
- -- }
- -- }
- -- }
- -- }
-
- -- See https://github.com/neovim/neovim/issues/23291
- -- disable lsp watcher.
- -- Too lags on linux for python projects
- -- because pyright and nvim both create too many watchers otherwise
- if capabilities.workspace == nil then
- capabilities.workspace = {}
- capabilities.workspace.didChangeWatchedFiles = {}
- end
- capabilities.workspace.didChangeWatchedFiles.dynamicRegistration = false
-
- lspconfig.pyright.setup {
- capabilities = capabilities,
- flags = lsp_flags,
- settings = {
- python = {
- analysis = {
- autoSearchPaths = true,
- useLibraryCodeForTypes = true,
- diagnosticMode = 'workspace',
- },
- },
- },
- root_dir = function(fname)
- return util.root_pattern('.git', 'setup.py', 'setup.cfg', 'pyproject.toml', 'requirements.txt')(fname) or util.path.dirname(fname)
- end,
- }
- end,
- },
-
- { -- Autoformat
- 'stevearc/conform.nvim',
- enabled = true,
- config = function()
- require('conform').setup {
- notify_on_error = false,
- format_on_save = {
- timeout_ms = 500,
- lsp_fallback = true,
- },
- formatters_by_ft = {
- lua = { 'mystylua' },
- python = { 'isort', 'black' },
- },
- formatters = {
- mystylua = {
- command = 'stylua',
- args = { '--indent-type', 'Spaces', '--indent-width', '2', '-' },
- },
- },
- }
- -- Customize the "injected" formatter
- require('conform').formatters.injected = {
- -- Set the options field
- 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',
- r = 'r',
- typescript = 'ts',
- },
- -- Map of treesitter language to formatters to use
- -- (defaults to the value from formatters_by_ft)
- lang_to_formatters = {},
- },
- }
- end,
- },
-
- -- completion
- {
- 'hrsh7th/nvim-cmp',
- event = 'InsertEnter',
- dependencies = {
- { 'hrsh7th/cmp-nvim-lsp' },
- { 'hrsh7th/cmp-nvim-lsp-signature-help' },
- { 'hrsh7th/cmp-buffer' },
- { 'hrsh7th/cmp-path' },
- { 'hrsh7th/cmp-calc' },
- { 'hrsh7th/cmp-emoji' },
- { 'saadparwaiz1/cmp_luasnip' },
- { 'f3fora/cmp-spell' },
- { 'ray-x/cmp-treesitter' },
- { 'kdheepak/cmp-latex-symbols' },
- { 'jmbuhr/cmp-pandoc-references' },
- { 'L3MON4D3/LuaSnip' },
- { 'rafamadriz/friendly-snippets' },
- { 'onsails/lspkind-nvim' },
- },
- config = function()
- local cmp = require 'cmp'
- local luasnip = require 'luasnip'
- local lspkind = require 'lspkind'
-
- local has_words_before = function()
- 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
-
- cmp.setup {
- snippet = {
- expand = function(args)
- luasnip.lsp_expand(args.body)
- end,
- },
- completion = { completeopt = 'menu,menuone,noinsert' },
- mapping = {
- [''] = cmp.mapping.scroll_docs(-4),
- [''] = cmp.mapping.scroll_docs(4),
-
- [''] = cmp.mapping(function(fallback)
- if luasnip.expand_or_jumpable() then
- luasnip.expand_or_jump()
- fallback()
- end
- end, { 'i', 's' }),
- [''] = cmp.mapping(function(fallback)
- if luasnip.jumpable(-1) then
- luasnip.jump(-1)
- else
- fallback()
- end
- end, { 'i', 's' }),
- [''] = cmp.mapping.abort(),
- [''] = cmp.mapping.confirm {
- select = true,
- },
- [''] = cmp.mapping.confirm {
- select = true,
- },
-
- [''] = cmp.mapping(function(fallback)
- if cmp.visible() then
- cmp.select_next_item()
- 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()
- else
- fallback()
- end
- end, { 'i', 's' }),
-
- [''] = cmp.mapping(function()
- if luasnip.expand_or_locally_jumpable() then
- luasnip.expand_or_jump()
- end
- end, { 'i', 's' }),
- [''] = cmp.mapping(function()
- if luasnip.locally_jumpable(-1) then
- luasnip.jump(-1)
- end
- end, { 'i', 's' }),
- },
- autocomplete = false,
-
- ---@diagnostic disable-next-line: missing-fields
- formatting = {
- format = lspkind.cmp_format {
- mode = 'symbol',
- menu = {
- otter = '[🦦]',
- nvim_lsp = '[LSP]',
- luasnip = '[snip]',
- buffer = '[buf]',
- path = '[path]',
- spell = '[spell]',
- pandoc_references = '[ref]',
- tags = '[tag]',
- treesitter = '[TS]',
- calc = '[calc]',
- latex_symbols = '[tex]',
- emoji = '[emoji]',
- },
- },
- },
- sources = {
- { name = 'otter' }, -- for code chunks in quarto
- { name = 'path' },
- { name = 'nvim_lsp' },
- { name = 'nvim_lsp_signature_help' },
- { name = 'luasnip', keyword_length = 3, max_item_count = 3 },
- { name = 'pandoc_references' },
- { name = 'buffer', keyword_length = 5, max_item_count = 3 },
- { name = 'spell' },
- { name = 'treesitter', keyword_length = 5, max_item_count = 3 },
- { name = 'calc' },
- { name = 'latex_symbols' },
- { name = 'emoji' },
- },
- view = {
- entries = 'native',
- },
- window = {
- documentation = {
- border = require('misc.style').border,
- },
- },
- }
-
- -- for friendly snippets
- require('luasnip.loaders.from_vscode').lazy_load()
- -- for custom snippets
- require('luasnip.loaders.from_vscode').lazy_load { paths = { vim.fn.stdpath 'config' .. '/snips' } }
- -- link quarto and rmarkdown to markdown snippets
- luasnip.filetype_extend('quarto', { 'markdown' })
- luasnip.filetype_extend('rmarkdown', { 'markdown' })
- end,
- },
-
- { -- gh copilot
- 'zbirenbaum/copilot.lua',
- enabled = false,
- config = function()
- require('copilot').setup {
- suggestion = {
- enabled = true,
- auto_trigger = true,
- debounce = 75,
- keymap = {
- accept = '',
- accept_word = false,
- accept_line = false,
- next = '',
- prev = '',
- dismiss = '',
- },
- },
- panel = { enabled = false },
- }
- end,
- },
-
- -- send code from python/r/qmd documets to a terminal or REPL
- -- like ipython, R, bash
- {
+ { -- send code from python/r/qmd documets to a terminal or REPL
+ -- like ipython, R, bash
'jpalardy/vim-slime',
init = function()
vim.b['quarto_is_python_chunk'] = false
@@ -631,8 +82,7 @@ return {
end,
},
- -- paste an image from the clipboard or drag-and-drop
- {
+ { -- paste an image from the clipboard or drag-and-drop
'HakonHarnes/img-clip.nvim',
event = 'BufEnter',
ft = { 'markdown', 'quarto', 'latex' },
@@ -660,8 +110,7 @@ return {
end,
},
- -- preview equations
- {
+ { -- preview equations
'jbyuki/nabla.nvim',
keys = {
{ 'qm', ':lua require"nabla".toggle_virt()', desc = 'toggle [m]ath equations' },
diff --git a/lua/plugins/treesitter.lua b/lua/plugins/treesitter.lua
new file mode 100644
index 0000000..82f6f58
--- /dev/null
+++ b/lua/plugins/treesitter.lua
@@ -0,0 +1,85 @@
+return {
+ {
+ 'nvim-treesitter/nvim-treesitter',
+ dependencies = {
+ { 'nvim-treesitter/nvim-treesitter-textobjects' },
+ },
+ run = ':TSUpdate',
+ config = function()
+ ---@diagnostic disable-next-line: missing-fields
+ require('nvim-treesitter.configs').setup {
+ auto_install = true,
+ ensure_installed = {
+ 'r',
+ 'python',
+ 'markdown',
+ 'markdown_inline',
+ 'julia',
+ 'bash',
+ 'yaml',
+ 'lua',
+ 'vim',
+ 'query',
+ 'vimdoc',
+ 'latex', -- requires tree-sitter-cli (installed automatically via Mason)
+ 'html',
+ 'css',
+ 'dot',
+ 'javascript',
+ 'mermaid',
+ 'norg',
+ 'typescript',
+ },
+ highlight = {
+ enable = true,
+ additional_vim_regex_highlighting = false,
+ },
+ indent = {
+ enable = true,
+ },
+ incremental_selection = {
+ enable = true,
+ keymaps = {
+ init_selection = 'gnn',
+ node_incremental = 'grn',
+ scope_incremental = 'grc',
+ node_decremental = 'grm',
+ },
+ },
+ textobjects = {
+ select = {
+ enable = true,
+ lookahead = true,
+ keymaps = {
+ -- You can use the capture groups defined in textobjects.scm
+ ['af'] = '@function.outer',
+ ['if'] = '@function.inner',
+ ['ac'] = '@class.outer',
+ ['ic'] = '@class.inner',
+ },
+ },
+ move = {
+ enable = true,
+ set_jumps = true, -- whether to set jumps in the jumplist
+ goto_next_start = {
+ [']m'] = '@function.outer',
+ [']]'] = '@class.inner',
+ },
+ goto_next_end = {
+ [']M'] = '@function.outer',
+ [']['] = '@class.outer',
+ },
+ goto_previous_start = {
+ ['[m'] = '@function.outer',
+ ['[['] = '@class.inner',
+ },
+ goto_previous_end = {
+ ['[M'] = '@function.outer',
+ ['[]'] = '@class.outer',
+ },
+ },
+ },
+ }
+ end,
+ },
+}