rst.vim (12786B)
1 " Vim reST syntax file 2 " Language: reStructuredText documentation format 3 " Maintainer: Marshall Ward <marshall.ward@gmail.com> 4 " Previous Maintainer: Nikolai Weibull <now@bitwi.se> 5 " Reference: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html 6 " Website: https://github.com/marshallward/vim-restructuredtext 7 " Latest Revision: 2025-10-13 8 9 if exists("b:current_syntax") 10 finish 11 endif 12 13 let s:cpo_save = &cpo 14 set cpo&vim 15 16 " reStructuredText is case-insensitive 17 syntax case ignore 18 19 syn match rstTransition /^[=`:.'"~^_*+#-]\{4,}\s*$/ 20 21 syn cluster rstCruft contains=rstEmphasis,rstStrongEmphasis, 22 \ rstInterpretedTextOrHyperlinkReference,rstInlineLiteral, 23 \ rstSubstitutionReference,rstInlineInternalTargets,rstFootnoteReference, 24 \ rstHyperlinkReference 25 26 syn region rstLiteralBlock matchgroup=rstDelimiter 27 \ start='\(^\z(\s*\).*\)\@<=::\n\s*\n' skip='^\s*$' end='^\(\z1\s\+\)\@!' 28 \ contains=@NoSpell 29 30 syn region rstQuotedLiteralBlock matchgroup=rstDelimiter 31 \ start="::\_s*\n\ze\z([!\"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]\)" 32 \ end='^\z1\@!' contains=@NoSpell 33 34 syn region rstDoctestBlock matchgroup=rstDoctestBlockPrompt 35 \ start='^>>>\s' end='^$' 36 \ contains=rstDoctestBlockPrompt 37 38 syn match rstDoctestBlockPrompt contained '^>>>\s' 39 40 syn region rstTable transparent start='^\n\s*+[-=+]\+' end='^$' 41 \ contains=rstTableLines,@rstCruft 42 syn match rstTableLines contained display '|\|+\%(=\+\|-\+\)\=' 43 44 syn region rstSimpleTable transparent 45 \ start='^\n\%(\s*\)\@>\%(\%(=\+\)\@>\%(\s\+\)\@>\)\%(\%(\%(=\+\)\@>\%(\s*\)\@>\)\+\)\@>$' 46 \ end='^$' 47 \ contains=rstSimpleTableLines,@rstCruft 48 syn match rstSimpleTableLines contained display 49 \ '^\%(\s*\)\@>\%(\%(=\+\)\@>\%(\s\+\)\@>\)\%(\%(\%(=\+\)\@>\%(\s*\)\@>\)\+\)\@>$' 50 syn match rstSimpleTableLines contained display 51 \ '^\%(\s*\)\@>\%(\%(-\+\)\@>\%(\s\+\)\@>\)\%(\%(\%(-\+\)\@>\%(\s*\)\@>\)\+\)\@>$' 52 53 syn cluster rstDirectives contains=rstFootnote,rstCitation, 54 \ rstHyperlinkTarget,rstExDirective 55 56 syn match rstExplicitMarkup '^\s*\.\.\_s' 57 \ nextgroup=@rstDirectives,rstSubstitutionDefinition 58 \ contains=rstComment 59 60 " "Simple reference names are single words consisting of alphanumerics plus 61 " isolated (no two adjacent) internal hyphens, underscores, periods, colons 62 " and plus signs." 63 let s:ReferenceName = '[[:alnum:]]\%([-_.:+]\?[[:alnum:]]\+\)*' 64 65 syn keyword rstTodo contained FIXME TODO XXX NOTE 66 67 syn region rstComment 68 \ start='\v^\z(\s*)\.\.(\_s+[\[|_]|\_s+.*::)@!' skip=+^$+ end=/^\(\z1 \)\@!/ 69 \ contains=@Spell,rstTodo 70 71 " Note: Order matters for rstCitation and rstFootnote as the regex for 72 " citations also matches numeric only patterns, e.g. [1], which are footnotes. 73 " Since we define rstFootnote after rstCitation, it takes precedence, see 74 " |:syn-define|. 75 execute 'syn region rstCitation contained matchgroup=rstDirective' . 76 \ ' start=+\[' . s:ReferenceName . '\]\_s+' . 77 \ ' skip=+^$+' . 78 \ ' end=+^\s\@!+ contains=@Spell,@rstCruft' 79 80 execute 'syn region rstFootnote contained matchgroup=rstDirective' . 81 \ ' start=+\[\%(\d\+\|#\%(' . s:ReferenceName . '\)\=\|\*\)\]\_s+' . 82 \ ' skip=+^$+' . 83 \ ' end=+^\s\@!+ contains=@Spell,@rstCruft' 84 85 syn region rstHyperlinkTarget contained matchgroup=rstDirective 86 \ start='_\%(_\|[^:\\]*\%(\\.[^:\\]*\)*\):\_s' skip=+^$+ end=+^\s\@!+ 87 88 syn region rstHyperlinkTarget contained matchgroup=rstDirective 89 \ start='_`[^`\\]*\%(\\.[^`\\]*\)*`:\_s' skip=+^$+ end=+^\s\@!+ 90 91 syn region rstHyperlinkTarget matchgroup=rstDirective 92 \ start=+^__\_s+ skip=+^$+ end=+^\s\@!+ 93 94 execute 'syn region rstExDirective contained matchgroup=rstDirective' . 95 \ ' start=+' . s:ReferenceName . '::\_s+' . 96 \ ' skip=+^$+' . 97 \ ' end=+^\s\@!+ contains=@Spell,@rstCruft,rstLiteralBlock,rstExplicitMarkup' 98 99 execute 'syn match rstSubstitutionDefinition contained' . 100 \ ' /|.*|\_s\+/ nextgroup=@rstDirectives' 101 102 103 "" Inline Markup "" 104 105 function! s:DefineOneInlineMarkup(name, start, middle, end, char_left, char_right) 106 " Only escape the first char of a multichar delimiter (e.g. \* inside **) 107 if a:start[0] == '\' 108 let first = a:start[0:1] 109 else 110 let first = a:start[0] 111 endif 112 113 if a:start != '``' 114 let rst_contains=' contains=@Spell,rstEscape' . a:name 115 execute 'syn match rstEscape'.a:name.' +\\\\\|\\'.first.'+'.' contained' 116 else 117 let rst_contains=' contains=@Spell' 118 endif 119 120 execute 'syn region rst' . a:name . 121 \ ' start=+' . a:char_left . '\zs' . a:start . 122 \ '\ze[^[:space:]' . a:char_right . a:start[strlen(a:start) - 1] . ']+' . 123 \ a:middle . 124 \ ' end=+' . a:end . '\ze\%($\|\s\|[''"’)\]}>/:.,;!?\\-]\)+' . 125 \ rst_contains 126 127 if a:start != '``' 128 execute 'hi def link rstEscape'.a:name.' Special' 129 endif 130 endfunction 131 132 function! s:DefineInlineMarkup(name, start, middle, end) 133 if a:middle == '`' 134 let middle = ' skip=+\s'.a:middle.'+' 135 else 136 let middle = ' skip=+\\\\\|\\' . a:middle . '\|\s' . a:middle . '+' 137 endif 138 139 " Some characters may precede or follow an inline token 140 call s:DefineOneInlineMarkup(a:name, a:start, middle, a:end, "'", "'") 141 call s:DefineOneInlineMarkup(a:name, a:start, middle, a:end, '"', '"') 142 call s:DefineOneInlineMarkup(a:name, a:start, middle, a:end, '(', ')') 143 call s:DefineOneInlineMarkup(a:name, a:start, middle, a:end, '\[', '\]') 144 call s:DefineOneInlineMarkup(a:name, a:start, middle, a:end, '{', '}') 145 call s:DefineOneInlineMarkup(a:name, a:start, middle, a:end, '<', '>') 146 call s:DefineOneInlineMarkup(a:name, a:start, middle, a:end, '’', '’') 147 148 " TODO: Additional whitespace Unicode characters: Pd, Po, Pi, Pf, Ps 149 call s:DefineOneInlineMarkup(a:name, a:start, middle, a:end, '\%(^\|\s\|\%ua0\|[/:]\)', '') 150 151 execute 'syn match rst' . a:name . 152 \ ' +\%(^\|\s\|\%ua0\|[''"([{</:]\)\zs' . a:start . 153 \ '[^[:space:]' . a:start[strlen(a:start) - 1] . ']' 154 \ a:end . '\ze\%($\|\s\|[''")\]}>/:.,;!?\\-]\)+' 155 156 execute 'hi def link rst' . a:name . 'Delimiter' . ' rst' . a:name 157 endfunction 158 159 call s:DefineInlineMarkup('Emphasis', '\*', '\*', '\*') 160 call s:DefineInlineMarkup('StrongEmphasis', '\*\*', '\*', '\*\*') 161 call s:DefineInlineMarkup('InterpretedTextOrHyperlinkReference', '`', '`', '`_\{0,2}') 162 call s:DefineInlineMarkup('InlineLiteral', '``', '`', '``') 163 call s:DefineInlineMarkup('SubstitutionReference', '|', '|', '|_\{0,2}') 164 call s:DefineInlineMarkup('InlineInternalTargets', '_`', '`', '`') 165 166 " Sections are identified through their titles, which are marked up with 167 " adornment: "underlines" below the title text, or underlines and matching 168 " "overlines" above the title. An underline/overline is a single repeated 169 " punctuation character that begins in column 1 and forms a line extending at 170 " least as far as the right edge of the title text. 171 " 172 " It is difficult to count characters in a regex, but we at least special-case 173 " the case where the title has at least three characters to require the 174 " adornment to have at least three characters as well, in order to handle 175 " properly the case of a literal block: 176 " 177 " this is the end of a paragraph 178 " :: 179 " this is a literal block 180 syn match rstSections "\v^%(([=`:.'"~^_*+#-])\1+\n)?.{1,2}\n([=`:.'"~^_*+#-])\2+$" 181 \ contains=@Spell 182 syn match rstSections "\v^%(([=`:.'"~^_*+#-])\1{2,}\n)?.{3,}\n([=`:.'"~^_*+#-])\2{2,}$" 183 \ contains=@Spell 184 185 " TODO: Can’t remember why these two can’t be defined like the ones above. 186 execute 'syn match rstFootnoteReference contains=@NoSpell' . 187 \ ' +\%(\s\|^\)\[\%(\d\+\|#\%(' . s:ReferenceName . '\)\=\|\*\)\]_+' 188 189 execute 'syn match rstCitationReference contains=@NoSpell' . 190 \ ' +\%(\s\|^\)\[' . s:ReferenceName . '\]_\ze\%($\|\s\|[''")\]}>/:.,;!?\\-]\)+' 191 192 execute 'syn match rstHyperlinkReference' . 193 \ ' /\<' . s:ReferenceName . '__\=\ze\%($\|\s\|[''")\]}>/:.,;!?\\-]\)/' 194 195 syn match rstStandaloneHyperlink contains=@NoSpell 196 \ "\<\%(\%(\%(https\=\|file\|ftp\|gopher\)://\|\%(mailto\|news\):\)[^[:space:]'\"<>]\+\|www[[:alnum:]_-]*\.[[:alnum:]_-]\+\.[^[:space:]'\"<>]\+\)[[:alnum:]/]" 197 198 " `code` is the standard reST directive for source code. 199 " `code-block` and `sourcecode` are nearly identical directives in Sphinx. 200 syn region rstCodeBlock contained matchgroup=rstDirective 201 \ start=+\%(sourcecode\|code\%(-block\)\=\)::\s*\(\S*\)\?\s*\n\%(\s*:.*:\s*.*\s*\n\)*\n\ze\z(\s\+\)+ 202 \ skip=+^$+ 203 \ end=+^\z1\@!+ 204 \ contains=@NoSpell 205 syn cluster rstDirectives add=rstCodeBlock 206 207 if !exists('g:rst_syntax_code_list') 208 " A mapping from a Vim filetype to a list of alias patterns (pattern 209 " branches to be specific, see ':help /pattern'). E.g. given: 210 " 211 " let g:rst_syntax_code_list = { 212 " \ 'cpp': ['cpp', 'c++'], 213 " \ } 214 " 215 " then the respective contents of the following two rST directives: 216 " 217 " .. code:: cpp 218 " 219 " auto i = 42; 220 " 221 " .. code:: C++ 222 " 223 " auto i = 42; 224 " 225 " will both be highlighted as C++ code. As shown by the latter block 226 " pattern matching will be case-insensitive. 227 let g:rst_syntax_code_list = { 228 \ 'vim': ['vim'], 229 \ 'java': ['java'], 230 \ 'cpp': ['cpp', 'c++'], 231 \ 'lisp': ['lisp'], 232 \ 'php': ['php'], 233 \ 'python': ['python'], 234 \ 'perl': ['perl'], 235 \ 'sh': ['sh'], 236 \ } 237 elseif type(g:rst_syntax_code_list) == type([]) 238 " backward compatibility with former list format 239 let s:old_spec = g:rst_syntax_code_list 240 let g:rst_syntax_code_list = {} 241 for s:elem in s:old_spec 242 let g:rst_syntax_code_list[s:elem] = [s:elem] 243 endfor 244 endif 245 246 for s:filetype in keys(g:rst_syntax_code_list) 247 unlet! b:current_syntax 248 " guard against setting 'isk' option which might cause problems (issue #108) 249 let prior_isk = &l:iskeyword 250 let s:alias_pattern = '' 251 \.'\%(' 252 \.join(g:rst_syntax_code_list[s:filetype], '\|') 253 \.'\)' 254 255 exe 'syn include @rst'.s:filetype.' syntax/'.s:filetype.'.vim' 256 exe 'syn region rstDirective'.s:filetype 257 \.' matchgroup=rstDirective fold' 258 \.' start="\c\%(sourcecode\|code\%(-block\)\=\)::\s\+'.s:alias_pattern.'\_s*\n\ze\z(\s\+\)"' 259 \.' skip=#^$#' 260 \.' end=#^\z1\@!#' 261 \.' contains=@NoSpell,@rst'.s:filetype 262 exe 'syn cluster rstDirectives add=rstDirective'.s:filetype 263 264 " reset 'isk' setting, if it has been changed 265 if &l:iskeyword !=# prior_isk 266 let &l:iskeyword = prior_isk 267 endif 268 unlet! prior_isk 269 endfor 270 271 272 " Enable top level spell checking 273 syntax spell toplevel 274 275 exe "syn sync minlines=" . get(g:, 'rst_minlines', 50) . " linebreaks=2" 276 277 hi def link rstTodo Todo 278 hi def link rstComment Comment 279 hi def link rstSections Title 280 hi def link rstTransition rstSections 281 hi def link rstLiteralBlock String 282 hi def link rstQuotedLiteralBlock String 283 hi def link rstDoctestBlock PreProc 284 hi def link rstDoctestBlockPrompt rstDelimiter 285 hi def link rstTableLines rstDelimiter 286 hi def link rstSimpleTableLines rstTableLines 287 hi def link rstExplicitMarkup rstDirective 288 hi def link rstDirective Keyword 289 hi def link rstFootnote String 290 hi def link rstCitation String 291 hi def link rstHyperlinkTarget String 292 hi def link rstExDirective String 293 hi def link rstSubstitutionDefinition rstDirective 294 hi def link rstDelimiter Delimiter 295 hi def link rstInterpretedTextOrHyperlinkReference Identifier 296 hi def link rstInlineLiteral String 297 hi def link rstSubstitutionReference PreProc 298 hi def link rstInlineInternalTargets Identifier 299 hi def link rstFootnoteReference Identifier 300 hi def link rstCitationReference Identifier 301 hi def link rstHyperLinkReference Identifier 302 hi def link rstStandaloneHyperlink Identifier 303 hi def link rstCodeBlock String 304 if exists('g:rst_use_emphasis_colors') 305 " TODO: Less arbitrary color selection 306 hi def rstEmphasis ctermfg=13 term=italic cterm=italic gui=italic 307 hi def rstStrongEmphasis ctermfg=1 term=bold cterm=bold gui=bold 308 else 309 hi def rstEmphasis term=italic cterm=italic gui=italic 310 hi def rstStrongEmphasis term=bold cterm=bold gui=bold 311 endif 312 313 let b:current_syntax = "rst" 314 315 let &cpo = s:cpo_save 316 unlet s:cpo_save