blob: 93343dd7296467a7369f29840d03cba17c295109 [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 Aquino34745942023-09-27 13:56:02 -03004" Last Change: Wed May 17, 2023 06:34AM
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
Bram Moolenaar11e3c5b2021-04-21 18:09:37 +020019" Highlight the header of the chunks as R code
20let g:rmd_syn_hl_chunk = get(g:, 'rmd_syn_hl_chunk', 0)
Bram Moolenaarfc65cab2018-08-28 22:58:02 +020021
22" Pandoc-syntax has more features, but it is slower.
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +020023" https://github.com/vim-pandoc/vim-pandoc-syntax
Bram Moolenaardd60c362023-02-27 15:49:53 +000024
25" Don't waste time loading syntax that will be discarded:
26let s:save_pandoc_lngs = get(g:, 'pandoc#syntax#codeblocks#embeds#langs', [])
27let g:pandoc#syntax#codeblocks#embeds#langs = []
28
Jakson Alves de Aquino34745942023-09-27 13:56:02 -030029let g:rmd_dynamic_fenced_languages = get(g:, 'rmd_dynamic_fenced_languages', v:true)
30
Bram Moolenaardd60c362023-02-27 15:49:53 +000031" Step_1: Source pandoc.vim if it is installed:
Bram Moolenaardb6ea062014-07-10 22:01:47 +020032runtime syntax/pandoc.vim
33if exists("b:current_syntax")
Bram Moolenaardd60c362023-02-27 15:49:53 +000034 if hlexists('pandocDelimitedCodeBlock')
35 syn clear pandocDelimitedCodeBlock
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +020036 endif
Bram Moolenaardd60c362023-02-27 15:49:53 +000037
38 if len(s:save_pandoc_lngs) > 0 && !exists('g:rmd_fenced_languages')
39 let g:rmd_fenced_languages = deepcopy(s:save_pandoc_lngs)
40 endif
41
42 " Recognize inline R code
43 syn region rmdrInline matchgroup=rmdInlineDelim start="`r " end="`" contains=@Rmdr containedin=pandocLaTeXRegion,yamlFlowString keepend
Bram Moolenaarfc65cab2018-08-28 22:58:02 +020044else
Bram Moolenaardd60c362023-02-27 15:49:53 +000045 " Step_2: Source markdown.vim if pandoc.vim is not installed
46
47 " Configuration if not using pandoc syntax:
48 " Add syntax highlighting of YAML header
49 let g:rmd_syn_hl_yaml = get(g:, 'rmd_syn_hl_yaml', 1)
50 " Add syntax highlighting of citation keys
51 let g:rmd_syn_hl_citations = get(g:, 'rmd_syn_hl_citations', 1)
52
53 " R chunks will not be highlighted by syntax/markdown because their headers
54 " follow a non standard pattern: "```{lang" instead of "^```lang".
55 " Make a copy of g:markdown_fenced_languages to highlight the chunks later:
56 if exists('g:markdown_fenced_languages') && !exists('g:rmd_fenced_languages')
57 let g:rmd_fenced_languages = deepcopy(g:markdown_fenced_languages)
58 endif
59
60 if exists('g:markdown_fenced_languages') && len(g:markdown_fenced_languages) > 0
61 let s:save_mfl = deepcopy(g:markdown_fenced_languages)
62 endif
63 " Don't waste time loading syntax that will be discarded:
64 let g:markdown_fenced_languages = []
65 runtime syntax/markdown.vim
66 if exists('s:save_mfl') > 0
67 let g:markdown_fenced_languages = deepcopy(s:save_mfl)
68 unlet s:save_mfl
69 endif
70 syn region rmdrInline matchgroup=rmdInlineDelim start="`r " end="`" contains=@Rmdr keepend
71
72 " Step_2a: Add highlighting for both YAML and citations which are pandoc
73 " specific, but also used in Rmd files
74
75 " You don't need this if either your markdown/syntax.vim already highlights
76 " the YAML header or you are writing standard markdown
77 if g:rmd_syn_hl_yaml
78 " Basic highlighting of YAML header
79 syn match rmdYamlFieldTtl /^\s*\zs\w\%(-\|\w\)*\ze:/ contained
80 syn match rmdYamlFieldTtl /^\s*-\s*\zs\w\%(-\|\w\)*\ze:/ contained
81 syn region yamlFlowString matchgroup=yamlFlowStringDelimiter start='"' skip='\\"' end='"' contains=yamlEscape,rmdrInline contained
82 syn region yamlFlowString matchgroup=yamlFlowStringDelimiter start="'" skip="''" end="'" contains=yamlSingleEscape,rmdrInline contained
83 syn match yamlEscape contained '\\\%([\\"abefnrtv\^0_ NLP\n]\|x\x\x\|u\x\{4}\|U\x\{8}\)'
84 syn match yamlSingleEscape contained "''"
85 syn match yamlComment /#.*/ contained
Viktor Szépe3fc7a7e2023-08-23 21:20:00 +020086 " A second colon is a syntax error, unless within a string or following !expr
Bram Moolenaardd60c362023-02-27 15:49:53 +000087 syn match yamlColonError /:\s*[^'^"^!]*:/ contained
88 if &filetype == 'quarto'
89 syn region pandocYAMLHeader matchgroup=rmdYamlBlockDelim start=/\%(\%^\|\_^\s*\n\)\@<=\_^-\{3}\ze\n.\+/ end=/^---$/ keepend contains=rmdYamlFieldTtl,yamlFlowString,yamlComment,yamlColonError
90 else
91 syn region pandocYAMLHeader matchgroup=rmdYamlBlockDelim start=/\%(\%^\|\_^\s*\n\)\@<=\_^-\{3}\ze\n.\+/ end=/^\([-.]\)\1\{2}$/ keepend contains=rmdYamlFieldTtl,yamlFlowString,yamlComment,yamlColonError
92 endif
93 hi def link rmdYamlBlockDelim Delimiter
94 hi def link rmdYamlFieldTtl Identifier
95 hi def link yamlFlowString String
96 hi def link yamlComment Comment
97 hi def link yamlColonError Error
98 endif
99
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300100 " Conceal char for manual line break
101 if &encoding ==# 'utf-8'
102 syn match rmdNewLine ' $' conceal cchar=↵
103 endif
104
Bram Moolenaardd60c362023-02-27 15:49:53 +0000105 " You don't need this if either your markdown/syntax.vim already highlights
106 " citations or you are writing standard markdown
107 if g:rmd_syn_hl_citations
108 " From vim-pandoc-syntax
109 " parenthetical citations
110 syn match pandocPCite /\^\@<!\[[^\[\]]\{-}-\{0,1}@[[:alnum:]_][[:alnum:]à-öø-ÿÀ-ÖØ-ß_:.#$%&\-+?<>~\/]*.\{-}\]/ contains=pandocEmphasis,pandocStrong,pandocLatex,pandocCiteKey,@Spell,pandocAmpersandEscape display
111 " in-text citations with location
112 syn match pandocICite /@[[:alnum:]_][[:alnum:]à-öø-ÿÀ-ÖØ-ß_:.#$%&\-+?<>~\/]*\s\[.\{-1,}\]/ contains=pandocCiteKey,@Spell display
113 " cite keys
114 syn match pandocCiteKey /\(-\=@[[:alnum:]_][[:alnum:]à-öø-ÿÀ-ÖØ-ß_:.#$%&\-+?<>~\/]*\)/ containedin=pandocPCite,pandocICite contains=@NoSpell display
115 syn match pandocCiteAnchor /[-@]/ contained containedin=pandocCiteKey display
116 syn match pandocCiteLocator /[\[\]]/ contained containedin=pandocPCite,pandocICite
117 hi def link pandocPCite Operator
118 hi def link pandocICite Operator
119 hi def link pandocCiteKey Label
120 hi def link pandocCiteAnchor Operator
121 hi def link pandocCiteLocator Operator
122 endif
Bram Moolenaardb6ea062014-07-10 22:01:47 +0200123endif
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +0200124
Bram Moolenaardd60c362023-02-27 15:49:53 +0000125" Step_3: Highlight code blocks.
126
127syn region rmdCodeBlock matchgroup=rmdCodeDelim start="^\s*```\s*{.*}$" matchgroup=rmdCodeDelim end="^\s*```\ze\s*$" keepend
128syn region rmdCodeBlock matchgroup=rmdCodeDelim start="^\s*```.+$" matchgroup=rmdCodeDelim end="^```$" keepend
129hi link rmdCodeBlock Special
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +0200130
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200131" Now highlight chunks:
Bram Moolenaardd60c362023-02-27 15:49:53 +0000132syn region knitrBodyOptions start='^#| ' end='$' contained containedin=rComment,pythonComment contains=knitrBodyVar,knitrBodyValue transparent
133syn match knitrBodyValue ': \zs.*\ze$' keepend contained containedin=knitrBodyOptions
134syn match knitrBodyVar '| \zs\S\{-}\ze:' contained containedin=knitrBodyOptions
135
136let g:rmd_fenced_languages = get(g:, 'rmd_fenced_languages', ['r'])
Bram Moolenaardd60c362023-02-27 15:49:53 +0000137
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300138let s:no_syntax_vim = []
139function IncludeLanguage(lng)
140 if a:lng =~ '='
141 let ftpy = substitute(a:lng, '.*=', '', '')
142 let lnm = substitute(a:lng, '=.*', '', '')
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200143 else
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300144 let ftpy = a:lng
145 let lnm = a:lng
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200146 endif
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300147 if index(s:no_syntax_vim, ftpy) >= 0
148 return
149 endif
150 if len(globpath(&rtp, "syntax/" . ftpy . ".vim"))
151 unlet! b:current_syntax
152 exe 'syn include @Rmd'.lnm.' syntax/'.ftpy.'.vim'
153 let b:current_syntax = "rmd"
154 if g:rmd_syn_hl_chunk
155 exe 'syn match knitrChunkDelim /```\s*{\s*'.lnm.'/ contained containedin=knitrChunkBrace contains=knitrChunkLabel'
156 exe 'syn match knitrChunkLabelDelim /```\s*{\s*'.lnm.',\=\s*[-[:alnum:]]\{-1,}[,}]/ contained containedin=knitrChunkBrace'
157 syn match knitrChunkDelim /}\s*$/ contained containedin=knitrChunkBrace
158 exe 'syn match knitrChunkBrace /```\s*{\s*'.lnm.'.*$/ contained containedin=rmd'.lnm.'Chunk contains=knitrChunkDelim,knitrChunkLabelDelim,@Rmd'.lnm
159 exe 'syn region rmd'.lnm.'Chunk start="^\s*```\s*{\s*=\?'.lnm.'\>.*$" matchgroup=rmdCodeDelim end="^\s*```\ze\s*$" keepend contains=knitrChunkBrace,@Rmd'.lnm
160
161 hi link knitrChunkLabel Identifier
162 hi link knitrChunkDelim rmdCodeDelim
163 hi link knitrChunkLabelDelim rmdCodeDelim
164 else
165 exe 'syn region rmd'.lnm.'Chunk matchgroup=rmdCodeDelim start="^\s*```\s*{\s*=\?'.lnm.'\>.*$" matchgroup=rmdCodeDelim end="^\s*```\ze\s*$" keepend contains=@Rmd'.lnm
166 endif
167 else
168 " Avoid the cost of running globpath() whenever the buffer is saved
169 let s:no_syntax_vim += [ftpy]
170 endif
171endfunction
172
173for s:type in g:rmd_fenced_languages
174 call IncludeLanguage(s:type)
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +0200175endfor
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200176unlet! s:type
Bram Moolenaarcd5c8f82017-04-09 20:11:58 +0200177
Jakson Alves de Aquino34745942023-09-27 13:56:02 -0300178function CheckRmdFencedLanguages()
179 let alines = getline(1, '$')
180 call filter(alines, "v:val =~ '^```{'")
181 call map(alines, "substitute(v:val, '^```{', '', '')")
182 call map(alines, "substitute(v:val, '\\W.*', '', '')")
183 for tpy in alines
184 if len(tpy) == 0
185 continue
186 endif
187 let has_lng = 0
188 for lng in g:rmd_fenced_languages
189 if tpy == lng
190 let has_lng = 1
191 continue
192 endif
193 endfor
194 if has_lng == 0
195 let g:rmd_fenced_languages += [tpy]
196 call IncludeLanguage(tpy)
197 endif
198 endfor
199endfunction
200
201if g:rmd_dynamic_fenced_languages
202 call CheckRmdFencedLanguages()
203 augroup RmdSyntax
204 autocmd!
205 autocmd BufWritePost <buffer> call CheckRmdFencedLanguages()
206 augroup END
207endif
208
Bram Moolenaardd60c362023-02-27 15:49:53 +0000209" Step_4: Highlight code recognized by pandoc but not defined in pandoc.vim yet:
210syn match pandocDivBegin '^:::\+ {.\{-}}' contains=pandocHeaderAttr
211syn match pandocDivEnd '^:::\+$'
Bram Moolenaar11e3c5b2021-04-21 18:09:37 +0200212
Bram Moolenaardd60c362023-02-27 15:49:53 +0000213hi def link knitrBodyVar PreProc
214hi def link knitrBodyValue Constant
215hi def link knitrBodyOptions rComment
216hi def link pandocDivBegin Delimiter
217hi def link pandocDivEnd Delimiter
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200218hi def link rmdInlineDelim Delimiter
219hi def link rmdCodeDelim Delimiter
Bram Moolenaardb6ea062014-07-10 22:01:47 +0200220
Bram Moolenaardd60c362023-02-27 15:49:53 +0000221if len(s:save_pandoc_lngs)
222 let g:pandoc#syntax#codeblocks#embeds#langs = s:save_pandoc_lngs
Bram Moolenaardb6ea062014-07-10 22:01:47 +0200223endif
Bram Moolenaardd60c362023-02-27 15:49:53 +0000224unlet s:save_pandoc_lngs
Bram Moolenaarfc65cab2018-08-28 22:58:02 +0200225let &cpo = s:cpo_save
226unlet s:cpo_save
227
Bram Moolenaardd60c362023-02-27 15:49:53 +0000228let b:current_syntax = "rmd"
229
Bram Moolenaardb6ea062014-07-10 22:01:47 +0200230" vim: ts=8 sw=2