neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

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