├── after
└── queries
│ ├── r
│ └── textobjects.scm
│ ├── bash
│ └── textobjects.scm
│ ├── python
│ └── textobjects.scm
│ └── markdown
│ └── textobjects.scm
├── doc
├── tags
└── quarto.txt
├── examples
├── example3.qmd
├── example2.qmd
└── example.qmd
├── .gitignore
├── stylua.toml
├── lua
└── quarto
│ ├── runner
│ ├── slime.lua
│ ├── molten.lua
│ ├── iron.lua
│ └── init.lua
│ ├── tools.lua
│ ├── config.lua
│ ├── util.lua
│ └── init.lua
├── ftplugin
└── quarto.lua
├── plugin
└── quarto.lua
├── LICENSE
├── .github
├── workflows
│ └── ci.yml
└── CODE_OF_CONDUCT.md
├── README.md
└── CHANGELOG.md
/after/queries/r/textobjects.scm:
--------------------------------------------------------------------------------
1 | ; extends
2 | (
3 | (comment) @codechunk.inner
4 | (#eq? @codechunk.inner "#%%")
5 | )
6 |
--------------------------------------------------------------------------------
/after/queries/bash/textobjects.scm:
--------------------------------------------------------------------------------
1 | ; extends
2 | (
3 | (comment) @codechunk.inner
4 | (#eq? @codechunk.inner "#%%")
5 | )
6 |
--------------------------------------------------------------------------------
/after/queries/python/textobjects.scm:
--------------------------------------------------------------------------------
1 | ; extends
2 | (
3 | (comment) @codechunk.inner
4 | (#eq? @codechunk.inner "#%%")
5 | )
6 |
--------------------------------------------------------------------------------
/after/queries/markdown/textobjects.scm:
--------------------------------------------------------------------------------
1 | ; extends
2 | (code_fence_content) @codechunk.inner
3 |
4 | (fenced_code_block) @codechunk.outer
5 |
--------------------------------------------------------------------------------
/doc/tags:
--------------------------------------------------------------------------------
1 | QuartoPreview quarto.txt /*QuartoPreview*
2 | quarto.nvim quarto.txt /*quarto.nvim*
3 | quarto.quartoPreview() quarto.txt /*quarto.quartoPreview()*
4 |
--------------------------------------------------------------------------------
/examples/example3.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Hello world
3 | format: lua
4 | ---
5 |
6 | ```{lua}
7 | local x = 1
8 | local y = 2
9 | local z = x + y
10 | ```
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | example_files/
2 | example.html
3 | example*.R
4 | example*.py
5 | example*.hs
6 | examples/*.docx
7 | examples/*.html
8 | examples/*.pdf
9 | .DS_Store
10 |
--------------------------------------------------------------------------------
/stylua.toml:
--------------------------------------------------------------------------------
1 | column_width = 160
2 | line_endings = "Unix"
3 | indent_type = "Spaces"
4 | indent_width = 2
5 | quote_style = "AutoPreferSingle"
6 | call_parentheses = "None"
7 | [sort_requires]
8 | enabled = true
9 |
--------------------------------------------------------------------------------
/examples/example2.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Hello world
3 | format: html
4 | ---
5 |
6 | # Hello
7 |
8 | ```{haskell}
9 | xs :: [Int]
10 | xs = [x | x <- [1..10]]
11 | ```
12 |
13 |
14 | ```{haskell}
15 | s :: Int
16 | s = sum xs
17 | ```
18 |
--------------------------------------------------------------------------------
/lua/quarto/runner/slime.lua:
--------------------------------------------------------------------------------
1 | local concat = require('quarto.tools').concat
2 |
3 | ---Run the code cell with slime
4 | ---@param cell CodeCell
5 | ---@param _ boolean
6 | local function run(cell, _)
7 | local text_lines = concat(cell.text)
8 | vim.fn['slime#send'](text_lines)
9 | end
10 |
11 | ---@class CodeRunner
12 | local M = { run = run }
13 |
14 | return M
15 |
--------------------------------------------------------------------------------
/ftplugin/quarto.lua:
--------------------------------------------------------------------------------
1 | vim.b.slime_cell_delimiter = '```'
2 |
3 | -- TODO: Workaround while nvim-treesitter doesn't link those anymore
4 | -- until our ouwn pandoc grammar is ready
5 | vim.treesitter.language.register('markdown', { 'quarto', 'rmd' })
6 |
7 | local config = require('quarto.config').config
8 | local quarto = require 'quarto'
9 |
10 | if config.lspFeatures.enabled then
11 | quarto.activate()
12 | end
13 |
--------------------------------------------------------------------------------
/lua/quarto/runner/molten.lua:
--------------------------------------------------------------------------------
1 | ---Run the code cell with molten
2 | ---@param cell CodeCell
3 | ---@param ignore_cols boolean
4 | local function run(cell, ignore_cols)
5 | local range = cell.range
6 | if ignore_cols then
7 | vim.fn.MoltenEvaluateRange(range.from[1] + 1, range.to[1])
8 | else
9 | vim.fn.MoltenEvaluateRange(range.from[1] + 1, range.to[1], range.from[2] + 1, range.to[2] + 1)
10 | end
11 | end
12 |
13 | ---@class CodeRunner
14 | local M = { run = run }
15 |
16 | return M
17 |
--------------------------------------------------------------------------------
/lua/quarto/tools.lua:
--------------------------------------------------------------------------------
1 | local M = {}
2 |
3 | M.contains = function(list, x)
4 | for _, v in pairs(list) do
5 | if v == x then
6 | return true
7 | end
8 | end
9 | return false
10 | end
11 |
12 | M.replace_header_div = function(response)
13 | response.contents = response.contents:gsub('
', '')
14 | return response
15 | end
16 |
17 | M.concat = function(ls)
18 | if type(ls) ~= 'table' then
19 | return ls .. '\n\n'
20 | end
21 | local s = ''
22 | for _, l in ipairs(ls) do
23 | if l ~= '' then
24 | s = s .. '\n' .. l
25 | end
26 | end
27 | return s .. '\n'
28 | end
29 |
30 | return M
31 |
--------------------------------------------------------------------------------
/lua/quarto/runner/iron.lua:
--------------------------------------------------------------------------------
1 | local concat = require('quarto.tools').concat
2 | local iron_send = require('iron.core').send
3 |
4 | ---Run the code cell with iron
5 | ---@param cell CodeCell
6 | ---@param _ boolean
7 | local function run(cell, _)
8 | local text_lines = concat(cell.text)
9 | local lang = cell.lang or 'quarto'
10 | -- forward to iron.send
11 | -- first arg is filetype. if not supplied, iron.core.send would infer "quarto".
12 | -- Iron lets the user map a filetype to a repl binary, e.g. {"python" = "ipython", "r" = "radian"}
13 | -- so we can pass the cell.lang to get the same feel from a .qmd file.
14 | iron_send(lang, text_lines)
15 | end
16 |
17 | ---@class CodeRunner
18 | local M = { run = run }
19 |
20 | return M
21 |
--------------------------------------------------------------------------------
/lua/quarto/config.lua:
--------------------------------------------------------------------------------
1 | local M = {}
2 |
3 | M.defaultConfig = {
4 | debug = false,
5 | closePreviewOnExit = true,
6 | lspFeatures = {
7 | enabled = true,
8 | chunks = 'curly',
9 | languages = nil, -- nil means all languages found in the document
10 | diagnostics = {
11 | enabled = true,
12 | triggers = { 'BufWritePost' },
13 | },
14 | completion = {
15 | enabled = true,
16 | },
17 | },
18 | codeRunner = {
19 | enabled = true,
20 | default_method = 'slime', -- "molten", "slime", "iron" or
21 | ft_runners = {}, -- filetype to runner, ie. `{ python = "molten" }`.
22 | -- Takes precedence over `default_method`
23 | never_run = { 'yaml' }, -- filetypes which are never sent to a code runner
24 | },
25 | }
26 |
27 | -- use defaultConfig if not setup
28 | M.config = M.config or M.defaultConfig
29 |
30 | return M
31 |
--------------------------------------------------------------------------------
/examples/example.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: Hello World
3 | format: html
4 | ---
5 |
6 | # Hello World
7 |
8 | This is some python code, in which we define a function `hello`:
9 |
10 | ```{python}
11 | def hello():
12 | print("Hello")
13 | ```
14 |
15 | ```bash
16 | git clone
17 | ```
18 |
19 | This is how we call it:
20 |
21 | ```{python}
22 | hello()
23 | ```
24 |
25 | ```haskell
26 | xs = [x | x <- [1..10]]
27 | ```
28 |
29 | We can press `gd` to go to its definition.
30 |
31 | Let's demonstrate the autocompletion:
32 |
33 | ```{python}
34 | import numpy as np
35 | ```
36 |
37 | And code hovers with `K`.
38 |
39 | # Furthermore
40 |
41 | And then we can work on integrating this for multiple
42 | languages in the same document, like this R function:
43 |
44 | ```{r}
45 | hello_from_r <- function() {
46 | print("hello")
47 | }
48 | ```
49 |
50 | Unsurprisingly, the function does the same thing:
51 |
52 | ```{r}
53 | hello_from_r()
54 | ```
55 |
56 |
57 | ```{r}
58 | library(tidyverse)
59 | ```
60 |
61 |
--------------------------------------------------------------------------------
/plugin/quarto.lua:
--------------------------------------------------------------------------------
1 | if vim.fn.has 'nvim-0.9.0' ~= 1 then
2 | local msg = [[
3 | quarto-dev/quarto-nvim and jmbuhr/otter.nvim require Neovim version >= 0.9.0 (https://github.com/neovim/neovim/releases/tag/stable).
4 | If you are unable to update Neovim, you can specify a specific version of the plugins involved instead of the latest stable version.
5 | How you do this will vary depending on your plugin manager, but you can see one example using `lazy.nvim` here:
6 |
7 | ]]
8 | vim.notify_once(msg, vim.log.levels.WARN)
9 | end
10 |
11 | vim.api.nvim_create_user_command('QuartoPreview', require('quarto').quartoPreview, { nargs = '*' })
12 | vim.api.nvim_create_user_command('QuartoPreviewNoWatch', require('quarto').quartoPreviewNoWatch, { nargs = '*' })
13 | vim.api.nvim_create_user_command('QuartoUpdatePreview', require('quarto').quartoUpdatePreview, { nargs = '*' })
14 | vim.api.nvim_create_user_command('QuartoClosePreview', require('quarto').quartoClosePreview, {})
15 | vim.api.nvim_create_user_command('QuartoActivate', require('quarto').activate, {})
16 | vim.api.nvim_create_user_command('QuartoHelp', require('quarto').searchHelp, { nargs = 1 })
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | quarto-nvim
2 | Copyright (C) 2022-2023 Posit Software, PBC
3 |
4 | With the exceptions noted below, this code is released under the
5 | [GPL](http://www.gnu.org/copyleft/gpl.html), version 2 or later:
6 |
7 | This program is free software; you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation; either version 2 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 |
21 | The GNU General Public License is available in the file COPYING.md in
22 | the source distribution. On Debian systems, the complete text of the
23 | GPL can be found in `/usr/share/common-licenses/GPL`.
24 |
25 | Quarto-nvim's source code is at https://github.com/quarto-dev/quarto-nvim
26 |
--------------------------------------------------------------------------------
/lua/quarto/util.lua:
--------------------------------------------------------------------------------
1 | -- functions from neovim/nvim-lspconfig
2 | local M = {}
3 |
4 | local nvim_eleven = vim.fn.has 'nvim-0.11' == 1
5 |
6 | function M.tbl_flatten(t)
7 | --- @diagnostic disable-next-line:deprecated
8 | return nvim_eleven and vim.iter(t):flatten(math.huge):totable() or vim.tbl_flatten(t)
9 | end
10 |
11 | function M.strip_archive_subpath(path)
12 | -- Matches regex from zip.vim / tar.vim
13 | path = vim.fn.substitute(path, 'zipfile://\\(.\\{-}\\)::[^\\\\].*$', '\\1', '')
14 | path = vim.fn.substitute(path, 'tarfile:\\(.\\{-}\\)::.*$', '\\1', '')
15 | return path
16 | end
17 |
18 | function M.search_ancestors(startpath, func)
19 | if nvim_eleven then
20 | vim.validate('func', func, 'function')
21 | end
22 | if func(startpath) then
23 | return startpath
24 | end
25 | local guard = 100
26 | for path in vim.fs.parents(startpath) do
27 | -- Prevent infinite recursion if our algorithm breaks
28 | guard = guard - 1
29 | if guard == 0 then
30 | return
31 | end
32 |
33 | if func(path) then
34 | return path
35 | end
36 | end
37 | end
38 |
39 | local function escape_wildcards(path)
40 | return path:gsub('([%[%]%?%*])', '\\%1')
41 | end
42 |
43 | function M.root_pattern(...)
44 | local patterns = M.tbl_flatten { ... }
45 | return function(startpath)
46 | startpath = M.strip_archive_subpath(startpath)
47 | for _, pattern in ipairs(patterns) do
48 | local match = M.search_ancestors(startpath, function(path)
49 | for _, p in ipairs(vim.fn.glob(table.concat({ escape_wildcards(path), pattern }, '/'), true, true)) do
50 | if vim.uv.fs_stat(p) then
51 | return path
52 | end
53 | end
54 | end)
55 |
56 | if match ~= nil then
57 | return match
58 | end
59 | end
60 | end
61 | end
62 |
63 | return M
64 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | pull_request:
5 | workflow_dispatch:
6 |
7 | jobs:
8 | tests:
9 | strategy:
10 | matrix:
11 | # os: [ubuntu-latest, windows-latest]
12 | os: [ubuntu-latest]
13 | runs-on: ${{ matrix.os }}
14 | steps:
15 | - uses: actions/checkout@v3
16 | - name: Install Neovim
17 | shell: bash
18 | run: |
19 | sudo apt install libfuse2
20 | sudo wget -q https://github.com/neovim/neovim/releases/download/stable/nvim-linux-x86_64.appimage -O /usr/bin/nvim
21 | sudo chmod 0755 /usr/bin/nvim
22 | - name: Run Tests
23 | run: |
24 | nvim --version
25 | [ ! -d tests ] && exit 0
26 | nvim --headless -u tests/init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/init.lua', sequential = true}"
27 | docs:
28 | runs-on: ubuntu-latest
29 | needs: tests
30 | if: ${{ github.ref == 'refs/heads/main' }}
31 | steps:
32 | - uses: actions/checkout@v3
33 | - name: panvimdoc
34 | uses: kdheepak/panvimdoc@main
35 | with:
36 | vimdoc: quarto
37 | version: "Neovim >= 0.8.0"
38 | demojify: true
39 | treesitter: true
40 | - name: Push changes
41 | uses: stefanzweifel/git-auto-commit-action@v4
42 | with:
43 | commit_message: "chore(build): auto-generate vimdoc"
44 | commit_user_name: "github-actions[bot]"
45 | commit_user_email: "github-actions[bot]@users.noreply.github.com"
46 | commit_author: "github-actions[bot] "
47 | release:
48 | name: release
49 | if: ${{ github.ref == 'refs/heads/main' }}
50 | needs:
51 | - docs
52 | - tests
53 | runs-on: ubuntu-latest
54 | steps:
55 | - uses: google-github-actions/release-please-action@v3
56 | id: release
57 | with:
58 | release-type: simple
59 | package-name: quarto-nvim
60 | - uses: actions/checkout@v2
61 | - name: tag stable versions
62 | if: ${{ steps.release.outputs.release_created }}
63 | run: |
64 | git config user.name github-actions[bot]
65 | git config user.email github-actions[bot]@users.noreply.github.com
66 | git remote add gh-token "https://${{ secrets.GITHUB_TOKEN }}@github.com/google-github-actions/release-please-action.git"
67 | git tag -d stable || true
68 | git push origin :stable || true
69 | git tag -a stable -m "Last Stable Release"
70 | git push origin stable
71 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | codeofconduct@rstudio.com.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/lua/quarto/runner/init.lua:
--------------------------------------------------------------------------------
1 | --- Code runner, configurable to use different engines.
2 | local Runner = {}
3 |
4 | local otterkeeper = require 'otter.keeper'
5 | local config = require('quarto.config').config
6 |
7 | local no_code_found = 'No code chunks found for the current language, which is detected based on the current code block. Is your cursor in a code block?'
8 |
9 | local function overlaps_range(range, other)
10 | return range.from[1] <= other.to[1] and other.from[1] <= range.to[1]
11 | end
12 |
13 | ---pull the code chunks that overlap the given range, removes cells with a language that's in the
14 | ---ignore list
15 | ---@param lang string?
16 | ---@param code_chunks table
17 | ---@param range CodeRange
18 | ---@return CodeCell[]
19 | local function extract_code_cells_in_range(lang, code_chunks, range)
20 | local chunks = {}
21 |
22 | if lang then
23 | for _, chunk in ipairs(code_chunks[lang]) do
24 | if overlaps_range(chunk.range, range) then
25 | table.insert(chunks, chunk)
26 | end
27 | end
28 | else
29 | for l, lang_chunks in pairs(code_chunks) do
30 | if vim.tbl_contains(config.codeRunner.never_run, l) then
31 | goto continue
32 | end
33 | for _, chunk in ipairs(lang_chunks) do
34 | if overlaps_range(chunk.range, range) then
35 | table.insert(chunks, chunk)
36 | end
37 | end
38 | ::continue::
39 | end
40 | end
41 |
42 | return chunks
43 | end
44 |
45 | -- type `Range` is already defined twice in nvim core
46 | ---@class CodeRange
47 | ---@field from table
48 | ---@field to table
49 |
50 | ---@class CodeCell
51 | ---@field lang string?
52 | ---@field text table
53 | ---@field range CodeRange
54 |
55 | ---@class CodeRunner
56 | ---@field run function
57 |
58 | ---send code cell to the correct repl based on language, and user configuration
59 | ---@param cell CodeCell
60 | ---@param opts table?
61 | local function send(cell, opts)
62 | opts = opts or { ignore_cols = false }
63 | local runner = config.codeRunner.default_method
64 | local ft_runners = config.codeRunner.ft_runners
65 | if cell.lang ~= nil and ft_runners[cell.lang] ~= nil then
66 | runner = ft_runners[cell.lang]
67 | end
68 |
69 | -- if user passes a fn to config.codeRunner.default_method, we use that.
70 | -- (this also means fns are allowed as values in ft_runners)
71 | -- otherwise we lookup a string for pre-packaged runner function, e.g. "molten"
72 | if type(runner) == 'function' then
73 | runner(cell, opts.ignore_cols)
74 | elseif type(runner) == 'string' then
75 | require('quarto.runner.' .. runner).run(cell, opts.ignore_cols)
76 | else
77 | vim.notify("[Quarto] couldn't find appropriate code runner for language: " .. cell.lang, vim.log.levels.ERROR)
78 | end
79 | end
80 |
81 | ---run the code chunks for the given language that overlap the given range
82 | ---@param range CodeRange a range, for with any overlapping code cells are run
83 | ---@param multi_lang boolean?
84 | local function run(range, multi_lang)
85 | local buf = vim.api.nvim_get_current_buf()
86 | local lang = nil
87 | if not multi_lang then
88 | lang = otterkeeper.get_current_language_context()
89 | end
90 |
91 | otterkeeper.sync_raft(buf)
92 | local oa = otterkeeper.rafts[buf]
93 | if oa == nil then
94 | vim.notify("[Quarto] it seems that the code runner isn't initialized for this buffer.", vim.log.levels.ERROR)
95 | return
96 | end
97 | local chunks = otterkeeper.rafts[buf].code_chunks
98 |
99 | local filtered = extract_code_cells_in_range(lang, chunks, range)
100 |
101 | if #filtered == 0 then
102 | print(no_code_found)
103 | return
104 | end
105 | for _, chunk in ipairs(filtered) do
106 | send(chunk, { ignore_cols = true })
107 | end
108 | end
109 |
110 | ---@param multi_lang boolean?
111 | Runner.run_cell = function(multi_lang)
112 | local y = vim.api.nvim_win_get_cursor(0)[1] - 1
113 | local r = { y, 0 }
114 | local range = { from = r, to = r }
115 |
116 | run(range, multi_lang)
117 | end
118 |
119 | ---@param multi_lang boolean?
120 | Runner.run_above = function(multi_lang)
121 | local y = vim.api.nvim_win_get_cursor(0)[1] - 1
122 | local range = { from = { 0, 0 }, to = { y, 0 } }
123 |
124 | run(range, multi_lang)
125 | end
126 |
127 | ---@param multi_lang boolean?
128 | Runner.run_below = function(multi_lang)
129 | local y = vim.api.nvim_win_get_cursor(0)[1] - 1
130 | local range = { from = { y, 0 }, to = { math.huge, 0 } }
131 |
132 | run(range, multi_lang)
133 | end
134 |
135 | ---@param multi_lang boolean?
136 | Runner.run_all = function(multi_lang)
137 | local range = { from = { 0, 0 }, to = { math.huge, 0 } }
138 |
139 | run(range, multi_lang)
140 | end
141 |
142 | Runner.run_line = function()
143 | local buf = vim.api.nvim_get_current_buf()
144 | local lang = otterkeeper.get_current_language_context()
145 | local pos = vim.api.nvim_win_get_cursor(0)
146 |
147 | ---@type CodeCell
148 | local cell = {
149 | lang = lang,
150 | range = { from = { pos[1] - 1, 0 }, to = { pos[1], 0 } },
151 | text = vim.api.nvim_buf_get_lines(buf, pos[1] - 1, pos[1], false),
152 | }
153 |
154 | send(cell, { ignore_cols = true })
155 | end
156 |
157 | -- NOTE: This function will not work the same with molten as it does with slime/iron. Generally, code
158 | -- runners which run code based on the CodeCell range field, will not work when the user selects
159 | -- code across cells. But it will work if a selection is entirely within a cell.
160 | -- Also: This function cannot run multiple languages at once.
161 | Runner.run_range = function()
162 | local lines = otterkeeper.get_language_lines_in_visual_selection(true)
163 | if lines == nil then
164 | print(no_code_found)
165 | return
166 | end
167 |
168 | local vstart = vim.fn.getpos "'<"
169 | local vend = vim.fn.getpos "'>"
170 |
171 | if vstart and vend then
172 | local range = { from = { vstart[2] - 1, vstart[1] }, to = { vend[2], vend[1] } }
173 | send { lang = otterkeeper.get_current_language_context(), range = range, text = lines }
174 | else
175 | print 'No visual selection'
176 | end
177 | end
178 |
179 | return Runner
180 |
--------------------------------------------------------------------------------
/lua/quarto/init.lua:
--------------------------------------------------------------------------------
1 | local M = {}
2 | local api = vim.api
3 | local cfg = require 'quarto.config'
4 | local tools = require 'quarto.tools'
5 | local util = require 'quarto.util'
6 |
7 | ---Quarto preview
8 | ---@param opts table
9 | ---@return nil|string url
10 | function M.quartoPreview(opts)
11 | opts = opts or {}
12 | local args = opts.args or ''
13 |
14 | -- Find root directory / check if it is a project
15 | local buffer_path = api.nvim_buf_get_name(0)
16 | local root_dir = util.root_pattern '_quarto.yml'(buffer_path)
17 | local cmd
18 | local mode
19 |
20 | -- check for
21 | --
22 | -- editor:
23 | -- render-on-save: false
24 | --
25 | -- in _quarto.yml or the current qmd file
26 |
27 | local render_on_save = true
28 |
29 | local lines
30 | if root_dir then
31 | local quarto_config = root_dir .. '/_quarto.yml'
32 | lines = vim.fn.readfile(quarto_config)
33 | else
34 | -- assumption: the yaml header is not longer than a generous 500 lines
35 | lines = vim.api.nvim_buf_get_lines(0, 0, 500, false)
36 | end
37 |
38 | local query = 'render%-on%-save: false'
39 | for _, line in ipairs(lines) do
40 | if line:find(query) then
41 | render_on_save = false
42 | break
43 | end
44 | end
45 |
46 | if not render_on_save and string.find(args, '%-%-no%-watch%-inputs') == nil then
47 | args = args .. ' --no-watch-inputs'
48 | end
49 |
50 | if root_dir then
51 | mode = 'project'
52 | cmd = 'quarto preview ' .. vim.fn.shellescape(root_dir) .. ' ' .. args
53 | else
54 | mode = 'file'
55 | cmd = 'quarto preview ' .. vim.fn.shellescape(buffer_path) .. ' ' .. args
56 | end
57 |
58 | -- Check file extensions
59 | local quarto_extensions = { '.qmd', '.Rmd', '.ipynb', '.md' }
60 | local file_extension = buffer_path:match '^.+(%..+)$'
61 | if mode == 'file' and not file_extension then
62 | vim.notify 'Not in a file. exiting.'
63 | return
64 | end
65 | if mode == 'file' and not tools.contains(quarto_extensions, file_extension) then
66 | vim.notify('Not a quarto file, ends in ' .. file_extension .. ' exiting.')
67 | return
68 | end
69 |
70 | -- Store current tabpage
71 | local current_tabpage = vim.api.nvim_get_current_tabpage()
72 |
73 | -- Open a new tab for the terminal
74 | vim.cmd 'tabnew'
75 | local term_buf = vim.api.nvim_create_buf(true, false)
76 | vim.api.nvim_set_current_buf(term_buf)
77 |
78 | vim.fn.termopen(cmd, {
79 | on_exit = function(_, exit_code, _)
80 | if exit_code ~= 0 then
81 | vim.notify('Quarto preview exited with code ' .. exit_code, vim.log.levels.ERROR)
82 | end
83 | end,
84 | })
85 |
86 | -- Store the terminal buffer and return to previous tab
87 | local quartoOutputBuf = vim.api.nvim_get_current_buf()
88 |
89 | -- go back to the previous tab
90 | vim.api.nvim_set_current_tabpage(current_tabpage)
91 | api.nvim_buf_set_var(0, 'quartoOutputBuf', quartoOutputBuf)
92 |
93 | -- Close preview terminal on exit of the Quarto buffer
94 | if cfg.config and cfg.config.closePreviewOnExit then
95 | api.nvim_create_autocmd({ 'QuitPre', 'WinClosed' }, {
96 | buffer = api.nvim_get_current_buf(),
97 | group = api.nvim_create_augroup('quartoPreview', {}),
98 | callback = function(_, _)
99 | if api.nvim_buf_is_loaded(quartoOutputBuf) then
100 | api.nvim_buf_delete(quartoOutputBuf, { force = true })
101 | end
102 | end,
103 | })
104 | end
105 | end
106 |
107 | function M.quartoPreviewNoWatch()
108 | M.quartoPreview { args = '--no-watch-inputs' }
109 | end
110 |
111 | function M.quartoUpdatePreview()
112 | local quartoOutputBuf = api.nvim_buf_get_var(0, 'quartoOutputBuf')
113 | local query_start = 'Browse at http'
114 | local lines = vim.api.nvim_buf_get_lines(quartoOutputBuf, 0, -1, false)
115 | local url = nil
116 | for _, line in ipairs(lines) do
117 | if line:find(query_start) then
118 | url = 'http' .. line:sub(#query_start + 1)
119 | break
120 | end
121 | end
122 | if not url then
123 | vim.notify('Could not find the preview url in the terminal buffer. Maybe it is still warming up. Check the buffer and try again.', vim.log.levels.WARN)
124 | return
125 | end
126 | api.nvim_buf_set_var(0, 'quartoUrl', url)
127 | local request_url = url .. 'quarto-render/'
128 | local get_request = 'curl -s ' .. request_url
129 | local response = vim.fn.system(get_request)
130 | if response ~= 'rendered' then
131 | vim.notify_once('Failed to update preview with command: ' .. get_request, vim.log.levels.ERROR)
132 | end
133 | end
134 |
135 | function M.quartoClosePreview()
136 | local success, quartoOutputBuf = pcall(api.nvim_buf_get_var, 0, 'quartoOutputBuf')
137 | if not success then
138 | return
139 | end
140 | if api.nvim_buf_is_loaded(quartoOutputBuf) then
141 | api.nvim_buf_delete(quartoOutputBuf, { force = true })
142 | end
143 | end
144 |
145 | M.searchHelp = function(cmd_input)
146 | local topic = cmd_input.args
147 | local url = 'https://quarto.org/?q=' .. topic .. '&show-results=1'
148 | local sysname = vim.loop.os_uname().sysname
149 | local cmd
150 | if sysname == 'Linux' then
151 | cmd = 'xdg-open "' .. url .. '"'
152 | elseif sysname == 'Darwin' then
153 | cmd = 'open "' .. url .. '"'
154 | else
155 | print 'sorry, I do not know how to make Windows open a url with the default browser. This feature currently only works on linux and mac.'
156 | return
157 | end
158 | vim.fn.jobstart(cmd)
159 | end
160 |
161 | -- from https://github.com/neovim/nvim-lspconfig/blob/f98fa715acc975c2dd5fb5ba7ceddeb1cc725ad2/lua/lspconfig/util.lua#L23
162 | function M.bufname_valid(bufname)
163 | if bufname:match '^/' or bufname:match '^[a-zA-Z]:' or bufname:match '^zipfile://' or bufname:match '^tarfile:' then
164 | return true
165 | end
166 | return false
167 | end
168 |
169 | M.activate = function()
170 | local bufname = vim.api.nvim_buf_get_name(0)
171 | -- do not activate in special buffers, for example 'fugitive://...'
172 | if not M.bufname_valid(bufname) then
173 | return
174 | end
175 | local tsquery = nil
176 | if cfg.config.lspFeatures.chunks == 'curly' then
177 | tsquery = [[
178 | (fenced_code_block
179 | (info_string
180 | (language) @_lang
181 | ) @info
182 | (#match? @info "{")
183 | (code_fence_content) @content (#offset! @content)
184 | )
185 | ((html_block) @html @combined)
186 |
187 | ((minus_metadata) @yaml (#offset! @yaml 1 0 -1 0))
188 | ((plus_metadata) @toml (#offset! @toml 1 0 -1 0))
189 |
190 | ]]
191 | end
192 | require('otter').activate(cfg.config.lspFeatures.languages, cfg.config.lspFeatures.completion.enabled, cfg.config.lspFeatures.diagnostics.enabled, tsquery)
193 | end
194 |
195 | -- setup
196 | M.setup = function(opt)
197 | cfg.config = vim.tbl_deep_extend('force', cfg.defaultConfig, opt or {})
198 |
199 | if cfg.config.codeRunner.enabled then
200 | -- setup top level run functions
201 | local runner = require 'quarto.runner'
202 | M.quartoSend = runner.run_cell
203 | M.quartoSendAbove = runner.run_above
204 | M.quartoSendBelow = runner.run_below
205 | M.quartoSendAll = runner.run_all
206 | M.quartoSendRange = runner.run_range
207 | M.quartoSendLine = runner.run_line
208 |
209 | -- setup run user commands
210 | api.nvim_create_user_command('QuartoSend', function(_)
211 | runner.run_cell()
212 | end, {})
213 | api.nvim_create_user_command('QuartoSendAbove', function(_)
214 | runner.run_above()
215 | end, {})
216 | api.nvim_create_user_command('QuartoSendBelow', function(_)
217 | runner.run_below()
218 | end, {})
219 | api.nvim_create_user_command('QuartoSendAll', function(_)
220 | runner.run_all()
221 | end, {})
222 | api.nvim_create_user_command('QuartoSendRange', function(_)
223 | runner.run_range()
224 | end, { range = 2 })
225 | api.nvim_create_user_command('QuartoSendLine', function(_)
226 | runner.run_line()
227 | end, {})
228 | end
229 | end
230 |
231 | return M
232 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # quarto-nvim
2 |
3 | Quarto-nvim provides tools for working on [Quarto](https://quarto.org/) manuscripts in Neovim.
4 | You can get started with Quarto [here](https://quarto.org/docs/get-started/).
5 |
6 | ## Walkthrough
7 |
8 | The [get started section](https://quarto.org/docs/get-started/hello/neovim.html) also comes with a video version to walk you through.
9 | The playlist is extended as more features are added, so join us for a "Coffee with Quarto and Neovim":
10 |
11 |
12 |
13 | ## Setup
14 |
15 | You can install `quarto-nvim` from GitHub with your favourite Neovim plugin manager
16 | like [lazy.nvim](https://github.com/folke/lazy.nvim),
17 | [packer.nvim](https://github.com/wbthomason/packer.nvim) or [VimPlug](https://github.com/junegunn/vim-plug).
18 |
19 | Example `lazy.nvim` plugin specification:
20 |
21 | ```lua
22 | -- plugins/quarto.lua
23 | return {
24 | {
25 | "quarto-dev/quarto-nvim",
26 | dependencies = {
27 | "jmbuhr/otter.nvim",
28 | "nvim-treesitter/nvim-treesitter",
29 | },
30 | },
31 | }
32 | ```
33 |
34 | Because Quarto provides a lot of functionality through integration with existing plugins,
35 | you may want to experiment with my [config](https://github.com/jmbuhr/nvim-config)
36 | and then pick the relevant parts from the
37 | [`lua/plugins/quarto.lua`](https://github.com/jmbuhr/nvim-config/blob/main/lua/plugins/quarto.lua) file
38 | to integrate it into your own existing configuration.
39 |
40 | Plugins and their configuration to look out for in either of those files are:
41 |
42 | ```lua
43 | {
44 | 'quarto-dev/quarto-nvim',
45 | 'jmbuhr/otter.nvim',
46 | 'hrsh7th/nvim-cmp',
47 | 'neovim/nvim-lspconfig',
48 | 'nvim-treesitter/nvim-treesitter'
49 | }
50 | ```
51 |
52 | Quarto-nvim requires [Neovim stable version](https://github.com/neovim/neovim/releases/tag/stable) >= `v0.10.0`.
53 |
54 | ## Usage
55 |
56 | ### Configure
57 |
58 | You can pass a lua table with options to the setup function or via `lazy.nvim`s `opts` keyword.
59 | It will be merged with the default options, which are shown below in the example.
60 | If you want to use the defaults, simply call `setup` without arguments or with an empty table.
61 |
62 | ```lua
63 | require('quarto').setup{
64 | debug = false,
65 | closePreviewOnExit = true,
66 | lspFeatures = {
67 | enabled = true,
68 | chunks = "curly",
69 | languages = { "r", "python", "julia", "bash", "html" },
70 | diagnostics = {
71 | enabled = true,
72 | triggers = { "BufWritePost" },
73 | },
74 | completion = {
75 | enabled = true,
76 | },
77 | },
78 | codeRunner = {
79 | enabled = true,
80 | default_method = "slime", -- "molten", "slime", "iron" or
81 | ft_runners = {}, -- filetype to runner, ie. `{ python = "molten" }`.
82 | -- Takes precedence over `default_method`
83 | never_run = { 'yaml' }, -- filetypes which are never sent to a code runner
84 | },
85 | }
86 | ```
87 |
88 | ### Preview
89 |
90 | Use the command
91 |
92 | ```vim
93 | QuartoPreview
94 | ```
95 |
96 | or access the function from lua, e.g. to create a keybinding:
97 |
98 | ```lua
99 | local quarto = require('quarto')
100 | quarto.setup()
101 | vim.keymap.set('n', 'qp', quarto.quartoPreview, { silent = true, noremap = true })
102 | ```
103 |
104 | Then use the keyboard shortcut to open `quarto preview` for the current file or project in the active working directory in the neovim integrated terminal in a new tab.
105 |
106 | Note: While you can use `QuartoPreview` without configuring the plugin via `quarto.setup`,
107 | other features strictly require it.
108 |
109 | ## Language support
110 |
111 | `quarto-nvim` automatically activates `otter.nvim` for quarto files if language features are enabled.
112 |
113 | ### Demo
114 |
115 | https://user-images.githubusercontent.com/17450586/209436101-4dd560f4-c876-4dbc-a0f4-b3a2cbff0748.mp4
116 |
117 | ### Usage
118 |
119 | You can open the hover documentation for R, python and julia code chunks with `K`, got-to-definition with `gd` etc.
120 | and get autocompletion via the lsp source for your completion plugin.
121 |
122 | A list of currently available language server requests can be found in the [otter.nvim documentation](https://github.com/jmbuhr/otter.nvim?tab=readme-ov-file#lsp-methods-currently-implemented).
123 |
124 | ### R diagnostics configuration
125 |
126 | To make diagnostics work with R you have to configure the linter a bit, since the language
127 | buffers in the background separate code with blank links, which we want to ignore.
128 | Otherwise you get a lot more diagnostics than you probably want.
129 | Add file `.lintr` to your home folder and fill it with:
130 |
131 | ```
132 | linters: linters_with_defaults(
133 | trailing_blank_lines_linter = NULL,
134 | trailing_whitespace_linter = NULL
135 | )
136 | ```
137 |
138 | You can now also enable other lsp features, such as the show hover function
139 | and shortcut, independent of showing diagnostics by enabling lsp features
140 | but not enabling diagnostics.
141 |
142 | ### Other Edge Cases
143 |
144 | Other languages might have similar issues (e.g. I see a lot of warnings about whitespace when activating diagnostics with `lua`).
145 | If you come across them and have a fix, I will be very happy about a pull request!
146 | Or, what might ultimately be the cleaner way of documenting language specific issues, an entry in the [wiki](https://github.com/quarto-dev/quarto-nvim/wiki).
147 |
148 | ## Running Code
149 |
150 | Quarto-nvim doesn't run code for you, instead, it will interface with existing code running
151 | plugins and tell them what to run. There are currently three such code running plugins that quarto
152 | will work with:
153 |
154 | 1. [molten-nvim](https://github.com/benlubas/molten-nvim) - a code runner that supports the jupyter
155 | kernel, renders output below each code cell, and optionally renders images in the terminal.
156 | 2. [vim-slime](https://github.com/jpalardy/vim-slime) - a general purpose code runner with support
157 | for sending code to integrated nvim terminals, tmux panes, and many others.
158 | 3. [iron.nvim](https://github.com/Vigemus/iron.nvim) - general purpose code runner and library for
159 | within-neovim REPL interaction in splits or floating windows.
160 |
161 | We recommend picking a code runner, setting it up based on its respective README and then coming back
162 | to this point to learn how Quarto will augment that code runner.
163 |
164 | This plugin enables easily sending code cells to your code runner.
165 | There are two different ways to do this:
166 | commands, covered below; and lua functions, covered right here.
167 | _By default these functions will only run cells that are the same language as the current cell._
168 |
169 | Quarto exposes code running functions through to runner module: `require('quarto.runner')`.
170 | Those
171 | functions are:
172 |
173 | - `run_cell()` - runs the current cell
174 | - `run_above(multi_lang)` - runs all the cells above the current one, **and** the current one, in order
175 | - `run_below(multi_lang)` - runs all the cells below the current one, **and** the current one, in order
176 | - `run_all(multi_lang)` - runs all the cells in the document
177 | - `run_line(multi_lang)` - runs the line of code at your cursor
178 | - `run_range()` - run code cells inside (touched by) the current visual range
179 |
180 | Each function that takes the optional `multi_lang` argument will run cells of all languages when
181 | called with the value `true`, and will only run cells that match the language of the current cell
182 | otherwise. As a result, just calling `run_all()` will run all cells that match the language of the
183 | current cell.
184 |
185 | Here are some example run mappings:
186 |
187 | ```lua
188 | local runner = require("quarto.runner")
189 | vim.keymap.set("n", "rc", runner.run_cell, { desc = "run cell", silent = true })
190 | vim.keymap.set("n", "ra", runner.run_above, { desc = "run cell and above", silent = true })
191 | vim.keymap.set("n", "rA", runner.run_all, { desc = "run all cells", silent = true })
192 | vim.keymap.set("n", "rl", runner.run_line, { desc = "run line", silent = true })
193 | vim.keymap.set("v", "r", runner.run_range, { desc = "run visual range", silent = true })
194 | vim.keymap.set("n", "RA", function()
195 | runner.run_all(true)
196 | end, { desc = "run all cells of all languages", silent = true })
197 | ```
198 |
199 |
200 | ## Available Commands
201 |
202 | ```vim
203 | QuartoPreview
204 | QuartoClosePreview
205 | QuartoHelp <..>
206 | QuartoActivate
207 | QuartoDiagnostics
208 | QuartoSend
209 | QuartoSendAbove
210 | QuartoSendBelow
211 | QuartoSendAll
212 | QuartoSendLine
213 | ```
214 |
215 | ## Recommended Plugins
216 |
217 | Quarto works great with a number of plugins in the neovim ecosystem.
218 | You can find my (@jmbuhr) personal (and thus up-to-date) configuration for use with Quarto, R and python here:
219 |
220 |
221 |
222 | But remember, the best config is always your own.
223 |
--------------------------------------------------------------------------------
/doc/quarto.txt:
--------------------------------------------------------------------------------
1 | *quarto.txt* For Neovim >= 0.8.0 Last change: 2025 August 23
2 |
3 | ==============================================================================
4 | Table of Contents *quarto-table-of-contents*
5 |
6 | 1. quarto-nvim |quarto-quarto-nvim|
7 | - Walkthrough |quarto-quarto-nvim-walkthrough|
8 | - Setup |quarto-quarto-nvim-setup|
9 | - Usage |quarto-quarto-nvim-usage|
10 | - Language support |quarto-quarto-nvim-language-support|
11 | - Running Code |quarto-quarto-nvim-running-code|
12 | - Available Commands |quarto-quarto-nvim-available-commands|
13 | - Recommended Plugins |quarto-quarto-nvim-recommended-plugins|
14 | 2. Links |quarto-links|
15 |
16 | ==============================================================================
17 | 1. quarto-nvim *quarto-quarto-nvim*
18 |
19 | Quarto-nvim provides tools for working on Quarto
20 | manuscripts in Neovim. You can get started with Quarto here
21 | .
22 |
23 |
24 | WALKTHROUGH *quarto-quarto-nvim-walkthrough*
25 |
26 | The get started section
27 | also comes with a video version to walk you through. The playlist is extended
28 | as more features are added, so join us for a "Coffee with Quarto and Neovim"
29 |
30 |
31 |
32 |
33 | SETUP *quarto-quarto-nvim-setup*
34 |
35 | Youcan install `quarto-nvim` from GitHub with your favourite Neovim plugin
36 | manager like lazy.nvim , packer.nvim
37 | or VimPlug
38 | .
39 |
40 | Example `lazy.nvim` plugin specification:
41 |
42 | >lua
43 | -- plugins/quarto.lua
44 | return {
45 | {
46 | "quarto-dev/quarto-nvim",
47 | dependencies = {
48 | "jmbuhr/otter.nvim",
49 | "nvim-treesitter/nvim-treesitter",
50 | },
51 | },
52 | }
53 | <
54 |
55 | Because Quarto provides a lot of functionality through integration with
56 | existing plugins, you may want to experiment with my config
57 | and then pick the relevant parts from
58 | the `lua/plugins/quarto.lua`
59 | file
60 | to integrate it into your own existing configuration.
61 |
62 | Plugins and their configuration to look out for in either of those files are:
63 |
64 | >lua
65 | {
66 | 'quarto-dev/quarto-nvim',
67 | 'jmbuhr/otter.nvim',
68 | 'hrsh7th/nvim-cmp',
69 | 'neovim/nvim-lspconfig',
70 | 'nvim-treesitter/nvim-treesitter'
71 | }
72 | <
73 |
74 | Quarto-nvim requires Neovim stable version
75 | >= `v0.10.0`.
76 |
77 |
78 | USAGE *quarto-quarto-nvim-usage*
79 |
80 |
81 | CONFIGURE ~
82 |
83 | You can pass a lua table with options to the setup function or via `lazy.nvim`s
84 | `opts` keyword. It will be merged with the default options, which are shown
85 | below in the example. If you want to use the defaults, simply call `setup`
86 | without arguments or with an empty table.
87 |
88 | >lua
89 | require('quarto').setup{
90 | debug = false,
91 | closePreviewOnExit = true,
92 | lspFeatures = {
93 | enabled = true,
94 | chunks = "curly",
95 | languages = { "r", "python", "julia", "bash", "html" },
96 | diagnostics = {
97 | enabled = true,
98 | triggers = { "BufWritePost" },
99 | },
100 | completion = {
101 | enabled = true,
102 | },
103 | },
104 | codeRunner = {
105 | enabled = true,
106 | default_method = "slime", -- "molten", "slime", "iron" or
107 | ft_runners = {}, -- filetype to runner, ie. `{ python = "molten" }`.
108 | -- Takes precedence over `default_method`
109 | never_run = { 'yaml' }, -- filetypes which are never sent to a code runner
110 | },
111 | }
112 | <
113 |
114 |
115 | PREVIEW ~
116 |
117 | Use the command
118 |
119 | >vim
120 | QuartoPreview
121 | <
122 |
123 | or access the function from lua, e.g. to create a keybinding:
124 |
125 | >lua
126 | local quarto = require('quarto')
127 | quarto.setup()
128 | vim.keymap.set('n', 'qp', quarto.quartoPreview, { silent = true, noremap = true })
129 | <
130 |
131 | Then use the keyboard shortcut to open `quarto preview` for the current file or
132 | project in the active working directory in the neovim integrated terminal in a
133 | new tab.
134 |
135 | Note: While you can use `QuartoPreview` without configuring the plugin via
136 | `quarto.setup`, other features strictly require it.
137 |
138 |
139 | LANGUAGE SUPPORT *quarto-quarto-nvim-language-support*
140 |
141 | `quarto-nvim` automatically activates `otter.nvim` for quarto files if language
142 | features are enabled.
143 |
144 |
145 | DEMO ~
146 |
147 |
148 | https://user-images.githubusercontent.com/17450586/209436101-4dd560f4-c876-4dbc-a0f4-b3a2cbff0748.mp4
149 |
150 |
151 | USAGE ~
152 |
153 | You can open the hover documentation for R, python and julia code chunks with
154 | `K`, got-to-definition with `gd` etc. and get autocompletion via the lsp source
155 | for your completion plugin.
156 |
157 | A list of currently available language server requests can be found in the
158 | otter.nvim documentation
159 | .
160 |
161 |
162 | R DIAGNOSTICS CONFIGURATION ~
163 |
164 | To make diagnostics work with R you have to configure the linter a bit, since
165 | the language buffers in the background separate code with blank links, which we
166 | want to ignore. Otherwise you get a lot more diagnostics than you probably
167 | want. Add file `.lintr` to your home folder and fill it with:
168 |
169 | >
170 | linters: linters_with_defaults(
171 | trailing_blank_lines_linter = NULL,
172 | trailing_whitespace_linter = NULL
173 | )
174 | <
175 |
176 | You can now also enable other lsp features, such as the show hover function and
177 | shortcut, independent of showing diagnostics by enabling lsp features but not
178 | enabling diagnostics.
179 |
180 |
181 | OTHER EDGE CASES ~
182 |
183 | Other languages might have similar issues (e.g. I see a lot of warnings about
184 | whitespace when activating diagnostics with `lua`). If you come across them and
185 | have a fix, I will be very happy about a pull request! Or, what might
186 | ultimately be the cleaner way of documenting language specific issues, an entry
187 | in the wiki .
188 |
189 |
190 | RUNNING CODE *quarto-quarto-nvim-running-code*
191 |
192 | Quarto-nvim doesn’t run code for you, instead, it will interface with
193 | existing code running plugins and tell them what to run. There are currently
194 | three such code running plugins that quarto will work with:
195 |
196 | 1. molten-nvim - a code runner that supports the jupyter
197 | kernel, renders output below each code cell, and optionally renders images in the terminal.
198 | 2. vim-slime - a general purpose code runner with support
199 | for sending code to integrated nvim terminals, tmux panes, and many others.
200 | 3. iron.nvim - general purpose code runner and library for
201 | within-neovim REPL interaction in splits or floating windows.
202 |
203 | We recommend picking a code runner, setting it up based on its respective
204 | README and then coming back to this point to learn how Quarto will augment that
205 | code runner.
206 |
207 | This plugin enables easily sending code cells to your code runner. There are
208 | two different ways to do this: commands, covered below; and lua functions,
209 | covered right here. _By default these functions will only run cells that are
210 | the same language as the current cell._
211 |
212 | Quarto exposes code running functions through to runner module:
213 | `require('quarto.runner')`. Those functions are:
214 |
215 | - `run_cell()` - runs the current cell
216 | - `run_above(multi_lang)` - runs all the cells above the current one, **and** the current one, in order
217 | - `run_below(multi_lang)` - runs all the cells below the current one, **and** the current one, in order
218 | - `run_all(multi_lang)` - runs all the cells in the document
219 | - `run_line(multi_lang)` - runs the line of code at your cursor
220 | - `run_range()` - run code cells inside (touched by) the current visual range
221 |
222 | Each function that takes the optional `multi_lang` argument will run cells of
223 | all languages when called with the value `true`, and will only run cells that
224 | match the language of the current cell otherwise. As a result, just calling
225 | `run_all()` will run all cells that match the language of the current cell.
226 |
227 | Here are some example run mappings:
228 |
229 | >lua
230 | local runner = require("quarto.runner")
231 | vim.keymap.set("n", "rc", runner.run_cell, { desc = "run cell", silent = true })
232 | vim.keymap.set("n", "ra", runner.run_above, { desc = "run cell and above", silent = true })
233 | vim.keymap.set("n", "rA", runner.run_all, { desc = "run all cells", silent = true })
234 | vim.keymap.set("n", "rl", runner.run_line, { desc = "run line", silent = true })
235 | vim.keymap.set("v", "r", runner.run_range, { desc = "run visual range", silent = true })
236 | vim.keymap.set("n", "RA", function()
237 | runner.run_all(true)
238 | end, { desc = "run all cells of all languages", silent = true })
239 | <
240 |
241 |
242 | AVAILABLE COMMANDS *quarto-quarto-nvim-available-commands*
243 |
244 | >vim
245 | QuartoPreview
246 | QuartoClosePreview
247 | QuartoHelp <..>
248 | QuartoActivate
249 | QuartoDiagnostics
250 | QuartoSend
251 | QuartoSendAbove
252 | QuartoSendBelow
253 | QuartoSendAll
254 | QuartoSendLine
255 | <
256 |
257 |
258 | RECOMMENDED PLUGINS *quarto-quarto-nvim-recommended-plugins*
259 |
260 | Quarto works great with a number of plugins in the neovim ecosystem. You can
261 | find my (@jmbuhr) personal (and thus up-to-date) configuration for use with
262 | Quarto, R and python here:
263 |
264 |
265 |
266 | But remember, the best config is always your own.
267 |
268 | ==============================================================================
269 | 2. Links *quarto-links*
270 |
271 | 1. *@jmbuhr*:
272 |
273 | Generated by panvimdoc
274 |
275 | vim:tw=78:ts=8:noet:ft=help:norl:
276 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [1.2.5](https://github.com/quarto-dev/quarto-nvim/compare/v1.2.4...v1.2.5) (2025-08-23)
4 |
5 |
6 | ### Bug Fixes
7 |
8 | * pass `root_dir` to preview for project type ([#190](https://github.com/quarto-dev/quarto-nvim/issues/190)) ([e6b34cd](https://github.com/quarto-dev/quarto-nvim/commit/e6b34cdc56cad6e83f4d8c7737f4444f78ad29a2))
9 |
10 | ## [1.2.4](https://github.com/quarto-dev/quarto-nvim/compare/v1.2.3...v1.2.4) (2025-04-10)
11 |
12 |
13 | ### Bug Fixes
14 |
15 | * add `search_ancestors` function to util ([#178](https://github.com/quarto-dev/quarto-nvim/issues/178)) ([6cf5fce](https://github.com/quarto-dev/quarto-nvim/commit/6cf5fce22c993ef06b954ad00fe722d9d162549e))
16 |
17 | ## [1.2.3](https://github.com/quarto-dev/quarto-nvim/compare/v1.2.2...v1.2.3) (2025-04-08)
18 |
19 |
20 | ### Bug Fixes
21 |
22 | * copy lspconfig util functions to remove dependency. fixes [#175](https://github.com/quarto-dev/quarto-nvim/issues/175) ([06a9082](https://github.com/quarto-dev/quarto-nvim/commit/06a9082ade62f103663e14ce721dc7d372294895))
23 |
24 | ## [1.2.2](https://github.com/quarto-dev/quarto-nvim/compare/v1.2.1...v1.2.2) (2025-03-24)
25 |
26 |
27 | ### Bug Fixes
28 |
29 | * make usercommands require quarto only on use ([74df376](https://github.com/quarto-dev/quarto-nvim/commit/74df3767fa6c0d00e152e317c983c495f2cade6a))
30 |
31 | ## [1.2.1](https://github.com/quarto-dev/quarto-nvim/compare/v1.2.0...v1.2.1) (2025-02-03)
32 |
33 |
34 | ### Bug Fixes
35 |
36 | * do not activate in special buffers (fixes [#164](https://github.com/quarto-dev/quarto-nvim/issues/164)) ([#165](https://github.com/quarto-dev/quarto-nvim/issues/165)) ([bb710f4](https://github.com/quarto-dev/quarto-nvim/commit/bb710f433ea6b0acaeb672d26f6641bd5723e0ef))
37 |
38 | ## [1.2.0](https://github.com/quarto-dev/quarto-nvim/compare/v1.1.0...v1.2.0) (2024-12-22)
39 |
40 |
41 | ### Features
42 |
43 | * detect editor: render-on-save: false ([#159](https://github.com/quarto-dev/quarto-nvim/issues/159)) ([ca2d1c7](https://github.com/quarto-dev/quarto-nvim/commit/ca2d1c790e0d8a46096ed428255c65eb8eff7c30))
44 | * QuartoPreviewNoWatch and QuartoUpdatePreview commands ([#157](https://github.com/quarto-dev/quarto-nvim/issues/157)) ([601c32b](https://github.com/quarto-dev/quarto-nvim/commit/601c32b89cc77f0f44b9208a1347c3294a64338c))
45 |
46 | ## [1.1.0](https://github.com/quarto-dev/quarto-nvim/compare/v1.0.2...v1.1.0) (2024-12-20)
47 |
48 |
49 | ### Features
50 |
51 | * support Iron.nvim and user functions for sending code to REPL ([#109](https://github.com/quarto-dev/quarto-nvim/issues/109)) ([eb98585](https://github.com/quarto-dev/quarto-nvim/commit/eb98585b13f8d5ddc21c9a9413b7b3b848099653))
52 |
53 |
54 | ### Bug Fixes
55 |
56 | * Replace term:// with termopen() to resolve wildcard expansion error ([#153](https://github.com/quarto-dev/quarto-nvim/issues/153)) ([f810c62](https://github.com/quarto-dev/quarto-nvim/commit/f810c62842e9a8e9e2d44fa8a47196e7d36d79c4))
57 |
58 | ## [1.0.2](https://github.com/quarto-dev/quarto-nvim/compare/v1.0.1...v1.0.2) (2024-10-09)
59 |
60 |
61 | ### Bug Fixes
62 |
63 | * register markdown grammar for quarto and rmd until our own grammar is ready (https://github.com/jmbuhr/tree-sitter-pandoc-markdown) refs https://github.com/jmbuhr/otter.nvim/issues/179 ([1d10f13](https://github.com/quarto-dev/quarto-nvim/commit/1d10f135937613ca71a84975fe75b5b76a35065e))
64 |
65 | ## [1.0.1](https://github.com/quarto-dev/quarto-nvim/compare/v1.0.0...v1.0.1) (2024-06-30)
66 |
67 |
68 | ### Bug Fixes
69 |
70 | * use renamed table from otter.nvim. fixes [#137](https://github.com/quarto-dev/quarto-nvim/issues/137). ([c873128](https://github.com/quarto-dev/quarto-nvim/commit/c87312856469c8aaa30b3097a1280eea58ab1902))
71 |
72 | ## [1.0.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.18.2...v1.0.0) (2024-06-29)
73 |
74 |
75 | ### ⚠ BREAKING CHANGES
76 |
77 | * remove the need for custom otter.nvim keybindings ([#135](https://github.com/quarto-dev/quarto-nvim/issues/135))
78 |
79 | ### Features
80 |
81 | * remove the need for custom otter.nvim keybindings ([#135](https://github.com/quarto-dev/quarto-nvim/issues/135)) ([1665721](https://github.com/quarto-dev/quarto-nvim/commit/1665721f7ba16671f519f3cd87382bc28258af04))
82 |
83 | ## [0.18.2](https://github.com/quarto-dev/quarto-nvim/compare/v0.18.1...v0.18.2) (2024-02-19)
84 |
85 |
86 | ### Bug Fixes
87 |
88 | * only require otter when activating ([5336b86](https://github.com/quarto-dev/quarto-nvim/commit/5336b86dc3d0517075debe8906671daeeab9f5ed))
89 |
90 | ## [0.18.1](https://github.com/quarto-dev/quarto-nvim/compare/v0.18.0...v0.18.1) (2024-02-19)
91 |
92 |
93 | ### Bug Fixes
94 |
95 | * make disabling keybindings easier. refs [#116](https://github.com/quarto-dev/quarto-nvim/issues/116) ([1b15dd1](https://github.com/quarto-dev/quarto-nvim/commit/1b15dd175b974cb8c83b022f68cc07c02c9c465b))
96 | * QuartoHover user function ([#113](https://github.com/quarto-dev/quarto-nvim/issues/113)) ([a4760c0](https://github.com/quarto-dev/quarto-nvim/commit/a4760c0b275972bc8ef577f7521771d17cb0cd17))
97 |
98 | ## [0.18.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.17.0...v0.18.0) (2023-11-29)
99 |
100 |
101 | ### Features
102 |
103 | * configurable code runner with molten-nvim integration ([#99](https://github.com/quarto-dev/quarto-nvim/issues/99)) ([eacd8ff](https://github.com/quarto-dev/quarto-nvim/commit/eacd8ff211923c1b11a021ae6291bc34d9472948))
104 |
105 |
106 | ### Bug Fixes
107 |
108 | * get current bufnr in ftplugin instead of relying on 0 ([af34813](https://github.com/quarto-dev/quarto-nvim/commit/af3481378ba7b664499fd1bbb9ae5fd6612d04fc))
109 | * readme configruation ([#103](https://github.com/quarto-dev/quarto-nvim/issues/103)) ([1fe0f16](https://github.com/quarto-dev/quarto-nvim/commit/1fe0f163c42efdddb4d8b9ac8ac0e55eb20ff17c))
110 | * user commands giving bad args to run funcs ([#101](https://github.com/quarto-dev/quarto-nvim/issues/101)) ([68ac6c0](https://github.com/quarto-dev/quarto-nvim/commit/68ac6c0500bcd0f3e978bd16c7d56e93ee8928da))
111 |
112 | ## [0.17.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.16.0...v0.17.0) (2023-09-08)
113 |
114 |
115 | ### Features
116 |
117 | * pass arguments to quarto preview ([#88](https://github.com/quarto-dev/quarto-nvim/issues/88)) ([bad6f70](https://github.com/quarto-dev/quarto-nvim/commit/bad6f70269bcaf063513782c085aa2295ed3af25))
118 |
119 | ## [0.16.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.15.1...v0.16.0) (2023-08-27)
120 |
121 |
122 | ### Features
123 |
124 | * format current code chunk ([4ba80ce](https://github.com/quarto-dev/quarto-nvim/commit/4ba80ce2ba73811228df88f4aa5294f528912417))
125 |
126 | ## [0.15.1](https://github.com/quarto-dev/quarto-nvim/compare/v0.15.0...v0.15.1) (2023-07-17)
127 |
128 |
129 | ### Bug Fixes
130 |
131 | * clarify autcommand usage for setting keymaps ([d076de2](https://github.com/quarto-dev/quarto-nvim/commit/d076de2a43ad6b856b64da29dfa89cc1f6fba3f1))
132 |
133 | ## [0.15.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.14.1...v0.15.0) (2023-07-16)
134 |
135 |
136 | ### Features
137 |
138 | * trigger release! ([fa9cc99](https://github.com/quarto-dev/quarto-nvim/commit/fa9cc994c4d76fa1e72778f5857cb8038451499f))
139 |
140 | ## [0.14.1](https://github.com/quarto-dev/quarto-nvim/compare/v0.14.0...v0.14.1) (2023-07-05)
141 |
142 |
143 | ### Bug Fixes
144 |
145 | * fix [#76](https://github.com/quarto-dev/quarto-nvim/issues/76). Stick with vim.loop until stable release of nvim 0.10 (then it's vim.uv) ([aa42597](https://github.com/quarto-dev/quarto-nvim/commit/aa4259729e8b0878be8e06e98f601569059284b9))
146 |
147 | ## [0.14.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.13.2...v0.14.0) (2023-06-28)
148 |
149 |
150 | ### Features
151 |
152 | * **lsp:** keybindings for ask_type_definition and ask_document_symbol ([2fd6169](https://github.com/quarto-dev/quarto-nvim/commit/2fd616956e65c9073d043eb631e251da6faa3404))
153 |
154 |
155 | ### Bug Fixes
156 |
157 | * replace deprecated vim.loop with vim.uv ([1f043c8](https://github.com/quarto-dev/quarto-nvim/commit/1f043c81ec9e75046a6e1f315561e6333656d5c7))
158 |
159 |
160 | ### Performance Improvements
161 |
162 | * put quarto init in ftplugin instea of autocommand ([1f2ccef](https://github.com/quarto-dev/quarto-nvim/commit/1f2ccefc22d3cad64bd10782b1670d8b6835cf1e))
163 |
164 | ## [0.13.2](https://github.com/quarto-dev/quarto-nvim/compare/v0.13.1...v0.13.2) (2023-06-21)
165 |
166 |
167 | ### Performance Improvements
168 |
169 | * don't register quarto->markdown treesitter ft ([9f02823](https://github.com/quarto-dev/quarto-nvim/commit/9f02823d7b38b2e9c578bac085c430f14b74df3b))
170 |
171 | ## [0.13.1](https://github.com/quarto-dev/quarto-nvim/compare/v0.13.0...v0.13.1) (2023-06-06)
172 |
173 |
174 | ### Bug Fixes
175 |
176 | * slime-sending. ([795133e](https://github.com/quarto-dev/quarto-nvim/commit/795133eaa3ee9995674d81f8718623f5aaf03bca))
177 |
178 | ## [0.13.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.12.0...v0.13.0) (2023-06-06)
179 |
180 |
181 | ### Features
182 |
183 | * enable completion for html also if only activating `curly` chunks ([27ac79f](https://github.com/quarto-dev/quarto-nvim/commit/27ac79fb897cee6452d05711241ff6318cd25a9d))
184 |
185 | ## [0.12.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.11.0...v0.12.0) (2023-06-03)
186 |
187 |
188 | ### Features
189 |
190 | * add html support through otter.nvim update (injections) ([f9fbdab](https://github.com/quarto-dev/quarto-nvim/commit/f9fbdab68d4af02733e1b983f494ecd56e8f1050))
191 |
192 | ## [0.11.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.10.1...v0.11.0) (2023-05-26)
193 |
194 |
195 | ### Features
196 |
197 | * trigger release for otter.nvim rename and references ([2c013ae](https://github.com/quarto-dev/quarto-nvim/commit/2c013ae7f05554a78d9cb956ec73444513f336bf))
198 |
199 | ## [0.10.1](https://github.com/quarto-dev/quarto-nvim/compare/v0.10.0...v0.10.1) (2023-05-20)
200 |
201 |
202 | ### Bug Fixes
203 |
204 | * remove duplicate queries (closes [#58](https://github.com/quarto-dev/quarto-nvim/issues/58)) ([9306dcc](https://github.com/quarto-dev/quarto-nvim/commit/9306dcc7272655e46712a26a15c65d801b8b7b2e))
205 |
206 | ## [0.10.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.9.0...v0.10.0) (2023-05-06)
207 |
208 |
209 | ### Features
210 |
211 | * QuartoSendBelow and QuartoSendRange ([7226eee](https://github.com/quarto-dev/quarto-nvim/commit/7226eeecd42182c0051a0959983e15e9a4e0b939))
212 |
213 |
214 | ### Performance Improvements
215 |
216 | * **diagnostics:** initialize diagnostic namespaces once and save ids ([f60eb6a](https://github.com/quarto-dev/quarto-nvim/commit/f60eb6a877c17af8c92490e96148172463b68627))
217 |
218 | ## [0.9.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.8.1...v0.9.0) (2023-04-20)
219 |
220 |
221 | ### Features
222 |
223 | * QuartoSendAbove and QuartoSendAll commands to send code to ([cb2bb7d](https://github.com/quarto-dev/quarto-nvim/commit/cb2bb7d47f02b5abfa60fa80d24fe4b4b9120d92))
224 |
225 | ## [0.8.1](https://github.com/quarto-dev/quarto-nvim/compare/v0.8.0...v0.8.1) (2023-04-20)
226 |
227 |
228 | ### Bug Fixes
229 |
230 | * quarto preview in Windows/PowerShell ([#53](https://github.com/quarto-dev/quarto-nvim/issues/53)) ([8980f73](https://github.com/quarto-dev/quarto-nvim/commit/8980f739045867b2c59612e380ecb32dbf3df803))
231 |
232 | ## [0.8.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.7.3...v0.8.0) (2023-04-09)
233 |
234 |
235 | ### Features
236 |
237 | * nvim 0.9.0! ([#50](https://github.com/quarto-dev/quarto-nvim/issues/50)) ([ac9bafe](https://github.com/quarto-dev/quarto-nvim/commit/ac9bafe821aecfa7e3071d5b6e936588e0deff4c))
238 |
239 | ## [0.7.3](https://github.com/quarto-dev/quarto-nvim/compare/v0.7.2...v0.7.3) (2023-04-04)
240 |
241 |
242 | ### Bug Fixes
243 |
244 | * activate quarto only once per buffer ([9de52c8](https://github.com/quarto-dev/quarto-nvim/commit/9de52c85423fbc218f7324be4af662c32aee3da9))
245 | * remove debug statement ([a9f9f98](https://github.com/quarto-dev/quarto-nvim/commit/a9f9f98da951ee7146d519ddc624013e6bdcd6aa))
246 |
247 | ## [0.7.2](https://github.com/quarto-dev/quarto-nvim/compare/v0.7.1...v0.7.2) (2023-03-11)
248 |
249 |
250 | ### Bug Fixes
251 |
252 | * fix package name for release ([96f741c](https://github.com/quarto-dev/quarto-nvim/commit/96f741cd04dd769e9ce1c1aaa913ee6296594a47))
253 |
254 | ## [0.7.1](https://github.com/quarto-dev/quarto-nvim/compare/v0.7.0...v0.7.1) (2023-03-11)
255 |
256 |
257 | ### Bug Fixes
258 |
259 | * format and trigger release ([9e2adb0](https://github.com/quarto-dev/quarto-nvim/commit/9e2adb0e93e2d3c7ae1ce0471bcd8113faa03521))
260 | * just use otter.ask_hover ([31ba845](https://github.com/quarto-dev/quarto-nvim/commit/31ba845274e2a1f77dd5ebe2890e182856776a15))
261 |
262 | ## [0.7.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.6.0...v0.7.0) (2023-02-15)
263 |
264 |
265 | ### Features
266 |
267 | * use quarto filetype instead of markdown ([#26](https://github.com/quarto-dev/quarto-nvim/issues/26)) ([449e877](https://github.com/quarto-dev/quarto-nvim/commit/449e877005d544dc931be36177728482aec49a03))
268 |
269 | ## [0.6.0](https://github.com/quarto-dev/quarto-nvim/compare/v0.5.3...v0.6.0) (2023-01-25)
270 |
271 |
272 | ### Features
273 |
274 | * add group for qmd open autocmd. ([#27](https://github.com/quarto-dev/quarto-nvim/issues/27)) ([467da36](https://github.com/quarto-dev/quarto-nvim/commit/467da365225d9606e074cdb8eb7cb3e520ecc270))
275 |
--------------------------------------------------------------------------------