├── LICENSE ├── README.md ├── lua └── rnoweb-nvim │ ├── dbug.lua │ ├── helpers.lua │ ├── info.lua │ ├── init.lua │ ├── node_hooks.lua │ ├── super_list.lua │ └── symbols.lua ├── plugin └── init.lua └── readme_media ├── main.Rnw ├── off.png └── on.png /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Brian Albert Monroe 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rnoweb-nvim 2 | 3 | A Neovim plugin to conceal commands in Rnoweb and LaTeX documents using Treesitter and Extmarks. 4 | 5 | The plugin aims to be substantially quicker than an equivalent using regular 6 | regex concealing (e.g. with the illustrious 7 | [VimTex](https://github.com/lervag/vimtex) plugin). 8 | 9 | This plugin also aims to provide niceties to Rnoweb documents (the main reason 10 | I created it). In particular, inline code segments are replaced in-document 11 | provided that their results are included in a `inline` directory. 12 | 13 | ### Goals 14 | - Be fast. This means using treesitter to do the heavy lifting 15 | - Multi-character conceal 16 | - Work with Rnoweb or stand-alone Latex 17 | - Conceal latex symbols and provide interface for users to specify their own conceals 18 | 19 | ### Current Functionality 20 | - Basic citation conceal 21 | - Inline code substitution for rnoweb 22 | - Equation numbering 23 | - Figure numbering 24 | - Footnote numbering 25 | - Diacritic replacement 26 | - Subscript/superscripts 27 | - Replacement of in document text macros (`\newcommand`'s where the result of the command is only text) 28 | - Lots of pre-defined conceals for common commands 29 | 30 | ### Improvements 31 | - Citation conceal with bibliography lookup and hopefully the anti-conceal functionality. Not doing this before anti-conceal is mainlined. 32 | - Constant improvements in equation and environment numbering 33 | - General environment numbering 34 | 35 | # Installation and User Configuration 36 | 37 | The following should work with packer. Note that I also configure some 38 | user-specific replacements that appear in the following screenshots. There 39 | aren't necessary for the plugin to work out of the box. 40 | 41 | ```{lua} 42 | 43 | use { 'bamonroe/rnoweb-nvim', 44 | requires = { 45 | 'nvim-lua/plenary.nvim' 46 | }, 47 | ft = {'rnoweb', "latex", "tex"}, 48 | dependencies = { 49 | 'nvim-lua/plenary.nvim' 50 | }, 51 | config = function() 52 | require('rnoweb-nvim').setup() 53 | 54 | -- Set some of my own symbols that are likely not in anyone else's docs 55 | local sym = require('rnoweb-nvim.symbols') 56 | sym.set_sym("latex", "\\gi", {"g⁻¹"}) 57 | sym.set_sym("latex", "\\@", {""}) 58 | sym.set_sym("latex", '\\CE', {"CE"}) 59 | sym.set_sym("latex", '\\CS', {"ECS"}) 60 | sym.set_sym("latex", '\\Pr', {"Pr"}) 61 | sym.set_sym("latex", '\\pr', {"Pr(", ")"}) 62 | sym.set_sym("latex", "\\email", {"✉ :", ""}) 63 | sym.set_sym("latex", "\\gbar", {"(", " ︳", ")"}) 64 | sym.set_sym("latex", "\\gbar*", {"", " ︳", ""}) 65 | 66 | end 67 | } 68 | 69 | ``` 70 | 71 | The user-configuration function `set_sym` takes as first argument the name of 72 | the language that tree-sitter will be using (basically always latex), and then 73 | the command to be concealed, and finally a table with up to n + 1 entries where 74 | n is the number of arguments for the command. 75 | 76 | The first entry will replace the beginning of the command up to the first 77 | brace, the second entry will replace the first closing argument brace and (if 78 | applicable) the second opening brace, the third argument will replace the 79 | second closing brace and (if applicable) the third opening brace, etc. Thus, 80 | note that fields 2+ can only be of length 2 or less. 81 | 82 | 83 | # Screenshots 84 | 85 | Some example LaTeX without the plugin: 86 | 87 | ![Without Plugin](readme_media/off.png) 88 | 89 | With the plugin, note numbered equations, sub and superscripts, numbered 90 | footnotes, citation concealment, and inline replacement of `\newcommand` that 91 | defines a text-only replacement: 92 | ![With Plugin](readme_media/on.png) 93 | -------------------------------------------------------------------------------- /lua/rnoweb-nvim/dbug.lua: -------------------------------------------------------------------------------- 1 | M = {} 2 | 3 | M.print = function(line) 4 | 5 | local fp = io.open("/tmp/dbug", "a") 6 | 7 | if fp ~= nil then 8 | line = vim.inspect(line) 9 | fp:write(line .. ":nl:") 10 | fp:flush() 11 | fp:close() 12 | end 13 | end 14 | 15 | return M 16 | -------------------------------------------------------------------------------- /lua/rnoweb-nvim/helpers.lua: -------------------------------------------------------------------------------- 1 | local ts = vim.treesitter 2 | local info = require'rnoweb-nvim.info' 3 | local db = require'rnoweb-nvim.dbug' 4 | 5 | local M = {} 6 | 7 | -- A debugging function that writes to a file 8 | M.db = function (line) 9 | line = vim.inspect(line) 10 | local file = "/tmp/dbug" 11 | local fp = io.open(file, "a") 12 | fp:write(line, "\n") 13 | fp:flush() 14 | fp:close() 15 | end 16 | 17 | M.split = function(pString, pPattern) 18 | local Table = {} -- NOTE: use {n = 0} in Lua-5.0 19 | local fpat = "(.-)" .. pPattern 20 | local last_end = 1 21 | local s, e, cap = pString:find(fpat, 1) 22 | while s do 23 | if s ~= 1 or cap ~= "" then 24 | table.insert(Table,cap) 25 | end 26 | last_end = e+1 27 | s, e, cap = pString:find(fpat, last_end) 28 | end 29 | if last_end <= #pString then 30 | cap = pString:sub(last_end) 31 | table.insert(Table, cap) 32 | end 33 | return Table 34 | end 35 | 36 | local add_front = function(s, n) 37 | for _=1,n do 38 | s = " " .. s 39 | end 40 | return s 41 | end 42 | 43 | local add_back = function(s, n) 44 | for _=1,n do 45 | s = s .." " 46 | end 47 | return s 48 | end 49 | 50 | M.center_pad = function(s, a) 51 | local famt = math.floor(a / 2) 52 | local bamt = math.ceil(a / 2) 53 | s = add_front(s, famt) 54 | s = add_back(s, bamt) 55 | return s 56 | end 57 | -- 58 | -- Function to get length of string in charachter count, not byte count 59 | M.slen = function(s) 60 | if s == nil then return(0) end 61 | local _, count = string.gsub(s, "[^\128-\193]", "") 62 | return count 63 | end 64 | 65 | M.tlen = function(t) 66 | local n = 0 67 | for _, _ in pairs(t) do 68 | n = n + 1 69 | end 70 | return n 71 | end 72 | 73 | M.ncols = function(lnum) 74 | local line = vim.api.nvim_buf_get_lines(info.bufnr, lnum, lnum + 1, true)[1] 75 | local tabstop = vim.api.nvim_get_option("tabstop") 76 | line = line:gsub("\t", string.rep(" ", tabstop)) 77 | return M.slen(line) 78 | end 79 | 80 | M.gmatch = function(s) 81 | local out = {} 82 | if s == nil then return out end 83 | for c in s:gmatch("[%z\1-\127\194-\244][\128-\191]*") do 84 | out[#out+1] = c 85 | end 86 | return out 87 | end 88 | 89 | M.isin = function(v, t) 90 | local res = false 91 | for _, val in pairs(t) do 92 | if not res then 93 | res = v == val 94 | end 95 | end 96 | return res 97 | end 98 | 99 | -- Check if a file exists 100 | M.file_exists = function(file) 101 | local f = io.open(file, "rb") 102 | if f then f:close() end 103 | return f ~= nil 104 | end 105 | 106 | -- Read lines from file into a table 107 | M.read_lines = function(file) 108 | if not M.file_exists(file) then return {} end 109 | local lines = {} 110 | for line in io.lines(file) do 111 | lines[#lines + 1] = line 112 | end 113 | return lines 114 | end 115 | 116 | M.write_lines = function(file, lines) 117 | -- Get rid of the words file if it exists 118 | if M.file_exists(file) then 119 | os.remove(file) 120 | end 121 | 122 | local fp = io.open(file, "a") 123 | for k,_ in pairs(lines) do 124 | fp:write(k, "\n") 125 | end 126 | fp:close() 127 | 128 | end 129 | 130 | M.gtext = function(node) 131 | return ts.get_node_text(node, info.bufnr) 132 | end 133 | 134 | M.mc_conceal = function(bufnr, ns, beg_line, beg_col, opts, node_len) 135 | 136 | 137 | local conceal_text = opts["conceal"] 138 | local conceal_len = M.slen(conceal_text) 139 | local conceal_utf8 = M.gmatch(conceal_text) 140 | 141 | opts["hl_group"] = opts["hl_group"] and opts["hl_group"] or "Conceal" 142 | 143 | local padding = node_len - conceal_len 144 | 145 | -- Firstly, conceal the padding 146 | local nopts = { 147 | end_line = opts["end_line"], 148 | end_col = beg_col + padding, 149 | virt_text = {{'', opts["hl_group"]}}, 150 | virt_text_pos = "overlay", 151 | virt_text_hide = true, 152 | conceal = '', 153 | -- hl_group = opts["hl_group"], 154 | } 155 | 156 | if padding > -1 then 157 | info.ids[#info.ids+1] = vim.api.nvim_buf_set_extmark( 158 | bufnr, 159 | ns, 160 | beg_line, 161 | beg_col, 162 | nopts) 163 | end 164 | 165 | local end_line = opts["end_line"] or beg_line 166 | 167 | for i = 1,conceal_len do 168 | local nbeg_col = beg_col + padding + i - 1 169 | local end_col = beg_col + padding + i 170 | 171 | local cchar = conceal_utf8[i] 172 | local vtext = '' 173 | local pos = "overlay" 174 | 175 | nopts = { 176 | end_line = end_line, 177 | end_col = end_col, 178 | virt_text = {{vtext, opts["hl_group"]}}, 179 | virt_text_pos = pos, 180 | virt_text_hide = true, 181 | hl_group = opts["hl_group"], 182 | conceal = cchar, 183 | strict = false, 184 | } 185 | 186 | info.ids[#info.ids+1] = vim.api.nvim_buf_set_extmark( 187 | bufnr, 188 | ns, 189 | beg_line, 190 | nbeg_col, 191 | nopts) 192 | end 193 | end 194 | 195 | M.in_tablev= function(v, t) 196 | for _,i in pairs(t) do 197 | if i == v then 198 | return(true) 199 | end 200 | end 201 | return false 202 | end 203 | 204 | M.in_tablek= function(v, t) 205 | for k,_ in pairs(t) do 206 | if k == v then 207 | return(true) 208 | end 209 | end 210 | return false 211 | end 212 | 213 | return M 214 | -------------------------------------------------------------------------------- /lua/rnoweb-nvim/info.lua: -------------------------------------------------------------------------------- 1 | local M = { 2 | iset = false 3 | } 4 | 5 | M.reset = function() 6 | M.ids = {} 7 | M.lab_numbers = {} 8 | M.counts = { 9 | footnote = 0, 10 | figures = 0, 11 | sections = 0, 12 | equations = 0, 13 | } 14 | end 15 | 16 | M.set_info = function() 17 | if M.iset == false then 18 | local bn = vim.api.nvim_get_current_buf() 19 | M.ft = vim.bo.filetype 20 | M.ns = vim.api.nvim_create_namespace("rnoweb-nvim") 21 | M.bufnr = bn 22 | M.iset = true 23 | M.reset() 24 | end 25 | end 26 | 27 | return M 28 | -------------------------------------------------------------------------------- /lua/rnoweb-nvim/init.lua: -------------------------------------------------------------------------------- 1 | local info = require'rnoweb-nvim.info' 2 | local sym = require'rnoweb-nvim.symbols' 3 | local nh = require'rnoweb-nvim.node_hooks' 4 | local h = require'rnoweb-nvim.helpers' 5 | local q = vim.treesitter.query 6 | 7 | local M = {} 8 | M.opts = { 9 | filetypes = {"*.Rnw", "*.tex"}, 10 | tex2latex = true, 11 | set_conceal = true, 12 | setup = false, 13 | } 14 | M.auid = vim.api.nvim_create_augroup("rnoweb-nvim-pkg", { 15 | clear = true, 16 | }) 17 | 18 | M.tex2latex = function() 19 | if vim.bo.filetype == "tex" then 20 | vim.print("changing to latex") 21 | vim.bo.filetype = "latex" 22 | end 23 | end 24 | 25 | -- Initial setup function 26 | M.setup = function(opts) 27 | opts = opts and opts or M.opts 28 | for k,v in pairs(opts) do 29 | M.opts[k] = v 30 | end 31 | M.opts.setup = true 32 | 33 | if M.opts.set_conceal then 34 | vim.o.conceallevel = 2 35 | end 36 | 37 | -- Optionally force tex files to be recocnized as latex 38 | if M.opts.tex2latex then 39 | -- Change tex to latex 40 | vim.api.nvim_create_autocmd({"FileType"}, { 41 | group = M.auid, 42 | pattern = {"*.tex"}, 43 | callback = M.tex2latex 44 | }) 45 | end 46 | end 47 | 48 | -- This function deletes the extmarks that have been created by this plugin 49 | -- This is how marks are "refreshed" 50 | M.del_marks = function() 51 | local v = vim.api 52 | for _, val in pairs(info.ids) do 53 | v.nvim_buf_del_extmark(info.bufnr, info.ns, val) 54 | end 55 | -- We've deleted the marks, now clear the saved id numbers 56 | info.reset() 57 | end 58 | 59 | -- This function replaces inline R code with the results of that code 60 | -- not useful for stand-alone LaTeX 61 | M.mask_inline = function() 62 | 63 | -- Inline R code will only work with rnoweb queries 64 | if info.ft ~= "rnoweb" then 65 | return {} 66 | end 67 | 68 | -- Get the parser for this buffer 69 | local parser = vim.treesitter.get_parser(info.bufnr) 70 | local tree = parser:parse() 71 | local root = tree[1]:root() 72 | 73 | local inline = q.parse("rnoweb", "(rinline (renv_content) @inline_content)") 74 | 75 | local count = 0 76 | for _, match, _ in inline:iter_matches(root, info.bufnr) do 77 | for _, node in pairs(match) do 78 | -- Need to count matches to correctly get the code results 79 | count = count + 1 80 | -- Get the rane of this node 81 | local l0, c0, _, c1 = node:range() 82 | -- Get the text that will be in this ndoe 83 | local fname = "./inline/" .. count .. ".txt" 84 | local text = h.read_lines(fname)[1] 85 | -- Length of the space available (assuming on the same line) 86 | local clen = c1 - c0 87 | local ntext = h.gtext(node) 88 | 89 | text = text and text or ntext 90 | text = string.sub(text, 1, clen) 91 | 92 | local opts = { 93 | end_col = c1, 94 | virt_text_pos = "overlay", 95 | virt_text_hide = true, 96 | conceal = text 97 | } 98 | 99 | -- Multi-character conceal 100 | h.mc_conceal( 101 | info.bufnr, 102 | info.ns, 103 | l0, 104 | c0, 105 | opts, 106 | clen 107 | ) 108 | 109 | end 110 | end 111 | end 112 | 113 | -- This is the meaty function that does the latex concealing 114 | -- Works for rnoweb and latex filetypes 115 | M.mask_texsym = function() 116 | 117 | local parser = vim.treesitter.get_parser(info.bufnr) 118 | parser:for_each_tree(function(_, tree) 119 | local ttree = tree:parse() 120 | local root = ttree[1]:root() 121 | 122 | for _, d in sym.get_queries(root, info.bufnr) do 123 | local lang = d["lang"] 124 | local imatch = d["match"] 125 | local cmd = d["cmd"] 126 | 127 | for _, match, meta in imatch do 128 | -- We want to know if there's multiple matches to correctly parse the 129 | -- metadata per match 130 | local nmatches = h.tlen(match) 131 | for id, node in pairs(match) do 132 | -- Get the per-match metadata 133 | local mmeta = meta and meta or {} 134 | if nmatches > 0 then 135 | mmeta = meta[id] and meta[id] or {} 136 | end 137 | -- Ignore nodes marked with the "ignore" metadata 138 | if mmeta["ignore"] == nil or mmeta["ignore"] == "false" then 139 | nh[cmd](lang, node, mmeta) 140 | end 141 | end 142 | end 143 | end 144 | end) 145 | 146 | end 147 | 148 | -- This is the main function to call 149 | M.refresh = function() 150 | info.set_info() 151 | M.del_marks() 152 | M.mask_texsym() 153 | M.mask_inline() 154 | end 155 | 156 | --[[ 157 | M.test = function() 158 | vim.print('in test') 159 | local parser = vim.treesitter.get_parser(info.bufnr, "latex") 160 | local tree = parser:parse() 161 | local root = tree[1]:root() 162 | sym.get_inline_text_macros(root, info.bufnr) 163 | 164 | end 165 | --]] 166 | 167 | return M 168 | -------------------------------------------------------------------------------- /lua/rnoweb-nvim/node_hooks.lua: -------------------------------------------------------------------------------- 1 | local ts = vim.treesitter 2 | local q = vim.treesitter.query 3 | local sym = require'rnoweb-nvim.symbols' 4 | local h = require'rnoweb-nvim.helpers' 5 | local info = require'rnoweb-nvim.info' 6 | local ss = require'rnoweb-nvim.super_list' 7 | 8 | local M = {} 9 | 10 | -- For simple conceals of a node with a given bit of text 11 | local nconceal = function(n, tinfo, offsets, range) 12 | 13 | tinfo = tinfo["text"] and tinfo or {text = tinfo} 14 | local text = tinfo["text"] 15 | local higroup = tinfo["hi"] and tinfo["hi"] or "Conceal" 16 | 17 | -- Default offsets are 0 18 | local off = { 19 | bl = 0, 20 | el = 0, 21 | bc = 0, 22 | ec = 0, 23 | } 24 | 25 | -- Allow some adjustment 26 | if offsets ~= nil then 27 | for k, v in pairs(offsets) do 28 | off[k] = v 29 | end 30 | end 31 | 32 | -- Get the range of the curly gruoup and conceal it all 33 | if range == nil then 34 | range = {n:range()} 35 | end 36 | local beg_line = range[1] + off.bl 37 | local end_line = range[3] + off.el 38 | -- As well as the carat or underscore character in the previous node 39 | local beg_col = range[2] + off.bc 40 | local end_col = range[4] + off.ec 41 | 42 | local opts = { 43 | end_col = end_col, 44 | end_line = end_line, 45 | virt_text_pos = "overlay", 46 | virt_text_hide = true, 47 | conceal = text, 48 | hl_group = higroup, 49 | } 50 | 51 | local clen = end_col - beg_col 52 | h.mc_conceal( 53 | info.bufnr, 54 | info.ns, 55 | beg_line, 56 | beg_col, 57 | opts, 58 | clen 59 | ) 60 | 61 | end 62 | 63 | local author_year = function(a) 64 | local out = {} 65 | for _, v in pairs(a) do 66 | local author = v:match('^([^%d]+)%d+') 67 | local year = v:match('^[^%d]+(%d+)') 68 | out[#out+1] = { 69 | author = author, 70 | year = year, 71 | } 72 | end 73 | return(out) 74 | end 75 | 76 | local find_type 77 | find_type = function(n, type, recursive) 78 | 79 | if recursive == nil then 80 | recursive = false 81 | end 82 | 83 | local res = {} 84 | for child, _ in n:iter_children() do 85 | local val = child:type() 86 | if type == val then 87 | table.insert(res, child) 88 | end 89 | if child:child_count() > 0 and recursive then 90 | local nres = find_type(child, type, recursive) 91 | for _, nc in pairs(nres) do 92 | table.insert(res, nc) 93 | end 94 | end 95 | end 96 | return res 97 | end 98 | 99 | -- Format citations nicely 100 | M.citation = function(lang, node, meta) 101 | 102 | local l0, c0, _, c1 = node:range() 103 | 104 | local clen = c1 - c0 105 | local text = h.gtext(node) 106 | 107 | -- Is this a parencite? 108 | local is_paren = text:match("^\\parencite") and true or false 109 | 110 | -- The query for the author 111 | local kq = q.parse(lang, "(curly_group_text_list (text) @keys )") 112 | -- The query for any pre-notes 113 | local pq = q.parse(lang, "(brack_group (text) @prenote )") 114 | 115 | local keys = {} 116 | for _, v in kq:iter_captures(node, info.bufnr) do 117 | local k = h.gtext(v) 118 | keys[#keys+1] = k 119 | end 120 | 121 | keys = author_year(keys) 122 | 123 | -- Some citations will have a prenote 124 | local counter = 0 125 | for _, v in pq:iter_captures(node, info.bufnr) do 126 | counter = counter + 1 127 | keys[counter].pn = h.gtext(v) 128 | end 129 | 130 | for k, _ in pairs(keys) do 131 | keys[k].pn = keys[k].pn == nil and "" or ", p." .. keys[k].pn 132 | end 133 | 134 | local display = "" 135 | if is_paren then 136 | display = "(" 137 | local lkeys = #keys 138 | for _, v in pairs(keys) do 139 | lkeys = lkeys - 1 140 | display = display .. v["author"] .. " " .. v["year"] .. v["pn"] .. (lkeys > 0 and ", " or "") 141 | end 142 | display = display .. ")" 143 | else 144 | local lkeys = #keys 145 | for _, v in pairs(keys) do 146 | lkeys = lkeys - 1 147 | display = display .. v["author"] .. 148 | " (" .. v["year"] .. 149 | v["pn"] .. 150 | ")" .. (lkeys > 0 and ", " or "") 151 | end 152 | end 153 | 154 | local opts = { 155 | end_col = c1, 156 | hl_group = "@text.reference", 157 | virt_text_pos = "overlay", 158 | virt_text_hide = true, 159 | conceal = display 160 | } 161 | 162 | h.mc_conceal( 163 | info.bufnr, 164 | info.ns, 165 | l0, 166 | c0, 167 | opts, 168 | clen 169 | ) 170 | end 171 | 172 | -- This is the main concealing function 173 | local conceal_cmd_fn = function(lang, node, cmd_name) 174 | 175 | local field = "command" 176 | if lang == "rnoweb" then 177 | field = "Sexpr" 178 | end 179 | 180 | -- Full range of the node 181 | local node_range = {node:range()} 182 | -- Node for the command name 183 | local cmd_node = node:field(field) 184 | -- Rang of the command name 185 | local cmd_range = {cmd_node[1]:range()} 186 | -- Get the table of arg nodes 187 | local arg_nodes = node:field("arg") 188 | -- Number of argument groups 189 | local nargs = #arg_nodes 190 | 191 | -- This bit allows us to totally overwrite the command and all arguments if the "txt" field is present 192 | -- This will only happen if this cmd_name has an associated function to call to determine the text 193 | local text = sym.get_sym_text(lang, cmd_name, node) 194 | if text ~= nil then 195 | if text["txt"] ~= nil then 196 | cmd_range[2] = text["bcol"] 197 | cmd_range[4] = text["ecol"] 198 | text = text["txt"] 199 | end 200 | end 201 | 202 | local ntext = #text 203 | 204 | -- Get the table of ranges for args 205 | local arg_ranges = {} 206 | if nargs > 0 then 207 | for i = 1,nargs do 208 | arg_ranges[#arg_ranges+1] = {arg_nodes[i]:range()} 209 | end 210 | end 211 | 212 | -- We always start at the beginning of the main node 213 | local beg_line = node_range[1] 214 | local beg_col = node_range[2] 215 | 216 | local end_line 217 | local end_col 218 | 219 | if nargs == 0 or ntext == 1 then 220 | end_line = cmd_range[3] 221 | end_col = cmd_range[4] 222 | else 223 | end_line = arg_ranges[1][1] 224 | end_col = arg_ranges[1][2] + 1 225 | end 226 | 227 | local clen = end_col - beg_col 228 | 229 | -- Opening symbol 230 | local opts = { 231 | end_col = end_col, 232 | end_line = end_line, 233 | virt_text_pos = "overlay", 234 | virt_text_hide = true, 235 | conceal = text[1], 236 | hl_group = "@function" 237 | } 238 | 239 | h.mc_conceal( 240 | info.bufnr, 241 | info.ns, 242 | beg_line, 243 | beg_col, 244 | opts, 245 | clen 246 | ) 247 | 248 | if ntext == 1 then 249 | return(nil) 250 | end 251 | 252 | -- Loop through the args, applying conceals in order 253 | beg_line = node_range[1] 254 | beg_col = node_range[2] 255 | 256 | 257 | local ftext = text 258 | 259 | for i = 1,nargs do 260 | 261 | beg_line = arg_ranges[i][3] 262 | beg_col = arg_ranges[i][4] 263 | if i < nargs then 264 | end_line = arg_ranges[i][3] 265 | end_col = arg_ranges[i][4] + 1 266 | else 267 | beg_col = beg_col - 1 268 | end_line = arg_ranges[i][3] 269 | end_col = arg_ranges[i][4] 270 | end 271 | 272 | text = ftext[i + 1] 273 | 274 | -- Opening symbol 275 | opts = { 276 | end_col = end_col, 277 | end_line = end_line, 278 | virt_text_pos = "overlay", 279 | virt_text_hide = true, 280 | conceal = text, 281 | hl_group = "@function" 282 | } 283 | 284 | clen = end_col - beg_col 285 | 286 | h.mc_conceal( 287 | info.bufnr, 288 | info.ns, 289 | beg_line, 290 | beg_col, 291 | opts, 292 | clen 293 | ) 294 | end 295 | 296 | end 297 | 298 | M.conceal_cmd = function(lang, node, _) 299 | 300 | local field = "command" 301 | if lang == "rnoweb" then 302 | field = "Sexpr" 303 | end 304 | 305 | local cmd_node = node:field(field) 306 | cmd_node = cmd_node[1] 307 | if cmd_node == nil then return nil end 308 | 309 | local cmd_name = ts.get_node_text(cmd_node, info.bufnr) 310 | 311 | local text = sym.get_sym_text(lang, cmd_name, node) 312 | if text ~= nil then 313 | conceal_cmd_fn(lang, node, cmd_name) 314 | end 315 | end 316 | 317 | M.text_mode = function(_, node, _) 318 | 319 | local range = {node:range()} 320 | local end_col = {node:child(1):range()} 321 | end_col = end_col[2] 322 | 323 | -- 1 beg_line 324 | -- 2 beg_col 325 | -- 3 end_line 326 | -- 4 end_col 327 | 328 | range[3] = range[1] 329 | range[4] = end_col + 0 330 | 331 | nconceal(node, "", {}, range) 332 | end 333 | 334 | -- Math delimter function 335 | M.mdelimit = function(_, node, _) 336 | 337 | local nrange = {node:range()} 338 | 339 | local lchild = node:child(1) 340 | local lrange = {lchild:range()} 341 | local rchild = node:child(node:child_count() - 1) 342 | local rrange = {rchild:range()} 343 | 344 | local ldelim = h.gtext(lchild) 345 | local rdelim = h.gtext(rchild) 346 | 347 | local ldelim_d = sym.get_sym_text("latex", ldelim) 348 | local rdelim_d = sym.get_sym_text("latex", rdelim) 349 | 350 | ldelim = ldelim_d ~= nil and ldelim_d[1] or ldelim 351 | rdelim = rdelim_d ~= nil and rdelim_d[1] or rdelim 352 | 353 | local beg_line = nrange[1] 354 | local end_line = lrange[1] 355 | 356 | local beg_col = nrange[2] 357 | local end_col = lrange[4] 358 | 359 | local opts = { 360 | end_col = end_col, 361 | end_line = end_line, 362 | virt_text_pos = "overlay", 363 | virt_text_hide = true, 364 | conceal = ldelim 365 | } 366 | 367 | local clen = end_col - beg_col 368 | h.mc_conceal( 369 | info.bufnr, 370 | info.ns, 371 | beg_line, 372 | beg_col, 373 | opts, 374 | clen 375 | ) 376 | 377 | beg_line = rrange[1] 378 | end_line = nrange[3] 379 | 380 | beg_col = rrange[2] - 6 381 | end_col = nrange[4] 382 | 383 | opts = { 384 | end_col = end_col, 385 | end_line = end_line, 386 | virt_text_pos = "overlay", 387 | virt_text_hide = true, 388 | conceal = rdelim 389 | } 390 | 391 | clen = end_col - beg_col 392 | h.mc_conceal( 393 | info.bufnr, 394 | info.ns, 395 | beg_line, 396 | beg_col, 397 | opts, 398 | clen 399 | ) 400 | 401 | end 402 | 403 | M.subsuper = function(_, node, meta) 404 | 405 | -- First we need to check the node's parents to see if we encounter a label_definition 406 | local parent = node:parent() 407 | local type = parent:type() 408 | local check = false 409 | while type ~= "source_file" do 410 | type = parent:type() 411 | if type == "label_definition" then 412 | return(nil) 413 | elseif type == "math_environment" then 414 | check = true 415 | break 416 | end 417 | parent = parent:parent() 418 | end 419 | 420 | -- Are we in some kind of math_environment? 421 | if not check then 422 | return(nil) 423 | end 424 | 425 | 426 | -- OK so we're actually in a math_environment 427 | -- Sub or super? 428 | local kind = meta["kind"] 429 | 430 | local beg_line, beg_col, end_line, end_col = node:range() 431 | -- I want to conceal the _ or ^ as well 432 | beg_col = beg_col - 1 433 | 434 | -- We need to substitute the text with the unicode sub/super characters 435 | local text = ts.get_node_text(node, 0) 436 | local tlen = #text 437 | 438 | local out = "" 439 | 440 | -- If the string is one character long, its not a curly group 441 | if tlen == 1 then 442 | out = ss[kind][text] == nil and text or ss[kind][text] 443 | else 444 | for i = 2, (tlen - 1) do 445 | local c = string.sub(text, i, i) 446 | c = ss[kind][c] == nil and c or ss[kind][c] 447 | out = out .. c 448 | end 449 | end 450 | 451 | -- Opening symbol 452 | local opts = { 453 | end_col = end_col, 454 | end_line = end_line, 455 | virt_text_pos = "overlay", 456 | virt_text_hide = true, 457 | conceal = out, 458 | hl_group = "@function" 459 | } 460 | 461 | local clen = end_col - beg_col 462 | h.mc_conceal( 463 | info.bufnr, 464 | info.ns, 465 | beg_line, 466 | beg_col, 467 | opts, 468 | clen 469 | ) 470 | 471 | end 472 | 473 | M.footnote = function(_, node, _) 474 | 475 | info.counts.footnote = info.counts.footnote + 1 476 | 477 | local text = "" .. info.counts.footnote .. "" 478 | local kind = "superscript" 479 | local res = "" 480 | for letter in text:gmatch(".") do 481 | -- If the symbol is in the table, use it, otherwise go back to original 482 | letter = ss[kind][letter] and ss[kind][letter] or letter 483 | res = res .. letter 484 | end 485 | 486 | nconceal(node, res) 487 | 488 | end 489 | 490 | M.fig_lab_count = function(_, node, _) 491 | local lab = h.gtext(node) 492 | info.counts.figures = info.counts.figures + 1 493 | info.lab_numbers[lab] = info.counts.figures 494 | end 495 | 496 | M.section_count = function(_, node, _) 497 | info.counts.sections = info.counts.sections + 1 498 | local sec = info.counts.sections 499 | for c, _ in node:iter_children() do 500 | local type = c:type() 501 | if type == "label_definition" then 502 | local lab = c:field("name")[1]:child(1) 503 | lab = h.gtext(lab) 504 | info.lab_numbers[lab] = sec 505 | end 506 | end 507 | end 508 | 509 | --------- 510 | -- Some helper functions for math count 511 | -------- 512 | 513 | -- Check if the node is the command "\\" 514 | local line_end_check = function(n) 515 | local res = false 516 | if n:type() == "generic_command" then 517 | local cmd = n:field("command")[1] 518 | local v = h.gtext(cmd) 519 | -- This was annoying, but the only way I could actuall test for a line 520 | -- break command 521 | local j = "\\\\" 522 | res = v == j 523 | end 524 | return res 525 | end 526 | 527 | -- Increment the equation number in the main table, and collect the result in 528 | -- the local table 529 | local inc_eqs = function(eqs) 530 | info.counts.equations = info.counts.equations + 1 531 | table.insert(eqs, info.counts.equations) 532 | return eqs 533 | end 534 | 535 | -- Add the label to label counter 536 | local add_lab = function(set, lab) 537 | info.lab_numbers[lab] = info.counts[set] 538 | end 539 | 540 | -- Get the label from a label node 541 | local get_lab = function(n) 542 | return h.gtext(n:field("name")[1]:child(1)) 543 | end 544 | 545 | M.math_count = function(_, node, _) 546 | 547 | -- What kind of math environment 548 | local beg = node:field("begin")[1] 549 | local type = h.gtext(beg:field("name")[1]:child(1)) 550 | local eqs = {} 551 | 552 | -- Align environments are kinda tricky 553 | if type == "align" then 554 | eqs = inc_eqs(eqs) 555 | 556 | -- Get past the begin environment 557 | local ctx = node:child(1) 558 | 559 | -- If the first child is a label definition, it gets assigned now, 560 | -- all subsequent label definitions are handled in the below loop 561 | if ctx:type() == "label_definition" then 562 | local lab = get_lab(ctx) 563 | add_lab("equations", lab) 564 | ctx = ctx:next_sibling() 565 | end 566 | 567 | -- If we hit a line break, we increment the equation number, 568 | -- if we hit a label definition, we assigned the label 569 | while true do 570 | 571 | -- If at any point you have consequtive equations in an align 572 | -- environment, they all get subsumed under a parent "text" node 573 | if ctx:type() == "text" then 574 | for c, _ in ctx:iter_children() do 575 | if line_end_check(c) then 576 | eqs = inc_eqs(eqs) 577 | elseif c:type() == "label_definition" then 578 | local lab = get_lab(c) 579 | add_lab("equations", lab) 580 | end 581 | end 582 | -- But you can have environments in an align node too, e.g. a split environment 583 | -- So you need to cycle through siblings and increment as needed 584 | elseif ctx:type() == "label_definition" then 585 | local lab = get_lab(ctx) 586 | add_lab("equations", lab) 587 | elseif line_end_check(ctx) then 588 | eqs = inc_eqs(eqs) 589 | elseif ctx:type() == "end" then 590 | break 591 | 592 | end 593 | 594 | ctx = ctx:next_sibling() 595 | end 596 | 597 | -- Equation environments are much easier 598 | elseif type == "equation" then 599 | eqs = inc_eqs(eqs) 600 | local ctx = find_type(node, "label_definition", true)[1] 601 | if ctx ~= nil then 602 | local lab = get_lab(ctx) 603 | add_lab("equations", lab) 604 | end 605 | end 606 | 607 | local neqs = h.tlen(eqs) 608 | -- Virtual text to apply next to the equation environments indicating 609 | -- equation numbers 610 | local text 611 | if neqs == 1 then 612 | text = "Equation " .. eqs[1] 613 | elseif neqs > 1 then 614 | text = "Equations " 615 | for _, v in pairs(eqs) do 616 | text = text .. v .. ", " 617 | end 618 | text = text:sub(1, -3) 619 | else 620 | return(nil) 621 | end 622 | text = " " .. text 623 | 624 | local range = {node:range()} 625 | local beg_line = range[1] 626 | local beg_col = range[2] 627 | local end_line = range[3] 628 | 629 | local nopts = { 630 | end_row = end_line, 631 | virt_text = {{text, "Conceal"}}, 632 | virt_text_pos = "eol", 633 | virt_text_hide = true, 634 | } 635 | 636 | info.ids[#info.ids+1] = vim.api.nvim_buf_set_extmark( 637 | info.bufnr, 638 | info.ns, 639 | beg_line, 640 | beg_col, 641 | nopts) 642 | 643 | end 644 | 645 | M.ref = function(_, node, _) 646 | 647 | -- Get the "names" of the reference (I'm assuming 1 arg references right 648 | -- now), and the first child will disclude the surrounding braces 649 | local lab = node:field("names")[1]:child(1) 650 | -- This is the reference 651 | lab = h.gtext(lab) 652 | 653 | -- See if its in the table 654 | local num = info.lab_numbers[lab] 655 | num = num and num or lab 656 | 657 | local text = "" .. num .. "" 658 | 659 | local tinfo = { 660 | text = text, 661 | hi = "@text.reference" 662 | } 663 | 664 | nconceal(node, tinfo) 665 | 666 | end 667 | 668 | return M 669 | -------------------------------------------------------------------------------- /lua/rnoweb-nvim/super_list.lua: -------------------------------------------------------------------------------- 1 | local M = {superscript = {}, subscript = {}, diacritics = {}} 2 | M.superscript[" "] = '' 3 | M.superscript["0"] = '⁰' 4 | M.superscript["1"] = '¹' 5 | M.superscript["2"] = '²' 6 | M.superscript["3"] = '³' 7 | M.superscript["4"] = '⁴' 8 | M.superscript["5"] = '⁵' 9 | M.superscript["6"] = '⁶' 10 | M.superscript["7"] = '⁷' 11 | M.superscript["8"] = '⁸' 12 | M.superscript["9"] = '⁹' 13 | M.superscript["a"] = 'ᵃ' 14 | M.superscript["b"] = 'ᵇ' 15 | M.superscript["c"] = 'ᶜ' 16 | M.superscript["d"] = 'ᵈ' 17 | M.superscript["e"] = 'ᵉ' 18 | M.superscript["f"] = 'ᶠ' 19 | M.superscript["g"] = 'ᵍ' 20 | M.superscript["h"] = 'ʰ' 21 | M.superscript["i"] = 'ⁱ' 22 | M.superscript["j"] = 'ʲ' 23 | M.superscript["k"] = 'ᵏ' 24 | M.superscript["l"] = 'ˡ' 25 | M.superscript["m"] = 'ᵐ' 26 | M.superscript["n"] = 'ⁿ' 27 | M.superscript["o"] = 'ᵒ' 28 | M.superscript["p"] = 'ᵖ' 29 | M.superscript["r"] = 'ʳ' 30 | M.superscript["s"] = 'ˢ' 31 | M.superscript["t"] = 'ᵗ' 32 | M.superscript["u"] = 'ᵘ' 33 | M.superscript["v"] = 'ᵛ' 34 | M.superscript["w"] = 'ʷ' 35 | M.superscript["x"] = 'ˣ' 36 | M.superscript["y"] = 'ʸ' 37 | M.superscript["z"] = 'ᶻ' 38 | M.superscript["A"] = 'ᴬ' 39 | M.superscript["B"] = 'ᴮ' 40 | M.superscript["D"] = 'ᴰ' 41 | M.superscript["E"] = 'ᴱ' 42 | M.superscript["G"] = 'ᴳ' 43 | M.superscript["H"] = 'ᴴ' 44 | M.superscript["I"] = 'ᴵ' 45 | M.superscript["J"] = 'ᴶ' 46 | M.superscript["K"] = 'ᴷ' 47 | M.superscript["L"] = 'ᴸ' 48 | M.superscript["M"] = 'ᴹ' 49 | M.superscript["N"] = 'ᴺ' 50 | M.superscript["O"] = 'ᴼ' 51 | M.superscript["P"] = 'ᴾ' 52 | M.superscript["R"] = 'ᴿ' 53 | M.superscript["T"] = 'ᵀ' 54 | M.superscript["U"] = 'ᵁ' 55 | M.superscript["V"] = 'ⱽ' 56 | M.superscript["W"] = 'ᵂ' 57 | M.superscript[","] = '︐' 58 | M.superscript[":"] = '︓' 59 | M.superscript[";"] = '︔' 60 | M.superscript["+"] = '⁺' 61 | M.superscript["-"] = '⁻' 62 | M.superscript["<"] = '˂' 63 | M.superscript[">"] = '˃' 64 | M.superscript["/"] = 'ˊ' 65 | M.superscript["("] = '⁽' 66 | M.superscript[")"] = '⁾' 67 | M.superscript["."] = '˙' 68 | M.superscript["="] = '˭' 69 | 70 | 71 | M.subscript[' '] = '' 72 | M.subscript['0'] = '₀' 73 | M.subscript['1'] = '₁' 74 | M.subscript['2'] = '₂' 75 | M.subscript['3'] = '₃' 76 | M.subscript['4'] = '₄' 77 | M.subscript['5'] = '₅' 78 | M.subscript['6'] = '₆' 79 | M.subscript['7'] = '₇' 80 | M.subscript['8'] = '₈' 81 | M.subscript['9'] = '₉' 82 | M.subscript['a'] = 'ₐ' 83 | M.subscript['e'] = 'ₑ' 84 | M.subscript['h'] = 'ₕ' 85 | M.subscript['i'] = 'ᵢ' 86 | M.subscript['j'] = 'ⱼ' 87 | M.subscript['k'] = 'ₖ' 88 | M.subscript['l'] = 'ₗ' 89 | M.subscript['m'] = 'ₘ' 90 | M.subscript['n'] = 'ₙ' 91 | M.subscript['o'] = 'ₒ' 92 | M.subscript['p'] = 'ₚ' 93 | M.subscript['r'] = 'ᵣ' 94 | M.subscript['s'] = 'ₛ' 95 | M.subscript['t'] = 'ₜ' 96 | M.subscript['u'] = 'ᵤ' 97 | M.subscript['v'] = 'ᵥ' 98 | M.subscript['x'] = 'ₓ' 99 | M.subscript[','] = '︐' 100 | M.subscript['+'] = '₊' 101 | M.subscript['-'] = '₋' 102 | M.subscript['/'] = 'ˏ' 103 | M.subscript['('] = '₍' 104 | M.subscript[')'] = '₎' 105 | M.subscript['.'] = '.' 106 | M.subscript['\\beta'] = 'ᵦ' 107 | M.subscript['\\delta'] ='ᵨ' 108 | M.subscript['\\phi'] ='ᵩ' 109 | M.subscript['\\gamma'] ='ᵧ' 110 | M.subscript['\\chi'] ='ᵪ' 111 | 112 | -- diacritics 113 | --[[ 114 | 115 | `{} 116 | '{} 117 | ^{} 118 | "{} 119 | H{} 120 | ~{} 121 | c{} 122 | k{} 123 | l{} 124 | ={} 125 | b{} 126 | .{} 127 | d{} 128 | r{} 129 | u{} 130 | v{} 131 | t{} 132 | o{} 133 | i{} 134 | 135 | --]] 136 | 137 | M.diacritics["a"] = { 138 | ["\\`"] = "à", 139 | ["\\'"] = "á", 140 | ["\\^"] = "â", 141 | ["\\~"] = "ã", 142 | ['\\"'] = "ä", 143 | ["\\r"] = "å", 144 | } 145 | 146 | M.diacritics["c"] = { 147 | ["\\'"] = "ć", 148 | ["\\^"] = "ĉ", 149 | ["\\."] = "ċ", 150 | ["\\c"] = "ç", 151 | ['\\v'] = "č", 152 | } 153 | 154 | M.diacritics["d"] = { 155 | ["\\'"] = "ď", 156 | } 157 | 158 | M.diacritics["e"] = { 159 | ["\\`"] = "è", 160 | ["\\'"] = "é", 161 | ["\\^"] = "ê", 162 | ['\\"'] = "ë", 163 | ['\\.'] = "ė", 164 | ['\\='] = "ē", 165 | ['\\c'] = "ę", 166 | ['\\u'] = "ĕ", 167 | ['\\v'] = "ě", 168 | } 169 | 170 | M.diacritics["g"] = { 171 | ["\\'"] = "ģ", 172 | ["\\^"] = "ĝ", 173 | ['\\.'] = "ġ", 174 | ['\\u'] = "ğ", 175 | } 176 | 177 | M.diacritics["h"] = { 178 | ["\\^"] = "ĥ", 179 | ['\\='] = "ħ", 180 | } 181 | 182 | M.diacritics["i"] = { 183 | ["\\`"] = "ì", 184 | ["\\'"] = "í", 185 | ["\\^"] = "î", 186 | ['\\"'] = "ï", 187 | ['\\~'] = "ĩ", 188 | ['\\='] = "ī", 189 | ['\\c'] = "į", 190 | ['\\u'] = "ĭ", 191 | ['\\i'] = "ı", 192 | } 193 | 194 | M.diacritics["j"] = { 195 | ["\\^"] = "ĵ", 196 | } 197 | 198 | M.diacritics["l"] = { 199 | ["\\`"] = "ļ", 200 | ["\\'"] = "ĺ", 201 | ['\\o'] = "ł", 202 | ['\\.'] = "ŀ", 203 | } 204 | 205 | M.diacritics["n"] = { 206 | ["\\~"] = "ñ", 207 | } 208 | 209 | M.diacritics["o"] = { 210 | ["\\`"] = "ò", 211 | ["\\'"] = "ó", 212 | ["\\^"] = "ô", 213 | ["\\~"] = "õ", 214 | ['\\"'] = "ö", 215 | ['\\o'] = "ø", 216 | } 217 | 218 | M.diacritics["u"] = { 219 | ["\\`"] = "ù", 220 | ["\\'"] = "ú", 221 | ["\\^"] = "û", 222 | ['\\"'] = "ü", 223 | } 224 | 225 | M.diacritics["y"] = { 226 | ["\\`"] = "ý", 227 | ['\\"'] = "ÿ", 228 | } 229 | 230 | M.get_diacritic = function(cmd, txt, node) 231 | -- If we're in this command, we already know exactly where the thing we want to use is 232 | local cc = node:child_count() 233 | local brow, bcol, erow, ecol = node:range() 234 | local nt 235 | if cc > 1 then 236 | nt = vim.treesitter.get_node_text(node:child(1):child(1), 0) 237 | else 238 | local line = vim.api.nvim_buf_get_lines(0, erow, erow + 1, true) 239 | nt = string.sub(line[1], ecol+1, ecol + 1) 240 | ecol = ecol + 1 241 | end 242 | 243 | if M.diacritics[nt] ~= nil then 244 | if M.diacritics[nt][cmd] ~= nil then 245 | local val = {txt = {M.diacritics[nt][cmd]}, full = true, bcol = bcol, ecol = ecol} 246 | return val 247 | end 248 | end 249 | return txt 250 | end 251 | 252 | 253 | return M 254 | -------------------------------------------------------------------------------- /lua/rnoweb-nvim/symbols.lua: -------------------------------------------------------------------------------- 1 | local ts = vim.treesitter 2 | local q = vim.treesitter.query 3 | local info = require'rnoweb-nvim.info' 4 | local super = require'rnoweb-nvim.super_list' 5 | 6 | local M = { 7 | sym = { 8 | r = {}, 9 | latex = {}, 10 | rnoweb = {}, 11 | }, 12 | lang_queries = { 13 | rnoweb = {"rnoweb", "r", "latex"}, 14 | latex = {"latex"}, 15 | }, 16 | queries = { 17 | r = {}, 18 | latex = {}, 19 | rnoweb = {}, 20 | }, 21 | } 22 | 23 | -- Not many rnoweb queies available 24 | table.insert(M.queries.rnoweb, { 25 | fn = "conceal_cmd", 26 | query = "(rinline) @cmd", 27 | }) 28 | 29 | M.sym.rnoweb["\\Sexpr"] = {"ﳒ"} 30 | 31 | -- Lots for Latex 32 | table.insert(M.queries.latex, { 33 | fn = "conceal_cmd", 34 | query = "(generic_command (command_name)) @cmd", 35 | }) 36 | 37 | table.insert(M.queries.latex, { 38 | fn = "citation", 39 | query = "(citation) @cite", 40 | }) 41 | 42 | table.insert(M.queries.latex, { 43 | fn = "mdelimit", 44 | query = "(math_delimiter) @math", 45 | }) 46 | 47 | table.insert(M.queries.latex, { 48 | fn = "text_mode", 49 | query = [[ 50 | (text_mode) @cmd 51 | ]], 52 | }) 53 | 54 | table.insert(M.queries.latex, { 55 | fn = "footnote", 56 | query = [[ 57 | (generic_command 58 | (command_name) @cmd (#eq? @cmd "\\footnote") 59 | ) 60 | ]], 61 | }) 62 | 63 | table.insert(M.queries.latex, { 64 | fn = "fig_lab_count", 65 | query = [[ 66 | (generic_environment 67 | (begin 68 | (curly_group_text 69 | (text 70 | (word) @ename (#eq? @ename "figure") (#set! @ename "ignore" "true") 71 | ) 72 | ) 73 | ) 74 | (label_definition 75 | (curly_group_text 76 | (text) @figlab 77 | ) 78 | ) 79 | ) 80 | ]], 81 | }) 82 | 83 | table.insert(M.queries.latex, { 84 | fn = "section_count", 85 | query = [[ 86 | (section) @sec 87 | ]], 88 | }) 89 | 90 | table.insert(M.queries.latex, { 91 | fn = "math_count", 92 | query = [[ 93 | (math_environment) @sec 94 | ]], 95 | }) 96 | 97 | table.insert(M.queries.latex, { 98 | fn = "ref", 99 | query = [[ 100 | (label_reference 101 | (curly_group_text_list 102 | (text) 103 | ) 104 | ) @ref 105 | ]], 106 | }) 107 | 108 | 109 | 110 | -- Sub/superscripts are a bit of a pain to capture 111 | 112 | table.insert(M.queries.latex, { 113 | fn = "subsuper", 114 | query = [[ 115 | (superscript 116 | (curly_group 117 | (text) 118 | ) @tval (#set! @tval "kind" "superscript") 119 | ) 120 | ]], 121 | }) 122 | 123 | ---[===[ 124 | table.insert(M.queries.latex, { 125 | fn = "subsuper", 126 | query = [[ 127 | (subscript 128 | (curly_group 129 | (text) 130 | ) @tval (#set! @tval "kind" "subscript") 131 | ) 132 | ]], 133 | }) 134 | 135 | table.insert(M.queries.latex, { 136 | fn = "subsuper", 137 | query = [[ 138 | (superscript 139 | (_) @tval 140 | (#set! @tval "kind" "superscript") 141 | ) 142 | ]], 143 | }) 144 | 145 | table.insert(M.queries.latex, { 146 | fn = "subsuper", 147 | query = [[ 148 | (subscript 149 | (_) @tval 150 | (#set! @tval "kind" "subscript") 151 | ) 152 | ]], 153 | }) 154 | 155 | --]===] 156 | 157 | 158 | -- Lots of latex replacements 159 | -- Start with the greeks 160 | M.sym.latex['\\alpha'] = {{"α"}} 161 | M.sym.latex['\\beta'] = {{"β"}} 162 | M.sym.latex["\\delta"] = {{"δ"}} 163 | M.sym.latex["\\chi"] = {{"χ"}} 164 | M.sym.latex['\\eta'] = {{"η"}} 165 | M.sym.latex['\\epsilon'] = {{"ε"}} 166 | M.sym.latex["\\gamma"] = {{"γ"}} 167 | M.sym.latex["\\iota"] = {{"ι"}} 168 | M.sym.latex["\\kappa"] = {{"κ"}} 169 | M.sym.latex['\\lambda'] = {{"λ"}} 170 | M.sym.latex['\\mu'] = {{"μ"}} 171 | M.sym.latex['\\nu'] = {{"ν"}} 172 | M.sym.latex['\\omicron'] = {{"ο"}} 173 | M.sym.latex['\\omega'] = {{"ω"}} 174 | M.sym.latex['\\phi'] = {{"φ"}} 175 | M.sym.latex['\\pi'] = {{"π"}} 176 | M.sym.latex['\\psi'] = {{"ψ"}} 177 | M.sym.latex['\\rho'] = {{"ρ"}} 178 | M.sym.latex['\\sigma'] = {{"σ"}} 179 | M.sym.latex['\\tau'] = {{"τ"}} 180 | M.sym.latex["\\theta"] = {{"θ"}} 181 | M.sym.latex["\\upsilon"] = {{"υ"}} 182 | M.sym.latex['\\varsigma'] = {{"ς"}} 183 | M.sym.latex['\\xi'] = {{"ξ"}} 184 | M.sym.latex['\\zeta'] = {{"ζ"}} 185 | 186 | M.sym.latex["\\varepsilon"] = {{"ε"}} 187 | M.sym.latex["\\backepsilon"] = {{"϶"}} 188 | M.sym.latex["\\varphi"] = {{"φ"}} 189 | M.sym.latex["\\vartheta"] = {{"ϑ"}} 190 | M.sym.latex["\\varpi"] = {{"ϖ"}} 191 | M.sym.latex["\\digamma"] = {{"ϝ"}} 192 | M.sym.latex["\\varkappa"] = {{"ϰ"}} 193 | M.sym.latex["\\varrho"] = {{"ϱ"}} 194 | 195 | M.sym.latex['\\Delta'] = {{"Δ"}} 196 | M.sym.latex['\\Gamma'] = {{"Γ"}} 197 | M.sym.latex['\\Theta'] = {{"Θ"}} 198 | M.sym.latex['\\Lambda'] = {{"Λ"}} 199 | M.sym.latex['\\Omega'] = {{"Ω"}} 200 | M.sym.latex['\\Phi'] = {{"Φ"}} 201 | M.sym.latex['\\Pi'] = {{"Π"}} 202 | M.sym.latex['\\Psi'] = {{"Ψ"}} 203 | M.sym.latex['\\Sigma'] = {{"Σ"}} 204 | M.sym.latex["\\Xi"] = {{"Ξ"}} 205 | M.sym.latex["\\Upsilon"] = {{"Υ"}} 206 | 207 | -- Binary operation symbols 208 | M.sym.latex['\\pm'] = {{"±"}} 209 | M.sym.latex['\\mp'] = {{"∓ "}} 210 | M.sym.latex['\\times'] = {{""}} 211 | M.sym.latex['\\div'] = {{"÷"}} 212 | M.sym.latex['\\ast'] = {{"∗ "}} 213 | M.sym.latex['\\star'] = {{"⋆"}} 214 | M.sym.latex['\\circ'] = {{"◦"}} 215 | M.sym.latex['\\bullet'] = {{"•"}} 216 | M.sym.latex['\\cdot'] = {{"·"}} 217 | 218 | M.sym.latex['\\cap'] = {{"∩"}} 219 | M.sym.latex['\\cup'] = {{"∪"}} 220 | M.sym.latex['\\uplus'] = {{"⊎"}} 221 | M.sym.latex['\\sqcap'] = {{"⊓"}} 222 | M.sym.latex['\\sqcup'] = {{"⊔"}} 223 | M.sym.latex['\\vee'] = {{"⋁ "}} 224 | M.sym.latex['\\wedge'] = {{"⋀ "}} 225 | M.sym.latex['\\setminus'] = {{"\\"}} 226 | M.sym.latex['\\wr'] = {{"≀"}} 227 | M.sym.latex["\\land"] = {{"∧"}} 228 | M.sym.latex["\\lor"] = {{"∨"}} 229 | 230 | M.sym.latex['\\diamond'] = {{"⋄"}} 231 | M.sym.latex['\\bigtriangleup'] = {{"△ "}} 232 | M.sym.latex['\\bigtriangledown'] = {{"▽"}} 233 | M.sym.latex['\\triangleleft'] = {{"◃"}} 234 | M.sym.latex['\\triangleright'] = {{"▹"}} 235 | M.sym.latex['\\lhd'] = {{"◁ "}} 236 | M.sym.latex['\\rhd'] = {{"▷ "}} 237 | M.sym.latex['\\unlhd'] = {{"⊴ "}} 238 | M.sym.latex['\\unrhd'] = {{"⊵ "}} 239 | 240 | M.sym.latex['\\oplus'] = {{"⊕ "}} 241 | M.sym.latex['\\ominus'] = {{"⊖ "}} 242 | M.sym.latex['\\otimes'] = {{"⊗ "}} 243 | M.sym.latex['\\oslash'] = {{"⊘ "}} 244 | M.sym.latex['\\odot'] = {{"⊙ "}} 245 | M.sym.latex['\\bigcirc'] = {{"◯ "}} 246 | M.sym.latex['\\dagger'] = {{"†"}} 247 | M.sym.latex['\\ddagger'] = {{"‡"}} 248 | M.sym.latex['\\amalg'] = {{"⨿"}} 249 | 250 | -- Relation symbols 251 | M.sym.latex['\\leq'] = {{"≤"}} 252 | M.sym.latex['\\prec'] = {{"≺ "}} 253 | M.sym.latex['\\ll'] = {{"≪ "}} 254 | M.sym.latex['\\preceq'] = {{"⪯ "}} 255 | M.sym.latex['\\subset'] = {{"⊂ "}} 256 | M.sym.latex['\\supseteq'] = {{"⊇ "}} 257 | M.sym.latex['\\sqsubset'] = {{"⊏ "}} 258 | M.sym.latex['\\sqsubseteq'] = {{"⊑ "}} 259 | M.sym.latex['\\in'] = {{"∈ "}} 260 | M.sym.latex['\\vdash'] = {{"⊢ "}} 261 | M.sym.latex["\\owns"] = {{"∋"}} 262 | M.sym.latex["\\le"] = {{"≤"}} 263 | M.sym.latex["\\ge"] = {{"≥"}} 264 | M.sym.latex["\\ne"] = {{"≠"}} 265 | M.sym.latex["\\leqq"] = {{"≦"}} 266 | M.sym.latex["\\geqq"] = {{"≧"}} 267 | M.sym.latex["\\ngtr"] = {{"≯"}} 268 | M.sym.latex["\\nleq"] = {{"≰"}} 269 | M.sym.latex["\\ngeq"] = {{"≱"}} 270 | M.sym.latex["\\lesssim"] = {{"≲"}} 271 | M.sym.latex["\\gtrsim"] = {{"≳"}} 272 | M.sym.latex["\\nlesssim"] = {{"≴"}} 273 | M.sym.latex["\\ngtrsim"] = {{"≵"}} 274 | M.sym.latex["\\lessgtr"] = {{"≶"}} 275 | M.sym.latex["\\gtrless"] = {{"≷"}} 276 | M.sym.latex["\\nlessgtr"] = {{"≸"}} 277 | M.sym.latex["\\ngtrless"] = {{"≹"}} 278 | M.sym.latex["\\precsim"] = {{"≾"}} 279 | M.sym.latex["\\succsim"] = {{"≿"}} 280 | M.sym.latex["\\nprec"] = {{"⊀"}} 281 | M.sym.latex["\\nsucc"] = {{"⊁"}} 282 | M.sym.latex["\\nsubset"] = {{"⊄"}} 283 | M.sym.latex["\\nsupset"] = {{"⊅"}} 284 | M.sym.latex["\\nsubseteq"] = {{"⊈"}} 285 | M.sym.latex["\\nsupseteq"] = {{"⊉"}} 286 | M.sym.latex["\\subsetneq"] = {{"⊊"}} 287 | M.sym.latex["\\supsetneq"] = {{"⊋"}} 288 | 289 | M.sym.latex['\\geq'] = {{"≥"}} 290 | M.sym.latex['\\succ'] = {{"≻ "}} 291 | M.sym.latex['\\succeq'] = {{"⪰ "}} 292 | M.sym.latex['\\gg'] = {{"≫ "}} 293 | M.sym.latex['\\supset'] = {{"⊃ "}} 294 | M.sym.latex['\\subseteq'] = {{"⊆ "}} 295 | M.sym.latex['\\sqsupset'] = {{"⊐ "}} 296 | M.sym.latex['\\sqsupseteq'] = {{"⊒ "}} 297 | M.sym.latex['\\ni'] = {{"∋ "}} 298 | M.sym.latex['\\dashv'] = {{"⊣ "}} 299 | 300 | M.sym.latex['\\equiv'] = {{"≡ "}} 301 | M.sym.latex['\\sim'] = {{"∼ "}} 302 | M.sym.latex['\\simeq'] = {{"≃ "}} 303 | M.sym.latex['\\asymp'] = {{"≍ "}} 304 | M.sym.latex['\\approx'] = {{"≈"}} 305 | M.sym.latex['\\cong'] = {{"≅"}} 306 | M.sym.latex['\\neq'] = {{"≠"}} 307 | M.sym.latex['\\doteq'] = {{"≐ "}} 308 | M.sym.latex['\\propto'] = {{"∝"}} 309 | 310 | M.sym.latex['\\models'] = {{"⊧"}} 311 | M.sym.latex['\\perp'] = {{"⊥ "}} 312 | M.sym.latex['\\mid'] = {{"|"}} 313 | M.sym.latex['\\parallel'] = {{"∥"}} 314 | M.sym.latex['\\bowtie'] = {{"⨝ "}} 315 | M.sym.latex['\\Join'] = {{"⋈ "}} 316 | M.sym.latex['\\smile'] = {{"⌣ "}} 317 | M.sym.latex['\\frown'] = {{"⌢ "}} 318 | 319 | -- Arrows 320 | M.sym.latex['\\leftarrow'] = {{"← "}} 321 | M.sym.latex['\\longleftarrow'] = {{"⟵ "}} 322 | M.sym.latex['\\uparrow'] = {{"↑ "}} 323 | M.sym.latex['\\Leftarrow'] = {{"⇐ "}} 324 | M.sym.latex['\\Longleftarrow'] = {{"⟸ "}} 325 | M.sym.latex['\\Uparrow'] = {{"⇑ "}} 326 | M.sym.latex['\\rightarrow'] = {{"→ "}} 327 | M.sym.latex['\\longrightarrow'] = {{"⟶ "}} 328 | M.sym.latex['\\downarrow'] = {{"↓ "}} 329 | M.sym.latex['\\Rightarrow'] = {{"⇒ "}} 330 | M.sym.latex['\\Longrightarrow'] = {{"⟹ "}} 331 | M.sym.latex['\\Downarrow'] = {{"⇓ "}} 332 | M.sym.latex['\\leftrightarrow'] = {{"↔ "}} 333 | M.sym.latex['\\longleftrightarrow'] = {{"⟷ "}} 334 | M.sym.latex['\\updownarrow'] = {{"↕ "}} 335 | M.sym.latex['\\Leftrightarrow'] = {{"⇔ "}} 336 | M.sym.latex['\\Longleftrightarrow'] = {{"⟺ "}} 337 | M.sym.latex['\\Updownarrow'] = {{"⇕ "}} 338 | M.sym.latex['\\mapsto'] = {{"↦ "}} 339 | M.sym.latex['\\longmapsto'] = {{"⟼ "}} 340 | M.sym.latex['\\nearrow'] = {{"↗ "}} 341 | M.sym.latex['\\hookleftarrow'] = {{"↩ "}} 342 | M.sym.latex['\\hookrightarrow'] = {{"↪ "}} 343 | M.sym.latex['\\searrow'] = {{"↘ "}} 344 | M.sym.latex['\\leftharpoonup'] = {{"↼ "}} 345 | M.sym.latex['\\rightharpoonup'] = {{"⇀ "}} 346 | M.sym.latex['\\swarrow'] = {{"↙ "}} 347 | M.sym.latex['\\leftharpoondown'] = {{"↽ "}} 348 | M.sym.latex['\\rightharpoondown'] = {{"⇁ "}} 349 | M.sym.latex['\\nwarrow'] = {{"↖ "}} 350 | M.sym.latex['\\rightleftharpoons'] = {{"⇌ "}} 351 | M.sym.latex["\\gets"] = {{"←"}} 352 | M.sym.latex["\\to"] = {{"→"}} 353 | M.sym.latex["\\nleftarrow"] = {{"↚"}} 354 | M.sym.latex["\\nrightarrow"] = {{"↛"}} 355 | M.sym.latex["\\twoheadleftarrow"] = {{"↞"}} 356 | M.sym.latex["\\twoheadrightarrow"] = {{"↠"}} 357 | M.sym.latex["\\leftarrowtail"] = {{"↢"}} 358 | M.sym.latex["\\rightarrowtail"] = {{"↣"}} 359 | M.sym.latex["\\mapsfrom"] = {{"↤"}} 360 | M.sym.latex["\\looparrowleft"] = {{"↫"}} 361 | M.sym.latex["\\looparrowright"] = {{"↬"}} 362 | M.sym.latex["\\leftrightsquigarrow"] = {{"↭"}} 363 | M.sym.latex["\\nleftrightarrow"] = {{"↮"}} 364 | M.sym.latex["\\curvearrowleft"] = {{"↶"}} 365 | M.sym.latex["\\curvearrowright"] = {{"↷"}} 366 | M.sym.latex["\\circlearrowleft"] = {{"↺"}} 367 | M.sym.latex["\\circlearrowright"] = {{"↻"}} 368 | M.sym.latex["\\upharpoonright"] = {{"↾"}} 369 | M.sym.latex["\\restriction"] = {{"↾"}} 370 | M.sym.latex["\\upharpoonleft"] = {{"↿"}} 371 | M.sym.latex["\\downharpoonright"] = {{"⇂"}} 372 | M.sym.latex["\\downharpoonleft"] = {{"⇃"}} 373 | M.sym.latex["\\rightleftarrows"] = {{"⇄"}} 374 | M.sym.latex["\\updownarrows"] = {{"⇅"}} 375 | M.sym.latex["\\leftrightarrows"] = {{"⇆"}} 376 | M.sym.latex["\\leftleftarrows"] = {{"⇇"}} 377 | M.sym.latex["\\upuparrows"] = {{"⇈"}} 378 | M.sym.latex["\\rightrightarrows"] = {{"⇉"}} 379 | M.sym.latex["\\downdownarrows"] = {{"⇊"}} 380 | M.sym.latex["\\leftrightharpoons"] = {{"⇋"}} 381 | M.sym.latex["\\nLeftarrow"] = {{"⇍"}} 382 | M.sym.latex["\\nLeftrightarrow"] = {{"⇎"}} 383 | M.sym.latex["\\nRightarrow"] = {{"⇏"}} 384 | M.sym.latex["\\Nwarrow"] = {{"⇖"}} 385 | M.sym.latex["\\Nearrow"] = {{"⇗"}} 386 | M.sym.latex["\\Searrow"] = {{"⇘"}} 387 | M.sym.latex["\\Swarrow"] = {{"⇙"}} 388 | M.sym.latex["\\Lleftarrow"] = {{"⇚"}} 389 | M.sym.latex["\\Rrightarrow"] = {{"⇛"}} 390 | M.sym.latex["\\leftsquigarrow"] = {{"⇜"}} 391 | M.sym.latex["\\rightsquigarrow"] = {{"⇝"}} 392 | M.sym.latex["\\dashleftarrow"] = {{"⇠"}} 393 | M.sym.latex["\\dashrightarrow"] = {{"⇢"}} 394 | M.sym.latex["\\LeftArrowBar"] = {{"⇤"}} 395 | M.sym.latex["\\RightArrowBar"] = {{"⇥"}} 396 | M.sym.latex["\\downuparrows"] = {{"⇵"}} 397 | M.sym.latex["\\Lsh"] = {{"↰"}} 398 | M.sym.latex["\\Rsh"] = {{"↱"}} 399 | 400 | -- Math things 401 | M.sym.latex['\\over'] = {{"/"}} 402 | M.sym.latex['\\partial'] = {{"∂"}} 403 | M.sym.latex['\\infty'] = {{"∞"}} 404 | M.sym.latex['\\succcurlyeq'] = {{"≽"}} 405 | M.sym.latex['\\preccurlyeq'] = {{"≼"}} 406 | M.sym.latex['\\int'] = {{"∫"}} 407 | M.sym.latex['\\sum'] = {{"∑"}} 408 | M.sym.latex['\\ln'] = {{"ln"}} 409 | M.sym.latex['\\exp'] = {{"ℯ"}} 410 | M.sym.latex['\\forall'] = {{"∀"}} 411 | M.sym.latex['\\exists'] = {{"∃"}} 412 | M.sym.latex['\\sqrt'] = {{"√"}} 413 | M.sym.latex["\\sqrt[3]"] = {{"∛"}} 414 | M.sym.latex["\\sqrt[4]"] = {{"∜"}} 415 | 416 | M.sym.latex['\\cos'] = {{"cos"}} 417 | M.sym.latex['\\tan'] = {{"tan"}} 418 | M.sym.latex['\\sin'] = {{"sin"}} 419 | M.sym.latex['\\arccos'] = {{"arccos"}} 420 | M.sym.latex['\\arctan'] = {{"arctan"}} 421 | M.sym.latex['\\arcsin'] = {{"arcsin"}} 422 | 423 | M.sym.latex['\\lbrace'] = {{"{"}} 424 | M.sym.latex['\\rbrace'] = {{"}"}} 425 | M.sym.latex['\\{'] = {{"{"}} 426 | M.sym.latex['\\}'] = {{"}"}} 427 | 428 | 429 | -- Non-greeks 430 | M.sym.latex["\\therefore"] = {{"∴"}} 431 | M.sym.latex["\\ldots"] = {{"…"}} 432 | M.sym.latex["\\\\"] = {{"↲ "}} 433 | M.sym.latex["\\nabla"] = {{"∇"}} 434 | M.sym.latex["\\because"] = {{"∵"}} 435 | M.sym.latex["\\vdots"] = {{"⋮"}} 436 | M.sym.latex["\\cdots"] = {{"⋯"}} 437 | M.sym.latex["\\ddots"] = {{"⋱"}} 438 | 439 | M.sym.latex["\\ntrianglerighteq"] = {{"⋭"}} 440 | M.sym.latex["\\hermitconjmatrix"] = {{"⊹"}} 441 | M.sym.latex["\\vartriangleright"] = {{"⊳"}} 442 | M.sym.latex["\\sphericalangle"] = {{"∢"}} 443 | M.sym.latex["\\smallsetminus"] = {{"∖"}} 444 | M.sym.latex["\\measuredangle"] = {{"∡"}} 445 | M.sym.latex["\\fallingdotseq"] = {{"≒"}} 446 | M.sym.latex["\\risingdotseq"] = {{"≓"}} 447 | M.sym.latex["\\corresponds"] = {{"≙"}} 448 | M.sym.latex["\\circledcirc"] = {{"⊚"}} 449 | 450 | M.sym.latex["\\circledast"] = {{"⊛"}} 451 | M.sym.latex["\\complement"] = {{"∁"}} 452 | M.sym.latex["\\varnothing"] = {{"∅"}} 453 | 454 | M.sym.latex["\\nexists"] = {{"∄"}} 455 | M.sym.latex["\\notin"] = {{"∉"}} 456 | M.sym.latex["\\prod"] = {{"∏"}} 457 | M.sym.latex["\\coprod"] = {{"∐"}} 458 | M.sym.latex["\\minus"] = {{"−"}} 459 | M.sym.latex["\\dotplus"] = {{"∔"}} 460 | M.sym.latex["\\divslash"] = {{"∕"}} 461 | M.sym.latex["\\angle"] = {{"∠"}} 462 | M.sym.latex["\\iint"] = {{"∬"}} 463 | M.sym.latex["\\iiint"] = {{"∭"}} 464 | M.sym.latex["\\oint"] = {{"∮"}} 465 | M.sym.latex["\\backsim"] = {{"∽"}} 466 | M.sym.latex["\\nsim"] = {{"≁"}} 467 | M.sym.latex["\\eqsim"] = {{"≂"}} 468 | M.sym.latex["\\napprox"] = {{"≉"}} 469 | M.sym.latex["\\approxeq"] = {{"≊"}} 470 | M.sym.latex["\\Bumpeq"] = {{"≎"}} 471 | M.sym.latex["\\bumpeq"] = {{"≏"}} 472 | M.sym.latex["\\coloneq"] = {{"≔"}} 473 | M.sym.latex["\\eqcolon"] = {{"≕"}} 474 | M.sym.latex["\\eqcirc"] = {{"≖"}} 475 | M.sym.latex["\\circeq"] = {{"≗"}} 476 | M.sym.latex["\\top"] = {{"⊤"}} 477 | M.sym.latex["\\bot"] = {{"⊥"}} 478 | M.sym.latex["\\vDash"] = {{"⊨"}} 479 | M.sym.latex["\\Vvdash"] = {{"⊪"}} 480 | M.sym.latex["\\VDash"] = {{"⊫"}} 481 | M.sym.latex["\\nvdash"] = {{"⊬"}} 482 | M.sym.latex["\\nvDash"] = {{"⊭"}} 483 | M.sym.latex["\\nVdash"] = {{"⊮"}} 484 | M.sym.latex["\\nVDash"] = {{"⊯"}} 485 | M.sym.latex["\\prurel"] = {{"⊰"}} 486 | 487 | M.sym.latex["\\vartriangleleft"] = {{"⊲"}} 488 | M.sym.latex["\\trianglerighteq"] = {{"⊵"}} 489 | M.sym.latex["\\trianglelefteq"] = {{"⊴"}} 490 | M.sym.latex["\\original"] = {{"⊶"}} 491 | M.sym.latex["\\image"] = {{"⊷"}} 492 | M.sym.latex["\\multimap"] = {{"⊸"}} 493 | M.sym.latex["\\intercal"] = {{"⊺"}} 494 | M.sym.latex["\\veebar"] = {{"⊻"}} 495 | M.sym.latex["\\barwedge"] = {{"⊼"}} 496 | M.sym.latex["\\barvee"] = {{"⊽"}} 497 | M.sym.latex["\\rightanglearc"] = {{"⊾"}} 498 | M.sym.latex["\\varlrtriangle"] = {{"⊿"}} 499 | M.sym.latex["\\bigwedge"] = {{"⋀"}} 500 | M.sym.latex["\\bigvee"] = {{"⋁"}} 501 | M.sym.latex["\\bigcap"] = {{"⋂"}} 502 | M.sym.latex["\\bigcup"] = {{"⋃"}} 503 | M.sym.latex["\\divideontimes"] = {{"⋇"}} 504 | M.sym.latex["\\ltimes"] = {{"⋉"}} 505 | M.sym.latex["\\rtimes"] = {{"⋊"}} 506 | M.sym.latex["\\leftthreetimes"] = {{"⋋"}} 507 | M.sym.latex["\\rightthreetimes"] = {{"⋌"}} 508 | M.sym.latex["\\backsimeq"] = {{"⋍"}} 509 | M.sym.latex["\\curlyvee"] = {{"⋎"}} 510 | M.sym.latex["\\curlywedge"] = {{"⋏"}} 511 | M.sym.latex["\\Subset"] = {{"⋐"}} 512 | M.sym.latex["\\Supset"] = {{"⋑"}} 513 | M.sym.latex["\\Cap"] = {{"⋒"}} 514 | M.sym.latex["\\Cup"] = {{"⋓"}} 515 | M.sym.latex["\\pitchfork"] = {{"⋔"}} 516 | M.sym.latex["\\equalparallel"] = {{"⋕"}} 517 | M.sym.latex["\\lessdot"] = {{"⋖"}} 518 | M.sym.latex["\\gtrdot"] = {{"⋗"}} 519 | M.sym.latex["\\lll"] = {{"⋘"}} 520 | M.sym.latex["\\llless"] = {{"⋘"}} 521 | M.sym.latex["\\ggg"] = {{"⋙"}} 522 | M.sym.latex["\\gggtr"] = {{"⋙"}} 523 | M.sym.latex["\\lesseqgtr"] = {{"⋚"}} 524 | M.sym.latex["\\gtreqless"] = {{"⋛"}} 525 | M.sym.latex["\\eqless"] = {{"⋜"}} 526 | M.sym.latex["\\eqgtr"] = {{"⋝"}} 527 | M.sym.latex["\\curlyeqprec"] = {{"⋞"}} 528 | M.sym.latex["\\curlyeqsucc"] = {{"⋟"}} 529 | M.sym.latex["\\npreccurlyeq"] = {{"⋠"}} 530 | M.sym.latex["\\nsucccurlyeq"] = {{"⋡"}} 531 | M.sym.latex["\\nsqsubseteq"] = {{"⋢"}} 532 | M.sym.latex["\\nsqsupseteq"] = {{"⋣"}} 533 | M.sym.latex["\\sqsubsetneq"] = {{"⋤"}} 534 | M.sym.latex["\\sqsupsetneq"] = {{"⋥"}} 535 | M.sym.latex["\\lnsim"] = {{"⋦"}} 536 | M.sym.latex["\\gnsim"] = {{"⋧"}} 537 | M.sym.latex["\\precnsim"] = {{"⋨"}} 538 | M.sym.latex["\\succnsim"] = {{"⋩"}} 539 | M.sym.latex["\\ntriangleleft"] = {{"⋪"}} 540 | M.sym.latex["\\ntriangleright"] = {{"⋫"}} 541 | M.sym.latex["\\ntrianglelefteq"] = {{"⋬"}} 542 | M.sym.latex["\\udots"] = {{"⋰"}} 543 | 544 | -- Spacing commands 545 | M.sym.latex['\\qquad'] = {{" "}} 546 | M.sym.latex['\\quad'] = {{" "}} 547 | M.sym.latex['\\;'] = {{" "}} 548 | M.sym.latex['\\,'] = {{" "}} 549 | M.sym.latex['\\:'] = {{" "}} 550 | M.sym.latex['\\>'] = {{" "}} 551 | M.sym.latex['\\space'] = {{" "}} 552 | M.sym.latex['\\ '] = {{" "}} 553 | M.sym.latex['\\!'] = {{""}} 554 | 555 | -- Hide the escaping backslash with some 556 | M.sym.latex['\\%'] = {{"%"}} 557 | M.sym.latex['\\&'] = {{"&"}} 558 | 559 | -- Just remove some things 560 | M.sym.latex['\\displaystyle'] = {{""}} 561 | M.sym.latex['\\noindent'] = {{""}} 562 | M.sym.latex['\\textcite'] = {{""}} 563 | M.sym.latex['\\parencite'] = {{""}} 564 | M.sym.latex['\\left'] = {{""}} 565 | M.sym.latex['\\right'] = {{""}} 566 | M.sym.latex['\\textbf'] = {{"", ""}} 567 | M.sym.latex['\\mathrm'] = {{"", ""}} 568 | 569 | -- Commands with arguments 570 | M.sym.latex["\\enquote"] = {{"“", "”"}} 571 | M.sym.latex["\\textelp"] = {{"…", ""}} 572 | M.sym.latex["\\textins"] = {{"[", "]"}} 573 | M.sym.latex["\\textit"] = {{"", ""}} 574 | M.sym.latex["\\mathit"] = {{"", ""}} 575 | M.sym.latex["\\text"] = {{"", ""}} 576 | M.sym.latex["\\begin"] = {{"[", "]"}} 577 | M.sym.latex["\\frac"] = {{"(", "╱ ", ")"}} 578 | M.sym.latex["\\nicefrac"] = {{"(", "╱ ", ")"}} 579 | M.sym.latex["\\dfrac"] = {{"(", "╱ ", ")"}} 580 | 581 | -- These commands look to replace the full command + text 582 | M.sym.latex['\\`'] = {txt = {"", ""}, fn = super.get_diacritic} 583 | M.sym.latex["\\'"] = {txt = {"", ""}, fn = super.get_diacritic} 584 | M.sym.latex['\\^'] = {txt = {"", ""}, fn = super.get_diacritic} 585 | M.sym.latex['\\"'] = {txt = {"", ""}, fn = super.get_diacritic} 586 | M.sym.latex['\\H'] = {txt = {"", ""}, fn = super.get_diacritic} 587 | M.sym.latex['\\~'] = {txt = {"", ""}, fn = super.get_diacritic} 588 | M.sym.latex['\\c'] = {txt = {"", ""}, fn = super.get_diacritic} 589 | M.sym.latex['\\k'] = {txt = {"", ""}, fn = super.get_diacritic} 590 | M.sym.latex['\\l'] = {txt = {"", ""}, fn = super.get_diacritic} 591 | M.sym.latex['\\='] = {txt = {"", ""}, fn = super.get_diacritic} 592 | M.sym.latex['\\b'] = {txt = {"", ""}, fn = super.get_diacritic} 593 | M.sym.latex['\\.'] = {txt = {"", ""}, fn = super.get_diacritic} 594 | M.sym.latex['\\d'] = {txt = {"", ""}, fn = super.get_diacritic} 595 | M.sym.latex['\\r'] = {txt = {"", ""}, fn = super.get_diacritic} 596 | M.sym.latex['\\u'] = {txt = {"", ""}, fn = super.get_diacritic} 597 | M.sym.latex['\\v'] = {txt = {"", ""}, fn = super.get_diacritic} 598 | M.sym.latex['\\t'] = {txt = {"", ""}, fn = super.get_diacritic} 599 | M.sym.latex['\\o'] = {txt = {"", ""}, fn = super.get_diacritic} 600 | M.sym.latex['\\i'] = {txt = {"", ""}, fn = super.get_diacritic} 601 | 602 | -- Latex mappings can also include the underscored 603 | --for k, _ in pairs(M.sym.latex) do 604 | -- M.sym.latex[k .. "_"] = {M.sym.latex[k][1] .. "_"} 605 | --end 606 | 607 | -- I use this to set project specific replacements 608 | M.set_sym = function(lang, key, sym) 609 | if M.sym[lang] == nil then 610 | M.sym[lang] = {} 611 | end 612 | M.sym[lang][key] = sym 613 | end 614 | 615 | M.get_sym_text = function(lang, cmd, node) 616 | local ldict = M.sym[lang] 617 | if ldict[cmd] == nil then 618 | return nil 619 | end 620 | local cdict = M.sym[lang][cmd] 621 | local cfn = cdict["fn"] 622 | local text = cdict["txt"] 623 | 624 | -- If no function is defined, then return the first element as the text table 625 | if cfn == nil then 626 | return cdict[1] 627 | end 628 | 629 | local new_text = cfn(cmd, text, node) 630 | 631 | return new_text 632 | 633 | 634 | end 635 | 636 | M.set_query = function(lang, key, query) 637 | if M.sym[lang] == nil then 638 | M.sym[lang] = {} 639 | end 640 | M.sym[lang][key] = query 641 | end 642 | 643 | 644 | -- Sometimes I define new macros that are just text shortcuts 645 | -- Let's find those newcommands and define them as simple conceals 646 | local get_inline_text_macros = function(root, bufnr) 647 | 648 | local query = [[ 649 | (new_command_definition 650 | (curly_group_command_name 651 | (command_name) @cname 652 | ) 653 | (curly_group 654 | (text) @text 655 | ) 656 | ) 657 | ]] 658 | query = q.parse("latex", query) 659 | 660 | for _, match, _ in query:iter_matches(root, bufnr) do 661 | local key = "" 662 | for id, node in pairs(match) do 663 | if id == 1 then 664 | key = ts.get_node_text(node, 0) 665 | M.sym.latex[key] = {} 666 | else 667 | local val = ts.get_node_text(node, 0) 668 | 669 | local klen = string.len(key) 670 | local vlen = string.len(val) 671 | 672 | if vlen > klen then 673 | val = string.sub(val, 1, klen) 674 | end 675 | 676 | -- I'm making this two arguments to get rid of possible braces 677 | M.sym.latex[key] = {{val, ""}} 678 | end 679 | end 680 | end 681 | 682 | end 683 | 684 | -- Get the queries applicable to this filetype 685 | M.get_queries = function(root, bufnr) 686 | 687 | -- Get any inline latex macros 688 | get_inline_text_macros(root, bufnr) 689 | 690 | local out = {} 691 | for _, lang in pairs(M.lang_queries[info.ft]) do 692 | for _, k in ipairs(M.queries[lang]) do 693 | local name = k["fn"] 694 | local query = k["query"] 695 | query = q.parse(lang, query) 696 | out[#out+1] = { 697 | cmd = name, 698 | lang = lang, 699 | match = query:iter_matches(root, bufnr) 700 | } 701 | end 702 | end 703 | return pairs(out) 704 | end 705 | 706 | return M 707 | -------------------------------------------------------------------------------- /plugin/init.lua: -------------------------------------------------------------------------------- 1 | local v = vim.api 2 | local rnw = require('rnoweb-nvim') 3 | 4 | 5 | --these are convienience functions I use when debugging, I'll keep them commented out, but I don't want to delete the code 6 | 7 | --[[ 8 | vim.keymap.set('n', ' 5', function() 9 | require('lazy.core.loader').reload("rnoweb-nvim") 10 | vim.print("refreshed rnw") 11 | end) 12 | vim.keymap.set('n', ' 1', function() 13 | vim.print("in 1") 14 | rnw.test() 15 | end) 16 | --]] 17 | 18 | 19 | v.nvim_create_autocmd({"CursorHold", "BufEnter", "BufWritePost"}, { 20 | group = rnw.auid, 21 | pattern = {"*.Rnw", "*.tex"}, 22 | callback = function() 23 | if rnw.opts.setup then 24 | if rnw.opts.tex2latex then 25 | rnw.tex2latex() 26 | end 27 | require('rnoweb-nvim').refresh() 28 | end 29 | end 30 | }) 31 | -------------------------------------------------------------------------------- /readme_media/main.Rnw: -------------------------------------------------------------------------------- 1 | \documentclass[12pt,a4paper]{article} 2 | 3 | \include{packages.tex} 4 | 5 | \begin{document} 6 | 7 | \newcommand{\certeq}{CE} 8 | 9 | Expected Utility Theory, due to \textcite{VonNeumann1944}, is defined as: 10 | \begin{equation} 11 | \label{eq:eut} 12 | EU = \sum_i p_i \times u(x_i) 13 | \end{equation} 14 | \noindent where $u(x_i)$ is a utility function with the usual properties.\footnote{ 15 | For the uninitiated: completeness, transitivity, continuity, and positive 16 | monotonicity. 17 | } The popular CRRA utility function \parencite{Pratt1964} is often used: 18 | \begin{equation} 19 | \label{eq:crra} 20 | u(x) = \frac{x^{1 - r}}{1 - r} 21 | \end{equation} 22 | \noindent A certainty equivalent, $\certeq{}$, of (\ref{eq:eut}) assuming the utility 23 | function in (\ref{eq:crra}) is defined as: 24 | \begin{align} 25 | \frac{\certeq{}^{1 - r}}{1 - r} &= EU \\ 26 | \certeq{} &= \left[EU(1-r)\right]^\frac{1}{1 - r} 27 | \end{align} 28 | 29 | \end{document} 30 | 31 | -------------------------------------------------------------------------------- /readme_media/off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamonroe/rnoweb-nvim/dd5876c8762c750cbf05199aee28fed4de9c34df/readme_media/off.png -------------------------------------------------------------------------------- /readme_media/on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bamonroe/rnoweb-nvim/dd5876c8762c750cbf05199aee28fed4de9c34df/readme_media/on.png --------------------------------------------------------------------------------