plugins ohne plugin-manager
This commit is contained in:
parent
33a5f1d341
commit
fbeace99e5
260
.vim/doc/Tabular.txt
Normal file
260
.vim/doc/Tabular.txt
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
*Tabular.txt* Configurable, flexible, intuitive text aligning
|
||||
|
||||
*tabular* *tabular.vim*
|
||||
|
||||
#|#|#|#|#| #| #| ~
|
||||
#| #|#|#| #|#|#| #| #| #| #|#|#| #| #|#| ~
|
||||
#| #| #| #| #| #| #| #| #| #| #|#| ~
|
||||
#| #| #| #| #| #| #| #| #| #| #| ~
|
||||
#| #|#|#| #|#|#| #|#|#| #| #|#|#| #| ~
|
||||
|
||||
For Vim version 7.0 or newer
|
||||
|
||||
By Matt Wozniski
|
||||
mjw@drexel.edu
|
||||
|
||||
Reference Manual ~
|
||||
|
||||
*tabular-toc*
|
||||
|
||||
1. Description |tabular-intro|
|
||||
2. Walkthrough |tabular-walkthrough|
|
||||
3. Scripting |tabular-scripting|
|
||||
|
||||
The functionality mentioned here is a plugin, see |add-plugin|.
|
||||
You can avoid loading this plugin by setting the "Tabular_loaded" global
|
||||
variable in your |vimrc| file: >
|
||||
:let g:tabular_loaded = 1
|
||||
|
||||
==============================================================================
|
||||
1. Description *tabular-intro*
|
||||
|
||||
Sometimes, it's useful to line up text. Naturally, it's nicer to have the
|
||||
computer do this for you, since aligning things by hand quickly becomes
|
||||
unpleasant. While there are other plugins for aligning text, the ones I've
|
||||
tried are either impossibly difficult to understand and use, or too simplistic
|
||||
to handle complicated tasks. This plugin aims to make the easy things easy
|
||||
and the hard things possible, without providing an unnecessarily obtuse
|
||||
interface. It's still a work in progress, and criticisms are welcome.
|
||||
|
||||
==============================================================================
|
||||
2. Walkthrough *tabular-walkthrough* *:Tabularize*
|
||||
|
||||
Tabular's commands are based largely on regular expressions. The basic
|
||||
technique used by Tabular is taking some regex to match field delimiters,
|
||||
splitting the input lines at those delimiters, trimming unnecessary spaces
|
||||
from the non-delimiter parts, padding the non-delimiter parts of the lines
|
||||
with spaces to make them the same length, and joining things back together
|
||||
again.
|
||||
|
||||
For instance, consider starting with the following lines:
|
||||
>
|
||||
Some short phrase,some other phrase
|
||||
A much longer phrase here,and another long phrase
|
||||
<
|
||||
Let's say we want to line these lines up at the commas. We can tell
|
||||
Tabularize to do this by passing a pattern matching , to the Tabularize
|
||||
command:
|
||||
>
|
||||
:Tabularize /,
|
||||
|
||||
Some short phrase , some other phrase
|
||||
A much longer phrase here , and another long phrase
|
||||
<
|
||||
I encourage you to try copying those lines to another buffer and trying to
|
||||
call :Tabularize. You'll want to take notice of two things quickly: First,
|
||||
instead of requiring a range, Tabularize tries to figure out what you want to
|
||||
happen. Since it knows that you want to act on lines matching a comma, it
|
||||
will look upwards and downwards for lines around the current line that match a
|
||||
comma, and consider all contiguous lines matching the pattern to be the range
|
||||
to be acted upon. You can always override this by specifying a range, though.
|
||||
|
||||
The second thing you should notice is that you'll almost certainly be able to
|
||||
abbreviate :Tabularize to :Tab - using this form in mappings and scripts is
|
||||
discouraged as it will make conflicts with other scripts more likely, but for
|
||||
interactive use it's a nice timesaver. Another convenience feature is that
|
||||
running :Tabularize without providing a new pattern will cause it to reuse the
|
||||
last pattern it was called with.
|
||||
|
||||
So, anyway, now the commas line up. Splitting the lines on commas, Tabular
|
||||
realized that 'Some short phrase' would need to be padded with spaces to match
|
||||
the length of 'A much longer phrase here', and it did that before joining the
|
||||
lines back together. You'll also notice that, in addition to the spaces
|
||||
inserting for padding, extra spaces were inserted between fields. That's
|
||||
because by default, Tabular prints things left-aligned with one space between
|
||||
fields. If you wanted to print things right-aligned with no spaces between
|
||||
fields, you would provide a different format to the Tabularize command:
|
||||
>
|
||||
:Tabularize /,/r0
|
||||
|
||||
Some short phrase, some other phrase
|
||||
A much longer phrase here,and another long phrase
|
||||
<
|
||||
A format specifier is either l, r, or c, followed by one or more digits. If
|
||||
the letter is l, the field will be left aligned, similarly for r and right
|
||||
aligning and c and center aligning. The number following the letter is the
|
||||
number of spaces padding to insert before the start of the next field.
|
||||
Multiple format specifiers can be added to the same command - each field will
|
||||
be printed with the next format specifier in the list; when they all have been
|
||||
used the first will be used again, and so on. So, the last command right
|
||||
aligned every field, then inserted 0 spaces of padding before the next field.
|
||||
What if we wanted to right align the text before the comma, and left align the
|
||||
text after the comma? The command would look like this:
|
||||
>
|
||||
:Tabularize /,/r1c1l0
|
||||
|
||||
Some short phrase , some other phrase
|
||||
A much longer phrase here , and another long phrase
|
||||
<
|
||||
That command would be read as "Align the matching text, splitting fields on
|
||||
commas. Print everything before the first comma right aligned, then 1 space,
|
||||
then the comma center aligned, then 1 space, then everything after the comma
|
||||
left aligned." Notice that the alignment of the field the comma is in is
|
||||
irrelevant - since it's only 1 cell wide, it looks the same whether it's right,
|
||||
left, or center aligned. Also notice that the 0 padding spaces specified for
|
||||
the 3rd field are unused - but they would be used if there were enough fields
|
||||
to require looping through the fields again. For instance:
|
||||
>
|
||||
abc,def,ghi
|
||||
a,b
|
||||
a,b,c
|
||||
|
||||
:Tabularize /,/r1c1l0
|
||||
|
||||
abc , def, ghi
|
||||
a , b
|
||||
a , b , c
|
||||
<
|
||||
Notice that now, the format pattern has been reused; field 4 (the second comma)
|
||||
is right aligned, field 5 is center aligned. No spaces were inserted between
|
||||
the 3rd field (containing "def") and the 4th field (the second comma) because
|
||||
the format specified 'l0'.
|
||||
|
||||
But, what if you only wanted to act on the first comma on the line, rather than
|
||||
all of the commas on the line? Let's say we want everything before the first
|
||||
comma right aligned, then the comma, then everything after the comma left
|
||||
aligned:
|
||||
>
|
||||
abc,def,ghi
|
||||
a,b
|
||||
a,b,c
|
||||
|
||||
:Tabularize /^[^,]*\zs,/r0c0l0
|
||||
|
||||
abc,def,ghi
|
||||
a,b
|
||||
a,b,c
|
||||
<
|
||||
Here, we used a Vim regex that would only match the first comma on the line.
|
||||
It matches the beginning of the line, followed by all the non-comma characters
|
||||
up to the first comma, and then forgets about what it matched so far and
|
||||
pretends that the match starts exactly at the comma.
|
||||
|
||||
But, now that this command does exactly what we want it to, it's become pretty
|
||||
unwieldy. It would be unpleasant to need to type that more than once or
|
||||
twice. The solution is to assign a name to it.
|
||||
>
|
||||
:AddTabularPattern first_comma /^[^,]*\zs,/r0c0l0
|
||||
<
|
||||
Now, typing ":Tabularize first_comma" will do the same thing as typing the
|
||||
whole pattern out each time. Of course this is more useful if you store the
|
||||
name in a file to be used later.
|
||||
|
||||
NOTE: In order to make these new commands available every time vim starts,
|
||||
you'll need to put those new commands into a .vim file in a plugin directory
|
||||
somewhere in your 'runtimepath'. In order to make sure that Tabular.vim has
|
||||
already been loaded before your file tries to use :AddTabularPattern or
|
||||
:AddTabularPipeline, the new file should be installed in an after/plugin
|
||||
directory in 'runtimepath'. In general, it will be safe to find out where the
|
||||
TabularMaps.vim plugin was installed, and place other files extending
|
||||
Tabular.vim in the same directory as TabularMaps.vim. For more information,
|
||||
and some suggested best practices, check out the |tabular-scripting| section.
|
||||
|
||||
Lastly, we'll approach the case where tabular cannot achieve your desired goal
|
||||
just by splitting lines appart, trimming whitespace, padding with whitespace,
|
||||
and rejoining the lines. As an example, consider the multiple_spaces command
|
||||
from TabularMaps.vim. The goal is to split using two or more spaces as a
|
||||
field delimiter, and join fields back together, properly lined up, with only
|
||||
two spaces between the end of each field and the beginning of the next.
|
||||
Unfortunately, Tabular can't do this with only the commands we know so far:
|
||||
>
|
||||
:Tabularize / /
|
||||
<
|
||||
The above function won't work, because it will consider "a b" as 5 fields
|
||||
delimited by two pairs of 2 spaces ( 'a', ' ', '', ' ', 'b' ) instead of as
|
||||
3 fields delimited by one set of 2 or more spaces ( 'a', ' ', 'b' ).
|
||||
>
|
||||
:Tabularize / \+/
|
||||
<
|
||||
The above function won't work either, because it will leave the delimiter as 4
|
||||
spaces when used against "a b", meaning that we would fail at our goal of
|
||||
collapsing everything down to two spaces between fields. So, we need a new
|
||||
command to get around this:
|
||||
>
|
||||
:AddTabularPipeline multiple_spaces / \{2,}/
|
||||
\ map(a:lines, "substitute(v:val, ' \{2,}', ' ', 'g')")
|
||||
\ | tabular#TabularizeStrings(a:lines, ' ', 'l0')
|
||||
<
|
||||
Yeah. I know it looks complicated. Bear with me. I probably will try to add
|
||||
in some shortcuts for this syntax, but this verbose will be guaranteed to
|
||||
always work.
|
||||
|
||||
You should already recognize the name being assigned. The next thing to
|
||||
happen is / \{2,}/ which is a pattern specifying which lines should
|
||||
automatically be included in the range when no range is given. Without this,
|
||||
there would be no pattern to use for extending the range. Everything after
|
||||
that is a | separated list of expressions to be evaluated. In the context in
|
||||
which they will be evaluated, a:lines will be set to a List of Strings
|
||||
containing the text of the lines being filtered as they procede through the
|
||||
pipeline you've set up. The \ at the start of the lines are just vim's line
|
||||
continuation marker; you needn't worry much about them. So, the first
|
||||
expression in the pipeline transforms each line by replacing every instance of
|
||||
2 or more spaces with exactly two spaces. The second command in the pipeline
|
||||
performs the equivalent of ":Tabularize / /l0"; the only difference is that
|
||||
it is operating on a List of Strings rather than text in the buffer. At the
|
||||
end of the pipeline, the Strings in the modified a:lines (or the return value
|
||||
of the last expression in the pipeline, if it returns a List) will replace the
|
||||
chosen range.
|
||||
|
||||
==============================================================================
|
||||
3. Extending *tabular-scripting*
|
||||
|
||||
As mentioned above, the most important consideration when extending Tabular
|
||||
with new maps or commands is that your plugin must be loaded after Tabular.vim
|
||||
has finished loading, and only if Tabular.vim has loaded successfully. The
|
||||
easiest approach to making sure it loads after Tabular.vim is simply putting
|
||||
the new file (we'll call it "tabular_extra.vim" as an example) into an
|
||||
"after/plugin/" directory in 'runtimepath', for instance:
|
||||
>
|
||||
~/.vim/after/plugin/tabular_extra.vim
|
||||
<
|
||||
The default set of mappings, found in "TabularMaps.vim", is installed in
|
||||
the after/plugin/ subdirectory of whatever directory Tabular was installed to.
|
||||
|
||||
The other important consideration is making sure that your commands are only
|
||||
called if Tabular.vim was actually loaded. The easiest way to do this is by
|
||||
checking for the existence of the :Tabularize command at the start of your
|
||||
plugin. A short example plugin would look like this:
|
||||
>
|
||||
" after/plugin/my_tabular_commands.vim
|
||||
" Provides extra :Tabularize commands
|
||||
|
||||
if !exists(':Tabularize')
|
||||
finish " Give up here; the Tabular plugin musn't have been loaded
|
||||
endif
|
||||
|
||||
" Make line wrapping possible by resetting the 'cpo' option, first saving it
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
AddTabularPattern! asterisk /*/l1
|
||||
|
||||
AddTabularPipeline! remove_leading_spaces /^ /
|
||||
\ map(a:lines, "substitute(v:val, '^ *', '', '')")
|
||||
|
||||
" Restore the saved value of 'cpo'
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
<
|
||||
==============================================================================
|
||||
vim:tw=78:fo=tcq2:isk=!-~,^*,^\|,^\":ts=8:ft=help:norl:
|
||||
789
.vim/ftplugin/markdown.vim
Normal file
789
.vim/ftplugin/markdown.vim
Normal file
|
|
@ -0,0 +1,789 @@
|
|||
"TODO print messages when on visual mode. I only see VISUAL, not the messages.
|
||||
|
||||
" Function interface phylosophy:
|
||||
"
|
||||
" - functions take arbitrary line numbers as parameters.
|
||||
" Current cursor line is only a suitable default parameter.
|
||||
"
|
||||
" - only functions that bind directly to user actions:
|
||||
"
|
||||
" - print error messages.
|
||||
" All intermediate functions limit themselves return `0` to indicate an error.
|
||||
"
|
||||
" - move the cursor. All other functions do not move the cursor.
|
||||
"
|
||||
" This is how you should view headers for the header mappings:
|
||||
"
|
||||
" |BUFFER
|
||||
" |
|
||||
" |Outside any header
|
||||
" |
|
||||
" a-+# a
|
||||
" |
|
||||
" |Inside a
|
||||
" |
|
||||
" a-+
|
||||
" b-+## b
|
||||
" |
|
||||
" |inside b
|
||||
" |
|
||||
" b-+
|
||||
" c-+### c
|
||||
" |
|
||||
" |Inside c
|
||||
" |
|
||||
" c-+
|
||||
" d-|# d
|
||||
" |
|
||||
" |Inside d
|
||||
" |
|
||||
" d-+
|
||||
" e-|e
|
||||
" |====
|
||||
" |
|
||||
" |Inside e
|
||||
" |
|
||||
" e-+
|
||||
|
||||
" For each level, contains the regexp that matches at that level only.
|
||||
"
|
||||
let s:levelRegexpDict = {
|
||||
\ 1: '\v^(#[^#]@=|.+\n\=+$)',
|
||||
\ 2: '\v^(##[^#]@=|.+\n-+$)',
|
||||
\ 3: '\v^###[^#]@=',
|
||||
\ 4: '\v^####[^#]@=',
|
||||
\ 5: '\v^#####[^#]@=',
|
||||
\ 6: '\v^######[^#]@='
|
||||
\ }
|
||||
|
||||
" Maches any header level of any type.
|
||||
"
|
||||
" This could be deduced from `s:levelRegexpDict`, but it is more
|
||||
" efficient to have a single regexp for this.
|
||||
"
|
||||
let s:headersRegexp = '\v^(#|.+\n(\=+|-+)$)'
|
||||
|
||||
" Returns the line number of the first header before `line`, called the
|
||||
" current header.
|
||||
"
|
||||
" If there is no current header, return `0`.
|
||||
"
|
||||
" @param a:1 The line to look the header of. Default value: `getpos('.')`.
|
||||
"
|
||||
function! s:GetHeaderLineNum(...)
|
||||
if a:0 == 0
|
||||
let l:l = line('.')
|
||||
else
|
||||
let l:l = a:1
|
||||
endif
|
||||
while(l:l > 0)
|
||||
if join(getline(l:l, l:l + 1), "\n") =~ s:headersRegexp
|
||||
return l:l
|
||||
endif
|
||||
let l:l -= 1
|
||||
endwhile
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" - if inside a header goes to it.
|
||||
" Return its line number.
|
||||
"
|
||||
" - if on top level outside any headers,
|
||||
" print a warning
|
||||
" Return `0`.
|
||||
"
|
||||
function! s:MoveToCurHeader()
|
||||
let l:lineNum = s:GetHeaderLineNum()
|
||||
if l:lineNum != 0
|
||||
call cursor(l:lineNum, 1)
|
||||
else
|
||||
echo 'outside any header'
|
||||
"normal! gg
|
||||
endif
|
||||
return l:lineNum
|
||||
endfunction
|
||||
|
||||
" Move cursor to next header of any level.
|
||||
"
|
||||
" If there are no more headers, print a warning.
|
||||
"
|
||||
function! s:MoveToNextHeader()
|
||||
if search(s:headersRegexp, 'W') == 0
|
||||
"normal! G
|
||||
echo 'no next header'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Move cursor to previous header (before current) of any level.
|
||||
"
|
||||
" If it does not exist, print a warning.
|
||||
"
|
||||
function! s:MoveToPreviousHeader()
|
||||
let l:curHeaderLineNumber = s:GetHeaderLineNum()
|
||||
let l:noPreviousHeader = 0
|
||||
if l:curHeaderLineNumber <= 1
|
||||
let l:noPreviousHeader = 1
|
||||
else
|
||||
let l:previousHeaderLineNumber = s:GetHeaderLineNum(l:curHeaderLineNumber - 1)
|
||||
if l:previousHeaderLineNumber == 0
|
||||
let l:noPreviousHeader = 1
|
||||
else
|
||||
call cursor(l:previousHeaderLineNumber, 1)
|
||||
endif
|
||||
endif
|
||||
if l:noPreviousHeader
|
||||
echo 'no previous header'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" - if line is inside a header, return the header level (h1 -> 1, h2 -> 2, etc.).
|
||||
"
|
||||
" - if line is at top level outside any headers, return `0`.
|
||||
"
|
||||
function! s:GetHeaderLevel(...)
|
||||
if a:0 == 0
|
||||
let l:line = line('.')
|
||||
else
|
||||
let l:line = a:1
|
||||
endif
|
||||
let l:linenum = s:GetHeaderLineNum(l:line)
|
||||
if l:linenum != 0
|
||||
return s:GetLevelOfHeaderAtLine(l:linenum)
|
||||
else
|
||||
return 0
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Returns the level of the header at the given line.
|
||||
"
|
||||
" If there is no header at the given line, returns `0`.
|
||||
"
|
||||
function! s:GetLevelOfHeaderAtLine(linenum)
|
||||
let l:lines = join(getline(a:linenum, a:linenum + 1), "\n")
|
||||
for l:key in keys(s:levelRegexpDict)
|
||||
if l:lines =~ get(s:levelRegexpDict, l:key)
|
||||
return l:key
|
||||
endif
|
||||
endfor
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" Move cursor to parent header of the current header.
|
||||
"
|
||||
" If it does not exit, print a warning and do nothing.
|
||||
"
|
||||
function! s:MoveToParentHeader()
|
||||
let l:linenum = s:GetParentHeaderLineNumber()
|
||||
if l:linenum != 0
|
||||
call cursor(l:linenum, 1)
|
||||
else
|
||||
echo 'no parent header'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Return the line number of the parent header of line `line`.
|
||||
"
|
||||
" If it has no parent, return `0`.
|
||||
"
|
||||
function! s:GetParentHeaderLineNumber(...)
|
||||
if a:0 == 0
|
||||
let l:line = line('.')
|
||||
else
|
||||
let l:line = a:1
|
||||
endif
|
||||
let l:level = s:GetHeaderLevel(l:line)
|
||||
if l:level > 1
|
||||
let l:linenum = s:GetPreviousHeaderLineNumberAtLevel(l:level - 1, l:line)
|
||||
return l:linenum
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" Return the line number of the previous header of given level.
|
||||
" in relation to line `a:1`. If not given, `a:1 = getline()`
|
||||
"
|
||||
" `a:1` line is included, and this may return the current header.
|
||||
"
|
||||
" If none return 0.
|
||||
"
|
||||
function! s:GetNextHeaderLineNumberAtLevel(level, ...)
|
||||
if a:0 < 1
|
||||
let l:line = line('.')
|
||||
else
|
||||
let l:line = a:1
|
||||
endif
|
||||
let l:l = l:line
|
||||
while(l:l <= line('$'))
|
||||
if join(getline(l:l, l:l + 1), "\n") =~ get(s:levelRegexpDict, a:level)
|
||||
return l:l
|
||||
endif
|
||||
let l:l += 1
|
||||
endwhile
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" Return the line number of the previous header of given level.
|
||||
" in relation to line `a:1`. If not given, `a:1 = getline()`
|
||||
"
|
||||
" `a:1` line is included, and this may return the current header.
|
||||
"
|
||||
" If none return 0.
|
||||
"
|
||||
function! s:GetPreviousHeaderLineNumberAtLevel(level, ...)
|
||||
if a:0 == 0
|
||||
let l:line = line('.')
|
||||
else
|
||||
let l:line = a:1
|
||||
endif
|
||||
let l:l = l:line
|
||||
while(l:l > 0)
|
||||
if join(getline(l:l, l:l + 1), "\n") =~ get(s:levelRegexpDict, a:level)
|
||||
return l:l
|
||||
endif
|
||||
let l:l -= 1
|
||||
endwhile
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" Move cursor to next sibling header.
|
||||
"
|
||||
" If there is no next siblings, print a warning and don't move.
|
||||
"
|
||||
function! s:MoveToNextSiblingHeader()
|
||||
let l:curHeaderLineNumber = s:GetHeaderLineNum()
|
||||
let l:curHeaderLevel = s:GetLevelOfHeaderAtLine(l:curHeaderLineNumber)
|
||||
let l:curHeaderParentLineNumber = s:GetParentHeaderLineNumber()
|
||||
let l:nextHeaderSameLevelLineNumber = s:GetNextHeaderLineNumberAtLevel(l:curHeaderLevel, l:curHeaderLineNumber + 1)
|
||||
let l:noNextSibling = 0
|
||||
if l:nextHeaderSameLevelLineNumber == 0
|
||||
let l:noNextSibling = 1
|
||||
else
|
||||
let l:nextHeaderSameLevelParentLineNumber = s:GetParentHeaderLineNumber(l:nextHeaderSameLevelLineNumber)
|
||||
if l:curHeaderParentLineNumber == l:nextHeaderSameLevelParentLineNumber
|
||||
call cursor(l:nextHeaderSameLevelLineNumber, 1)
|
||||
else
|
||||
let l:noNextSibling = 1
|
||||
endif
|
||||
endif
|
||||
if l:noNextSibling
|
||||
echo 'no next sibling header'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Move cursor to previous sibling header.
|
||||
"
|
||||
" If there is no previous siblings, print a warning and do nothing.
|
||||
"
|
||||
function! s:MoveToPreviousSiblingHeader()
|
||||
let l:curHeaderLineNumber = s:GetHeaderLineNum()
|
||||
let l:curHeaderLevel = s:GetLevelOfHeaderAtLine(l:curHeaderLineNumber)
|
||||
let l:curHeaderParentLineNumber = s:GetParentHeaderLineNumber()
|
||||
let l:previousHeaderSameLevelLineNumber = s:GetPreviousHeaderLineNumberAtLevel(l:curHeaderLevel, l:curHeaderLineNumber - 1)
|
||||
let l:noPreviousSibling = 0
|
||||
if l:previousHeaderSameLevelLineNumber == 0
|
||||
let l:noPreviousSibling = 1
|
||||
else
|
||||
let l:previousHeaderSameLevelParentLineNumber = s:GetParentHeaderLineNumber(l:previousHeaderSameLevelLineNumber)
|
||||
if l:curHeaderParentLineNumber == l:previousHeaderSameLevelParentLineNumber
|
||||
call cursor(l:previousHeaderSameLevelLineNumber, 1)
|
||||
else
|
||||
let l:noPreviousSibling = 1
|
||||
endif
|
||||
endif
|
||||
if l:noPreviousSibling
|
||||
echo 'no previous sibling header'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Toc(...)
|
||||
if a:0 > 0
|
||||
let l:window_type = a:1
|
||||
else
|
||||
let l:window_type = 'vertical'
|
||||
endif
|
||||
|
||||
|
||||
let l:bufnr = bufnr('%')
|
||||
let l:cursor_line = line('.')
|
||||
let l:cursor_header = 0
|
||||
let l:fenced_block = 0
|
||||
let l:front_matter = 0
|
||||
let l:header_list = []
|
||||
let l:header_max_len = 0
|
||||
let l:vim_markdown_toc_autofit = get(g:, "vim_markdown_toc_autofit", 0)
|
||||
let l:vim_markdown_frontmatter = get(g:, "vim_markdown_frontmatter", 0)
|
||||
for i in range(1, line('$'))
|
||||
let l:lineraw = getline(i)
|
||||
let l:l1 = getline(i+1)
|
||||
let l:line = substitute(l:lineraw, "#", "\\\#", "g")
|
||||
if l:line =~ '````*' || l:line =~ '\~\~\~\~*'
|
||||
if l:fenced_block == 0
|
||||
let l:fenced_block = 1
|
||||
elseif l:fenced_block == 1
|
||||
let l:fenced_block = 0
|
||||
endif
|
||||
elseif l:vim_markdown_frontmatter == 1
|
||||
if l:front_matter == 1
|
||||
if l:line == '---'
|
||||
let l:front_matter = 0
|
||||
endif
|
||||
elseif i == 1
|
||||
if l:line == '---'
|
||||
let l:front_matter = 1
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
if l:line =~ '^#\+' || (l:l1 =~ '^=\+\s*$' || l:l1 =~ '^-\+\s*$') && l:line =~ '^\S'
|
||||
let l:is_header = 1
|
||||
else
|
||||
let l:is_header = 0
|
||||
endif
|
||||
if l:is_header == 1 && l:fenced_block == 0 && l:front_matter == 0
|
||||
" append line to location list
|
||||
let l:item = {'lnum': i, 'text': l:line, 'valid': 1, 'bufnr': l:bufnr, 'col': 1}
|
||||
let l:header_list = l:header_list + [l:item]
|
||||
" set header number of the cursor position
|
||||
if l:cursor_header == 0
|
||||
if i == l:cursor_line
|
||||
let l:cursor_header = len(l:header_list)
|
||||
elseif i > l:cursor_line
|
||||
let l:cursor_header = len(l:header_list) - 1
|
||||
endif
|
||||
endif
|
||||
" keep track of the longest header size (heading level + title)
|
||||
let l:total_len = stridx(l:line, ' ') + strdisplaywidth(l:line)
|
||||
if l:total_len > l:header_max_len
|
||||
let l:header_max_len = l:total_len
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
call setloclist(0, l:header_list)
|
||||
if len(l:header_list) == 0
|
||||
echom "Toc: No headers."
|
||||
return
|
||||
endif
|
||||
|
||||
if l:window_type ==# 'horizontal'
|
||||
lopen
|
||||
elseif l:window_type ==# 'vertical'
|
||||
vertical lopen
|
||||
" auto-fit toc window when possible to shrink it
|
||||
if (&columns/2) > l:header_max_len && l:vim_markdown_toc_autofit == 1
|
||||
execute 'vertical resize ' . (l:header_max_len + 1)
|
||||
else
|
||||
execute 'vertical resize ' . (&columns/2)
|
||||
endif
|
||||
elseif l:window_type ==# 'tab'
|
||||
tab lopen
|
||||
else
|
||||
lopen
|
||||
endif
|
||||
setlocal modifiable
|
||||
for i in range(1, line('$'))
|
||||
" this is the location-list data for the current item
|
||||
let d = getloclist(0)[i-1]
|
||||
" atx headers
|
||||
if match(d.text, "^#") > -1
|
||||
let l:level = len(matchstr(d.text, '#*', 'g'))-1
|
||||
let d.text = substitute(d.text, '\v^#*[ ]*', '', '')
|
||||
let d.text = substitute(d.text, '\v[ ]*#*$', '', '')
|
||||
" setex headers
|
||||
else
|
||||
let l:next_line = getbufline(d.bufnr, d.lnum+1)
|
||||
if match(l:next_line, "=") > -1
|
||||
let l:level = 0
|
||||
elseif match(l:next_line, "-") > -1
|
||||
let l:level = 1
|
||||
endif
|
||||
endif
|
||||
call setline(i, repeat(' ', l:level). d.text)
|
||||
endfor
|
||||
setlocal nomodified
|
||||
setlocal nomodifiable
|
||||
execute 'normal! ' . l:cursor_header . 'G'
|
||||
endfunction
|
||||
|
||||
" Convert Setex headers in range `line1 .. line2` to Atx.
|
||||
"
|
||||
" Return the number of conversions.
|
||||
"
|
||||
function! s:SetexToAtx(line1, line2)
|
||||
let l:originalNumLines = line('$')
|
||||
execute 'silent! ' . a:line1 . ',' . a:line2 . 'substitute/\v(.*\S.*)\n\=+$/# \1/'
|
||||
execute 'silent! ' . a:line1 . ',' . a:line2 . 'substitute/\v(.*\S.*)\n-+$/## \1/'
|
||||
return l:originalNumLines - line('$')
|
||||
endfunction
|
||||
|
||||
" If `a:1` is 0, decrease the level of all headers in range `line1 .. line2`.
|
||||
"
|
||||
" Otherwise, increase the level. `a:1` defaults to `0`.
|
||||
"
|
||||
function! s:HeaderDecrease(line1, line2, ...)
|
||||
if a:0 > 0
|
||||
let l:increase = a:1
|
||||
else
|
||||
let l:increase = 0
|
||||
endif
|
||||
if l:increase
|
||||
let l:forbiddenLevel = 6
|
||||
let l:replaceLevels = [5, 1]
|
||||
let l:levelDelta = 1
|
||||
else
|
||||
let l:forbiddenLevel = 1
|
||||
let l:replaceLevels = [2, 6]
|
||||
let l:levelDelta = -1
|
||||
endif
|
||||
for l:line in range(a:line1, a:line2)
|
||||
if join(getline(l:line, l:line + 1), "\n") =~ s:levelRegexpDict[l:forbiddenLevel]
|
||||
echomsg 'There is an h' . l:forbiddenLevel . ' at line ' . l:line . '. Aborting.'
|
||||
return
|
||||
endif
|
||||
endfor
|
||||
let l:numSubstitutions = s:SetexToAtx(a:line1, a:line2)
|
||||
let l:flags = (&gdefault ? '' : 'g')
|
||||
for l:level in range(replaceLevels[0], replaceLevels[1], -l:levelDelta)
|
||||
execute 'silent! ' . a:line1 . ',' . (a:line2 - l:numSubstitutions) . 'substitute/' . s:levelRegexpDict[l:level] . '/' . repeat('#', l:level + l:levelDelta) . '/' . l:flags
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
" Format table under cursor.
|
||||
"
|
||||
" Depends on Tabularize.
|
||||
"
|
||||
function! s:TableFormat()
|
||||
let l:pos = getpos('.')
|
||||
normal! {
|
||||
" Search instead of `normal! j` because of the table at beginning of file edge case.
|
||||
call search('|')
|
||||
normal! j
|
||||
" Remove everything that is not a pipe, colon or hyphen next to a colon othewise
|
||||
" well formated tables would grow because of addition of 2 spaces on the separator
|
||||
" line by Tabularize /|.
|
||||
let l:flags = (&gdefault ? '' : 'g')
|
||||
execute 's/\(:\@<!-:\@!\|[^|:-]\)//e' . l:flags
|
||||
execute 's/--/-/e' . l:flags
|
||||
Tabularize /|
|
||||
" Move colons for alignment to left or right side of the cell.
|
||||
execute 's/:\( \+\)|/\1:|/e' . l:flags
|
||||
execute 's/|\( \+\):/|:\1/e' . l:flags
|
||||
execute 's/ /-/' . l:flags
|
||||
call setpos('.', l:pos)
|
||||
endfunction
|
||||
|
||||
" Wrapper to do move commands in visual mode.
|
||||
"
|
||||
function! s:VisMove(f)
|
||||
norm! gv
|
||||
call function(a:f)()
|
||||
endfunction
|
||||
|
||||
" Map in both normal and visual modes.
|
||||
"
|
||||
function! s:MapNormVis(rhs,lhs)
|
||||
execute 'nn <buffer><silent> ' . a:rhs . ' :call ' . a:lhs . '()<cr>'
|
||||
execute 'vn <buffer><silent> ' . a:rhs . ' <esc>:call <sid>VisMove(''' . a:lhs . ''')<cr>'
|
||||
endfunction
|
||||
|
||||
" Parameters:
|
||||
"
|
||||
" - step +1 for right, -1 for left
|
||||
"
|
||||
" TODO: multiple lines.
|
||||
"
|
||||
function! s:FindCornerOfSyntax(lnum, col, step)
|
||||
let l:col = a:col
|
||||
let l:syn = synIDattr(synID(a:lnum, l:col, 1), 'name')
|
||||
while synIDattr(synID(a:lnum, l:col, 1), 'name') ==# l:syn
|
||||
let l:col += a:step
|
||||
endwhile
|
||||
return l:col - a:step
|
||||
endfunction
|
||||
|
||||
" Return the next position of the given syntax name,
|
||||
" inclusive on the given position.
|
||||
"
|
||||
" TODO: multiple lines
|
||||
"
|
||||
function! s:FindNextSyntax(lnum, col, name)
|
||||
let l:col = a:col
|
||||
let l:step = 1
|
||||
while synIDattr(synID(a:lnum, l:col, 1), 'name') !=# a:name
|
||||
let l:col += l:step
|
||||
endwhile
|
||||
return [a:lnum, l:col]
|
||||
endfunction
|
||||
|
||||
function! s:FindCornersOfSyntax(lnum, col)
|
||||
return [<sid>FindLeftOfSyntax(a:lnum, a:col), <sid>FindRightOfSyntax(a:lnum, a:col)]
|
||||
endfunction
|
||||
|
||||
function! s:FindRightOfSyntax(lnum, col)
|
||||
return <sid>FindCornerOfSyntax(a:lnum, a:col, 1)
|
||||
endfunction
|
||||
|
||||
function! s:FindLeftOfSyntax(lnum, col)
|
||||
return <sid>FindCornerOfSyntax(a:lnum, a:col, -1)
|
||||
endfunction
|
||||
|
||||
" Returns:
|
||||
"
|
||||
" - a string with the the URL for the link under the cursor
|
||||
" - an empty string if the cursor is not on a link
|
||||
"
|
||||
" TODO
|
||||
"
|
||||
" - multiline support
|
||||
" - give an error if the separator does is not on a link
|
||||
"
|
||||
function! s:Markdown_GetUrlForPosition(lnum, col)
|
||||
let l:lnum = a:lnum
|
||||
let l:col = a:col
|
||||
let l:syn = synIDattr(synID(l:lnum, l:col, 1), 'name')
|
||||
|
||||
if l:syn ==# 'mkdInlineURL' || l:syn ==# 'mkdURL' || l:syn ==# 'mkdLinkDefTarget'
|
||||
" Do nothing.
|
||||
elseif l:syn ==# 'mkdLink'
|
||||
let [l:lnum, l:col] = <sid>FindNextSyntax(l:lnum, l:col, 'mkdURL')
|
||||
let l:syn = 'mkdURL'
|
||||
elseif l:syn ==# 'mkdDelimiter'
|
||||
let l:line = getline(l:lnum)
|
||||
let l:char = l:line[col - 1]
|
||||
if l:char ==# '<'
|
||||
let l:col += 1
|
||||
elseif l:char ==# '>' || l:char ==# ')'
|
||||
let l:col -= 1
|
||||
elseif l:char ==# '[' || l:char ==# ']' || l:char ==# '('
|
||||
let [l:lnum, l:col] = <sid>FindNextSyntax(l:lnum, l:col, 'mkdURL')
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
|
||||
let [l:left, l:right] = <sid>FindCornersOfSyntax(l:lnum, l:col)
|
||||
return getline(l:lnum)[l:left - 1 : l:right - 1]
|
||||
endfunction
|
||||
|
||||
" Front end for GetUrlForPosition.
|
||||
"
|
||||
function! s:OpenUrlUnderCursor()
|
||||
let l:url = s:Markdown_GetUrlForPosition(line('.'), col('.'))
|
||||
if l:url != ''
|
||||
call s:VersionAwareNetrwBrowseX(l:url)
|
||||
else
|
||||
echomsg 'The cursor is not on a link.'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" We need a definition guard because we invoke 'edit' which will reload this
|
||||
" script while this function is running. We must not replace it.
|
||||
if !exists('*s:EditUrlUnderCursor')
|
||||
function s:EditUrlUnderCursor()
|
||||
let l:url = s:Markdown_GetUrlForPosition(line('.'), col('.'))
|
||||
if l:url != ''
|
||||
if get(g:, 'vim_markdown_autowrite', 0)
|
||||
write
|
||||
endif
|
||||
let l:anchor = ''
|
||||
if get(g:, 'vim_markdown_follow_anchor', 0)
|
||||
let l:parts = split(l:url, '#', 1)
|
||||
if len(l:parts) == 2
|
||||
let [l:url, l:anchor] = parts
|
||||
let l:anchorexpr = get(g:, 'vim_markdown_anchorexpr', '')
|
||||
if l:anchorexpr != ''
|
||||
let l:anchor = eval(substitute(
|
||||
\ l:anchorexpr, 'v:anchor',
|
||||
\ escape('"'.l:anchor.'"', '"'), ''))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
if l:url != ''
|
||||
let l:ext = ''
|
||||
if get(g:, 'vim_markdown_no_extensions_in_markdown', 0)
|
||||
" use another file extension if preferred
|
||||
if exists('g:vim_markdown_auto_extension_ext')
|
||||
let l:ext = '.'.g:vim_markdown_auto_extension_ext
|
||||
else
|
||||
let l:ext = '.md'
|
||||
endif
|
||||
endif
|
||||
let l:url = fnameescape(fnamemodify(expand('%:h').'/'.l:url.l:ext, ':.'))
|
||||
let l:editmethod = ''
|
||||
" determine how to open the linked file (split, tab, etc)
|
||||
if exists('g:vim_markdown_edit_url_in')
|
||||
if g:vim_markdown_edit_url_in == 'tab'
|
||||
let l:editmethod = 'tabnew'
|
||||
elseif g:vim_markdown_edit_url_in == 'vsplit'
|
||||
let l:editmethod = 'vsp'
|
||||
elseif g:vim_markdown_edit_url_in == 'hsplit'
|
||||
let l:editmethod = 'sp'
|
||||
else
|
||||
let l:editmethod = 'edit'
|
||||
endif
|
||||
else
|
||||
" default to current buffer
|
||||
let l:editmethod = 'edit'
|
||||
endif
|
||||
execute l:editmethod l:url
|
||||
endif
|
||||
if l:anchor != ''
|
||||
silent! execute '/'.l:anchor
|
||||
endif
|
||||
else
|
||||
echomsg 'The cursor is not on a link.'
|
||||
endif
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! s:VersionAwareNetrwBrowseX(url)
|
||||
if has('patch-7.4.567')
|
||||
call netrw#BrowseX(a:url, 0)
|
||||
else
|
||||
call netrw#NetrwBrowseX(a:url, 0)
|
||||
endif
|
||||
endf
|
||||
|
||||
function! s:MapNotHasmapto(lhs, rhs)
|
||||
if !hasmapto('<Plug>' . a:rhs)
|
||||
execute 'nmap <buffer>' . a:lhs . ' <Plug>' . a:rhs
|
||||
execute 'vmap <buffer>' . a:lhs . ' <Plug>' . a:rhs
|
||||
endif
|
||||
endfunction
|
||||
|
||||
call <sid>MapNormVis('<Plug>Markdown_MoveToNextHeader', '<sid>MoveToNextHeader')
|
||||
call <sid>MapNormVis('<Plug>Markdown_MoveToPreviousHeader', '<sid>MoveToPreviousHeader')
|
||||
call <sid>MapNormVis('<Plug>Markdown_MoveToNextSiblingHeader', '<sid>MoveToNextSiblingHeader')
|
||||
call <sid>MapNormVis('<Plug>Markdown_MoveToPreviousSiblingHeader', '<sid>MoveToPreviousSiblingHeader')
|
||||
call <sid>MapNormVis('<Plug>Markdown_MoveToParentHeader', '<sid>MoveToParentHeader')
|
||||
call <sid>MapNormVis('<Plug>Markdown_MoveToCurHeader', '<sid>MoveToCurHeader')
|
||||
nnoremap <Plug>Markdown_OpenUrlUnderCursor :call <sid>OpenUrlUnderCursor()<cr>
|
||||
nnoremap <Plug>Markdown_EditUrlUnderCursor :call <sid>EditUrlUnderCursor()<cr>
|
||||
|
||||
if !get(g:, 'vim_markdown_no_default_key_mappings', 0)
|
||||
call <sid>MapNotHasmapto(']]', 'Markdown_MoveToNextHeader')
|
||||
call <sid>MapNotHasmapto('[[', 'Markdown_MoveToPreviousHeader')
|
||||
call <sid>MapNotHasmapto('][', 'Markdown_MoveToNextSiblingHeader')
|
||||
call <sid>MapNotHasmapto('[]', 'Markdown_MoveToPreviousSiblingHeader')
|
||||
call <sid>MapNotHasmapto(']u', 'Markdown_MoveToParentHeader')
|
||||
call <sid>MapNotHasmapto(']c', 'Markdown_MoveToCurHeader')
|
||||
call <sid>MapNotHasmapto('gx', 'Markdown_OpenUrlUnderCursor')
|
||||
call <sid>MapNotHasmapto('ge', 'Markdown_EditUrlUnderCursor')
|
||||
endif
|
||||
|
||||
command! -buffer -range=% HeaderDecrease call s:HeaderDecrease(<line1>, <line2>)
|
||||
command! -buffer -range=% HeaderIncrease call s:HeaderDecrease(<line1>, <line2>, 1)
|
||||
command! -buffer -range=% SetexToAtx call s:SetexToAtx(<line1>, <line2>)
|
||||
command! -buffer TableFormat call s:TableFormat()
|
||||
command! -buffer Toc call s:Toc()
|
||||
command! -buffer Toch call s:Toc('horizontal')
|
||||
command! -buffer Tocv call s:Toc('vertical')
|
||||
command! -buffer Toct call s:Toc('tab')
|
||||
|
||||
" Heavily based on vim-notes - http://peterodding.com/code/vim/notes/
|
||||
if exists('g:vim_markdown_fenced_languages')
|
||||
let s:filetype_dict = {}
|
||||
for s:filetype in g:vim_markdown_fenced_languages
|
||||
let key = matchstr(s:filetype, "[^=]*")
|
||||
let val = matchstr(s:filetype, "[^=]*$")
|
||||
let s:filetype_dict[key] = val
|
||||
endfor
|
||||
else
|
||||
let s:filetype_dict = {
|
||||
\ 'c++': 'cpp',
|
||||
\ 'viml': 'vim',
|
||||
\ 'bash': 'sh',
|
||||
\ 'ini': 'dosini'
|
||||
\ }
|
||||
endif
|
||||
|
||||
function! s:MarkdownHighlightSources(force)
|
||||
" Syntax highlight source code embedded in notes.
|
||||
" Look for code blocks in the current file
|
||||
let filetypes = {}
|
||||
for line in getline(1, '$')
|
||||
let ft = matchstr(line, '```\s*\zs[0-9A-Za-z_+-]*')
|
||||
if !empty(ft) && ft !~ '^\d*$' | let filetypes[ft] = 1 | endif
|
||||
endfor
|
||||
if !exists('b:mkd_known_filetypes')
|
||||
let b:mkd_known_filetypes = {}
|
||||
endif
|
||||
if !exists('b:mkd_included_filetypes')
|
||||
" set syntax file name included
|
||||
let b:mkd_included_filetypes = {}
|
||||
endif
|
||||
if !a:force && (b:mkd_known_filetypes == filetypes || empty(filetypes))
|
||||
return
|
||||
endif
|
||||
|
||||
" Now we're ready to actually highlight the code blocks.
|
||||
let startgroup = 'mkdCodeStart'
|
||||
let endgroup = 'mkdCodeEnd'
|
||||
for ft in keys(filetypes)
|
||||
if a:force || !has_key(b:mkd_known_filetypes, ft)
|
||||
if has_key(s:filetype_dict, ft)
|
||||
let filetype = s:filetype_dict[ft]
|
||||
else
|
||||
let filetype = ft
|
||||
endif
|
||||
let group = 'mkdSnippet' . toupper(substitute(filetype, "[+-]", "_", "g"))
|
||||
if !has_key(b:mkd_included_filetypes, filetype)
|
||||
let include = s:SyntaxInclude(filetype)
|
||||
let b:mkd_included_filetypes[filetype] = 1
|
||||
else
|
||||
let include = '@' . toupper(filetype)
|
||||
endif
|
||||
let command = 'syntax region %s matchgroup=%s start="^\s*```\s*%s$" matchgroup=%s end="\s*```$" keepend contains=%s%s'
|
||||
execute printf(command, group, startgroup, ft, endgroup, include, has('conceal') && get(g:, 'vim_markdown_conceal', 1) && get(g:, 'vim_markdown_conceal_code_blocks', 1) ? ' concealends' : '')
|
||||
execute printf('syntax cluster mkdNonListItem add=%s', group)
|
||||
|
||||
let b:mkd_known_filetypes[ft] = 1
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:SyntaxInclude(filetype)
|
||||
" Include the syntax highlighting of another {filetype}.
|
||||
let grouplistname = '@' . toupper(a:filetype)
|
||||
" Unset the name of the current syntax while including the other syntax
|
||||
" because some syntax scripts do nothing when "b:current_syntax" is set
|
||||
if exists('b:current_syntax')
|
||||
let syntax_save = b:current_syntax
|
||||
unlet b:current_syntax
|
||||
endif
|
||||
try
|
||||
execute 'syntax include' grouplistname 'syntax/' . a:filetype . '.vim'
|
||||
execute 'syntax include' grouplistname 'after/syntax/' . a:filetype . '.vim'
|
||||
catch /E484/
|
||||
" Ignore missing scripts
|
||||
endtry
|
||||
" Restore the name of the current syntax
|
||||
if exists('syntax_save')
|
||||
let b:current_syntax = syntax_save
|
||||
elseif exists('b:current_syntax')
|
||||
unlet b:current_syntax
|
||||
endif
|
||||
return grouplistname
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:MarkdownRefreshSyntax(force)
|
||||
if &filetype =~ 'markdown' && line('$') > 1
|
||||
call s:MarkdownHighlightSources(a:force)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:MarkdownClearSyntaxVariables()
|
||||
if &filetype =~ 'markdown'
|
||||
unlet! b:mkd_included_filetypes
|
||||
endif
|
||||
endfunction
|
||||
|
||||
augroup Mkd
|
||||
autocmd! * <buffer>
|
||||
autocmd BufWinEnter <buffer> call s:MarkdownRefreshSyntax(1)
|
||||
autocmd BufUnload <buffer> call s:MarkdownClearSyntaxVariables()
|
||||
autocmd BufWritePost <buffer> call s:MarkdownRefreshSyntax(0)
|
||||
autocmd InsertEnter,InsertLeave <buffer> call s:MarkdownRefreshSyntax(0)
|
||||
autocmd CursorHold,CursorHoldI <buffer> call s:MarkdownRefreshSyntax(0)
|
||||
augroup END
|
||||
346
.vim/plugin/Tabular.vim
Normal file
346
.vim/plugin/Tabular.vim
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
" Tabular: Align columnar data using regex-designated column boundaries
|
||||
" Maintainer: Matthew Wozniski (godlygeek@gmail.com)
|
||||
" Date: Thu, 03 May 2012 20:49:32 -0400
|
||||
" Version: 1.0
|
||||
"
|
||||
" Long Description:
|
||||
" Sometimes, it's useful to line up text. Naturally, it's nicer to have the
|
||||
" computer do this for you, since aligning things by hand quickly becomes
|
||||
" unpleasant. While there are other plugins for aligning text, the ones I've
|
||||
" tried are either impossibly difficult to understand and use, or too simplistic
|
||||
" to handle complicated tasks. This plugin aims to make the easy things easy
|
||||
" and the hard things possible, without providing an unnecessarily obtuse
|
||||
" interface. It's still a work in progress, and criticisms are welcome.
|
||||
"
|
||||
" License:
|
||||
" Copyright (c) 2012, Matthew J. Wozniski
|
||||
" All rights reserved.
|
||||
"
|
||||
" Redistribution and use in source and binary forms, with or without
|
||||
" modification, are permitted provided that the following conditions are met:
|
||||
" * Redistributions of source code must retain the above copyright notice,
|
||||
" this list of conditions and the following disclaimer.
|
||||
" * Redistributions in binary form must reproduce the above copyright
|
||||
" notice, this list of conditions and the following disclaimer in the
|
||||
" documentation and/or other materials provided with the distribution.
|
||||
" * The names of the contributors may not be used to endorse or promote
|
||||
" products derived from this software without specific prior written
|
||||
" permission.
|
||||
"
|
||||
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
|
||||
" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
" NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
" Abort if running in vi-compatible mode or the user doesn't want us.
|
||||
if &cp || exists('g:tabular_loaded')
|
||||
if &cp && &verbose
|
||||
echo "Not loading Tabular in compatible mode."
|
||||
endif
|
||||
finish
|
||||
endif
|
||||
|
||||
let g:tabular_loaded = 1
|
||||
|
||||
" Stupid vimscript crap {{{1
|
||||
let s:savecpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
" Private Things {{{1
|
||||
|
||||
" Dictionary of command name to command
|
||||
let s:TabularCommands = {}
|
||||
|
||||
" Generate tab completion list for :Tabularize {{{2
|
||||
" Return a list of commands that match the command line typed so far.
|
||||
" NOTE: Tries to handle commands with spaces in the name, but Vim doesn't seem
|
||||
" to handle that terribly well... maybe I should give up on that.
|
||||
function! s:CompleteTabularizeCommand(argstart, cmdline, cursorpos)
|
||||
let names = keys(s:TabularCommands)
|
||||
if exists("b:TabularCommands")
|
||||
let names += keys(b:TabularCommands)
|
||||
endif
|
||||
|
||||
let cmdstart = substitute(a:cmdline, '^\s*\S\+\s*', '', '')
|
||||
|
||||
return filter(names, 'v:val =~# ''^\V'' . escape(cmdstart, ''\'')')
|
||||
endfunction
|
||||
|
||||
" Choose the proper command map from the given command line {{{2
|
||||
" Returns [ command map, command line with leading <buffer> removed ]
|
||||
function! s:ChooseCommandMap(commandline)
|
||||
let map = s:TabularCommands
|
||||
let cmd = a:commandline
|
||||
|
||||
if cmd =~# '^<buffer>\s\+'
|
||||
if !exists('b:TabularCommands')
|
||||
let b:TabularCommands = {}
|
||||
endif
|
||||
let map = b:TabularCommands
|
||||
let cmd = substitute(cmd, '^<buffer>\s\+', '', '')
|
||||
endif
|
||||
|
||||
return [ map, cmd ]
|
||||
endfunction
|
||||
|
||||
" Parse '/pattern/format' into separate pattern and format parts. {{{2
|
||||
" If parsing fails, return [ '', '' ]
|
||||
function! s:ParsePattern(string)
|
||||
if a:string[0] != '/'
|
||||
return ['','']
|
||||
endif
|
||||
|
||||
let pat = '\\\@<!\%(\\\\\)\{-}\zs/' . tabular#ElementFormatPattern() . '*$'
|
||||
let format = matchstr(a:string[1:-1], pat)
|
||||
if !empty(format)
|
||||
let format = format[1 : -1]
|
||||
let pattern = a:string[1 : -len(format) - 2]
|
||||
else
|
||||
let pattern = a:string[1 : -1]
|
||||
endif
|
||||
|
||||
return [pattern, format]
|
||||
endfunction
|
||||
|
||||
" Split apart a list of | separated expressions. {{{2
|
||||
function! s:SplitCommands(string)
|
||||
if a:string =~ '^\s*$'
|
||||
return []
|
||||
endif
|
||||
|
||||
let end = match(a:string, "[\"'|]")
|
||||
|
||||
" Loop until we find a delimiting | or end-of-string
|
||||
while end != -1 && (a:string[end] != '|' || a:string[end+1] == '|')
|
||||
if a:string[end] == "'"
|
||||
let end = match(a:string, "'", end+1) + 1
|
||||
if end == 0
|
||||
throw "No matching end single quote"
|
||||
endif
|
||||
elseif a:string[end] == '"'
|
||||
" Find a " preceded by an even number of \ (or 0)
|
||||
let pattern = '\%(\\\@<!\%(\\\\\)*\)\@<="'
|
||||
let end = matchend(a:string, pattern, end+1) + 1
|
||||
if end == 0
|
||||
throw "No matching end double quote"
|
||||
endif
|
||||
else " Found ||
|
||||
let end += 2
|
||||
endif
|
||||
|
||||
let end = match(a:string, "[\"'|]", end)
|
||||
endwhile
|
||||
|
||||
if end == 0 || a:string[0 : end - (end > 0)] =~ '^\s*$'
|
||||
throw "Empty element"
|
||||
endif
|
||||
|
||||
if end == -1
|
||||
let rv = [ a:string ]
|
||||
else
|
||||
let rv = [ a:string[0 : end-1] ] + s:SplitCommands(a:string[end+1 : -1])
|
||||
endif
|
||||
|
||||
return rv
|
||||
endfunction
|
||||
|
||||
" Public Things {{{1
|
||||
|
||||
" Command associating a command name with a simple pattern command {{{2
|
||||
" AddTabularPattern[!] [<buffer>] name /pattern[/format]
|
||||
"
|
||||
" If <buffer> is provided, the command will only be available in the current
|
||||
" buffer, and will be used instead of any global command with the same name.
|
||||
"
|
||||
" If a command with the same name and scope already exists, it is an error,
|
||||
" unless the ! is provided, in which case the existing command will be
|
||||
" replaced.
|
||||
"
|
||||
" pattern is a regex describing the delimiter to be used.
|
||||
"
|
||||
" format describes the format pattern to be used. The default will be used if
|
||||
" none is provided.
|
||||
com! -nargs=+ -bang AddTabularPattern
|
||||
\ call AddTabularPattern(<q-args>, <bang>0)
|
||||
|
||||
function! AddTabularPattern(command, force)
|
||||
try
|
||||
let [ commandmap, rest ] = s:ChooseCommandMap(a:command)
|
||||
|
||||
let name = matchstr(rest, '.\{-}\ze\s*/')
|
||||
let pattern = substitute(rest, '.\{-}\s*\ze/', '', '')
|
||||
|
||||
let [ pattern, format ] = s:ParsePattern(pattern)
|
||||
|
||||
if empty(name) || empty(pattern)
|
||||
throw "Invalid arguments!"
|
||||
endif
|
||||
|
||||
if !a:force && has_key(commandmap, name)
|
||||
throw string(name) . " is already defined, use ! to overwrite."
|
||||
endif
|
||||
|
||||
let command = "tabular#TabularizeStrings(a:lines, " . string(pattern)
|
||||
|
||||
if !empty(format)
|
||||
let command .= ", " . string(format)
|
||||
endif
|
||||
|
||||
let command .= ")"
|
||||
|
||||
let commandmap[name] = { 'pattern' : pattern, 'commands' : [ command ] }
|
||||
catch
|
||||
echohl ErrorMsg
|
||||
echomsg "AddTabularPattern: " . v:exception
|
||||
echohl None
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" Command associating a command name with a pipeline of functions {{{2
|
||||
" AddTabularPipeline[!] [<buffer>] name /pattern/ func [ | func2 [ | func3 ] ]
|
||||
"
|
||||
" If <buffer> is provided, the command will only be available in the current
|
||||
" buffer, and will be used instead of any global command with the same name.
|
||||
"
|
||||
" If a command with the same name and scope already exists, it is an error,
|
||||
" unless the ! is provided, in which case the existing command will be
|
||||
" replaced.
|
||||
"
|
||||
" pattern is a regex that will be used to determine which lines will be
|
||||
" filtered. If the cursor line doesn't match the pattern, using the command
|
||||
" will be a no-op, otherwise the cursor and all contiguous lines matching the
|
||||
" pattern will be filtered.
|
||||
"
|
||||
" Each 'func' argument represents a function to be called. This function
|
||||
" will have access to a:lines, a List containing one String per line being
|
||||
" filtered.
|
||||
com! -nargs=+ -bang AddTabularPipeline
|
||||
\ call AddTabularPipeline(<q-args>, <bang>0)
|
||||
|
||||
function! AddTabularPipeline(command, force)
|
||||
try
|
||||
let [ commandmap, rest ] = s:ChooseCommandMap(a:command)
|
||||
|
||||
let name = matchstr(rest, '.\{-}\ze\s*/')
|
||||
let pattern = substitute(rest, '.\{-}\s*\ze/', '', '')
|
||||
|
||||
let commands = matchstr(pattern, '^/.\{-}\\\@<!\%(\\\\\)\{-}/\zs.*')
|
||||
let pattern = matchstr(pattern, '/\zs.\{-}\\\@<!\%(\\\\\)\{-}\ze/')
|
||||
|
||||
if empty(name) || empty(pattern)
|
||||
throw "Invalid arguments!"
|
||||
endif
|
||||
|
||||
if !a:force && has_key(commandmap, name)
|
||||
throw string(name) . " is already defined, use ! to overwrite."
|
||||
endif
|
||||
|
||||
let commandlist = s:SplitCommands(commands)
|
||||
|
||||
if empty(commandlist)
|
||||
throw "Must provide a list of functions!"
|
||||
endif
|
||||
|
||||
let commandmap[name] = { 'pattern' : pattern, 'commands' : commandlist }
|
||||
catch
|
||||
echohl ErrorMsg
|
||||
echomsg "AddTabularPipeline: " . v:exception
|
||||
echohl None
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" Tabularize /pattern[/format] {{{2
|
||||
" Tabularize name
|
||||
"
|
||||
" Align text, either using the given pattern, or the command associated with
|
||||
" the given name.
|
||||
com! -nargs=* -range -complete=customlist,<SID>CompleteTabularizeCommand
|
||||
\ Tabularize <line1>,<line2>call Tabularize(<q-args>)
|
||||
|
||||
function! Tabularize(command, ...) range
|
||||
let piperange_opt = {}
|
||||
if a:0
|
||||
let piperange_opt = a:1
|
||||
endif
|
||||
|
||||
if empty(a:command)
|
||||
if !exists("s:last_tabularize_command")
|
||||
echohl ErrorMsg
|
||||
echomsg "Tabularize hasn't been called yet; no pattern/command to reuse!"
|
||||
echohl None
|
||||
return
|
||||
endif
|
||||
else
|
||||
let s:last_tabularize_command = a:command
|
||||
endif
|
||||
|
||||
let command = s:last_tabularize_command
|
||||
|
||||
let range = a:firstline . ',' . a:lastline
|
||||
|
||||
try
|
||||
let [ pattern, format ] = s:ParsePattern(command)
|
||||
|
||||
if !empty(pattern)
|
||||
let cmd = "tabular#TabularizeStrings(a:lines, " . string(pattern)
|
||||
|
||||
if !empty(format)
|
||||
let cmd .= "," . string(format)
|
||||
endif
|
||||
|
||||
let cmd .= ")"
|
||||
|
||||
exe range . 'call tabular#PipeRangeWithOptions(pattern, [ cmd ], '
|
||||
\ . 'piperange_opt)'
|
||||
else
|
||||
if exists('b:TabularCommands') && has_key(b:TabularCommands, command)
|
||||
let usercmd = b:TabularCommands[command]
|
||||
elseif has_key(s:TabularCommands, command)
|
||||
let usercmd = s:TabularCommands[command]
|
||||
else
|
||||
throw "Unrecognized command " . string(command)
|
||||
endif
|
||||
|
||||
exe range . 'call tabular#PipeRangeWithOptions(usercmd["pattern"], '
|
||||
\ . 'usercmd["commands"], piperange_opt)'
|
||||
endif
|
||||
catch
|
||||
echohl ErrorMsg
|
||||
echomsg "Tabularize: " . v:exception
|
||||
echohl None
|
||||
return
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" GTabularize /pattern[/format] {{{2
|
||||
" GTabularize name
|
||||
"
|
||||
" Align text on only matching lines, either using the given pattern, or the
|
||||
" command associated with the given name. Mnemonically, this is similar to
|
||||
" the :global command, which takes some action on all rows matching a pattern
|
||||
" in a range. This command is different from normal :Tabularize in 3 ways:
|
||||
" 1) If a line in the range does not match the pattern, it will be left
|
||||
" unchanged, and not in any way affect the outcome of other lines in the
|
||||
" range (at least, normally - but Pipelines can and will still look at
|
||||
" non-matching rows unless they are specifically written to be aware of
|
||||
" tabular#DoGTabularize() and handle it appropriately).
|
||||
" 2) No automatic range determination - :Tabularize automatically expands
|
||||
" a single-line range (or a call with no range) to include all adjacent
|
||||
" matching lines. That behavior does not make sense for this command.
|
||||
" 3) If called without a range, it will act on all lines in the buffer (like
|
||||
" :global) rather than only a single line
|
||||
com! -nargs=* -range=% -complete=customlist,<SID>CompleteTabularizeCommand
|
||||
\ GTabularize <line1>,<line2>
|
||||
\ call Tabularize(<q-args>, { 'mode': 'GTabularize' } )
|
||||
|
||||
" Stupid vimscript crap, part 2 {{{1
|
||||
let &cpo = s:savecpo
|
||||
unlet s:savecpo
|
||||
|
||||
" vim:set sw=2 sts=2 fdm=marker:
|
||||
73
.vim/plugin/TabularMaps.vim
Normal file
73
.vim/plugin/TabularMaps.vim
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
" Copyright (c) 2016, Matthew J. Wozniski
|
||||
" All rights reserved.
|
||||
"
|
||||
" Redistribution and use in source and binary forms, with or without
|
||||
" modification, are permitted provided that the following conditions are met:
|
||||
" * Redistributions of source code must retain the above copyright notice,
|
||||
" this list of conditions and the following disclaimer.
|
||||
" * Redistributions in binary form must reproduce the above copyright
|
||||
" notice, this list of conditions and the following disclaimer in the
|
||||
" documentation and/or other materials provided with the distribution.
|
||||
" * The names of the contributors may not be used to endorse or promote
|
||||
" products derived from this software without specific prior written
|
||||
" permission.
|
||||
"
|
||||
" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
|
||||
" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
" NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
" NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
" EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if !exists(':Tabularize') || get(g:, 'no_default_tabular_maps', 0)
|
||||
finish " Tabular.vim wasn't loaded or the default maps are unwanted
|
||||
endif
|
||||
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
AddTabularPattern! assignment /[|&+*/%<>=!~-]\@<!\([<>!=]=\|=\~\)\@![|&+*/%<>=!~-]*=/l1r1
|
||||
AddTabularPattern! two_spaces / /l0
|
||||
|
||||
AddTabularPipeline! multiple_spaces / / map(a:lines, "substitute(v:val, ' *', ' ', 'g')") | tabular#TabularizeStrings(a:lines, ' ', 'l0')
|
||||
|
||||
AddTabularPipeline! argument_list /(.*)/ map(a:lines, 'substitute(v:val, ''\s*\([(,)]\)\s*'', ''\1'', ''g'')')
|
||||
\ | tabular#TabularizeStrings(a:lines, '[(,)]', 'l0')
|
||||
\ | map(a:lines, 'substitute(v:val, ''\(\s*\),'', '',\1 '', "g")')
|
||||
\ | map(a:lines, 'substitute(v:val, ''\s*)'', ")", "g")')
|
||||
|
||||
function! SplitCDeclarations(lines)
|
||||
let rv = []
|
||||
for line in a:lines
|
||||
" split the line into declaractions
|
||||
let split = split(line, '\s*[,;]\s*')
|
||||
" separate the type from the first declaration
|
||||
let type = substitute(split[0], '\%(\%([&*]\s*\)*\)\=\k\+$', '', '')
|
||||
" add the ; back on every declaration
|
||||
call map(split, 'v:val . ";"')
|
||||
" add the first element to the return as-is, and remove it from the list
|
||||
let rv += [ remove(split, 0) ]
|
||||
" transform the other elements by adding the type on at the beginning
|
||||
call map(split, 'type . v:val')
|
||||
" and add them all to the return
|
||||
let rv += split
|
||||
endfor
|
||||
return rv
|
||||
endfunction
|
||||
|
||||
AddTabularPipeline! split_declarations /,.*;/ SplitCDeclarations(a:lines)
|
||||
|
||||
AddTabularPattern! ternary_operator /^.\{-}\zs?\|:/l1
|
||||
|
||||
AddTabularPattern! cpp_io /<<\|>>/l1
|
||||
|
||||
AddTabularPattern! pascal_assign /:=/l1
|
||||
|
||||
AddTabularPattern! trailing_c_comments /\/\*\|\*\/\|\/\//l1
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
184
.vim/syntax/markdown.vim
Normal file
184
.vim/syntax/markdown.vim
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
" Vim syntax file
|
||||
" Language: Markdown
|
||||
" Maintainer: Ben Williams <benw@plasticboy.com>
|
||||
" URL: http://plasticboy.com/markdown-vim-mode/
|
||||
" Remark: Uses HTML syntax file
|
||||
" TODO: Handle stuff contained within stuff (e.g. headings within blockquotes)
|
||||
|
||||
|
||||
" Read the HTML syntax to start with
|
||||
if version < 600
|
||||
so <sfile>:p:h/html.vim
|
||||
else
|
||||
runtime! syntax/html.vim
|
||||
|
||||
if exists('b:current_syntax')
|
||||
unlet b:current_syntax
|
||||
endif
|
||||
endif
|
||||
|
||||
if version < 600
|
||||
syntax clear
|
||||
elseif exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
" don't use standard HiLink, it will not work with included syntax files
|
||||
if version < 508
|
||||
command! -nargs=+ HtmlHiLink hi link <args>
|
||||
else
|
||||
command! -nargs=+ HtmlHiLink hi def link <args>
|
||||
endif
|
||||
|
||||
syn spell toplevel
|
||||
syn case ignore
|
||||
syn sync linebreaks=1
|
||||
|
||||
let s:conceal = ''
|
||||
let s:concealends = ''
|
||||
let s:concealcode = ''
|
||||
if has('conceal') && get(g:, 'vim_markdown_conceal', 1)
|
||||
let s:conceal = ' conceal'
|
||||
let s:concealends = ' concealends'
|
||||
endif
|
||||
if has('conceal') && get(g:, 'vim_markdown_conceal_code_blocks', 1)
|
||||
let s:concealcode = ' concealends'
|
||||
endif
|
||||
|
||||
" additions to HTML groups
|
||||
if get(g:, 'vim_markdown_emphasis_multiline', 1)
|
||||
let s:oneline = ''
|
||||
else
|
||||
let s:oneline = ' oneline'
|
||||
endif
|
||||
syn region mkdItalic matchgroup=mkdItalic start="\%(\*\|_\)" end="\%(\*\|_\)"
|
||||
syn region mkdBold matchgroup=mkdBold start="\%(\*\*\|__\)" end="\%(\*\*\|__\)"
|
||||
syn region mkdBoldItalic matchgroup=mkdBoldItalic start="\%(\*\*\*\|___\)" end="\%(\*\*\*\|___\)"
|
||||
execute 'syn region htmlItalic matchgroup=mkdItalic start="\%(^\|\s\)\zs\*\ze[^\\\*\t ]\%(\%([^*]\|\\\*\|\n\)*[^\\\*\t ]\)\?\*\_W" end="[^\\\*\t ]\zs\*\ze\_W" keepend contains=@Spell' . s:oneline . s:concealends
|
||||
execute 'syn region htmlItalic matchgroup=mkdItalic start="\%(^\|\s\)\zs_\ze[^\\_\t ]" end="[^\\_\t ]\zs_\ze\_W" keepend contains=@Spell' . s:oneline . s:concealends
|
||||
execute 'syn region htmlBold matchgroup=mkdBold start="\%(^\|\s\)\zs\*\*\ze\S" end="\S\zs\*\*" keepend contains=@Spell' . s:oneline . s:concealends
|
||||
execute 'syn region htmlBold matchgroup=mkdBold start="\%(^\|\s\)\zs__\ze\S" end="\S\zs__" keepend contains=@Spell' . s:oneline . s:concealends
|
||||
execute 'syn region htmlBoldItalic matchgroup=mkdBoldItalic start="\%(^\|\s\)\zs\*\*\*\ze\S" end="\S\zs\*\*\*" keepend contains=@Spell' . s:oneline . s:concealends
|
||||
execute 'syn region htmlBoldItalic matchgroup=mkdBoldItalic start="\%(^\|\s\)\zs___\ze\S" end="\S\zs___" keepend contains=@Spell' . s:oneline . s:concealends
|
||||
|
||||
" [link](URL) | [link][id] | [link][] | 
|
||||
syn region mkdFootnotes matchgroup=mkdDelimiter start="\[^" end="\]"
|
||||
execute 'syn region mkdID matchgroup=mkdDelimiter start="\[" end="\]" contained oneline' . s:conceal
|
||||
execute 'syn region mkdURL matchgroup=mkdDelimiter start="(" end=")" contained oneline' . s:conceal
|
||||
execute 'syn region mkdLink matchgroup=mkdDelimiter start="\\\@<!!\?\[\ze[^]\n]*\n\?[^]\n]*\][[(]" end="\]" contains=@mkdNonListItem,@Spell nextgroup=mkdURL,mkdID skipwhite' . s:concealends
|
||||
|
||||
" Autolink without angle brackets.
|
||||
" mkd inline links: protocol optional user:pass@ sub/domain .com, .co.uk, etc optional port path/querystring/hash fragment
|
||||
" ------------ _____________________ ----------------------------- _________________________ ----------------- __
|
||||
syn match mkdInlineURL /https\?:\/\/\(\w\+\(:\w\+\)\?@\)\?\([A-Za-z0-9][-_0-9A-Za-z]*\.\)\{1,}\(\w\{2,}\.\?\)\{1,}\(:[0-9]\{1,5}\)\?\S*/
|
||||
|
||||
" Autolink with parenthesis.
|
||||
syn region mkdInlineURL matchgroup=mkdDelimiter start="(\(https\?:\/\/\(\w\+\(:\w\+\)\?@\)\?\([A-Za-z0-9][-_0-9A-Za-z]*\.\)\{1,}\(\w\{2,}\.\?\)\{1,}\(:[0-9]\{1,5}\)\?\S*)\)\@=" end=")"
|
||||
|
||||
" Autolink with angle brackets.
|
||||
syn region mkdInlineURL matchgroup=mkdDelimiter start="\\\@<!<\ze[a-z][a-z0-9,.-]\{1,22}:\/\/[^> ]*>" end=">"
|
||||
|
||||
" Link definitions: [id]: URL (Optional Title)
|
||||
syn region mkdLinkDef matchgroup=mkdDelimiter start="^ \{,3}\zs\[\^\@!" end="]:" oneline nextgroup=mkdLinkDefTarget skipwhite
|
||||
syn region mkdLinkDefTarget start="<\?\zs\S" excludenl end="\ze[>[:space:]\n]" contained nextgroup=mkdLinkTitle,mkdLinkDef skipwhite skipnl oneline
|
||||
syn region mkdLinkTitle matchgroup=mkdDelimiter start=+"+ end=+"+ contained
|
||||
syn region mkdLinkTitle matchgroup=mkdDelimiter start=+'+ end=+'+ contained
|
||||
syn region mkdLinkTitle matchgroup=mkdDelimiter start=+(+ end=+)+ contained
|
||||
|
||||
"HTML headings
|
||||
syn region htmlH1 matchgroup=mkdHeading start="^\s*#" end="$" contains=mkdLink,mkdInlineURL,@Spell
|
||||
syn region htmlH2 matchgroup=mkdHeading start="^\s*##" end="$" contains=mkdLink,mkdInlineURL,@Spell
|
||||
syn region htmlH3 matchgroup=mkdHeading start="^\s*###" end="$" contains=mkdLink,mkdInlineURL,@Spell
|
||||
syn region htmlH4 matchgroup=mkdHeading start="^\s*####" end="$" contains=mkdLink,mkdInlineURL,@Spell
|
||||
syn region htmlH5 matchgroup=mkdHeading start="^\s*#####" end="$" contains=mkdLink,mkdInlineURL,@Spell
|
||||
syn region htmlH6 matchgroup=mkdHeading start="^\s*######" end="$" contains=mkdLink,mkdInlineURL,@Spell
|
||||
syn match htmlH1 /^.\+\n=\+$/ contains=mkdLink,mkdInlineURL,@Spell
|
||||
syn match htmlH2 /^.\+\n-\+$/ contains=mkdLink,mkdInlineURL,@Spell
|
||||
|
||||
"define Markdown groups
|
||||
syn match mkdLineBreak / \+$/
|
||||
syn region mkdBlockquote start=/^\s*>/ end=/$/ contains=mkdLink,mkdInlineURL,mkdLineBreak,@Spell
|
||||
execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/\(\([^\\]\|^\)\\\)\@<!`/ end=/`/' . s:concealcode
|
||||
execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/\(\([^\\]\|^\)\\\)\@<!``/ skip=/[^`]`[^`]/ end=/\(\([^\\]\|^\)\\\)\@<!``/' . s:concealcode
|
||||
execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/^\s*\z(`\{3,}\)[^`]*$/ end=/^\s*\z1`*\s*$/' . s:concealcode
|
||||
execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/\(\([^\\]\|^\)\\\)\@<!\~\~/ end=/\(\([^\\]\|^\)\\\)\@<!\~\~/' . s:concealcode
|
||||
execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start=/^\s*\z(\~\{3,}\)\s*[0-9A-Za-z_+-]*\s*$/ end=/^\s*\z1\~*\s*$/' . s:concealcode
|
||||
execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start="<pre[^>]*\\\@<!>" end="</pre>"' . s:concealcode
|
||||
execute 'syn region mkdCode matchgroup=mkdCodeDelimiter start="<code[^>]*\\\@<!>" end="</code>"' . s:concealcode
|
||||
syn region mkdFootnote start="\[^" end="\]"
|
||||
syn match mkdCode /^\s*\n\(\(\s\{8,}[^ ]\|\t\t\+[^\t]\).*\n\)\+/
|
||||
syn match mkdCode /\%^\(\(\s\{4,}[^ ]\|\t\+[^\t]\).*\n\)\+/
|
||||
syn match mkdCode /^\s*\n\(\(\s\{4,}[^ ]\|\t\+[^\t]\).*\n\)\+/ contained
|
||||
syn match mkdListItem /^\s*\%([-*+]\|\d\+\.\)\ze\s\+/ contained
|
||||
syn region mkdListItemLine start="^\s*\%([-*+]\|\d\+\.\)\s\+" end="$" oneline contains=@mkdNonListItem,mkdListItem,@Spell
|
||||
syn region mkdNonListItemBlock start="\(\%^\(\s*\([-*+]\|\d\+\.\)\s\+\)\@!\|\n\(\_^\_$\|\s\{4,}[^ ]\|\t+[^\t]\)\@!\)" end="^\(\s*\([-*+]\|\d\+\.\)\s\+\)\@=" contains=@mkdNonListItem,@Spell
|
||||
syn match mkdRule /^\s*\*\s\{0,1}\*\s\{0,1}\*\(\*\|\s\)*$/
|
||||
syn match mkdRule /^\s*-\s\{0,1}-\s\{0,1}-\(-\|\s\)*$/
|
||||
syn match mkdRule /^\s*_\s\{0,1}_\s\{0,1}_\(_\|\s\)*$/
|
||||
|
||||
" YAML frontmatter
|
||||
if get(g:, 'vim_markdown_frontmatter', 0)
|
||||
syn include @yamlTop syntax/yaml.vim
|
||||
syn region Comment matchgroup=mkdDelimiter start="\%^---$" end="^\(---\|\.\.\.\)$" contains=@yamlTop keepend
|
||||
unlet! b:current_syntax
|
||||
endif
|
||||
|
||||
if get(g:, 'vim_markdown_toml_frontmatter', 0)
|
||||
try
|
||||
syn include @tomlTop syntax/toml.vim
|
||||
syn region Comment matchgroup=mkdDelimiter start="\%^+++$" end="^+++$" transparent contains=@tomlTop keepend
|
||||
unlet! b:current_syntax
|
||||
catch /E484/
|
||||
syn region Comment matchgroup=mkdDelimiter start="\%^+++$" end="^+++$"
|
||||
endtry
|
||||
endif
|
||||
|
||||
if get(g:, 'vim_markdown_json_frontmatter', 0)
|
||||
try
|
||||
syn include @jsonTop syntax/json.vim
|
||||
syn region Comment matchgroup=mkdDelimiter start="\%^{$" end="^}$" contains=@jsonTop keepend
|
||||
unlet! b:current_syntax
|
||||
catch /E484/
|
||||
syn region Comment matchgroup=mkdDelimiter start="\%^{$" end="^}$"
|
||||
endtry
|
||||
endif
|
||||
|
||||
if get(g:, 'vim_markdown_math', 0)
|
||||
syn include @tex syntax/tex.vim
|
||||
syn region mkdMath start="\\\@<!\$" end="\$" skip="\\\$" contains=@tex keepend
|
||||
syn region mkdMath start="\\\@<!\$\$" end="\$\$" skip="\\\$" contains=@tex keepend
|
||||
endif
|
||||
|
||||
" Strike through
|
||||
if get(g:, 'vim_markdown_strikethrough', 0)
|
||||
syn region mkdStrike matchgroup=mkdStrike start="\%(\~\~\)" end="\%(\~\~\)"
|
||||
HtmlHiLink mkdStrike htmlStrike
|
||||
endif
|
||||
|
||||
syn cluster mkdNonListItem contains=@htmlTop,htmlItalic,htmlBold,htmlBoldItalic,mkdFootnotes,mkdInlineURL,mkdLink,mkdLinkDef,mkdLineBreak,mkdBlockquote,mkdCode,mkdRule,htmlH1,htmlH2,htmlH3,htmlH4,htmlH5,htmlH6,mkdMath
|
||||
|
||||
"highlighting for Markdown groups
|
||||
HtmlHiLink mkdString String
|
||||
HtmlHiLink mkdCode String
|
||||
HtmlHiLink mkdCodeDelimiter String
|
||||
HtmlHiLink mkdCodeStart String
|
||||
HtmlHiLink mkdCodeEnd String
|
||||
HtmlHiLink mkdFootnote Comment
|
||||
HtmlHiLink mkdBlockquote Comment
|
||||
HtmlHiLink mkdListItem Identifier
|
||||
HtmlHiLink mkdRule Identifier
|
||||
HtmlHiLink mkdLineBreak Visual
|
||||
HtmlHiLink mkdFootnotes htmlLink
|
||||
HtmlHiLink mkdLink htmlLink
|
||||
HtmlHiLink mkdURL htmlString
|
||||
HtmlHiLink mkdInlineURL htmlLink
|
||||
HtmlHiLink mkdID Identifier
|
||||
HtmlHiLink mkdLinkDef mkdID
|
||||
HtmlHiLink mkdLinkDefTarget mkdURL
|
||||
HtmlHiLink mkdLinkTitle htmlString
|
||||
HtmlHiLink mkdDelimiter Delimiter
|
||||
|
||||
let b:current_syntax = "mkd"
|
||||
|
||||
delcommand HtmlHiLink
|
||||
" vim: ts=8
|
||||
Loading…
Reference in New Issue
Block a user