blob: 7b8b7fea50acdc1c278b3bd4cd891b7ba6fa87df [file] [log] [blame]
Bram Moolenaardd60c362023-02-27 15:49:53 +00001" Language: Markdown with chunks of R, Python and other languages
2" Maintainer: Jakson Aquino <jalvesaq@gmail.com>
Bram Moolenaar77cdfd12016-03-12 12:57:59 +01003" Homepage: https://github.com/jalvesaq/R-Vim-runtime
Jakson Alves de Aquino9042bd82023-12-25 09:22:27 +00004" Last Change: Sun Dec 24, 2023 07:21AM
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +02005"
6" For highlighting pandoc extensions to markdown like citations and TeX and
7" many other advanced features like folding of markdown sections, it is
8" recommended to install the vim-pandoc filetype plugin as well as the
9" vim-pandoc-syntax filetype plugin from https://github.com/vim-pandoc.
Bram Moolenaarfc65cab2018-08-28 22:58:02 +020010
Bram Moolenaardb6ea062014-07-10 22:01:47 +020011
Bram Moolenaar77cdfd12016-03-12 12:57:59 +010012if exists("b:current_syntax")
Bram Moolenaardb6ea062014-07-10 22:01:47 +020013 finish
14endif
15
Bram Moolenaardd60c362023-02-27 15:49:53 +000016let s:cpo_save = &cpo
17set cpo&vim
18
Jakson Alves de Aquino9042bd82023-12-25 09:22:27 +000019let g:rmd_include_latex = get(g:, 'rmd_include_latex', 1)
20if g:rmd_include_latex == 0 || g:rmd_include_latex == 1
21 let b:rmd_has_LaTeX = v:false
22elseif g:rmd_include_latex == 2
23 let b:rmd_has_LaTeX = v:true
24endif
25
Bram Moolenaar11e3c5b2021-04-21 18:09:37 +020026" Highlight the header of the chunks as R code
27let g:rmd_syn_hl_chunk = get(g:, 'rmd_syn_hl_chunk', 0)
Bram Moolenaarfc65cab2018-08-28 22:58:02 +020028
29" Pandoc-syntax has more features, but it is slower.
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +020030" https://github.com/vim-pandoc/vim-pandoc-syntax
Bram Moolenaardd60c362023-02-27 15:49:53 +000031
32" Don't waste time loading syntax that will be discarded:
33let s:save_pandoc_lngs = get(g:, 'pandoc#syntax#codeblocks#embeds#langs', [])
34let g:pandoc#syntax#codeblocks#embeds#langs = []
35
Jakson Alves de Aquino34745942023-09-27 13:56:02 -030036let g:rmd_dynamic_fenced_languages = get(g:, 'rmd_dynamic_fenced_languages', v:true)
37
Bram Moolenaardd60c362023-02-27 15:49:53 +000038" Step_1: Source pandoc.vim if it is installed:
Bram Moolenaardb6ea062014-07-10 22:01:47 +020039runtime syntax/pandoc.vim
40if exists("b:current_syntax")
Bram Moolenaardd60c362023-02-27 15:49:53 +000041 if hlexists('pandocDelimitedCodeBlock')
42 syn clear pandocDelimitedCodeBlock
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +020043 endif
Bram Moolenaardd60c362023-02-27 15:49:53 +000044
45 if len(s:save_pandoc_lngs) > 0 && !exists('g:rmd_fenced_languages')
46 let g:rmd_fenced_languages = deepcopy(s:save_pandoc_lngs)
47 endif
48
49 " Recognize inline R code
50 syn region rmdrInline matchgroup=rmdInlineDelim start="`r " end="`" contains=@Rmdr containedin=pandocLaTeXRegion,yamlFlowString keepend
Bram Moolenaarfc65cab2018-08-28 22:58:02 +020051else
Bram Moolenaardd60c362023-02-27 15:49:53 +000052 " Step_2: Source markdown.vim if pandoc.vim is not installed
53
54 " Configuration if not using pandoc syntax:
55 " Add syntax highlighting of YAML header
56 let g:rmd_syn_hl_yaml = get(g:, 'rmd_syn_hl_yaml', 1)
57 " Add syntax highlighting of citation keys
58 let g:rmd_syn_hl_citations = get(g:, 'rmd_syn_hl_citations', 1)
59
60 " R chunks will not be highlighted by syntax/markdown because their headers
61 " follow a non standard pattern: "```{lang" instead of "^```lang".
62 " Make a copy of g:markdown_fenced_languages to highlight the chunks later:
63 if exists('g:markdown_fenced_languages') && !exists('g:rmd_fenced_languages')
64 let g:rmd_fenced_languages = deepcopy(g:markdown_fenced_languages)
65 endif
66
67 if exists('g:markdown_fenced_languages') && len(g:markdown_fenced_languages) > 0
68 let s:save_mfl = deepcopy(g:markdown_fenced_languages)
69 endif
70 " Don't waste time loading syntax that will be discarded:
71 let g:markdown_fenced_languages = []
72 runtime syntax/markdown.vim
73 if exists('s:save_mfl') > 0
74 let g:markdown_fenced_languages = deepcopy(s:save_mfl)
75 unlet s:save_mfl
76 endif
77 syn region rmdrInline matchgroup=rmdInlineDelim start="`r " end="`" contains=@Rmdr keepend
78
79 " Step_2a: Add highlighting for both YAML and citations which are pandoc
80 " specific, but also used in Rmd files
81
82 " You don't need this if either your markdown/syntax.vim already highlights
83 " the YAML header or you are writing standard markdown
84 if g:rmd_syn_hl_yaml
85 " Basic highlighting of YAML header
86 syn match rmdYamlFieldTtl /^\s*\zs\w\%(-\|\w\)*\ze:/ contained
87 syn match rmdYamlFieldTtl /^\s*-\s*\zs\w\%(-\|\w\)*\ze:/ contained
88 syn region yamlFlowString matchgroup=yamlFlowStringDelimiter start='"' skip='\\"' end='"' contains=yamlEscape,rmdrInline contained
89 syn region yamlFlowString matchgroup=yamlFlowStringDelimiter start="'" skip="''" end="'" contains=yamlSingleEscape,rmdrInline contained
90 syn match yamlEscape contained '\\\%([\\"abefnrtv\^0_ NLP\n]\|x\x\x\|u\x\{4}\|U\x\{8}\)'
91 syn match yamlSingleEscape contained "''"
92 syn match yamlComment /#.*/ contained
Viktor Szépe3fc7a7e2023-08-23 21:20:00 +020093 " A second colon is a syntax error, unless within a string or following !expr
Bram Moolenaardd60c362023-02-27 15:49:53 +000094 syn match yamlColonError /:\s*[^'^"^!]*:/ contained
95 if &filetype == 'quarto'
96 syn region pandocYAMLHeader matchgroup=rmdYamlBlockDelim start=/\%(\%^\|\_^\s*\n\)\@<=\_^-\{3}\ze\n.\+/ end=/^---$/ keepend contains=rmdYamlFieldTtl,yamlFlowString,yamlComment,yamlColonError
97 else
98 syn region pandocYAMLHeader matchgroup=rmdYamlBlockDelim start=/\%(\%^\|\_^\s*\n\)\@<=\_^-\{3}\ze\n.\+/ end=/^\([-.]\)\1\{2}$/ keepend contains=rmdYamlFieldTtl,yamlFlowString,yamlComment,yamlColonError
99 endif
100 hi def link rmdYamlBlockDelim Delimiter
101 hi def link rmdYamlFieldTtl Identifier
102 hi def link yamlFlowString String
103 hi def link yamlComment Comment
104 hi def link yamlColonError Error
105 endif
106
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300107 " Conceal char for manual line break
108 if &encoding ==# 'utf-8'
109 syn match rmdNewLine ' $' conceal cchar=↵
110 endif
111
Bram Moolenaardd60c362023-02-27 15:49:53 +0000112 " You don't need this if either your markdown/syntax.vim already highlights
113 " citations or you are writing standard markdown
114 if g:rmd_syn_hl_citations
115 " From vim-pandoc-syntax
116 " parenthetical citations
117 syn match pandocPCite /\^\@<!\[[^\[\]]\{-}-\{0,1}@[[:alnum:]_][[:alnum:]à-öø-ÿÀ-ÖØ-ß_:.#$%&\-+?<>~\/]*.\{-}\]/ contains=pandocEmphasis,pandocStrong,pandocLatex,pandocCiteKey,@Spell,pandocAmpersandEscape display
118 " in-text citations with location
119 syn match pandocICite /@[[:alnum:]_][[:alnum:]à-öø-ÿÀ-ÖØ-ß_:.#$%&\-+?<>~\/]*\s\[.\{-1,}\]/ contains=pandocCiteKey,@Spell display
120 " cite keys
121 syn match pandocCiteKey /\(-\=@[[:alnum:]_][[:alnum:]à-öø-ÿÀ-ÖØ-ß_:.#$%&\-+?<>~\/]*\)/ containedin=pandocPCite,pandocICite contains=@NoSpell display
122 syn match pandocCiteAnchor /[-@]/ contained containedin=pandocCiteKey display
123 syn match pandocCiteLocator /[\[\]]/ contained containedin=pandocPCite,pandocICite
124 hi def link pandocPCite Operator
125 hi def link pandocICite Operator
126 hi def link pandocCiteKey Label
127 hi def link pandocCiteAnchor Operator
128 hi def link pandocCiteLocator Operator
129 endif
Bram Moolenaardb6ea062014-07-10 22:01:47 +0200130endif
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +0200131
Bram Moolenaardd60c362023-02-27 15:49:53 +0000132" Step_3: Highlight code blocks.
133
134syn region rmdCodeBlock matchgroup=rmdCodeDelim start="^\s*```\s*{.*}$" matchgroup=rmdCodeDelim end="^\s*```\ze\s*$" keepend
135syn region rmdCodeBlock matchgroup=rmdCodeDelim start="^\s*```.+$" matchgroup=rmdCodeDelim end="^```$" keepend
136hi link rmdCodeBlock Special
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +0200137
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200138" Now highlight chunks:
Bram Moolenaardd60c362023-02-27 15:49:53 +0000139syn region knitrBodyOptions start='^#| ' end='$' contained containedin=rComment,pythonComment contains=knitrBodyVar,knitrBodyValue transparent
140syn match knitrBodyValue ': \zs.*\ze$' keepend contained containedin=knitrBodyOptions
141syn match knitrBodyVar '| \zs\S\{-}\ze:' contained containedin=knitrBodyOptions
142
143let g:rmd_fenced_languages = get(g:, 'rmd_fenced_languages', ['r'])
Bram Moolenaardd60c362023-02-27 15:49:53 +0000144
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300145let s:no_syntax_vim = []
Jakson Alves de Aquino9042bd82023-12-25 09:22:27 +0000146function s:IncludeLanguage(lng)
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300147 if a:lng =~ '='
148 let ftpy = substitute(a:lng, '.*=', '', '')
149 let lnm = substitute(a:lng, '=.*', '', '')
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200150 else
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300151 let ftpy = a:lng
152 let lnm = a:lng
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200153 endif
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300154 if index(s:no_syntax_vim, ftpy) >= 0
155 return
156 endif
157 if len(globpath(&rtp, "syntax/" . ftpy . ".vim"))
158 unlet! b:current_syntax
159 exe 'syn include @Rmd'.lnm.' syntax/'.ftpy.'.vim'
160 let b:current_syntax = "rmd"
161 if g:rmd_syn_hl_chunk
162 exe 'syn match knitrChunkDelim /```\s*{\s*'.lnm.'/ contained containedin=knitrChunkBrace contains=knitrChunkLabel'
163 exe 'syn match knitrChunkLabelDelim /```\s*{\s*'.lnm.',\=\s*[-[:alnum:]]\{-1,}[,}]/ contained containedin=knitrChunkBrace'
164 syn match knitrChunkDelim /}\s*$/ contained containedin=knitrChunkBrace
165 exe 'syn match knitrChunkBrace /```\s*{\s*'.lnm.'.*$/ contained containedin=rmd'.lnm.'Chunk contains=knitrChunkDelim,knitrChunkLabelDelim,@Rmd'.lnm
166 exe 'syn region rmd'.lnm.'Chunk start="^\s*```\s*{\s*=\?'.lnm.'\>.*$" matchgroup=rmdCodeDelim end="^\s*```\ze\s*$" keepend contains=knitrChunkBrace,@Rmd'.lnm
167
168 hi link knitrChunkLabel Identifier
169 hi link knitrChunkDelim rmdCodeDelim
170 hi link knitrChunkLabelDelim rmdCodeDelim
171 else
172 exe 'syn region rmd'.lnm.'Chunk matchgroup=rmdCodeDelim start="^\s*```\s*{\s*=\?'.lnm.'\>.*$" matchgroup=rmdCodeDelim end="^\s*```\ze\s*$" keepend contains=@Rmd'.lnm
173 endif
174 else
175 " Avoid the cost of running globpath() whenever the buffer is saved
176 let s:no_syntax_vim += [ftpy]
177 endif
178endfunction
179
180for s:type in g:rmd_fenced_languages
Jakson Alves de Aquino9042bd82023-12-25 09:22:27 +0000181 call s:IncludeLanguage(s:type)
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +0200182endfor
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200183unlet! s:type
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +0200184
Jakson Alves de Aquino9042bd82023-12-25 09:22:27 +0000185let s:LaTeX_included = v:false
186function s:IncludeLaTeX()
187 let s:LaTeX_included = v:true
188 unlet! b:current_syntax
189 syn include @RmdLaTeX syntax/tex.vim
190 " From vim-pandoc-syntax
191 syn region rmdLaTeXInlineMath start=/\v\\@<!\$\S@=/ end=/\v\\@<!\$\d@!/ keepend contains=@RmdLaTeX
192 syn match rmdLaTeXCmd /\\[[:alpha:]]\+\(\({.\{-}}\)\=\(\[.\{-}\]\)\=\)*/ contains=@RmdLaTeX
193 syn region rmdLaTeX start='\$\$' end='\$\$' keepend contains=@RmdLaTeX
194 syn region rmdLaTeX start=/\\begin{\z(.\{-}\)}/ end=/\\end{\z1}/ keepend contains=@RmdLaTeX
195endfunction
196
197function s:CheckRmdFencedLanguages()
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300198 let alines = getline(1, '$')
199 call filter(alines, "v:val =~ '^```{'")
200 call map(alines, "substitute(v:val, '^```{', '', '')")
201 call map(alines, "substitute(v:val, '\\W.*', '', '')")
202 for tpy in alines
203 if len(tpy) == 0
204 continue
205 endif
206 let has_lng = 0
207 for lng in g:rmd_fenced_languages
208 if tpy == lng
209 let has_lng = 1
210 continue
211 endif
212 endfor
213 if has_lng == 0
214 let g:rmd_fenced_languages += [tpy]
Jakson Alves de Aquino9042bd82023-12-25 09:22:27 +0000215 call s:IncludeLanguage(tpy)
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300216 endif
217 endfor
Jakson Alves de Aquino9042bd82023-12-25 09:22:27 +0000218
219 if hlexists('pandocLaTeXCommand')
220 return
221 endif
222 if g:rmd_include_latex
223 if !b:rmd_has_LaTeX && (search('\$\$', 'wn') > 0 ||
224 \ search('\\begin{', 'wn') > 0) ||
225 \ search('\\[[:alpha:]]\+', 'wn') ||
226 \ search('\$[^\$]\+\$', 'wn')
227 let b:rmd_has_LaTeX = v:true
228 endif
229 if b:rmd_has_LaTeX && !s:LaTeX_included
230 call s:IncludeLaTeX()
231 endif
232 endif
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300233endfunction
234
235if g:rmd_dynamic_fenced_languages
Jakson Alves de Aquino9042bd82023-12-25 09:22:27 +0000236 call s:CheckRmdFencedLanguages()
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300237 augroup RmdSyntax
238 autocmd!
Jakson Alves de Aquino9042bd82023-12-25 09:22:27 +0000239 autocmd BufWritePost <buffer> call s:CheckRmdFencedLanguages()
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300240 augroup END
241endif
242
Bram Moolenaardd60c362023-02-27 15:49:53 +0000243" Step_4: Highlight code recognized by pandoc but not defined in pandoc.vim yet:
244syn match pandocDivBegin '^:::\+ {.\{-}}' contains=pandocHeaderAttr
245syn match pandocDivEnd '^:::\+$'
Bram Moolenaar11e3c5b2021-04-21 18:09:37 +0200246
Bram Moolenaardd60c362023-02-27 15:49:53 +0000247hi def link knitrBodyVar PreProc
248hi def link knitrBodyValue Constant
249hi def link knitrBodyOptions rComment
250hi def link pandocDivBegin Delimiter
251hi def link pandocDivEnd Delimiter
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200252hi def link rmdInlineDelim Delimiter
253hi def link rmdCodeDelim Delimiter
Bram Moolenaardb6ea062014-07-10 22:01:47 +0200254
Bram Moolenaardd60c362023-02-27 15:49:53 +0000255if len(s:save_pandoc_lngs)
256 let g:pandoc#syntax#codeblocks#embeds#langs = s:save_pandoc_lngs
Bram Moolenaardb6ea062014-07-10 22:01:47 +0200257endif
Bram Moolenaardd60c362023-02-27 15:49:53 +0000258unlet s:save_pandoc_lngs
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200259let &cpo = s:cpo_save
260unlet s:cpo_save
261
Jakson Alves de Aquino9042bd82023-12-25 09:22:27 +0000262syntax iskeyword clear
263
Bram Moolenaardd60c362023-02-27 15:49:53 +0000264let b:current_syntax = "rmd"
265
Bram Moolenaardb6ea062014-07-10 22:01:47 +0200266" vim: ts=8 sw=2