1
0
Fork 0
mirror of synced 2024-11-22 16:55:34 -05:00

Updated plugins and added vim-markdown

This commit is contained in:
Amir Salihefendic 2018-02-04 12:35:08 +01:00
parent 2514de5b22
commit 8eeefe86c2
111 changed files with 6623 additions and 923 deletions

View file

@ -439,6 +439,14 @@ fu! ctrlp#addfile(ch, file)
cal s:BuildPrompt(1) cal s:BuildPrompt(1)
endf endf
fu! s:safe_printf(format, ...)
try
retu call('printf', [a:format] + a:000)
cat
retu a:format
endt
endf
fu! s:UserCmd(lscmd) fu! s:UserCmd(lscmd)
let [path, lscmd] = [s:dyncwd, a:lscmd] let [path, lscmd] = [s:dyncwd, a:lscmd]
let do_ign = let do_ign =
@ -461,9 +469,9 @@ fu! s:UserCmd(lscmd)
let g:ctrlp_allfiles = [] let g:ctrlp_allfiles = []
let s:job = job_start([&shell, &shellcmdflag, printf(lscmd, path)], {'callback': 'ctrlp#addfile'}) let s:job = job_start([&shell, &shellcmdflag, printf(lscmd, path)], {'callback': 'ctrlp#addfile'})
elsei has('patch-7.4-597') && !(has('win32') || has('win64')) elsei has('patch-7.4-597') && !(has('win32') || has('win64'))
let g:ctrlp_allfiles = systemlist(printf(lscmd, path)) let g:ctrlp_allfiles = systemlist(s:safe_printf(lscmd, path))
el el
let g:ctrlp_allfiles = split(system(printf(lscmd, path)), "\n") let g:ctrlp_allfiles = split(system(s:safe_printf(lscmd, path)), "\n")
en en
if exists('+ssl') && exists('ssl') if exists('+ssl') && exists('ssl')
let &ssl = ssl let &ssl = ssl

View file

@ -59,10 +59,12 @@ fu! ctrlp#line#accept(dict)
let bufnr = str2nr(get(info, 1)) let bufnr = str2nr(get(info, 1))
if bufnr if bufnr
cal ctrlp#acceptfile(mode, bufnr, get(info, 2)) cal ctrlp#acceptfile(mode, bufnr, get(info, 2))
if !empty(input)
let @/ = input let @/ = input
call search(input, 'c') call search(input, 'c')
call histadd("search", input) call histadd("search", input)
en en
en
endf endf
fu! ctrlp#line#cmd(mode, ...) fu! ctrlp#line#cmd(mode, ...)

View file

@ -51,7 +51,7 @@ landscape is my colorscheme, which is a high-contrast cui-supported colorscheme,
+ [vim-airline](https://github.com/vim-airline/vim-airline) is a nice plugin, but it uses too much functions of other plugins, which should be done by users in `.vimrc`. + [vim-airline](https://github.com/vim-airline/vim-airline) is a nice plugin, but it uses too much functions of other plugins, which should be done by users in `.vimrc`.
## Spirit of this plugin ## Spirit of this plugin
+ Minimalism. The core script is very small to achive enough functions as a statusline plugin. + Minimalism. The core script is very small to achieve enough functions as a statusline plugin.
+ Configurability. You can create your own component and easily add to the statusline and the tabline. + Configurability. You can create your own component and easily add to the statusline and the tabline.
+ Orthogonality. The plugin does not rely on the implementation of other plugins. Such plugin crossing settings should be configured by users. + Orthogonality. The plugin does not rely on the implementation of other plugins. Such plugin crossing settings should be configured by users.
@ -110,7 +110,7 @@ if !has('gui_running')
endif endif
``` ```
Your statusline appears to work correctly? If yes, great, thanks for choosing lightline.vim! If no, please file a issue report to the [issue tracker](https://github.com/itchyny/lightline.vim/issues). Your statusline appears to work correctly? If yes, great, thanks for choosing lightline.vim! If no, please file an issue report to the [issue tracker](https://github.com/itchyny/lightline.vim/issues).
By the way, `-- INSERT --` is unnecessary anymore because the mode information is displayed in the statusline. By the way, `-- INSERT --` is unnecessary anymore because the mode information is displayed in the statusline.
![lightline.vim - tutorial](https://raw.githubusercontent.com/wiki/itchyny/lightline.vim/image/tutorial/13.png) ![lightline.vim - tutorial](https://raw.githubusercontent.com/wiki/itchyny/lightline.vim/image/tutorial/13.png)

View file

@ -2,7 +2,7 @@
" Filename: autoload/lightline.vim " Filename: autoload/lightline.vim
" Author: itchyny " Author: itchyny
" License: MIT License " License: MIT License
" Last Change: 2017/11/11 13:29:26. " Last Change: 2017/12/31 15:55:00.
" ============================================================================= " =============================================================================
let s:save_cpo = &cpo let s:save_cpo = &cpo
@ -84,20 +84,20 @@ endfunction
let s:_lightline = { let s:_lightline = {
\ 'active': { \ 'active': {
\ 'left': [ [ 'mode', 'paste' ], [ 'readonly', 'filename', 'modified' ] ], \ 'left': [['mode', 'paste'], ['readonly', 'filename', 'modified']],
\ 'right': [ [ 'lineinfo' ], [ 'percent' ], [ 'fileformat', 'fileencoding', 'filetype' ] ] \ 'right': [['lineinfo'], ['percent'], ['fileformat', 'fileencoding', 'filetype']]
\ }, \ },
\ 'inactive': { \ 'inactive': {
\ 'left': [ [ 'filename' ] ], \ 'left': [['filename']],
\ 'right': [ [ 'lineinfo' ], [ 'percent' ] ] \ 'right': [['lineinfo'], ['percent']]
\ }, \ },
\ 'tabline': { \ 'tabline': {
\ 'left': [ [ 'tabs' ] ], \ 'left': [['tabs']],
\ 'right': [ [ 'close' ] ] \ 'right': [['close']]
\ }, \ },
\ 'tab': { \ 'tab': {
\ 'active': [ 'tabnum', 'filename', 'modified' ], \ 'active': ['tabnum', 'filename', 'modified'],
\ 'inactive': [ 'tabnum', 'filename', 'modified' ] \ 'inactive': ['tabnum', 'filename', 'modified']
\ }, \ },
\ 'component': { \ 'component': {
\ 'mode': '%{lightline#mode()}', \ 'mode': '%{lightline#mode()}',
@ -295,7 +295,7 @@ function! lightline#highlight(...) abort
endfunction endfunction
function! s:subseparator(components, subseparator, expanded) abort function! s:subseparator(components, subseparator, expanded) abort
let [a, c, f, v, u ] = [ a:components, s:lightline.component, s:lightline.component_function, s:lightline.component_visible_condition, s:lightline.component_function_visible_condition ] let [a, c, f, v, u] = [a:components, s:lightline.component, s:lightline.component_function, s:lightline.component_visible_condition, s:lightline.component_function_visible_condition]
let xs = map(range(len(a:components)), 'a:expanded[v:val] ? "1" : let xs = map(range(len(a:components)), 'a:expanded[v:val] ? "1" :
\ has_key(f, a[v:val]) ? (has_key(u, a[v:val]) ? "(".u[a[v:val]].")" : (exists("*".f[a[v:val]]) ? "" : "exists(\"*".f[a[v:val]]."\")&&").f[a[v:val]]."()!=#\"\"") : \ has_key(f, a[v:val]) ? (has_key(u, a[v:val]) ? "(".u[a[v:val]].")" : (exists("*".f[a[v:val]]) ? "" : "exists(\"*".f[a[v:val]]."\")&&").f[a[v:val]]."()!=#\"\"") :
\ has_key(v, a[v:val]) ? "(".v[a[v:val]].")" : has_key(c, a[v:val]) ? "1" : "0"') \ has_key(v, a[v:val]) ? "(".v[a[v:val]].")" : has_key(c, a[v:val]) ? "1" : "0"')

View file

@ -0,0 +1,40 @@
" =============================================================================
" Filename: autoload/lightline/colorscheme/deus.vim
" Author: nikersify
" License: MIT License
" Last Change: 2018/01/24 13:26:00
" =============================================================================
let s:term_red = 204
let s:term_green = 114
let s:term_yellow = 180
let s:term_blue = 39
let s:term_purple = 170
let s:term_white = 145
let s:term_black = 235
let s:term_grey = 236
let s:p = {'normal': {}, 'inactive': {}, 'insert': {}, 'replace': {}, 'visual': {}, 'tabline': {}}
let s:p.normal.left = [ [ '#292c33', '#98c379', s:term_black, s:term_green, 'bold' ], [ '#98c379', '#292c33', s:term_green, s:term_black ] ]
let s:p.normal.right = [ [ '#292c33', '#98c379', s:term_black, s:term_green ], [ '#abb2bf', '#3e4452', s:term_white, s:term_grey ], [ '#98c379', '#292c33', s:term_green, s:term_black ] ]
let s:p.inactive.right = [ [ '#292c33', '#61afef', s:term_black, s:term_blue], [ '#abb2bf', '#3e4452', s:term_white, s:term_grey ] ]
let s:p.inactive.left = s:p.inactive.right[1:]
" her
let s:p.insert.left = [ [ '#292c33', '#61afef', s:term_black, s:term_blue, 'bold' ], [ '#61afef', '#292c33', s:term_blue, s:term_black ] ]
let s:p.insert.right = [ [ '#292c33', '#61afef', s:term_black, s:term_blue ], [ '#ABB2BF', '#3E4452', s:term_white, s:term_grey ], [ '#61afef', '#292c33', s:term_blue, s:term_black ] ]
let s:p.replace.left = [ [ '#292c33', '#e06c75', s:term_black, s:term_red, 'bold' ], [ '#e06c75', '#292c33', s:term_red, s:term_black ] ]
let s:p.replace.right = [ [ '#292c33', '#e06c75', s:term_black, s:term_red, 'bold' ], s:p.normal.right[1], [ '#e06c75', '#292c33', s:term_red, s:term_black ] ]
let s:p.visual.left = [ [ '#292c33', '#c678dd', s:term_black, s:term_purple, 'bold' ], [ '#c678dd', '#292c33', s:term_purple, s:term_black ] ]
let s:p.visual.right = [ [ '#292c33', '#c678dd', s:term_black, s:term_purple, 'bold' ], s:p.normal.right[1], [ '#c678dd', '#292c33', s:term_purple, s:term_black ] ]
let s:p.normal.middle = [ [ '#abb2bf', '#292c33', s:term_white, s:term_black ] ]
let s:p.insert.middle = s:p.normal.middle
let s:p.replace.middle = s:p.normal.middle
let s:p.tabline.left = [ s:p.normal.left[1] ]
let s:p.tabline.tabsel = [ s:p.normal.left[0] ]
let s:p.tabline.middle = s:p.normal.middle
let s:p.tabline.right = [ s:p.normal.left[1] ]
let s:p.normal.error = [ [ '#292c33', '#e06c75', s:term_black, s:term_red ] ]
let s:p.normal.warning = [ [ '#292c33', '#e5c07b', s:term_black, s:term_yellow ] ]
let g:lightline#colorscheme#deus#palette = lightline#colorscheme#fill(s:p)

View file

@ -4,7 +4,7 @@ Version: 0.1
Author: itchyny (https://github.com/itchyny) Author: itchyny (https://github.com/itchyny)
License: MIT License License: MIT License
Repository: https://github.com/itchyny/lightline.vim Repository: https://github.com/itchyny/lightline.vim
Last Change: 2017/11/18 20:07:05. Last Change: 2017/12/24 21:34:15.
CONTENTS *lightline-contents* CONTENTS *lightline-contents*
@ -29,7 +29,7 @@ The *lightline* plugin is a light and configurable statusline/tabline for Vim.
SPIRIT *lightline-spirit* SPIRIT *lightline-spirit*
Minimalism Minimalism
The core script is very small to achive enough functions as a The core script is very small to achieve enough functions as a
statusline plugin. statusline plugin.
Configurability Configurability
@ -157,7 +157,7 @@ OPTIONS *lightline-option*
A dictionary to store the visible conditions of the function A dictionary to store the visible conditions of the function
components. Each expression should correspond to the condition components. Each expression should correspond to the condition
each component is not empty. This configuration is used to each component is not empty. This configuration is used to
control the visibility of the subseparators. You can use this control the visibility of the sub-separators. You can use this
configuration to reduce the number of function calls for configuration to reduce the number of function calls for
performance improvement by setting the value 1 (to tell lightline performance improvement by setting the value 1 (to tell lightline
that the component is always visible). that the component is always visible).
@ -229,7 +229,8 @@ OPTIONS *lightline-option*
Currently, wombat, solarized, powerline, jellybeans, Tomorrow, Currently, wombat, solarized, powerline, jellybeans, Tomorrow,
Tomorrow_Night, Tomorrow_Night_Blue, Tomorrow_Night_Eighties, Tomorrow_Night, Tomorrow_Night_Blue, Tomorrow_Night_Eighties,
PaperColor, seoul256, landscape, one, Dracula, darcula, PaperColor, seoul256, landscape, one, Dracula, darcula,
molokai, materia, material, OldHope, nord and 16color are available. molokai, materia, material, OldHope, nord, 16color and deus
are available.
The default value is: The default value is:
> >
let g:lightline.colorscheme = 'default' let g:lightline.colorscheme = 'default'
@ -1200,7 +1201,7 @@ Problem 11: *lightline-problem-11*
Problem 12: *lightline-problem-12* Problem 12: *lightline-problem-12*
How to make the plus sign red like |powerline|? How to make the plus sign red like |powerline|?
Use the following setings. Use the following settings.
> >
let g:lightline = { let g:lightline = {
\ 'component': { \ 'component': {

View file

@ -21,7 +21,7 @@ Installation
git clone https://github.com/scrooloose/nerdtree.git ~/.vim/bundle/nerdtree git clone https://github.com/scrooloose/nerdtree.git ~/.vim/bundle/nerdtree
Then reload Vim, run `:helptags ~/.vim/bundle/nerdtree/doc/`, and check out `:help NERDTree.txt`. Then reload Vim, run `:helptags ~/.vim/bundle/nerdtree/doc/` or `:Helptags`, and check out `:help NERDTree.txt`.
#### [apt-vim](https://github.com/egalpin/apt-vim) #### [apt-vim](https://github.com/egalpin/apt-vim)

View file

@ -261,62 +261,50 @@ function! s:displayHelp()
call b:NERDTree.ui.centerView() call b:NERDTree.ui.centerView()
endfunction endfunction
" FUNCTION: s:findAndRevealPath(path) {{{1 " FUNCTION: s:findAndRevealPath(pathStr) {{{1
function! s:findAndRevealPath(path) function! s:findAndRevealPath(pathStr)
let l:path = a:path let l:pathStr = !empty(a:pathStr) ? a:pathStr : expand('%:p')
if empty(l:path) if empty(l:pathStr)
let l:path = expand('%:p') call nerdtree#echoWarning('no file for the current buffer')
return
endif endif
try try
let p = g:NERDTreePath.New(l:path) let l:pathObj = g:NERDTreePath.New(l:pathStr)
catch /^NERDTree.InvalidArgumentsError/ catch /^NERDTree.InvalidArgumentsError/
call nerdtree#echo("no file for the current buffer") call nerdtree#echoWarning('invalid path')
return return
endtry endtry
if p.isUnixHiddenPath()
let showhidden=g:NERDTreeShowHidden
let g:NERDTreeShowHidden = 1
endif
if !g:NERDTree.ExistsForTab() if !g:NERDTree.ExistsForTab()
try try
let cwd = g:NERDTreePath.New(getcwd()) let l:cwd = g:NERDTreePath.New(getcwd())
catch /^NERDTree.InvalidArgumentsError/ catch /^NERDTree.InvalidArgumentsError/
call nerdtree#echo("current directory does not exist.") call nerdtree#echo('current directory does not exist.')
let cwd = p.getParent() let l:cwd = l:pathObj.getParent()
endtry endtry
if p.isUnder(cwd) if l:pathObj.isUnder(l:cwd)
call g:NERDTreeCreator.CreateTabTree(cwd.str()) call g:NERDTreeCreator.CreateTabTree(l:cwd.str())
else else
call g:NERDTreeCreator.CreateTabTree(p.getParent().str()) call g:NERDTreeCreator.CreateTabTree(l:pathObj.getParent().str())
endif endif
else else
if !p.isUnder(g:NERDTreeFileNode.GetRootForTab().path) NERDTreeFocus
if !g:NERDTree.IsOpen()
call g:NERDTreeCreator.ToggleTabTree('')
else
call g:NERDTree.CursorToTreeWin()
endif
call b:NERDTree.ui.setShowHidden(g:NERDTreeShowHidden)
call s:chRoot(g:NERDTreeDirNode.New(p.getParent(), b:NERDTree))
else
if !g:NERDTree.IsOpen()
call g:NERDTreeCreator.ToggleTabTree("")
endif
endif
endif
call g:NERDTree.CursorToTreeWin()
let node = b:NERDTree.root.reveal(p)
call b:NERDTree.render()
call node.putCursorHere(1,0)
if p.isUnixHiddenFile() if !l:pathObj.isUnder(b:NERDTree.root.path)
let g:NERDTreeShowHidden = showhidden call s:chRoot(g:NERDTreeDirNode.New(l:pathObj.getParent(), b:NERDTree))
endif endif
endif
if l:pathObj.isHiddenUnder(b:NERDTree.root.path)
call b:NERDTree.ui.setShowHidden(1)
endif
let l:node = b:NERDTree.root.reveal(l:pathObj)
call b:NERDTree.render()
call l:node.putCursorHere(1, 0)
endfunction endfunction
"FUNCTION: s:handleLeftClick() {{{1 "FUNCTION: s:handleLeftClick() {{{1

View file

@ -126,20 +126,20 @@ The following features and functionality are provided by the NERD tree:
Changes made to one tree are reflected in both as they are actually the Changes made to one tree are reflected in both as they are actually the
same buffer. same buffer.
If only one other NERD tree exists, that tree is automatically mirrored. If If only one other NERD tree exists, that tree is automatically mirrored.
more than one exists, the script will ask which tree to mirror. If more than one exists, the script will ask which tree to mirror.
:NERDTreeClose *:NERDTreeClose* :NERDTreeClose *:NERDTreeClose*
Close the NERD tree in this tab. Close the NERD tree in this tab.
:NERDTreeFind *:NERDTreeFind* :NERDTreeFind [<path>] *:NERDTreeFind*
Find the current file in the tree. Without the optional argument, find and reveal the file for the active
buffer in the NERDTree window. With the <path> argument, find and
reveal the specified path.
If no tree exists and the current file is under vim's CWD, then init a Focus will be shifted to the NERDTree window, and the cursor will be
tree at the CWD and reveal the file. Otherwise init a tree in the current placed on the tree node for the determined path. If a NERDTree for the
file's directory. current tab does not exist, a new one will be initialized.
In any case, the current file is revealed and the cursor is placed on it.
:NERDTreeCWD *:NERDTreeCWD* :NERDTreeCWD *:NERDTreeCWD*
Change tree root to current directory. If no NERD tree exists for this Change tree root to current directory. If no NERD tree exists for this
@ -996,17 +996,16 @@ Other examples: >
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
*'NERDTreeStatusline'* *'NERDTreeStatusline'*
Values: Any valid statusline setting. Values: Any valid |'statusline'| setting.
Default: %{b:NERDTree.root.path.strForOS(0)} Default: %{exists('b:NERDTree')?b:NERDTree.root.path.str():''}
Tells the script what to use as the |'statusline'| setting for NERD tree Defines the value for the |'statusline'| setting in NERDTree windows.
windows.
Note that the statusline is set using |:let-&| not |:set| so escaping spaces Note: The setting is actually applied using |:let-&|, not |:set|, so
isn't necessary. escaping spaces is not necessary.
Setting this option to -1 will will deactivate it so that your global Setting this option to -1 will will deactivate it so that your global
statusline setting is used instead. |'statusline'| setting is used.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
*'NERDTreeWinPos'* *'NERDTreeWinPos'*
@ -1128,13 +1127,12 @@ NERDTreeAddKeyMap({options}) *NERDTreeAddKeyMap()*
Additionally, a "scope" argument may be supplied. This constrains the Additionally, a "scope" argument may be supplied. This constrains the
mapping so that it is only activated if the cursor is on a certain object. mapping so that it is only activated if the cursor is on a certain object.
That object is then passed into the handling method. Possible values are: That object is then passed into the handling method. Possible values are:
"FileNode" - a file node
"DirNode" - a directory node
"Node" - a file or directory node
"Bookmark" - A bookmark
"all" - the keymap is not constrained to any scope (default). When
thei is used, the handling function is not passed any arguments.
"FileNode" .... a file node
"DirNode" ..... a directory node
"Node" ........ a file node OR a directory node
"Bookmark" .... a bookmark
"all" ......... global scope; handler receives no arguments (default)
Example: > Example: >
call NERDTreeAddKeyMap({ call NERDTreeAddKeyMap({

View file

@ -54,3 +54,5 @@ function! s:FlagSet.renderToString()
return '[' . flagstring . ']' return '[' . flagstring . ']'
endfunction endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -38,17 +38,26 @@ function! s:NERDTree.Close()
endif endif
if winnr("$") != 1 if winnr("$") != 1
" Use the window ID to identify the currently active window or fall
" back on the buffer ID if win_getid/win_gotoid are not available, in
" which case we'll focus an arbitrary window showing the buffer.
let l:useWinId = exists('*win_getid') && exists('*win_gotoid')
if winnr() == s:NERDTree.GetWinNum() if winnr() == s:NERDTree.GetWinNum()
call nerdtree#exec("wincmd p") call nerdtree#exec("wincmd p")
let bufnr = bufnr("") let l:activeBufOrWin = l:useWinId ? win_getid() : bufnr("")
call nerdtree#exec("wincmd p") call nerdtree#exec("wincmd p")
else else
let bufnr = bufnr("") let l:activeBufOrWin = l:useWinId ? win_getid() : bufnr("")
endif endif
call nerdtree#exec(s:NERDTree.GetWinNum() . " wincmd w") call nerdtree#exec(s:NERDTree.GetWinNum() . " wincmd w")
close close
call nerdtree#exec(bufwinnr(bufnr) . " wincmd w") if l:useWinId
call nerdtree#exec("call win_gotoid(" . l:activeBufOrWin . ")")
else
call nerdtree#exec(bufwinnr(l:activeBufOrWin) . " wincmd w")
endif
else else
close close
endif endif
@ -195,3 +204,5 @@ endfunction
function! s:NERDTree.render() function! s:NERDTree.render()
call self.ui.render() call self.ui.render()
endfunction endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -32,4 +32,3 @@ function! s:Notifier.GetListenersForEvent(name)
endfunction endfunction
let g:NERDTreePathNotifier = deepcopy(s:Notifier) let g:NERDTreePathNotifier = deepcopy(s:Notifier)

View file

@ -14,21 +14,23 @@ lockvar s:NERDTreeSortStarIndex
let s:Path = {} let s:Path = {}
let g:NERDTreePath = s:Path let g:NERDTreePath = s:Path
" FUNCTION: Path.AbsolutePathFor(str) {{{1 " FUNCTION: Path.AbsolutePathFor(pathStr) {{{1
function! s:Path.AbsolutePathFor(str) function! s:Path.AbsolutePathFor(pathStr)
let prependCWD = 0 let l:prependWorkingDir = 0
if nerdtree#runningWindows() if nerdtree#runningWindows()
let prependCWD = a:str !~# '^.:\(\\\|\/\)' && a:str !~# '^\(\\\\\|\/\/\)' let l:prependWorkingDir = a:pathStr !~# '^.:\(\\\|\/\)' && a:pathStr !~# '^\(\\\\\|\/\/\)'
else else
let prependCWD = a:str !~# '^/' let l:prependWorkingDir = a:pathStr !~# '^/'
endif endif
let toReturn = a:str let l:result = a:pathStr
if prependCWD
let toReturn = getcwd() . s:Path.Slash() . a:str if l:prependWorkingDir
let l:result = getcwd() . s:Path.Slash() . a:pathStr
endif endif
return toReturn return l:result
endfunction endfunction
" FUNCTION: Path.bookmarkNames() {{{1 " FUNCTION: Path.bookmarkNames() {{{1
@ -405,6 +407,25 @@ function! s:Path.getSortKey()
return self._sortKey return self._sortKey
endfunction endfunction
" FUNCTION: Path.isHiddenUnder(path) {{{1
function! s:Path.isHiddenUnder(path)
if !self.isUnder(a:path)
return 0
endif
let l:startIndex = len(a:path.pathSegments)
let l:segments = self.pathSegments[l:startIndex : ]
for l:segment in l:segments
if l:segment =~# '^\.'
return 1
endif
endfor
return 0
endfunction
" FUNCTION: Path.isUnixHiddenFile() {{{1 " FUNCTION: Path.isUnixHiddenFile() {{{1
" check for unix hidden files " check for unix hidden files
@ -522,17 +543,16 @@ function! s:Path.equals(path)
return self.str() ==# a:path.str() return self.str() ==# a:path.str()
endfunction endfunction
" FUNCTION: Path.New() {{{1 " FUNCTION: Path.New(pathStr) {{{1
" The Constructor for the Path object function! s:Path.New(pathStr)
function! s:Path.New(path) let l:newPath = copy(self)
let newPath = copy(self)
call newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:path)) call l:newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:pathStr))
let newPath.cachedDisplayString = "" let l:newPath.cachedDisplayString = ''
let newPath.flagSet = g:NERDTreeFlagSet.New() let l:newPath.flagSet = g:NERDTreeFlagSet.New()
return newPath return l:newPath
endfunction endfunction
" FUNCTION: Path.Slash() {{{1 " FUNCTION: Path.Slash() {{{1

View file

@ -1,22 +1,25 @@
"CLASS: TreeFileNode " ============================================================================
"This class is the parent of the TreeDirNode class and is the " CLASS: TreeFileNode
"'Component' part of the composite design pattern between the treenode "
"classes. " This class is the parent of the "TreeDirNode" class and is the "Component"
"============================================================ " part of the composite design pattern between the NERDTree node classes.
" ============================================================================
let s:TreeFileNode = {} let s:TreeFileNode = {}
let g:NERDTreeFileNode = s:TreeFileNode let g:NERDTreeFileNode = s:TreeFileNode
"FUNCTION: TreeFileNode.activate(...) {{{1 " FUNCTION: TreeFileNode.activate(...) {{{1
function! s:TreeFileNode.activate(...) function! s:TreeFileNode.activate(...)
call self.open(a:0 ? a:1 : {}) call self.open(a:0 ? a:1 : {})
endfunction endfunction
"FUNCTION: TreeFileNode.bookmark(name) {{{1 " FUNCTION: TreeFileNode.bookmark(name) {{{1
"bookmark this node with a:name " bookmark this node with a:name
function! s:TreeFileNode.bookmark(name) function! s:TreeFileNode.bookmark(name)
"if a bookmark exists with the same name and the node is cached then save " if a bookmark exists with the same name and the node is cached then save
"it so we can update its display string " it so we can update its display string
let oldMarkedNode = {} let oldMarkedNode = {}
try try
let oldMarkedNode = g:NERDTreeBookmark.GetNodeForName(a:name, 1, self.getNerdtree()) let oldMarkedNode = g:NERDTreeBookmark.GetNodeForName(a:name, 1, self.getNerdtree())
@ -33,8 +36,8 @@ function! s:TreeFileNode.bookmark(name)
endif endif
endfunction endfunction
"FUNCTION: TreeFileNode.cacheParent() {{{1 " FUNCTION: TreeFileNode.cacheParent() {{{1
"initializes self.parent if it isnt already " initializes self.parent if it isnt already
function! s:TreeFileNode.cacheParent() function! s:TreeFileNode.cacheParent()
if empty(self.parent) if empty(self.parent)
let parentPath = self.path.getParent() let parentPath = self.path.getParent()
@ -45,7 +48,7 @@ function! s:TreeFileNode.cacheParent()
endif endif
endfunction endfunction
"FUNCTION: TreeFileNode.clearBookmarks() {{{1 " FUNCTION: TreeFileNode.clearBookmarks() {{{1
function! s:TreeFileNode.clearBookmarks() function! s:TreeFileNode.clearBookmarks()
for i in g:NERDTreeBookmark.Bookmarks() for i in g:NERDTreeBookmark.Bookmarks()
if i.path.equals(self.path) if i.path.equals(self.path)
@ -55,7 +58,7 @@ function! s:TreeFileNode.clearBookmarks()
call self.path.cacheDisplayString() call self.path.cacheDisplayString()
endfunction endfunction
"FUNCTION: TreeFileNode.copy(dest) {{{1 " FUNCTION: TreeFileNode.copy(dest) {{{1
function! s:TreeFileNode.copy(dest) function! s:TreeFileNode.copy(dest)
call self.path.copy(a:dest) call self.path.copy(a:dest)
let newPath = g:NERDTreePath.New(a:dest) let newPath = g:NERDTreePath.New(a:dest)
@ -68,44 +71,44 @@ function! s:TreeFileNode.copy(dest)
endif endif
endfunction endfunction
"FUNCTION: TreeFileNode.delete {{{1 " FUNCTION: TreeFileNode.delete {{{1
"Removes this node from the tree and calls the Delete method for its path obj " Removes this node from the tree and calls the Delete method for its path obj
function! s:TreeFileNode.delete() function! s:TreeFileNode.delete()
call self.path.delete() call self.path.delete()
call self.parent.removeChild(self) call self.parent.removeChild(self)
endfunction endfunction
"FUNCTION: TreeFileNode.displayString() {{{1 " FUNCTION: TreeFileNode.displayString() {{{1
" "
"Returns a string that specifies how the node should be represented as a " Returns a string that specifies how the node should be represented as a
"string " string
" "
"Return: " Return:
"a string that can be used in the view to represent this node " a string that can be used in the view to represent this node
function! s:TreeFileNode.displayString() function! s:TreeFileNode.displayString()
return self.path.flagSet.renderToString() . self.path.displayString() return self.path.flagSet.renderToString() . self.path.displayString()
endfunction endfunction
"FUNCTION: TreeFileNode.equals(treenode) {{{1 " FUNCTION: TreeFileNode.equals(treenode) {{{1
" "
"Compares this treenode to the input treenode and returns 1 if they are the " Compares this treenode to the input treenode and returns 1 if they are the
"same node. " same node.
" "
"Use this method instead of == because sometimes when the treenodes contain " Use this method instead of == because sometimes when the treenodes contain
"many children, vim seg faults when doing == " many children, vim seg faults when doing ==
" "
"Args: " Args:
"treenode: the other treenode to compare to " treenode: the other treenode to compare to
function! s:TreeFileNode.equals(treenode) function! s:TreeFileNode.equals(treenode)
return self.path.str() ==# a:treenode.path.str() return self.path.str() ==# a:treenode.path.str()
endfunction endfunction
"FUNCTION: TreeFileNode.findNode(path) {{{1 " FUNCTION: TreeFileNode.findNode(path) {{{1
"Returns self if this node.path.Equals the given path. " Returns self if this node.path.Equals the given path.
"Returns {} if not equal. " Returns {} if not equal.
" "
"Args: " Args:
"path: the path object to compare against " path: the path object to compare against
function! s:TreeFileNode.findNode(path) function! s:TreeFileNode.findNode(path)
if a:path.equals(self.path) if a:path.equals(self.path)
return self return self
@ -113,18 +116,18 @@ function! s:TreeFileNode.findNode(path)
return {} return {}
endfunction endfunction
"FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{1 " FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{1
" "
"Finds the next sibling for this node in the indicated direction. This sibling " Finds the next sibling for this node in the indicated direction. This sibling
"must be a directory and may/may not have children as specified. " must be a directory and may/may not have children as specified.
" "
"Args: " Args:
"direction: 0 if you want to find the previous sibling, 1 for the next sibling " direction: 0 if you want to find the previous sibling, 1 for the next sibling
" "
"Return: " Return:
"a treenode object or {} if no appropriate sibling could be found " a treenode object or {} if no appropriate sibling could be found
function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
"if we have no parent then we can have no siblings " if we have no parent then we can have no siblings
if self.parent != {} if self.parent != {}
let nextSibling = self.findSibling(a:direction) let nextSibling = self.findSibling(a:direction)
@ -139,37 +142,37 @@ function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
return {} return {}
endfunction endfunction
"FUNCTION: TreeFileNode.findSibling(direction) {{{1 " FUNCTION: TreeFileNode.findSibling(direction) {{{1
" "
"Finds the next sibling for this node in the indicated direction " Finds the next sibling for this node in the indicated direction
" "
"Args: " Args:
"direction: 0 if you want to find the previous sibling, 1 for the next sibling " direction: 0 if you want to find the previous sibling, 1 for the next sibling
" "
"Return: " Return:
"a treenode object or {} if no sibling could be found " a treenode object or {} if no sibling could be found
function! s:TreeFileNode.findSibling(direction) function! s:TreeFileNode.findSibling(direction)
"if we have no parent then we can have no siblings " if we have no parent then we can have no siblings
if self.parent != {} if self.parent != {}
"get the index of this node in its parents children " get the index of this node in its parents children
let siblingIndx = self.parent.getChildIndex(self.path) let siblingIndx = self.parent.getChildIndex(self.path)
if siblingIndx != -1 if siblingIndx != -1
"move a long to the next potential sibling node " move a long to the next potential sibling node
let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1 let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
"keep moving along to the next sibling till we find one that is valid " keep moving along to the next sibling till we find one that is valid
let numSiblings = self.parent.getChildCount() let numSiblings = self.parent.getChildCount()
while siblingIndx >= 0 && siblingIndx < numSiblings while siblingIndx >= 0 && siblingIndx < numSiblings
"if the next node is not an ignored node (i.e. wont show up in the " if the next node is not an ignored node (i.e. wont show up in the
"view) then return it " view) then return it
if self.parent.children[siblingIndx].path.ignore(self.getNerdtree()) ==# 0 if self.parent.children[siblingIndx].path.ignore(self.getNerdtree()) ==# 0
return self.parent.children[siblingIndx] return self.parent.children[siblingIndx]
endif endif
"go to next node " go to next node
let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1 let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
endwhile endwhile
endif endif
@ -178,13 +181,13 @@ function! s:TreeFileNode.findSibling(direction)
return {} return {}
endfunction endfunction
"FUNCTION: TreeFileNode.getNerdtree(){{{1 " FUNCTION: TreeFileNode.getNerdtree(){{{1
function! s:TreeFileNode.getNerdtree() function! s:TreeFileNode.getNerdtree()
return self._nerdtree return self._nerdtree
endfunction endfunction
"FUNCTION: TreeFileNode.GetRootForTab(){{{1 " FUNCTION: TreeFileNode.GetRootForTab(){{{1
"get the root node for this tab " get the root node for this tab
function! s:TreeFileNode.GetRootForTab() function! s:TreeFileNode.GetRootForTab()
if g:NERDTree.ExistsForTab() if g:NERDTree.ExistsForTab()
return getbufvar(t:NERDTreeBufName, 'NERDTree').root return getbufvar(t:NERDTreeBufName, 'NERDTree').root
@ -192,28 +195,32 @@ function! s:TreeFileNode.GetRootForTab()
return {} return {}
endfunction endfunction
"FUNCTION: TreeFileNode.GetSelected() {{{1 " FUNCTION: TreeFileNode.GetSelected() {{{1
"gets the treenode that the cursor is currently over " If the cursor is currently positioned on a tree node, return the node.
" Otherwise, return the empty dictionary.
function! s:TreeFileNode.GetSelected() function! s:TreeFileNode.GetSelected()
try try
let path = b:NERDTree.ui.getPath(line(".")) let l:path = b:NERDTree.ui.getPath(line('.'))
if path ==# {}
if empty(l:path)
return {} return {}
endif endif
return b:NERDTree.root.findNode(path)
return b:NERDTree.root.findNode(l:path)
catch /^NERDTree/ catch /^NERDTree/
return {} return {}
endtry endtry
endfunction endfunction
"FUNCTION: TreeFileNode.isVisible() {{{1 " FUNCTION: TreeFileNode.isVisible() {{{1
"returns 1 if this node should be visible according to the tree filters and " returns 1 if this node should be visible according to the tree filters and
"hidden file filters (and their on/off status) " hidden file filters (and their on/off status)
function! s:TreeFileNode.isVisible() function! s:TreeFileNode.isVisible()
return !self.path.ignore(self.getNerdtree()) return !self.path.ignore(self.getNerdtree())
endfunction endfunction
"FUNCTION: TreeFileNode.isRoot() {{{1 " FUNCTION: TreeFileNode.isRoot() {{{1
function! s:TreeFileNode.isRoot() function! s:TreeFileNode.isRoot()
if !g:NERDTree.ExistsForBuf() if !g:NERDTree.ExistsForBuf()
throw "NERDTree.NoTreeError: No tree exists for the current buffer" throw "NERDTree.NoTreeError: No tree exists for the current buffer"
@ -222,12 +229,12 @@ function! s:TreeFileNode.isRoot()
return self.equals(self.getNerdtree().root) return self.equals(self.getNerdtree().root)
endfunction endfunction
"FUNCTION: TreeFileNode.New(path, nerdtree) {{{1 " FUNCTION: TreeFileNode.New(path, nerdtree) {{{1
"Returns a new TreeNode object with the given path and parent " Returns a new TreeNode object with the given path and parent
" "
"Args: " Args:
"path: file/dir that the node represents " path: file/dir that the node represents
"nerdtree: the tree the node belongs to " nerdtree: the tree the node belongs to
function! s:TreeFileNode.New(path, nerdtree) function! s:TreeFileNode.New(path, nerdtree)
if a:path.isDirectory if a:path.isDirectory
return g:NERDTreeDirNode.New(a:path, a:nerdtree) return g:NERDTreeDirNode.New(a:path, a:nerdtree)
@ -240,40 +247,40 @@ function! s:TreeFileNode.New(path, nerdtree)
endif endif
endfunction endfunction
"FUNCTION: TreeFileNode.open() {{{1 " FUNCTION: TreeFileNode.open() {{{1
function! s:TreeFileNode.open(...) function! s:TreeFileNode.open(...)
let opts = a:0 ? a:1 : {} let opts = a:0 ? a:1 : {}
let opener = g:NERDTreeOpener.New(self.path, opts) let opener = g:NERDTreeOpener.New(self.path, opts)
call opener.open(self) call opener.open(self)
endfunction endfunction
"FUNCTION: TreeFileNode.openSplit() {{{1 " FUNCTION: TreeFileNode.openSplit() {{{1
"Open this node in a new window " Open this node in a new window
function! s:TreeFileNode.openSplit() function! s:TreeFileNode.openSplit()
call nerdtree#deprecated('TreeFileNode.openSplit', 'is deprecated, use .open() instead.') call nerdtree#deprecated('TreeFileNode.openSplit', 'is deprecated, use .open() instead.')
call self.open({'where': 'h'}) call self.open({'where': 'h'})
endfunction endfunction
"FUNCTION: TreeFileNode.openVSplit() {{{1 " FUNCTION: TreeFileNode.openVSplit() {{{1
"Open this node in a new vertical window " Open this node in a new vertical window
function! s:TreeFileNode.openVSplit() function! s:TreeFileNode.openVSplit()
call nerdtree#deprecated('TreeFileNode.openVSplit', 'is deprecated, use .open() instead.') call nerdtree#deprecated('TreeFileNode.openVSplit', 'is deprecated, use .open() instead.')
call self.open({'where': 'v'}) call self.open({'where': 'v'})
endfunction endfunction
"FUNCTION: TreeFileNode.openInNewTab(options) {{{1 " FUNCTION: TreeFileNode.openInNewTab(options) {{{1
function! s:TreeFileNode.openInNewTab(options) function! s:TreeFileNode.openInNewTab(options)
echomsg 'TreeFileNode.openInNewTab is deprecated' echomsg 'TreeFileNode.openInNewTab is deprecated'
call self.open(extend({'where': 't'}, a:options)) call self.open(extend({'where': 't'}, a:options))
endfunction endfunction
"FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{1 " FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{1
"Places the cursor on the line number this node is rendered on " Places the cursor on the line number this node is rendered on
" "
"Args: " Args:
"isJump: 1 if this cursor movement should be counted as a jump by vim " isJump: 1 if this cursor movement should be counted as a jump by vim
"recurseUpward: try to put the cursor on the parent if the this node isnt " recurseUpward: try to put the cursor on the parent if the this node isnt
"visible " visible
function! s:TreeFileNode.putCursorHere(isJump, recurseUpward) function! s:TreeFileNode.putCursorHere(isJump, recurseUpward)
let ln = self.getNerdtree().ui.getLineNum(self) let ln = self.getNerdtree().ui.getLineNum(self)
if ln != -1 if ln != -1
@ -294,18 +301,18 @@ function! s:TreeFileNode.putCursorHere(isJump, recurseUpward)
endif endif
endfunction endfunction
"FUNCTION: TreeFileNode.refresh() {{{1 " FUNCTION: TreeFileNode.refresh() {{{1
function! s:TreeFileNode.refresh() function! s:TreeFileNode.refresh()
call self.path.refresh(self.getNerdtree()) call self.path.refresh(self.getNerdtree())
endfunction endfunction
"FUNCTION: TreeFileNode.refreshFlags() {{{1 " FUNCTION: TreeFileNode.refreshFlags() {{{1
function! s:TreeFileNode.refreshFlags() function! s:TreeFileNode.refreshFlags()
call self.path.refreshFlags(self.getNerdtree()) call self.path.refreshFlags(self.getNerdtree())
endfunction endfunction
"FUNCTION: TreeFileNode.rename() {{{1 " FUNCTION: TreeFileNode.rename() {{{1
"Calls the rename method for this nodes path obj " Calls the rename method for this nodes path obj
function! s:TreeFileNode.rename(newName) function! s:TreeFileNode.rename(newName)
let newName = substitute(a:newName, '\(\\\|\/\)$', '', '') let newName = substitute(a:newName, '\(\\\|\/\)$', '', '')
call self.path.rename(newName) call self.path.rename(newName)
@ -320,17 +327,17 @@ function! s:TreeFileNode.rename(newName)
endif endif
endfunction endfunction
"FUNCTION: TreeFileNode.renderToString {{{1 " FUNCTION: TreeFileNode.renderToString {{{1
"returns a string representation for this tree to be rendered in the view " returns a string representation for this tree to be rendered in the view
function! s:TreeFileNode.renderToString() function! s:TreeFileNode.renderToString()
return self._renderToString(0, 0) return self._renderToString(0, 0)
endfunction endfunction
"Args: " Args:
"depth: the current depth in the tree for this call " depth: the current depth in the tree for this call
"drawText: 1 if we should actually draw the line for this node (if 0 then the " drawText: 1 if we should actually draw the line for this node (if 0 then the
"child nodes are rendered only) " child nodes are rendered only)
"for each depth in the tree " for each depth in the tree
function! s:TreeFileNode._renderToString(depth, drawText) function! s:TreeFileNode._renderToString(depth, drawText)
let output = "" let output = ""
if a:drawText ==# 1 if a:drawText ==# 1
@ -346,7 +353,7 @@ function! s:TreeFileNode._renderToString(depth, drawText)
let output = output . line . "\n" let output = output . line . "\n"
endif endif
"if the node is an open dir, draw its children " if the node is an open dir, draw its children
if self.path.isDirectory ==# 1 && self.isOpen ==# 1 if self.path.isDirectory ==# 1 && self.isOpen ==# 1
let childNodesToDraw = self.getVisibleChildren() let childNodesToDraw = self.getVisibleChildren()

View file

@ -1,11 +1,14 @@
"CLASS: UI " ============================================================================
"============================================================ " CLASS: UI
" ============================================================================
let s:UI = {} let s:UI = {}
let g:NERDTreeUI = s:UI let g:NERDTreeUI = s:UI
"FUNCTION: s:UI.centerView() {{{2 " FUNCTION: s:UI.centerView() {{{2
"centers the nerd tree window around the cursor (provided the nerd tree " centers the nerd tree window around the cursor (provided the nerd tree
"options permit) " options permit)
function! s:UI.centerView() function! s:UI.centerView()
if g:NERDTreeAutoCenter if g:NERDTreeAutoCenter
let current_line = winline() let current_line = winline()
@ -17,8 +20,8 @@ function! s:UI.centerView()
endif endif
endfunction endfunction
"FUNCTION: s:UI._dumpHelp {{{1 " FUNCTION: s:UI._dumpHelp {{{1
"prints out the quick help " prints out the quick help
function! s:UI._dumpHelp() function! s:UI._dumpHelp()
if self.getShowHelp() if self.getShowHelp()
let help = "\" NERDTree (" . nerdtree#version() . ") quickhelp~\n" let help = "\" NERDTree (" . nerdtree#version() . ") quickhelp~\n"
@ -93,7 +96,7 @@ function! s:UI._dumpHelp()
let help .= "\" ". g:NERDTreeMapToggleFiles .": files (" . (self.getShowFiles() ? "on" : "off") . ")\n" let help .= "\" ". g:NERDTreeMapToggleFiles .": files (" . (self.getShowFiles() ? "on" : "off") . ")\n"
let help .= "\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (self.getShowBookmarks() ? "on" : "off") . ")\n" let help .= "\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (self.getShowBookmarks() ? "on" : "off") . ")\n"
"add quickhelp entries for each custom key map " add quickhelp entries for each custom key map
let help .= "\"\n\" ----------------------------\n" let help .= "\"\n\" ----------------------------\n"
let help .= "\" Custom mappings~\n" let help .= "\" Custom mappings~\n"
for i in g:NERDTreeKeyMap.All() for i in g:NERDTreeKeyMap.All()
@ -124,7 +127,7 @@ function! s:UI._dumpHelp()
endfunction endfunction
"FUNCTION: s:UI.new(nerdtree) {{{1 " FUNCTION: s:UI.new(nerdtree) {{{1
function! s:UI.New(nerdtree) function! s:UI.New(nerdtree)
let newObj = copy(self) let newObj = copy(self)
let newObj.nerdtree = a:nerdtree let newObj.nerdtree = a:nerdtree
@ -137,22 +140,16 @@ function! s:UI.New(nerdtree)
return newObj return newObj
endfunction endfunction
"FUNCTION: s:UI.getPath(ln) {{{1 " FUNCTION: s:UI.getPath(ln) {{{1
"Gets the full path to the node that is rendered on the given line number " Return the "Path" object for the node that is rendered on the given line
" " number. If the "up a dir" line is selected, return the "Path" object for
"Args: " the parent of the root. Return the empty dictionary if the given line
"ln: the line number to get the path for " does not reference a tree node.
"
"Return:
"A path if a node was selected, {} if nothing is selected.
"If the 'up a dir' line was selected then the path to the parent of the
"current root is returned
function! s:UI.getPath(ln) function! s:UI.getPath(ln)
let line = getline(a:ln) let line = getline(a:ln)
let rootLine = self.getRootLineNum() let rootLine = self.getRootLineNum()
"check to see if we have the root node
if a:ln == rootLine if a:ln == rootLine
return self.nerdtree.root.path return self.nerdtree.root.path
endif endif
@ -161,9 +158,13 @@ function! s:UI.getPath(ln)
return self.nerdtree.root.path.getParent() return self.nerdtree.root.path.getParent()
endif endif
if a:ln < rootLine
return {}
endif
let indent = self._indentLevelFor(line) let indent = self._indentLevelFor(line)
"remove the tree parts and the leading space " remove the tree parts and the leading space
let curFile = self._stripMarkup(line) let curFile = self._stripMarkup(line)
let dir = "" let dir = ""
@ -173,7 +174,7 @@ function! s:UI.getPath(ln)
let curLine = getline(lnum) let curLine = getline(lnum)
let curLineStripped = self._stripMarkup(curLine) let curLineStripped = self._stripMarkup(curLine)
"have we reached the top of the tree? " have we reached the top of the tree?
if lnum == rootLine if lnum == rootLine
let dir = self.nerdtree.root.path.str({'format': 'UI'}) . dir let dir = self.nerdtree.root.path.str({'format': 'UI'}) . dir
break break
@ -193,19 +194,19 @@ function! s:UI.getPath(ln)
return toReturn return toReturn
endfunction endfunction
"FUNCTION: s:UI.getLineNum(file_node){{{1 " FUNCTION: s:UI.getLineNum(file_node){{{1
"returns the line number this node is rendered on, or -1 if it isnt rendered " returns the line number this node is rendered on, or -1 if it isnt rendered
function! s:UI.getLineNum(file_node) function! s:UI.getLineNum(file_node)
"if the node is the root then return the root line no. " if the node is the root then return the root line no.
if a:file_node.isRoot() if a:file_node.isRoot()
return self.getRootLineNum() return self.getRootLineNum()
endif endif
let totalLines = line("$") let totalLines = line("$")
"the path components we have matched so far " the path components we have matched so far
let pathcomponents = [substitute(self.nerdtree.root.path.str({'format': 'UI'}), '/ *$', '', '')] let pathcomponents = [substitute(self.nerdtree.root.path.str({'format': 'UI'}), '/ *$', '', '')]
"the index of the component we are searching for " the index of the component we are searching for
let curPathComponent = 1 let curPathComponent = 1
let fullpath = a:file_node.path.str({'format': 'UI'}) let fullpath = a:file_node.path.str({'format': 'UI'})
@ -213,7 +214,7 @@ function! s:UI.getLineNum(file_node)
let lnum = self.getRootLineNum() let lnum = self.getRootLineNum()
while lnum > 0 while lnum > 0
let lnum = lnum + 1 let lnum = lnum + 1
"have we reached the bottom of the tree? " have we reached the bottom of the tree?
if lnum ==# totalLines+1 if lnum ==# totalLines+1
return -1 return -1
endif endif
@ -241,8 +242,8 @@ function! s:UI.getLineNum(file_node)
return -1 return -1
endfunction endfunction
"FUNCTION: s:UI.getRootLineNum(){{{1 " FUNCTION: s:UI.getRootLineNum(){{{1
"gets the line number of the root node " gets the line number of the root node
function! s:UI.getRootLineNum() function! s:UI.getRootLineNum()
let rootLine = 1 let rootLine = 1
while getline(rootLine) !~# '^\(/\|<\)' while getline(rootLine) !~# '^\(/\|<\)'
@ -251,29 +252,29 @@ function! s:UI.getRootLineNum()
return rootLine return rootLine
endfunction endfunction
"FUNCTION: s:UI.getShowBookmarks() {{{1 " FUNCTION: s:UI.getShowBookmarks() {{{1
function! s:UI.getShowBookmarks() function! s:UI.getShowBookmarks()
return self._showBookmarks return self._showBookmarks
endfunction endfunction
"FUNCTION: s:UI.getShowFiles() {{{1 " FUNCTION: s:UI.getShowFiles() {{{1
function! s:UI.getShowFiles() function! s:UI.getShowFiles()
return self._showFiles return self._showFiles
endfunction endfunction
"FUNCTION: s:UI.getShowHelp() {{{1 " FUNCTION: s:UI.getShowHelp() {{{1
function! s:UI.getShowHelp() function! s:UI.getShowHelp()
return self._showHelp return self._showHelp
endfunction endfunction
"FUNCTION: s:UI.getShowHidden() {{{1 " FUNCTION: s:UI.getShowHidden() {{{1
function! s:UI.getShowHidden() function! s:UI.getShowHidden()
return self._showHidden return self._showHidden
endfunction endfunction
"FUNCTION: s:UI._indentLevelFor(line) {{{1 " FUNCTION: s:UI._indentLevelFor(line) {{{1
function! s:UI._indentLevelFor(line) function! s:UI._indentLevelFor(line)
"have to do this work around because match() returns bytes, not chars " have to do this work around because match() returns bytes, not chars
let numLeadBytes = match(a:line, '\M\[^ '.g:NERDTreeDirArrowExpandable.g:NERDTreeDirArrowCollapsible.']') let numLeadBytes = match(a:line, '\M\[^ '.g:NERDTreeDirArrowExpandable.g:NERDTreeDirArrowCollapsible.']')
" The next line is a backward-compatible workaround for strchars(a:line(0:numLeadBytes-1]). strchars() is in 7.3+ " The next line is a backward-compatible workaround for strchars(a:line(0:numLeadBytes-1]). strchars() is in 7.3+
let leadChars = len(split(a:line[0:numLeadBytes-1], '\zs')) let leadChars = len(split(a:line[0:numLeadBytes-1], '\zs'))
@ -281,27 +282,27 @@ function! s:UI._indentLevelFor(line)
return leadChars / s:UI.IndentWid() return leadChars / s:UI.IndentWid()
endfunction endfunction
"FUNCTION: s:UI.IndentWid() {{{1 " FUNCTION: s:UI.IndentWid() {{{1
function! s:UI.IndentWid() function! s:UI.IndentWid()
return 2 return 2
endfunction endfunction
"FUNCTION: s:UI.isIgnoreFilterEnabled() {{{1 " FUNCTION: s:UI.isIgnoreFilterEnabled() {{{1
function! s:UI.isIgnoreFilterEnabled() function! s:UI.isIgnoreFilterEnabled()
return self._ignoreEnabled == 1 return self._ignoreEnabled == 1
endfunction endfunction
"FUNCTION: s:UI.isMinimal() {{{1 " FUNCTION: s:UI.isMinimal() {{{1
function! s:UI.isMinimal() function! s:UI.isMinimal()
return g:NERDTreeMinimalUI return g:NERDTreeMinimalUI
endfunction endfunction
"FUNCTION: s:UI.MarkupReg() {{{1 " FUNCTION: s:UI.MarkupReg() {{{1
function! s:UI.MarkupReg() function! s:UI.MarkupReg()
return '^\(['.g:NERDTreeDirArrowExpandable.g:NERDTreeDirArrowCollapsible.'] \| \+['.g:NERDTreeDirArrowExpandable.g:NERDTreeDirArrowCollapsible.'] \| \+\)' return '^\(['.g:NERDTreeDirArrowExpandable.g:NERDTreeDirArrowCollapsible.'] \| \+['.g:NERDTreeDirArrowExpandable.g:NERDTreeDirArrowCollapsible.'] \| \+\)'
endfunction endfunction
"FUNCTION: s:UI._renderBookmarks {{{1 " FUNCTION: s:UI._renderBookmarks {{{1
function! s:UI._renderBookmarks() function! s:UI._renderBookmarks()
if !self.isMinimal() if !self.isMinimal()
@ -322,12 +323,12 @@ function! s:UI._renderBookmarks()
call cursor(line(".")+1, col(".")) call cursor(line(".")+1, col("."))
endfunction endfunction
"FUNCTION: s:UI.restoreScreenState() {{{1 " FUNCTION: s:UI.restoreScreenState() {{{1
" "
"Sets the screen state back to what it was when nerdtree#saveScreenState was last " Sets the screen state back to what it was when nerdtree#saveScreenState was last
"called. " called.
" "
"Assumes the cursor is in the NERDTree window " Assumes the cursor is in the NERDTree window
function! s:UI.restoreScreenState() function! s:UI.restoreScreenState()
if !has_key(self, '_screenState') if !has_key(self, '_screenState')
return return
@ -342,9 +343,9 @@ function! s:UI.restoreScreenState()
let &scrolloff=old_scrolloff let &scrolloff=old_scrolloff
endfunction endfunction
"FUNCTION: s:UI.saveScreenState() {{{1 " FUNCTION: s:UI.saveScreenState() {{{1
"Saves the current cursor position in the current buffer and the window " Saves the current cursor position in the current buffer and the window
"scroll position " scroll position
function! s:UI.saveScreenState() function! s:UI.saveScreenState()
let win = winnr() let win = winnr()
call g:NERDTree.CursorToTreeWin() call g:NERDTree.CursorToTreeWin()
@ -355,31 +356,31 @@ function! s:UI.saveScreenState()
call nerdtree#exec(win . "wincmd w") call nerdtree#exec(win . "wincmd w")
endfunction endfunction
"FUNCTION: s:UI.setShowHidden(val) {{{1 " FUNCTION: s:UI.setShowHidden(val) {{{1
function! s:UI.setShowHidden(val) function! s:UI.setShowHidden(val)
let self._showHidden = a:val let self._showHidden = a:val
endfunction endfunction
"FUNCTION: s:UI._stripMarkup(line){{{1 " FUNCTION: s:UI._stripMarkup(line){{{1
"returns the given line with all the tree parts stripped off " returns the given line with all the tree parts stripped off
" "
"Args: " Args:
"line: the subject line " line: the subject line
function! s:UI._stripMarkup(line) function! s:UI._stripMarkup(line)
let line = a:line let line = a:line
"remove the tree parts and the leading space " remove the tree parts and the leading space
let line = substitute (line, g:NERDTreeUI.MarkupReg(),"","") let line = substitute (line, g:NERDTreeUI.MarkupReg(),"","")
"strip off any read only flag " strip off any read only flag
let line = substitute (line, ' \['.g:NERDTreeGlyphReadOnly.'\]', "","") let line = substitute (line, ' \['.g:NERDTreeGlyphReadOnly.'\]', "","")
"strip off any bookmark flags " strip off any bookmark flags
let line = substitute (line, ' {[^}]*}', "","") let line = substitute (line, ' {[^}]*}', "","")
"strip off any executable flags " strip off any executable flags
let line = substitute (line, '*\ze\($\| \)', "","") let line = substitute (line, '*\ze\($\| \)', "","")
"strip off any generic flags " strip off any generic flags
let line = substitute (line, '\[[^]]*\]', "","") let line = substitute (line, '\[[^]]*\]', "","")
let line = substitute (line,' -> .*',"","") " remove link to let line = substitute (line,' -> .*',"","") " remove link to
@ -387,22 +388,22 @@ function! s:UI._stripMarkup(line)
return line return line
endfunction endfunction
"FUNCTION: s:UI.render() {{{1 " FUNCTION: s:UI.render() {{{1
function! s:UI.render() function! s:UI.render()
setlocal modifiable setlocal modifiable
"remember the top line of the buffer and the current line so we can " remember the top line of the buffer and the current line so we can
"restore the view exactly how it was " restore the view exactly how it was
let curLine = line(".") let curLine = line(".")
let curCol = col(".") let curCol = col(".")
let topLine = line("w0") let topLine = line("w0")
"delete all lines in the buffer (being careful not to clobber a register) " delete all lines in the buffer (being careful not to clobber a register)
silent 1,$delete _ silent 1,$delete _
call self._dumpHelp() call self._dumpHelp()
"delete the blank line before the help and add one after it " delete the blank line before the help and add one after it
if !self.isMinimal() if !self.isMinimal()
call setline(line(".")+1, "") call setline(line(".")+1, "")
call cursor(line(".")+1, col(".")) call cursor(line(".")+1, col("."))
@ -412,24 +413,24 @@ function! s:UI.render()
call self._renderBookmarks() call self._renderBookmarks()
endif endif
"add the 'up a dir' line " add the 'up a dir' line
if !self.isMinimal() if !self.isMinimal()
call setline(line(".")+1, s:UI.UpDirLine()) call setline(line(".")+1, s:UI.UpDirLine())
call cursor(line(".")+1, col(".")) call cursor(line(".")+1, col("."))
endif endif
"draw the header line " draw the header line
let header = self.nerdtree.root.path.str({'format': 'UI', 'truncateTo': winwidth(0)}) let header = self.nerdtree.root.path.str({'format': 'UI', 'truncateTo': winwidth(0)})
call setline(line(".")+1, header) call setline(line(".")+1, header)
call cursor(line(".")+1, col(".")) call cursor(line(".")+1, col("."))
"draw the tree " draw the tree
silent put =self.nerdtree.root.renderToString() silent put =self.nerdtree.root.renderToString()
"delete the blank line at the top of the buffer " delete the blank line at the top of the buffer
silent 1,1delete _ silent 1,1delete _
"restore the view " restore the view
let old_scrolloff=&scrolloff let old_scrolloff=&scrolloff
let &scrolloff=0 let &scrolloff=0
call cursor(topLine, 1) call cursor(topLine, 1)
@ -441,14 +442,14 @@ function! s:UI.render()
endfunction endfunction
"FUNCTION: UI.renderViewSavingPosition {{{1 " FUNCTION: UI.renderViewSavingPosition {{{1
"Renders the tree and ensures the cursor stays on the current node or the " Renders the tree and ensures the cursor stays on the current node or the
"current nodes parent if it is no longer available upon re-rendering " current nodes parent if it is no longer available upon re-rendering
function! s:UI.renderViewSavingPosition() function! s:UI.renderViewSavingPosition()
let currentNode = g:NERDTreeFileNode.GetSelected() let currentNode = g:NERDTreeFileNode.GetSelected()
"go up the tree till we find a node that will be visible or till we run " go up the tree till we find a node that will be visible or till we run
"out of nodes " out of nodes
while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot() while currentNode != {} && !currentNode.isVisible() && !currentNode.isRoot()
let currentNode = currentNode.parent let currentNode = currentNode.parent
endwhile endwhile
@ -460,7 +461,7 @@ function! s:UI.renderViewSavingPosition()
endif endif
endfunction endfunction
"FUNCTION: s:UI.toggleHelp() {{{1 " FUNCTION: s:UI.toggleHelp() {{{1
function! s:UI.toggleHelp() function! s:UI.toggleHelp()
let self._showHelp = !self._showHelp let self._showHelp = !self._showHelp
endfunction endfunction
@ -515,7 +516,9 @@ function! s:UI.toggleZoom()
endif endif
endfunction endfunction
"FUNCTION: s:UI.UpDirLine() {{{1 " FUNCTION: s:UI.UpDirLine() {{{1
function! s:UI.UpDirLine() function! s:UI.UpDirLine()
return '.. (up a dir)' return '.. (up a dir)'
endfunction endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -335,4 +335,5 @@ function! NERDTreeExecuteFile()
call system("open '" . treenode.path.str() . "'") call system("open '" . treenode.path.str() . "'")
endif endif
endfunction endfunction
" vim: set sw=4 sts=4 et fdm=marker: " vim: set sw=4 sts=4 et fdm=marker:

View file

@ -69,8 +69,9 @@ MATLAB, Mercury, NASM, Nix, Objective-C, Objective-C++, OCaml, Perl, Perl
NG, reStructuredText, RPM spec, Ruby, SASS/SCSS, Scala, Slim, SML, Solidity, NG, reStructuredText, RPM spec, Ruby, SASS/SCSS, Scala, Slim, SML, Solidity,
Sphinx, SQL, Stylus, Tcl, TeX, Texinfo, Twig, TypeScript, Vala, Verilog, VHDL, Sphinx, SQL, Stylus, Tcl, TeX, Texinfo, Twig, TypeScript, Vala, Verilog, VHDL,
Vim help, VimL, Vue.js, xHtml, XML, XSLT, XQuery, YACC, YAML, YANG data models, Vim help, VimL, Vue.js, xHtml, XML, XSLT, XQuery, YACC, YAML, YANG data models,
z80, Zope page templates, and Zsh. See the [manual][checkers] for details about YARA rules, z80, Zope page templates, and Zsh. See the [manual][checkers] for
the corresponding supported checkers (`:help syntastic-checkers` in Vim). details about the corresponding supported checkers (`:help syntastic-checkers`
in Vim).
A number of third-party Vim plugins also provide checkers for syntastic, for A number of third-party Vim plugins also provide checkers for syntastic, for
example: [merlin][merlin], [omnisharp-vim][omnisharp], [rust.vim][rust], example: [merlin][merlin], [omnisharp-vim][omnisharp], [rust.vim][rust],

View file

@ -634,17 +634,27 @@ endfunction " }}}2
function! syntastic#preprocess#mypy(errors) abort " {{{2 function! syntastic#preprocess#mypy(errors) abort " {{{2
let out = [] let out = []
for e in a:errors for e in a:errors
" new format " column numbers
let parts = matchlist(e, '\v^(.{-1,}):(\d+): error: (.+)') let parts = matchlist(e, '\v^(.{-1,}):(\d+):(\d+): ([ew])%(rror|arning): (.+)')
if len(parts) > 3 if len(parts) > 5
call add(out, join(parts[1:3], ':')) let parts[3] += 1
call add(out, join(parts[1:5], ':'))
continue continue
endif endif
" old format " no column numbers
let parts = matchlist(e, '\v^(.{-1,}):(\d+): ([ew])%(rror|arning): (.+)')
if len(parts) > 4
call add(out, join(parts[1:4], ':'))
continue
endif
" obsolete format
let parts = matchlist(e, '\v^(.{-1,}), line (\d+): (.+)') let parts = matchlist(e, '\v^(.{-1,}), line (\d+): (.+)')
if len(parts) > 3 if len(parts) > 3
call add(out, join(parts[1:3], ':')) let parts[4] = parts[3]
let parts[3] = 'e'
call add(out, join(parts[1:4], ':'))
endif endif
endfor endfor
return out return out

View file

@ -132,6 +132,7 @@ SYNTAX CHECKERS BY LANGUAGE *syntastic-checkers-lang*
YACC.....................................|syntastic-checkers-yacc| YACC.....................................|syntastic-checkers-yacc|
YAML.....................................|syntastic-checkers-yaml| YAML.....................................|syntastic-checkers-yaml|
YANG.....................................|syntastic-checkers-yang| YANG.....................................|syntastic-checkers-yang|
YARA.....................................|syntastic-checkers-yara|
Z80......................................|syntastic-checkers-z80| Z80......................................|syntastic-checkers-z80|
Zope Page Templates......................|syntastic-checkers-zpt| Zope Page Templates......................|syntastic-checkers-zpt|
@ -1119,7 +1120,7 @@ one option per line (cf. |syntastic-config-files|).
Note~ Note~
By default you can set "ClangTidy" parameters in By default you can set "ClangTidy" parameters in
'g:syntastic_ppc_clang_tidy_args', and compilation parameters (defines, 'g:syntastic_cpp_clang_tidy_args', and compilation parameters (defines,
optimisation flags, etc.) in the configuration file. optimisation flags, etc.) in the configuration file.
If you want "ClangTidy" to use compilation databases If you want "ClangTidy" to use compilation databases
@ -7629,6 +7630,36 @@ You probably also need a plugin to set |filetype| for YANG files, such as
https://github.com/nathanalderson/yang.vim https://github.com/nathanalderson/yang.vim
==============================================================================
SYNTAX CHECKERS FOR YARA *syntastic-checkers-yara*
The following checkers are available for YARA rule files (filetype "yara"):
1. yarac....................|syntastic-yara-yarac|
------------------------------------------------------------------------------
1. yarac *syntastic-yara-yarac*
Name: yarac
Maintainer: Albert Song <albb@teamt5.org>
"yarac" is the official compiler for YARA rule files. See the project's page
at GitHub for more information:
https://github.com/VirusTotal/yara
Checker options~
This checker is initialised using the "makeprgBuild()" function and thus it
accepts the standard options described at |syntastic-config-makeprg|.
Note~
You probably also need a plugin to set |filetype| for YARA rule files, such as
"vim-yara":
https://github.com/yaunj/vim-yara
============================================================================== ==============================================================================
SYNTAX CHECKERS FOR Z80 *syntastic-checkers-z80* SYNTAX CHECKERS FOR Z80 *syntastic-checkers-z80*

View file

@ -19,7 +19,7 @@ if has('reltime')
lockvar! g:_SYNTASTIC_START lockvar! g:_SYNTASTIC_START
endif endif
let g:_SYNTASTIC_VERSION = '3.8.0-97' let g:_SYNTASTIC_VERSION = '3.8.0-101'
lockvar g:_SYNTASTIC_VERSION lockvar g:_SYNTASTIC_VERSION
" Sanity checks {{{1 " Sanity checks {{{1

View file

@ -112,6 +112,7 @@ let s:_DEFAULT_CHECKERS = {
\ 'yacc': ['bison'], \ 'yacc': ['bison'],
\ 'yaml': ['jsyaml'], \ 'yaml': ['jsyaml'],
\ 'yang': ['pyang'], \ 'yang': ['pyang'],
\ 'yara': ['yarac'],
\ 'z80': ['z80syntaxchecker'], \ 'z80': ['z80syntaxchecker'],
\ 'zpt': ['zptlint'], \ 'zpt': ['zptlint'],
\ 'zsh': ['zsh'], \ 'zsh': ['zsh'],

View file

@ -69,6 +69,7 @@ rebar_opts(RebarFile) ->
Dir = get_root(filename:dirname(RebarFile)), Dir = get_root(filename:dirname(RebarFile)),
case file:consult(RebarFile) of case file:consult(RebarFile) of
{ok, Terms} -> {ok, Terms} ->
%% Add deps for a rebar (version < 3) project
RebarLibDirs = proplists:get_value(lib_dirs, Terms, []), RebarLibDirs = proplists:get_value(lib_dirs, Terms, []),
lists:foreach( lists:foreach(
fun(LibDir) -> fun(LibDir) ->
@ -76,8 +77,14 @@ rebar_opts(RebarFile) ->
end, RebarLibDirs), end, RebarLibDirs),
RebarDepsDir = proplists:get_value(deps_dir, Terms, "deps"), RebarDepsDir = proplists:get_value(deps_dir, Terms, "deps"),
code:add_pathsa(filelib:wildcard(RebarDepsDir ++ "/*/ebin")), code:add_pathsa(filelib:wildcard(RebarDepsDir ++ "/*/ebin")),
IncludeDeps = {i, filename:join(Dir, RebarDepsDir)},
proplists:get_value(erl_opts, Terms, []) ++ [IncludeDeps]; %% Add deps for rebar 3
code:add_pathsa(filelib:wildcard(Dir ++ "/_build/default/lib/*/ebin")),
%% Add include dependencies
IncludeDeps = [{i, IPath} || IPath <- filelib:wildcard(Dir ++ "/_build/default/lib/*")] ++
[{i, filename:join(Dir, RebarDepsDir)}, %% rebar 2 dependencies
{i, filename:join(Dir, "apps")}], %% rebar 3 multi-apps
proplists:get_value(erl_opts, Terms, []) ++ IncludeDeps;
{error, _} when RebarFile == "rebar.config" -> {error, _} when RebarFile == "rebar.config" ->
fallback_opts(); fallback_opts();
{error, _} -> {error, _} ->
@ -258,12 +265,19 @@ apps_dir_from_src(SrcFile) ->
SrcDir = filename:dirname(SrcFile), SrcDir = filename:dirname(SrcFile),
filename:join(SrcDir, "../../ebin"). filename:join(SrcDir, "../../ebin").
%% Find the root directory of the project
get_root(Dir) -> get_root(Dir) ->
Path = filename:split(filename:absname(Dir)), Path = filename:split(filename:absname(Dir)),
filename:join(get_root(lists:reverse(Path), Path)). filename:join(get_root(lists:reverse(Path), Path)).
get_root([], Path) -> get_root([], Path) ->
Path; Path;
%% Strip off /apps/<appname>/src from the end of the path
%% (rebar 3 multi-app project)
get_root(["src", _Appname, "apps" | Tail], _Path) ->
lists:reverse(Tail);
%% Strip off /src or /test from the end of the path
%% (single-app project)
get_root(["src" | Tail], _Path) -> get_root(["src" | Tail], _Path) ->
lists:reverse(Tail); lists:reverse(Tail);
get_root(["test" | Tail], _Path) -> get_root(["test" | Tail], _Path) ->

View file

@ -14,14 +14,21 @@ let s:save_cpo = &cpo
set cpo&vim set cpo&vim
function! SyntaxCheckers_python_mypy_GetLocList() dict function! SyntaxCheckers_python_mypy_GetLocList() dict
let makeprg = self.makeprgBuild({}) if !exists('s:mypy_new')
" creative versioning: version string has changed from v0.4.6 to v0.470
" XXX the test should be fine for now, since 470 > 4
let s:mypy_new = syntastic#util#versionIsAtLeast(self.getVersion(), [0, 4, 5])
endif
let errorformat = '%f:%l:%m' let makeprg = self.makeprgBuild({ 'args_after': (s:mypy_new ? '--show-column-numbers' : '') })
let errorformat =
\ '%f:%l:%c:%t:%m,' .
\ '%f:%l:%t:%m'
return SyntasticMake({ return SyntasticMake({
\ 'makeprg': makeprg, \ 'makeprg': makeprg,
\ 'errorformat': errorformat, \ 'errorformat': errorformat,
\ 'defaults': { 'type': 'E' },
\ 'returns': [0, 1], \ 'returns': [0, 1],
\ 'preprocess': 'mypy' }) \ 'preprocess': 'mypy' })
endfunction endfunction

View file

@ -0,0 +1,41 @@
"============================================================================
"File: yara.vim
"Description: Syntax checking plugin for syntastic
"Maintainer: Albert Song (albb@teamt5.org)
"License: This program is free software. It comes without any warranty,
" to the extent permitted by applicable law. You can redistribute
" it and/or modify it under the terms of the Do What The Fuck You
" Want To Public License, Version 2, as published by Sam Hocevar.
" See http://sam.zoy.org/wtfpl/COPYING for more details.
"
"============================================================================
if exists('g:loaded_syntastic_yara_yarac_checker')
finish
endif
let g:loaded_syntastic_yara_yarac_checker = 1
let s:save_cpo = &cpo
set cpo&vim
function! SyntaxCheckers_yara_yarac_GetLocList() dict
let makeprg = self.makeprgBuild({ 'fname_after' : syntastic#util#DevNull() })
let errorformat =
\ '%f(%l): %trror: %m,' .
\ '%f(%l): %tarning: %m,' .
\ '%f(%l): %m'
return SyntasticMake({
\ 'makeprg': makeprg,
\ 'errorformat': errorformat })
endfunction
call g:SyntasticRegistry.CreateAndRegisterChecker({
\ 'filetype': 'yara',
\ 'name': 'yarac'})
let &cpo = s:save_cpo
unlet s:save_cpo
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -77,17 +77,17 @@ that are part of Git repositories).
*fugitive-:Gmerge* *fugitive-:Gmerge*
:Gmerge [args] Calls git-merge and loads errors and conflicted files :Gmerge [args] Calls git-merge and loads errors and conflicted files
into the quickfix list. Opens a |:Gcommit| style into the |quickfix| list. Opens a |:Gcommit| style
split window for the commit message if the merge split window for the commit message if the merge
succeeds. If called during a merge conflict, the succeeds. If called during a merge conflict, the
conflicted files from the current index are loaded conflicted files from the current index are loaded
into the quickfix list. into the |quickfix| list.
*fugitive-:Gpull* *fugitive-:Gpull*
:Gpull [args] Like |:Gmerge|, but for git-pull. :Gpull [args] Like |:Gmerge|, but for git-pull.
*fugitive-:Gpush* *fugitive-:Gpush*
:Gpush [args] Invoke git-push, load the results into the quickfix :Gpush [args] Invoke git-push, load the results into the |quickfix|
list, and invoke |:cwindow| to reveal any errors. list, and invoke |:cwindow| to reveal any errors.
|:Dispatch| is used if available for asynchronous |:Dispatch| is used if available for asynchronous
invocation. invocation.
@ -103,20 +103,20 @@ that are part of Git repositories).
*fugitive-:Glog* *fugitive-:Glog*
:Glog [args] Load all previous revisions of the current file into :Glog [args] Load all previous revisions of the current file into
the quickfix list. Additional git-log arguments can the |quickfix| list. Additional git-log arguments can
be given (for example, --reverse). If "--" appears as be given (for example, --reverse). If "--" appears as
an argument, no file specific filtering is done, and an argument, no file specific filtering is done, and
previous commits rather than previous file revisions previous commits rather than previous file revisions
are loaded. are loaded.
:{range}Glog [args] Use git-log -L to load previous revisions of the given :{range}Glog [args] Use git-log -L to load previous revisions of the given
range of the current file into the quickfix list. The range of the current file into the |quickfix| list.
cursor is positioned on the first line of the first The cursor is positioned on the first line of the
diff hunk for each commit. first diff hunk for each commit.
*fugitive-:Gllog* *fugitive-:Gllog*
:Gllog [args] Like |:Glog|, but use the location list instead of the :Gllog [args] Like |:Glog|, but use the location list instead of the
quickfix list. |quickfix| list.
*fugitive-:Gedit* *fugitive-:Ge* *fugitive-:Gedit* *fugitive-:Ge*
:Gedit [revision] |:edit| a |fugitive-revision|. :Gedit [revision] |:edit| a |fugitive-revision|.

View file

@ -2749,6 +2749,8 @@ function! s:BufReadObject() abort
endif endif
endtry endtry
return ''
catch /^fugitive: rev-parse/
return '' return ''
catch /^fugitive:/ catch /^fugitive:/
return 'echoerr v:errmsg' return 'echoerr v:errmsg'

View file

@ -88,7 +88,10 @@ function! gitgutter#diff#run_diff(realtime, preserve_full_diff) abort
let op_mark_start = getpos("'[") let op_mark_start = getpos("'[")
let op_mark_end = getpos("']") let op_mark_end = getpos("']")
let current_buffer = bufnr('')
execute 'buffer '.bufnr
execute 'keepalt noautocmd silent write!' buff_file execute 'keepalt noautocmd silent write!' buff_file
execute 'buffer '.current_buffer
call setbufvar(bufnr, "&mod", modified) call setbufvar(bufnr, "&mod", modified)
call setpos("'[", op_mark_start) call setpos("'[", op_mark_start)

View file

@ -1 +1,2 @@
.local/
.git/ .git/

View file

@ -1,5 +1,75 @@
## unplanned ## unplanned
IMPROVEMENTS:
* Add descriptions to neosnippet abbrevations.
[[GH-1639]](https://github.com/fatih/vim-go/pull/1639)
* Show messages in the location list instead of the quickfix list when
`gometalinter` is run automatically when saving a buffer. Whether the
location list or quickfix list is used can be customized in the usual ways.
[[GH-1652]](https://github.com/fatih/vim-go/pull/1652)
BUG FIXES:
* Create quickfix list correctly when tests timeout.
[[GH-1633]](https://github.com/fatih/vim-go/pull/1633)
* Apply `g:go_test_timeout` when running `:GoTestFunc`.
[[GH-1631]](https://github.com/fatih/vim-go/pull/1631)
* The user's configured `g:go_doc_url` variable wasn't working correctly in the
case when the "gogetdoc" command isn't installed.
[[GH-1629]](https://github.com/fatih/vim-go/pull/1629)
* Highlight format specifiers with an index (e.g. `%[2]d`).
[[GH-1634]](https://github.com/fatih/vim-go/pull/1634)
* Respect `g:go_test_show_name` change for `:GoTest` when it changes during a
Vim session.
[[GH-1641]](https://github.com/fatih/vim-go/pull/1641)
* Show `g:go_test_show_name` value for `:GoTest` failures if it's available.
[[GH-1641]](https://github.com/fatih/vim-go/pull/1641)
* Make sure linter errors for the file being saved are shown in vim74 and nvim.
[[GH-1640]](https://github.com/fatih/vim-go/pull/1640)
* Make sure only linter errors for the file being saved are shown in vim8.
Previously, all linter errors for all files in the current file's directory
were being shown.
[[GH-1640]](https://github.com/fatih/vim-go/pull/1640)
* Make sure gometalinter is run on the given directories when arguments are
given to :GoMetaLinter.
[[GH-1640]](https://github.com/fatih/vim-go/pull/1640)
* Do not run disabled linters with `gometalinter`.
[[GH-1648]](https://github.com/fatih/vim-go/pull/1648)
* Do not prompt user to press enter after when `gometalinter` is called in
autosave mode.
[[GH-1654]](https://github.com/fatih/vim-go/pull/1654)
* Fix potential race conditions when using vim8 jobs.
[[GH-1656]](https://github.com/fatih/vim-go/pull/1656)
* Treat `'autowriteall'` the same as `'autowrite'` when determining whether to
write a buffer before calling some commands.
[[GH-1653]](https://github.com/fatih/vim-go/pull/1653)
* Show the file location of test errors when the message is empty or begins
with a newline.
[[GH-1664]](https://github.com/fatih/vim-go/pull/1664)
BACKWARDS INCOMPATIBILITIES:
* Highlighting function and method declarations/calls is fixed. To fix it we
had to remove the meaning of the previous settings. The following setting is
removed:
* `go_highlight_methods`
in favor of the following settings and changes:
* `go_highlight_functions`: This highlights now all function and method
declarations (whereas previously it would also highlight function and
method calls, not anymore)
* `go_highlight_function_calls`: This higlights now all all function and
method calls.
[[GH-1557]](https://github.com/fatih/vim-go/pull/1557)
* Rename g`g:go_metalinter_excludes` to `g:go_metalinter_disabled`.
[[GH-1648]](https://github.com/fatih/vim-go/pull/1648)
## 1.16 - (December 29, 2017)
FEATURES: FEATURES:
* Add `g:go_doc_url` to change the `godoc` server from `godoc.org` to a custom * Add `g:go_doc_url` to change the `godoc` server from `godoc.org` to a custom
@ -61,6 +131,11 @@ BUG FIXES:
[[GH-1587]](https://github.com/fatih/vim-go/pull/1587). [[GH-1587]](https://github.com/fatih/vim-go/pull/1587).
* Fix installation of `gocode` on MS-Windows. * Fix installation of `gocode` on MS-Windows.
[[GH-1606]](https://github.com/fatih/vim-go/pull/1606). [[GH-1606]](https://github.com/fatih/vim-go/pull/1606).
* Fix template creation for files in directories that don't exist yet.
[[GH-1618]](https://github.com/fatih/vim-go/pull/1618).
* Fix behavior of terminal windows and resize terminal windows correctly for
all valid `g:go_term_mode` values.
[[GH-1611]](https://github.com/fatih/vim-go/pull/1611).
BACKWARDS INCOMPATIBILITIES: BACKWARDS INCOMPATIBILITIES:
@ -76,6 +151,11 @@ BACKWARDS INCOMPATIBILITIES:
changed for all commands run from Vim. changed for all commands run from Vim.
[[GH-1461]](https://github.com/fatih/vim-go/pull/1461) and [[GH-1461]](https://github.com/fatih/vim-go/pull/1461) and
[[GH-1525]](https://github.com/fatih/vim-go/pull/1525). [[GH-1525]](https://github.com/fatih/vim-go/pull/1525).
* Update `:GoFillStruct` to check the current line (vs. the exact cursor
position) for a struct literal to fill. To support this, fillstruct made
[backwards imcompatible
changes](https://github.com/davidrjenni/reftools/pull/8).
[[GH-1607]](https://github.com/fatih/vim-go/pull/1607).
## 1.15 - (October 3, 2017) ## 1.15 - (October 3, 2017)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 747 KiB

View file

@ -1,5 +1,5 @@
function! go#cmd#autowrite() abort function! go#cmd#autowrite() abort
if &autowrite == 1 if &autowrite == 1 || &autowriteall == 1
silent! wall silent! wall
endif endif
endfunction endfunction
@ -269,7 +269,7 @@ function s:cmd_job(args) abort
" autowrite is not enabled for jobs " autowrite is not enabled for jobs
call go#cmd#autowrite() call go#cmd#autowrite()
function! s:error_info_cb(job, exit_status, data) closure abort function! s:complete(job, exit_status, data) closure abort
let status = { let status = {
\ 'desc': 'last status', \ 'desc': 'last status',
\ 'type': a:args.cmd[1], \ 'type': a:args.cmd[1],
@ -288,12 +288,13 @@ function s:cmd_job(args) abort
call go#statusline#Update(status_dir, status) call go#statusline#Update(status_dir, status)
endfunction endfunction
let a:args.error_info_cb = funcref('s:error_info_cb') let a:args.complete = funcref('s:complete')
let callbacks = go#job#Spawn(a:args) let callbacks = go#job#Spawn(a:args)
let start_options = { let start_options = {
\ 'callback': callbacks.callback, \ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb, \ 'exit_cb': callbacks.exit_cb,
\ 'close_cb': callbacks.close_cb,
\ } \ }
" pre start " pre start

View file

@ -51,7 +51,7 @@ function! go#coverage#Buffer(bang, ...) abort
if go#util#has_job() if go#util#has_job()
call s:coverage_job({ call s:coverage_job({
\ 'cmd': ['go', 'test', '-coverprofile', l:tmpname] + a:000, \ 'cmd': ['go', 'test', '-coverprofile', l:tmpname] + a:000,
\ 'custom_cb': function('s:coverage_callback', [l:tmpname]), \ 'complete': function('s:coverage_callback', [l:tmpname]),
\ 'bang': a:bang, \ 'bang': a:bang,
\ 'for': 'GoTest', \ 'for': 'GoTest',
\ }) \ })
@ -107,7 +107,7 @@ function! go#coverage#Browser(bang, ...) abort
if go#util#has_job() if go#util#has_job()
call s:coverage_job({ call s:coverage_job({
\ 'cmd': ['go', 'test', '-coverprofile', l:tmpname], \ 'cmd': ['go', 'test', '-coverprofile', l:tmpname],
\ 'custom_cb': function('s:coverage_browser_callback', [l:tmpname]), \ 'complete': function('s:coverage_browser_callback', [l:tmpname]),
\ 'bang': a:bang, \ 'bang': a:bang,
\ 'for': 'GoTest', \ 'for': 'GoTest',
\ }) \ })
@ -278,7 +278,8 @@ function s:coverage_job(args)
call go#cmd#autowrite() call go#cmd#autowrite()
let status_dir = expand('%:p:h') let status_dir = expand('%:p:h')
function! s:error_info_cb(job, exit_status, data) closure let Complete = a:args.complete
function! s:complete(job, exit_status, data) closure
let status = { let status = {
\ 'desc': 'last status', \ 'desc': 'last status',
\ 'type': "coverage", \ 'type': "coverage",
@ -290,14 +291,16 @@ function s:coverage_job(args)
endif endif
call go#statusline#Update(status_dir, status) call go#statusline#Update(status_dir, status)
return Complete(a:job, a:exit_status, a:data)
endfunction endfunction
let a:args.error_info_cb = funcref('s:error_info_cb') let a:args.complete = funcref('s:complete')
let callbacks = go#job#Spawn(a:args) let callbacks = go#job#Spawn(a:args)
let start_options = { let start_options = {
\ 'callback': callbacks.callback, \ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb, \ 'exit_cb': callbacks.exit_cb,
\ 'close_cb': callbacks.close_cb,
\ } \ }
" pre start " pre start

View file

@ -53,7 +53,7 @@ function! go#def#Jump(mode) abort
if go#util#has_job() if go#util#has_job()
let l:spawn_args = { let l:spawn_args = {
\ 'cmd': cmd, \ 'cmd': cmd,
\ 'custom_cb': function('s:jump_to_declaration_cb', [a:mode, bin_name]), \ 'complete': function('s:jump_to_declaration_cb', [a:mode, bin_name]),
\ } \ }
if &modified if &modified
@ -292,16 +292,12 @@ function! go#def#Stack(...) abort
endfunction endfunction
function s:def_job(args) abort function s:def_job(args) abort
function! s:error_info_cb(job, exit_status, data) closure
" do not print anything during async definition search&jump
endfunction
let a:args.error_info_cb = funcref('s:error_info_cb')
let callbacks = go#job#Spawn(a:args) let callbacks = go#job#Spawn(a:args)
let start_options = { let start_options = {
\ 'callback': callbacks.callback, \ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb, \ 'exit_cb': callbacks.exit_cb,
\ 'close_cb': callbacks.close_cb,
\ } \ }
if &modified if &modified

View file

@ -29,18 +29,7 @@ function! go#doc#OpenBrowser(...) abort
let name = out["name"] let name = out["name"]
let decl = out["decl"] let decl = out["decl"]
let godoc_url = get(g:, 'go_doc_url', 'https://godoc.org') let godoc_url = s:custom_godoc_url()
if godoc_url isnot 'https://godoc.org'
" strip last '/' character if available
let last_char = strlen(godoc_url) - 1
if godoc_url[last_char] == '/'
let godoc_url = strpart(godoc_url, 0, last_char)
endif
" custom godoc installations expects it
let godoc_url .= "/pkg"
endif
let godoc_url .= "/" . import let godoc_url .= "/" . import
if decl !~ "^package" if decl !~ "^package"
let godoc_url .= "#" . name let godoc_url .= "#" . name
@ -61,7 +50,7 @@ function! go#doc#OpenBrowser(...) abort
let exported_name = pkgs[1] let exported_name = pkgs[1]
" example url: https://godoc.org/github.com/fatih/set#Set " example url: https://godoc.org/github.com/fatih/set#Set
let godoc_url = "https://godoc.org/" . pkg . "#" . exported_name let godoc_url = s:custom_godoc_url() . "/" . pkg . "#" . exported_name
call go#tool#OpenBrowser(godoc_url) call go#tool#OpenBrowser(godoc_url)
endfunction endfunction
@ -217,13 +206,18 @@ function! s:godocWord(args) abort
return [pkg, exported_name] return [pkg, exported_name]
endfunction endfunction
function! s:godocNotFound(content) abort function! s:custom_godoc_url() abort
if len(a:content) == 0 let godoc_url = get(g:, 'go_doc_url', 'https://godoc.org')
return 1 if godoc_url isnot 'https://godoc.org'
" strip last '/' character if available
let last_char = strlen(godoc_url) - 1
if godoc_url[last_char] == '/'
let godoc_url = strpart(godoc_url, 0, last_char)
endif endif
" custom godoc installations expect /pkg before package names
return a:content =~# '^.*: no such file or directory\n$' let godoc_url .= "/pkg"
endif
return godoc_url
endfunction endfunction
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View file

@ -1,7 +1,8 @@
function! go#fillstruct#FillStruct() abort function! go#fillstruct#FillStruct() abort
let l:cmd = ['fillstruct', let l:cmd = ['fillstruct',
\ '-file', bufname(''), \ '-file', bufname(''),
\ '-offset', go#util#OffsetCursor()] \ '-offset', go#util#OffsetCursor(),
\ '-line', line('.')]
" Read from stdin if modified. " Read from stdin if modified.
if &modified if &modified
@ -23,27 +24,36 @@ function! go#fillstruct#FillStruct() abort
return return
endtry endtry
let l:code = split(l:json['code'], "\n") " Output is array:
"[
" {"start": 92, "end": 106, "code": "mail.Address{\n\tName: \"\",\n\tAddress: \"\",\n}"},
" {...second struct...}
" ]
let l:pos = getpos('.') let l:pos = getpos('.')
try try
for l:struct in l:json
let l:code = split(l:struct['code'], "\n")
" Add any code before/after the struct. " Add any code before/after the struct.
exe l:json['start'] . 'go' exe l:struct['start'] . 'go'
let l:code[0] = getline('.')[:col('.')-1] . l:code[0] let l:code[0] = getline('.')[:col('.')-1] . l:code[0]
exe l:json['end'] . 'go' exe l:struct['end'] . 'go'
let l:code[len(l:code)-1] .= getline('.')[col('.'):] let l:code[len(l:code)-1] .= getline('.')[col('.'):]
" Indent every line except the first one; makes it look nice. " Indent every line except the first one; makes it look nice.
let l:indent = repeat("\t", indent('.') / &ts) let l:indent = repeat("\t", indent('.') / &tabstop)
for i in range(1, len(l:code)-1) for l:i in range(1, len(l:code)-1)
let l:code[l:i] = l:indent . l:code[i] let l:code[l:i] = l:indent . l:code[l:i]
endfor endfor
" Out with the old ... " Out with the old ...
exe 'normal! ' . l:json['start'] . 'gov' . l:json['end'] . 'gox' exe 'normal! ' . l:struct['start'] . 'gov' . l:struct['end'] . 'gox'
" ... in with the new. " ... in with the new.
call setline('.', l:code[0]) call setline('.', l:code[0])
call append('.', l:code[1:]) call append('.', l:code[1:])
endfor
finally finally
call setpos('.', l:pos) call setpos('.', l:pos)
endtry endtry

View file

@ -3,7 +3,7 @@ func! Test_fillstruct() abort
let l:tmp = gotest#write_file('a/a.go', [ let l:tmp = gotest#write_file('a/a.go', [
\ 'package a', \ 'package a',
\ 'import "net/mail"', \ 'import "net/mail"',
\ 'var addr = mail.Address{}']) \ "var addr = mail.\x1fAddress{}"])
call go#fillstruct#FillStruct() call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [ call gotest#assert_buffer(1, [
@ -16,4 +16,75 @@ func! Test_fillstruct() abort
endtry endtry
endfunc endfunc
func! Test_fillstruct_line() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import "net/mail"',
\ "\x1f" . 'var addr = mail.Address{}'])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'var addr = mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_fillstruct_two_line() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ "\x1f" . 'func x() { fmt.Println(mail.Address{}, mail.Address{}) }'])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ 'func x() { fmt.Println(mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}, mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}) }'])
finally
"call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_fillstruct_two_cursor() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ "func x() { fmt.Println(mail.Address{}, mail.Ad\x1fdress{}) }"])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ 'func x() { fmt.Println(mail.Address{}, mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}) }'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View file

@ -153,8 +153,7 @@ function! go#fmt#update_file(source, target)
endfunction endfunction
" run runs the gofmt/goimport command for the given source file and returns " run runs the gofmt/goimport command for the given source file and returns
" the the output of the executed command. Target is the real file to be " the output of the executed command. Target is the real file to be formatted.
" formated.
function! go#fmt#run(bin_name, source, target) function! go#fmt#run(bin_name, source, target)
let cmd = s:fmt_cmd(a:bin_name, a:source, a:target) let cmd = s:fmt_cmd(a:bin_name, a:source, a:target)
if empty(cmd) if empty(cmd)

View file

@ -162,8 +162,12 @@ function! s:async_guru(args) abort
let status = {} let status = {}
let exitval = 0 let exitval = 0
let closed = 0
let exited = 0
function! s:exit_cb(job, exitval) closure function! s:exit_cb(job, exitval) closure
let exited = 1
let status = { let status = {
\ 'desc': 'last status', \ 'desc': 'last status',
\ 'type': statusline_type, \ 'type': statusline_type,
@ -176,9 +180,21 @@ function! s:async_guru(args) abort
endif endif
call go#statusline#Update(status_dir, status) call go#statusline#Update(status_dir, status)
if closed
call s:complete()
endif
endfunction endfunction
function! s:close_cb(ch) closure function! s:close_cb(ch) closure
let closed = 1
if exited
call s:complete()
endif
endfunction
function! s:complete() closure
let out = join(messages, "\n") let out = join(messages, "\n")
if has_key(a:args, 'custom_parse') if has_key(a:args, 'custom_parse')
@ -597,11 +613,9 @@ function! s:parse_guru_output(exit_val, output, title) abort
return return
endif endif
let old_errorformat = &errorformat
let errformat = "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m" let errformat = "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m"
let l:listtype = go#list#Type("_guru") let l:listtype = go#list#Type("_guru")
call go#list#ParseFormat(l:listtype, errformat, a:output, a:title) call go#list#ParseFormat(l:listtype, errformat, a:output, a:title)
let &errorformat = old_errorformat
let errors = go#list#Get(l:listtype) let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))

View file

@ -1,45 +1,79 @@
" Spawn returns callbacks to be used with job_start. It's abstracted to be " Spawn returns callbacks to be used with job_start. It is abstracted to be
" used with various go command, such as build, test, install, etc.. This avoid " used with various go commands, such as build, test, install, etc.. This
" us to write the same callback over and over for some commands. It's fully " allows us to avoid writing the same callback over and over for some
" customizable so each command can change it to it's own logic. " commands. It's fully customizable so each command can change it to it's own
" logic.
"
" args is a dictionary with the these keys:
" 'cmd':
" The value to pass to job_start().
" 'bang':
" Set to 0 to jump to the first error in the error list.
" Defaults to 0.
" 'for':
" The g:go_list_type_command key to use to get the error list type to use.
" Defaults to '_job'
" 'complete':
" A function to call after the job exits and the channel is closed. The
" function will be passed three arguments: the job, its exit code, and the
" list of messages received from the channel. The default value will
" process the messages and manage the error list after the job exits and
" the channel is closed.
" 'callback':
" A function to call when there is a message to read from the job's
" channel. The function will be passed two arguments: the channel and a
" message. See job-callback.
" The return value is a dictionary with these keys:
" 'callback':
" A function suitable to be passed as a job callback handler. See
" job-callback.
" 'exit_cb':
" A function suitable to be passed as a job exit_cb handler. See
" job-exit_cb.
" 'close_cb':
" A function suitable to be passed as a job close_cb handler. See
" job-close_cb.
function go#job#Spawn(args) function go#job#Spawn(args)
let cbs = { let cbs = {}
\ 'winnr': winnr(),
\ 'dir': getcwd(), let winnr = winnr()
\ 'jobdir': fnameescape(expand("%:p:h")), let dir = getcwd()
\ 'messages': [], let jobdir = fnameescape(expand("%:p:h"))
\ 'args': a:args.cmd, let messages = []
\ 'bang': 0, let args = a:args.cmd
\ 'for': "_job", let bang = 0
\ } let for = "_job"
if has_key(a:args, 'bang') if has_key(a:args, 'bang')
let cbs.bang = a:args.bang let l:bang = a:args.bang
endif endif
if has_key(a:args, 'for') if has_key(a:args, 'for')
let cbs.for = a:args.for let l:for = a:args.for
endif endif
" add final callback to be called if async job is finished let l:exited = 0
" The signature should be in form: func(job, exit_status, messages) let l:exit_status = 0
if has_key(a:args, 'custom_cb') let l:closed = 0
let cbs.custom_cb = a:args.custom_cb
endif
if has_key(a:args, 'error_info_cb') function! s:NopComplete(job, exit_status, data)
let cbs.error_info_cb = a:args.error_info_cb
endif
function cbs.callback(chan, msg) dict
call add(self.messages, a:msg)
endfunction endfunction
function cbs.exit_cb(job, exitval) dict let Complete = funcref('s:NopComplete')
if has_key(self, 'error_info_cb')
call self.error_info_cb(a:job, a:exitval, self.messages) if has_key(a:args, 'complete')
let Complete = a:args.complete
endif endif
function cbs.callback(chan, msg) dict closure
call add(messages, a:msg)
endfunction
function cbs.exit_cb(job, exitval) dict closure
let exit_status = a:exitval
let exited = 1
if get(g:, 'go_echo_command_info', 1) if get(g:, 'go_echo_command_info', 1)
if a:exitval == 0 if a:exitval == 0
call go#util#EchoSuccess("SUCCESS") call go#util#EchoSuccess("SUCCESS")
@ -48,55 +82,62 @@ function go#job#Spawn(args)
endif endif
endif endif
if has_key(self, 'custom_cb') if closed
call self.custom_cb(a:job, a:exitval, self.messages) call Complete(a:job, exit_status, messages)
call s:show_errors(a:job, exit_status, messages)
endif endif
endfunction
let l:listtype = go#list#Type(self.for) function cbs.close_cb(ch) dict closure
if a:exitval == 0 let closed = 1
if exited
let job = ch_getjob(a:ch)
call Complete(job, exit_status, messages)
call s:show_errors(job, exit_status, messages)
endif
endfunction
function! s:show_errors(job, exit_status, data) closure
let l:listtype = go#list#Type(for)
if a:exit_status == 0
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype) call go#list#Window(l:listtype)
return return
endif endif
call self.show_errors(l:listtype) let l:listtype = go#list#Type(for)
endfunction if len(a:data) == 0
call go#list#Clean(l:listtype)
function cbs.show_errors(listtype) dict call go#list#Window(l:listtype)
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
try
execute cd self.jobdir
let errors = go#tool#ParseErrors(self.messages)
let errors = go#tool#FilterValids(errors)
finally
execute cd . fnameescape(self.dir)
endtry
if !len(errors)
" failed to parse errors, output the original content
call go#util#EchoError(self.messages + [self.dir])
return return
endif endif
if self.winnr == winnr() let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
call go#list#Populate(a:listtype, errors, join(self.args)) try
call go#list#Window(a:listtype, len(errors)) execute cd jobdir
if !empty(errors) && !self.bang let errors = go#tool#ParseErrors(a:data)
call go#list#JumpToFirst(a:listtype) let errors = go#tool#FilterValids(errors)
finally
execute cd . fnameescape(dir)
endtry
if empty(errors)
" failed to parse errors, output the original content
call go#util#EchoError(messages + [dir])
return
endif
if winnr == winnr()
call go#list#Populate(l:listtype, errors, join(args))
call go#list#Window(l:listtype, len(errors))
if !bang
call go#list#JumpToFirst(l:listtype)
endif endif
endif endif
endfunction endfunction
" override callback handler if user provided it
if has_key(a:args, 'callback')
let cbs.callback = a:args.callback
endif
" override exit callback handler if user provided it
if has_key(a:args, 'exit_cb')
let cbs.exit_cb = a:args.exit_cb
endif
return cbs return cbs
endfunction endfunction
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View file

@ -10,8 +10,8 @@ if !exists("g:go_metalinter_enabled")
let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck'] let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']
endif endif
if !exists("g:go_metalinter_excludes") if !exists("g:go_metalinter_disabled")
let g:go_metalinter_excludes = [] let g:go_metalinter_disabled = []
endif endif
if !exists("g:go_golint_bin") if !exists("g:go_golint_bin")
@ -24,9 +24,9 @@ endif
function! go#lint#Gometa(autosave, ...) abort function! go#lint#Gometa(autosave, ...) abort
if a:0 == 0 if a:0 == 0
let goargs = shellescape(expand('%:p:h')) let goargs = [expand('%:p:h')]
else else
let goargs = go#util#Shelljoin(a:000) let goargs = a:000
endif endif
let bin_path = go#path#CheckBinPath("gometalinter") let bin_path = go#path#CheckBinPath("gometalinter")
@ -44,8 +44,8 @@ function! go#lint#Gometa(autosave, ...) abort
let cmd += ["--enable=".linter] let cmd += ["--enable=".linter]
endfor endfor
for exclude in g:go_metalinter_excludes for linter in g:go_metalinter_disabled
let cmd += ["--exclude=".exclude] let cmd += ["--disable=".linter]
endfor endfor
" gometalinter has a --tests flag to tell its linters whether to run " gometalinter has a --tests flag to tell its linters whether to run
@ -54,14 +54,20 @@ function! go#lint#Gometa(autosave, ...) abort
" test files. One example of a linter that will not run against tests if " test files. One example of a linter that will not run against tests if
" we do not specify this flag is errcheck. " we do not specify this flag is errcheck.
let cmd += ["--tests"] let cmd += ["--tests"]
" path
let cmd += [expand('%:p:h')]
else else
" the user wants something else, let us use it. " the user wants something else, let us use it.
let cmd += split(g:go_metalinter_command, " ") let cmd += split(g:go_metalinter_command, " ")
endif endif
if a:autosave
" redraw so that any messages that were displayed while writing the file
" will be cleared
redraw
" Include only messages for the active buffer for autosave.
let cmd += [printf('--include=^%s:.*$', fnamemodify(expand('%:p'), ":."))]
endif
" gometalinter has a default deadline of 5 seconds. " gometalinter has a default deadline of 5 seconds.
" "
" For async mode (s:lint_job), we want to override the default deadline only " For async mode (s:lint_job), we want to override the default deadline only
@ -78,27 +84,26 @@ function! go#lint#Gometa(autosave, ...) abort
let cmd += ["--deadline=" . deadline] let cmd += ["--deadline=" . deadline]
endif endif
call s:lint_job({'cmd': cmd}) let cmd += goargs
call s:lint_job({'cmd': cmd}, a:autosave)
return return
endif endif
" We're calling gometalinter synchronously. " We're calling gometalinter synchronously.
let cmd += ["--deadline=" . get(g:, 'go_metalinter_deadline', "5s")] let cmd += ["--deadline=" . get(g:, 'go_metalinter_deadline', "5s")]
let cmd += goargs
let [l:out, l:err] = go#util#Exec(cmd)
if a:autosave if a:autosave
" include only messages for the active buffer let l:listtype = go#list#Type("GoMetaLinterAutoSave")
let cmd += ["--include='^" . expand('%:p') . ".*$'"] else
let l:listtype = go#list#Type("GoMetaLinter")
endif endif
if l:err == 0
let meta_command = join(cmd, " ")
let out = go#util#System(meta_command)
let l:listtype = go#list#Type("GoMetaLinter")
if go#util#ShellError() == 0
redraw | echo
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype) call go#list#Window(l:listtype)
echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None
@ -240,7 +245,7 @@ function! go#lint#ToggleMetaLinterAutoSave() abort
call go#util#EchoProgress("auto metalinter enabled") call go#util#EchoProgress("auto metalinter enabled")
endfunction endfunction
function s:lint_job(args) function! s:lint_job(args, autosave)
let status_dir = expand('%:p:h') let status_dir = expand('%:p:h')
let started_at = reltime() let started_at = reltime()
@ -253,29 +258,28 @@ function s:lint_job(args)
" autowrite is not enabled for jobs " autowrite is not enabled for jobs
call go#cmd#autowrite() call go#cmd#autowrite()
if a:autosave
let l:listtype = go#list#Type("GoMetaLinterAutoSave")
else
let l:listtype = go#list#Type("GoMetaLinter") let l:listtype = go#list#Type("GoMetaLinter")
endif
let l:errformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m' let l:errformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m'
function! s:callback(chan, msg) closure let l:messages = []
let old_errorformat = &errorformat let l:exited = 0
let &errorformat = l:errformat let l:closed = 0
if l:listtype == "locationlist" let l:exit_status = 0
lad a:msg
elseif l:listtype == "quickfix"
caddexpr a:msg
endif
let &errorformat = old_errorformat
" TODO(jinleileiking): give a configure to jump or not
let l:winnr = winnr() let l:winnr = winnr()
let errors = go#list#Get(l:listtype) function! s:callback(chan, msg) closure
call go#list#Window(l:listtype, len(errors)) call add(messages, a:msg)
exe l:winnr . "wincmd w"
endfunction endfunction
function! s:exit_cb(job, exitval) closure function! s:exit_cb(job, exitval) closure
let exited = 1
let exit_status = a:exitval
let status = { let status = {
\ 'desc': 'last status', \ 'desc': 'last status',
\ 'type': "gometaliner", \ 'type': "gometaliner",
@ -293,16 +297,33 @@ function s:lint_job(args)
call go#statusline#Update(status_dir, status) call go#statusline#Update(status_dir, status)
if closed
call s:show_errors()
endif
endfunction
function! s:close_cb(ch) closure
let closed = 1
if exited
call s:show_errors()
endif
endfunction
function! s:show_errors() closure
" make sure the current window is the window from which gometalinter was
" run when the listtype is locationlist so that the location list for the
" correct window will be populated.
if l:listtype == 'locationlist'
exe l:winnr . "wincmd w"
endif
let l:errorformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m'
call go#list#ParseFormat(l:listtype, l:errorformat, messages, 'GoMetaLinter')
let errors = go#list#Get(l:listtype) let errors = go#list#Get(l:listtype)
if empty(errors)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
elseif has("patch-7.4.2200")
if l:listtype == 'quickfix'
call setqflist([], 'a', {'title': 'GoMetaLinter'})
else
call setloclist(0, [], 'a', {'title': 'GoMetaLinter'})
endif
endif
if get(g:, 'go_echo_command_info', 1) if get(g:, 'go_echo_command_info', 1)
call go#util#EchoSuccess("linting finished") call go#util#EchoSuccess("linting finished")
@ -312,12 +333,11 @@ function s:lint_job(args)
let start_options = { let start_options = {
\ 'callback': funcref("s:callback"), \ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"), \ 'exit_cb': funcref("s:exit_cb"),
\ 'close_cb': funcref("s:close_cb"),
\ } \ }
call job_start(a:args.cmd, start_options) call job_start(a:args.cmd, start_options)
call go#list#Clean(l:listtype)
if get(g:, 'go_echo_command_info', 1) if get(g:, 'go_echo_command_info', 1)
call go#util#EchoProgress("linting started ...") call go#util#EchoProgress("linting started ...")
endif endif

View file

@ -0,0 +1,105 @@
func! Test_Gometa() abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
let expected = [
\ {'lnum': 5, 'bufnr': 3, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ ]
" clear the quickfix lists
call setqflist([], 'r')
" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
" be autoloaded and the default for g:go_metalinter_enabled will be set so
" we can capture it to restore it after the test is run.
call go#lint#ToggleMetaLinterAutoSave()
" And restore it back to its previous value
call go#lint#ToggleMetaLinterAutoSave()
let orig_go_metalinter_enabled = g:go_metalinter_enabled
let g:go_metalinter_enabled = ['golint']
call go#lint#Gometa(0, $GOPATH . '/src/foo')
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
let g:go_metalinter_enabled = orig_go_metalinter_enabled
endfunc
func! Test_GometaWithDisabled() abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
let expected = [
\ {'lnum': 5, 'bufnr': 3, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ ]
" clear the quickfix lists
call setqflist([], 'r')
" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
" be autoloaded and the default for g:go_metalinter_disabled will be set so
" we can capture it to restore it after the test is run.
call go#lint#ToggleMetaLinterAutoSave()
" And restore it back to its previous value
call go#lint#ToggleMetaLinterAutoSave()
let orig_go_metalinter_disabled = g:go_metalinter_disabled
let g:go_metalinter_disabled = ['vet']
call go#lint#Gometa(0, $GOPATH . '/src/foo')
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
let g:go_metalinter_disabled = orig_go_metalinter_disabled
endfunc
func! Test_GometaAutoSave() abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
let expected = [
\ {'lnum': 5, 'bufnr': 2, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported (golint)'}
\ ]
let winnr = winnr()
" clear the location lists
call setloclist(l:winnr, [], 'r')
" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
" be autoloaded and the default for g:go_metalinter_autosave_enabled will be
" set so we can capture it to restore it after the test is run.
call go#lint#ToggleMetaLinterAutoSave()
" And restore it back to its previous value
call go#lint#ToggleMetaLinterAutoSave()
let orig_go_metalinter_autosave_enabled = g:go_metalinter_autosave_enabled
let g:go_metalinter_autosave_enabled = ['golint']
call go#lint#Gometa(1)
let actual = getloclist(l:winnr)
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getloclist(l:winnr)
endwhile
call gotest#assert_quickfix(actual, expected)
let g:go_metalinter_autosave_enabled = orig_go_metalinter_autosave_enabled
endfunc
" vim: sw=2 ts=2 et

View file

@ -78,6 +78,7 @@ function! go#list#ParseFormat(listtype, errformat, items, title) abort
" parse and populate the location list " parse and populate the location list
let &errorformat = a:errformat let &errorformat = a:errformat
try
if a:listtype == "locationlist" if a:listtype == "locationlist"
lgetexpr a:items lgetexpr a:items
if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif
@ -85,9 +86,10 @@ function! go#list#ParseFormat(listtype, errformat, items, title) abort
cgetexpr a:items cgetexpr a:items
if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif
endif endif
finally
"restore back "restore back
let &errorformat = old_errorformat let &errorformat = old_errorformat
endtry
endfunction endfunction
" Parse parses the given items based on the global errorformat and " Parse parses the given items based on the global errorformat and
@ -142,6 +144,7 @@ let s:default_list_type_commands = {
\ "GoInstall": "quickfix", \ "GoInstall": "quickfix",
\ "GoLint": "quickfix", \ "GoLint": "quickfix",
\ "GoMetaLinter": "quickfix", \ "GoMetaLinter": "quickfix",
\ "GoMetaLinterAutoSave": "locationlist",
\ "GoModifyTags": "locationlist", \ "GoModifyTags": "locationlist",
\ "GoRename": "quickfix", \ "GoRename": "quickfix",
\ "GoRun": "quickfix", \ "GoRun": "quickfix",

View file

@ -45,9 +45,9 @@ function! go#path#Default() abort
return $GOPATH return $GOPATH
endfunction endfunction
" HasPath checks whether the given path exists in GOPATH environment variable " s:HasPath checks whether the given path exists in GOPATH environment variable
" or not " or not
function! go#path#HasPath(path) abort function! s:HasPath(path) abort
let go_paths = split(go#path#Default(), go#util#PathListSep()) let go_paths = split(go#path#Default(), go#util#PathListSep())
let last_char = strlen(a:path) - 1 let last_char = strlen(a:path) - 1
@ -94,11 +94,11 @@ function! go#path#Detect() abort
" gb vendor plugin " gb vendor plugin
" (https://github.com/constabulary/gb/tree/master/cmd/gb-vendor) " (https://github.com/constabulary/gb/tree/master/cmd/gb-vendor)
let gb_vendor_root = src_path . "vendor" . go#util#PathSep() let gb_vendor_root = src_path . "vendor" . go#util#PathSep()
if isdirectory(gb_vendor_root) && !go#path#HasPath(gb_vendor_root) if isdirectory(gb_vendor_root) && !s:HasPath(gb_vendor_root)
let gopath = gb_vendor_root . go#util#PathListSep() . gopath let gopath = gb_vendor_root . go#util#PathListSep() . gopath
endif endif
if !go#path#HasPath(src_path) if !s:HasPath(src_path)
let gopath = src_path . go#util#PathListSep() . gopath let gopath = src_path . go#util#PathListSep() . gopath
endif endif
endif endif
@ -108,7 +108,7 @@ function! go#path#Detect() abort
if !empty(godeps_root) if !empty(godeps_root)
let godeps_path = join([fnamemodify(godeps_root, ':p:h:h'), "Godeps", "_workspace" ], go#util#PathSep()) let godeps_path = join([fnamemodify(godeps_root, ':p:h:h'), "Godeps", "_workspace" ], go#util#PathSep())
if !go#path#HasPath(godeps_path) if !s:HasPath(godeps_path)
let gopath = godeps_path . go#util#PathListSep() . gopath let gopath = godeps_path . go#util#PathListSep() . gopath
endif endif
endif endif
@ -164,7 +164,7 @@ function! go#path#CheckBinPath(binpath) abort
let $PATH = old_path let $PATH = old_path
if go#util#IsUsingCygwinShell() == 1 if go#util#IsUsingCygwinShell() == 1
return go#path#CygwinPath(binpath) return s:CygwinPath(binpath)
endif endif
return binpath return binpath
@ -183,13 +183,13 @@ function! go#path#CheckBinPath(binpath) abort
let $PATH = old_path let $PATH = old_path
if go#util#IsUsingCygwinShell() == 1 if go#util#IsUsingCygwinShell() == 1
return go#path#CygwinPath(a:binpath) return s:CygwinPath(a:binpath)
endif endif
return go_bin_path . go#util#PathSep() . basename return go_bin_path . go#util#PathSep() . basename
endfunction endfunction
function! go#path#CygwinPath(path) function! s:CygwinPath(path)
return substitute(a:path, '\\', '/', "g") return substitute(a:path, '\\', '/', "g")
endfunction endfunction

View file

@ -72,7 +72,11 @@ function! go#rename#Rename(bang, ...) abort
endfunction endfunction
function s:rename_job(args) function s:rename_job(args)
let exited = 0
let closed = 0
let exitval = 0
let messages = [] let messages = []
function! s:callback(chan, msg) closure function! s:callback(chan, msg) closure
call add(messages, a:msg) call add(messages, a:msg)
endfunction endfunction
@ -80,6 +84,9 @@ function s:rename_job(args)
let status_dir = expand('%:p:h') let status_dir = expand('%:p:h')
function! s:exit_cb(job, exitval) closure function! s:exit_cb(job, exitval) closure
let exited = 1
let exitval = a:exitval
let status = { let status = {
\ 'desc': 'last status', \ 'desc': 'last status',
\ 'type': "gorename", \ 'type': "gorename",
@ -92,12 +99,23 @@ function s:rename_job(args)
call go#statusline#Update(status_dir, status) call go#statusline#Update(status_dir, status)
if closed
call s:parse_errors(a:exitval, a:args.bang, messages) call s:parse_errors(a:exitval, a:args.bang, messages)
endif
endfunction
function! s:close_cb(ch) closure
let closed = 1
if exited
call s:parse_errors(exitval, a:args.bang, messages)
endif
endfunction endfunction
let start_options = { let start_options = {
\ 'callback': funcref("s:callback"), \ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"), \ 'exit_cb': funcref("s:exit_cb"),
\ 'close_cb': funcref("s:close_cb"),
\ } \ }
call go#statusline#Update(status_dir, { call go#statusline#Update(status_dir, {

View file

@ -6,9 +6,12 @@ function! go#template#create() abort
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd() let dir = getcwd()
execute cd . fnameescape(expand("%:p:h")) let l:package_name = -1
if isdirectory(expand('%:p:h'))
execute cd . fnameescape(expand('%:p:h'))
let l:package_name = go#tool#PackageName() let l:package_name = go#tool#PackageName()
endif
" if we can't figure out any package name(no Go files or non Go package " if we can't figure out any package name(no Go files or non Go package
" files) from the directory create the template or use the cwd " files) from the directory create the template or use the cwd

View file

@ -44,10 +44,6 @@ function! go#term#newmode(bang, cmd, mode) abort
let id = termopen(a:cmd, job) let id = termopen(a:cmd, job)
if l:winnr !=# winnr()
exe l:winnr . "wincmd w"
endif
execute cd . fnameescape(dir) execute cd . fnameescape(dir)
let job.id = id let job.id = id
@ -58,12 +54,13 @@ function! go#term#newmode(bang, cmd, mode) abort
let height = get(g:, 'go_term_height', winheight(0)) let height = get(g:, 'go_term_height', winheight(0))
let width = get(g:, 'go_term_width', winwidth(0)) let width = get(g:, 'go_term_width', winwidth(0))
" we are careful how to resize. for example it's vertical we don't change " we are careful how to resize. for example it's vsplit we don't change
" the height. The below command resizes the buffer " the height. The below command resizes the buffer
if a:mode == "split"
exe 'resize ' . height if mode =~ "vertical" || mode =~ "vsplit" || mode =~ "vnew"
elseif a:mode == "vertical"
exe 'vertical resize ' . width exe 'vertical resize ' . width
elseif mode =~ "split" || mode =~ "new"
exe 'resize ' . height
endif endif
" we also need to resize the pty, so there you go... " we also need to resize the pty, so there you go...
@ -71,6 +68,11 @@ function! go#term#newmode(bang, cmd, mode) abort
let s:jobs[id] = job let s:jobs[id] = job
stopinsert stopinsert
if l:winnr !=# winnr()
exe l:winnr . "wincmd w"
endif
return id return id
endfunction endfunction

View file

@ -0,0 +1,7 @@
package foo
import "fmt"
func MissingFooDoc() {
fmt.Println("missing doc")
}

View file

@ -0,0 +1,7 @@
package lint
import "fmt"
func MissingDoc() {
fmt.Println("missing doc")
}

View file

@ -0,0 +1,7 @@
package lint
import "fmt"
func AlsoMissingDoc() {
fmt.Println("missing doc")
}

View file

@ -0,0 +1 @@
/pkg

View file

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("vim-go"
}

View file

@ -0,0 +1,7 @@
package mock
import "testing"
func Fail(t *testing.T) {
t.Fatal("another package badness")
}

View file

@ -0,0 +1,59 @@
package play
import (
"sync"
"testing"
"play/mock"
)
func TestTopSubHelper(t *testing.T) {
t.Run("sub", func(t *testing.T) {
t.Log("log message")
t.Error("sub badness")
})
t.Error("badness")
helper(t)
}
func TestMultiline(t *testing.T) {
t.Error("this is an error\nand a second line, too")
t.Error("\nthis is another error")
}
func TestSub(t *testing.T) {
t.Run("indented", func(t *testing.T) {
t.Error("this is a sub-test error\nand a second line, too")
})
}
func TestOK(t *testing.T) {
t.Run("log", func(t *testing.T) {
t.Log("goodness")
})
}
// TestMocked tests behavior similar to what users may experience when using
// github.com/golang/mock/gomock.
func TestMocked(t *testing.T) {
mock.Fail(t)
}
func TestPanic(t *testing.T) {
panic("worst ever")
}
func TestConcurrentPanic(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
go func() {
panic("concurrent fail")
wg.Done()
}()
wg.Wait()
}
func helper(t *testing.T) {
t.Helper()
t.Fatal("helper badness")
}

View file

@ -0,0 +1,11 @@
package main
import "testing"
func TestHelloWorld(t *testing.T) {
t.Error("so long")
t.Run("sub", func(t *testing.T) {
t.Error("thanks for all the fish")
})
}

View file

@ -0,0 +1,47 @@
// Run a few parallel tests, all in parallel, using multiple techniques for
// causing the test to take a while so that the stacktraces resulting from a
// test timeout will contain several goroutines to avoid giving a false sense
// of confidence or creating error formats that don't account for the more
// complex scenarios that can occur with timeouts.
package main
import (
"testing"
"time"
)
func TestSleep(t *testing.T) {
t.Parallel()
time.Sleep(15 * time.Second)
t.Log("expected panic if run with timeout < 15s")
}
func TestRunning(t *testing.T) {
t.Parallel()
c := time.After(15 * time.Second)
Loop:
for {
select {
case <-c:
break Loop
default:
}
}
t.Log("expected panic if run with timeout < 15s")
}
func TestRunningAlso(t *testing.T) {
t.Parallel()
c := time.After(15 * time.Second)
Loop:
for {
select {
case <-c:
break Loop
default:
}
}
t.Log("expected panic if run with timeout < 15s")
}

View file

@ -1,6 +1,6 @@
" Test runs `go test` in the current directory. If compile is true, it'll " Test runs `go test` in the current directory. If compile is true, it'll
" compile the tests instead of running them (useful to catch errors in the " compile the tests instead of running them (useful to catch errors in the
" test files). Any other argument is appendend to the final `go test` command " test files). Any other argument is appended to the final `go test` command.
function! go#test#Test(bang, compile, ...) abort function! go#test#Test(bang, compile, ...) abort
let args = ["test"] let args = ["test"]
@ -72,6 +72,8 @@ function! go#test#Test(bang, compile, ...) abort
let command = "go " . join(args, ' ') let command = "go " . join(args, ' ')
let out = go#tool#ExecuteInDir(command) let out = go#tool#ExecuteInDir(command)
" TODO(bc): When the output is JSON, the JSON should be run through a
" filter to produce lines that are more easily described by errorformat.
let l:listtype = go#list#Type("GoTest") let l:listtype = go#list#Type("GoTest")
@ -80,10 +82,8 @@ function! go#test#Test(bang, compile, ...) abort
execute cd fnameescape(expand("%:p:h")) execute cd fnameescape(expand("%:p:h"))
if go#util#ShellError() != 0 if go#util#ShellError() != 0
let errors = s:parse_errors(split(out, '\n')) call go#list#ParseFormat(l:listtype, s:errorformat(), split(out, '\n'), command)
let errors = go#tool#FilterValids(errors) let errors = go#list#Get(l:listtype)
call go#list#Populate(l:listtype, errors, command)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
@ -129,12 +129,16 @@ function! go#test#Func(bang, ...) abort
if a:0 if a:0
call extend(args, a:000) call extend(args, a:000)
else
" only add this if no custom flags are passed
let timeout = get(g:, 'go_test_timeout', '10s')
call add(args, printf("-timeout=%s", timeout))
endif endif
call call('go#test#Test', args) call call('go#test#Test', args)
endfunction endfunction
function s:test_job(args) abort function! s:test_job(args) abort
let status_dir = expand('%:p:h') let status_dir = expand('%:p:h')
let started_at = reltime() let started_at = reltime()
@ -153,12 +157,19 @@ function s:test_job(args) abort
" autowrite is not enabled for jobs " autowrite is not enabled for jobs
call go#cmd#autowrite() call go#cmd#autowrite()
let l:exited = 0
let l:closed = 0
let l:exitval = 0
let messages = [] let messages = []
function! s:callback(chan, msg) closure function! s:callback(chan, msg) closure
call add(messages, a:msg) call add(messages, a:msg)
endfunction endfunction
function! s:exit_cb(job, exitval) closure function! s:exit_cb(job, exitval) closure
let exited = 1
let exitval = a:exitval
let status = { let status = {
\ 'desc': 'last status', \ 'desc': 'last status',
\ 'type': "test", \ 'type': "test",
@ -192,19 +203,23 @@ function s:test_job(args) abort
call go#statusline#Update(status_dir, status) call go#statusline#Update(status_dir, status)
let l:listtype = go#list#Type("GoTest") if closed
if a:exitval == 0 call s:show_errors(a:args, l:exitval, messages)
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
return
endif endif
endfunction
call s:show_errors(a:args, a:exitval, messages) function! s:close_cb(ch) closure
let closed = 1
if exited
call s:show_errors(a:args, l:exitval, messages)
endif
endfunction endfunction
let start_options = { let start_options = {
\ 'callback': funcref("s:callback"), \ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"), \ 'exit_cb': funcref("s:exit_cb"),
\ 'close_cb': funcref("s:close_cb"),
\ } \ }
" pre start " pre start
@ -223,13 +238,23 @@ endfunction
" a quickfix compatible list of errors. It's intended to be used only for go " a quickfix compatible list of errors. It's intended to be used only for go
" test output. " test output.
function! s:show_errors(args, exit_val, messages) abort function! s:show_errors(args, exit_val, messages) abort
let l:listtype = go#list#Type("GoTest")
if a:exit_val == 0
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
return
endif
" TODO(bc): When messages is JSON, the JSON should be run through a
" filter to produce lines that are more easily described by errorformat.
let l:listtype = go#list#Type("GoTest") let l:listtype = go#list#Type("GoTest")
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
try try
execute cd a:args.jobdir execute cd a:args.jobdir
let errors = s:parse_errors(a:messages) call go#list#ParseFormat(l:listtype, s:errorformat(), a:messages, join(a:args.cmd))
let errors = go#tool#FilterValids(errors) let errors = go#list#Get(l:listtype)
finally finally
execute cd . fnameescape(a:args.dir) execute cd . fnameescape(a:args.dir)
endtry endtry
@ -242,7 +267,6 @@ function! s:show_errors(args, exit_val, messages) abort
endif endif
if a:args.winnr == winnr() if a:args.winnr == winnr()
call go#list#Populate(l:listtype, errors, join(a:args.cmd))
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:args.bang if !empty(errors) && !a:args.bang
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
@ -250,100 +274,152 @@ function! s:show_errors(args, exit_val, messages) abort
endif endif
endfunction endfunction
function! s:parse_errors(lines) abort
let errors = []
let paniced = 0 " signals whether all remaining lines should be included in errors.
let test = ''
" NOTE(arslan): once we get JSON output everything will be easier :) let s:efm= ""
" https://github.com/golang/go/issues/2981 let s:go_test_show_name=0
for line in a:lines
let fatalerrors = matchlist(line, '^\(\(fatal error\|panic\):.*\)$') function! s:errorformat() abort
if !empty(fatalerrors) " NOTE(arslan): once we get JSON output everything will be easier :).
let paniced = 1 " TODO(bc): When the output is JSON, the JSON should be run through a
call add(errors, {"text": line}) " filter to produce lines that are more easily described by errorformat.
continue " https://github.com/golang/go/issues/2981.
let goroot = go#util#goroot()
let show_name=get(g:, 'go_test_show_name', 0)
if s:efm != "" && s:go_test_show_name == show_name
return s:efm
endif endif
let s:go_test_show_name = show_name
if !paniced " each level of test indents the test output 4 spaces. Capturing groups
" Matches failure lines. These lines always have zero or more leading spaces followed by '-- FAIL: ', following by the test name followed by a space the duration of the test in parentheses " (e.g. \(\)) cannot be used in an errorformat, but non-capturing groups can
" (e.g. \%(\)).
let indent = '%\\%( %\\)%#'
" match compiler errors
let format = "%f:%l:%c: %m"
" ignore `go test -v` output for starting tests
let format .= ",%-G=== RUN %.%#"
" ignore `go test -v` output for passing tests
let format .= ",%-G" . indent . "--- PASS: %.%#"
" Match failure lines.
"
" Test failures start with '--- FAIL: ', followed by the test name followed
" by a space the duration of the test in parentheses
"
" e.g.: " e.g.:
" '--- FAIL: TestSomething (0.00s)' " '--- FAIL: TestSomething (0.00s)'
let failure = matchlist(line, '^ *--- FAIL: \(.*\) (.*)$') if show_name
if get(g:, 'go_test_prepend_name', 0) let format .= ",%G" . indent . "--- FAIL: %m (%.%#)"
if !empty(failure) else
let test = failure[1] . ': ' let format .= ",%-G" . indent . "--- FAIL: %.%#"
continue
endif
endif
endif endif
let tokens = [] " Matches test output lines.
if paniced "
" Matches lines in stacktraces produced by panic. The lines always have " All test output lines start with the test indentation and a tab, followed
" one or more leading tabs, followed by the path to the file. The file " by the filename, a colon, the line number, another colon, a space, and the
" path is followed by a colon and then the line number within the file " message. e.g.:
" where the panic occurred. After that there's a space and hexadecimal " '\ttime_test.go:30: Likely problem: the time zone files have not been installed.'
" number. let format .= ",%A" . indent . "%\\t%\\+%f:%l: %m"
" also match lines that don't have a message (i.e. the message begins with a
" newline or is the empty string):
" e.g.:
" t.Errorf("\ngot %v; want %v", actual, expected)
" t.Error("")
let format .= ",%A" . indent . "%\\t%\\+%f:%l: "
" Match the 2nd and later lines of multi-line output. These lines are
" indented the number of spaces for the level of nesting of the test,
" followed by two tabs, followed by the message.
"
" Treat these lines as if they are stand-alone lines of output by using %G.
" It would also be valid to treat these lines as if they were the
" continuation of a multi-line error by using %C instead of %G, but that
" would also require that all test errors using a %A or %E modifier to
" indicate that they're multiple lines of output, but in that case the lines
" get concatenated in the quickfix list, which is not what users typically
" want when writing a newline into their test output.
let format .= ",%G" . indent . "%\\t%\\{2}%m"
" set the format for panics.
" handle panics from test timeouts
let format .= ",%+Gpanic: test timed out after %.%\\+"
" handle non-timeout panics
" In addition to 'panic', check for 'fatal error' to support older versions
" of Go that used 'fatal error'.
"
" Panics come in two flavors. When the goroutine running the tests panics,
" `go test` recovers and tries to exit more cleanly. In that case, the panic
" message is suffixed with ' [recovered]'. If the panic occurs in a
" different goroutine, it will not be suffixed with ' [recovered]'.
let format .= ",%+Afatal error: %.%# [recovered]"
let format .= ",%+Apanic: %.%# [recovered]"
let format .= ",%+Afatal error: %.%#"
let format .= ",%+Apanic: %.%#"
" Match address lines in stacktraces produced by panic.
"
" Address lines in the stack trace have leading tabs, followed by the path
" to the file. The file path is followed by a colon and then the line number
" within the file where the panic occurred. After that there's a space and
" hexadecimal number.
" "
" e.g.: " e.g.:
" '\t/usr/local/go/src/time.go:1313 +0x5d' " '\t/usr/local/go/src/time.go:1313 +0x5d'
let tokens = matchlist(line, '^\t\+\(.\{-}\.go\):\(\d\+\) \(+0x.*\)')
else
" Matches lines produced by `go test`. When the test binary cannot be
" compiled, the errors will be a filename, followed by a colon, followed
" by the line number, followed by another colon, a space, and then the
" compiler error.
" e.g.:
" 'quux.go:123: undefined: foo'
"
" When the test binary can be successfully compiled, but tests fail, all
" lines produced by `go test` that we're interested in start with zero
" or more spaces (increasing depth of subtests is represented by a
" similar increase in the number of spaces at the start of output lines.
" Top level tests start with zero leading spaces). Lines that indicate
" test status (e.g. RUN, FAIL, PASS) start after the spaces. Lines that
" indicate test failure location or test log message location (e.g.
" "testing.T".Log) begin with the appropriate number of spaces for the
" current test level, followed by a tab, a filename , a colon, the line
" number, another colon, a space, and the failure or log message.
"
" e.g.:
" '\ttime_test.go:30: Likely problem: the time zone files have not been installed.'
let tokens = matchlist(line, '^\%( *\t\+\)\?\(.\{-}\.go\):\(\d\+\):\s*\(.*\)')
endif
if !empty(tokens) " Check whether the line may refer to a file. " panicaddress, and readyaddress are identical except for
" strip endlines of form ^M " panicaddress sets the filename and line number.
let out = substitute(tokens[3], '\r$', '', '') let panicaddress = "%\\t%f:%l +0x%[0-9A-Fa-f]%\\+"
let file = fnamemodify(tokens[1], ':p') let readyaddress = "%\\t%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+"
" stdlib address is identical to readyaddress, except it matches files
" inside GOROOT.
let stdlibaddress = "%\\t" . goroot . "%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+"
" Preserve the line when the filename is not readable. This is an " Match and ignore the running goroutine line.
" unusual case, but possible; any test that produces lines that match let format .= ",%-Cgoroutine %\\d%\\+ [running]:"
" the pattern used in the matchlist assigned to tokens is a potential " Match address lines that refer to stdlib, but consider them informational
" source of this condition. For instance, github.com/golang/mock/gomock " only. This is to catch the lines after the first address line in the
" will sometimes produce lines that satisfy this condition. " running goroutine of a panic stack trace. Ideally, this wouldn't be
if !filereadable(file) " necessary, but when a panic happens in the goroutine running a test, it's
call add(errors, {"text": test . line}) " recovered and another panic is created, so the stack trace actually has
continue " the line that caused the original panic a couple of addresses down the
endif " stack.
let format .= ",%-C" . stdlibaddress
" Match address lines in the first matching goroutine. This means the panic
" message will only be shown as the error message in the first address of
" the running goroutine's stack.
let format .= ",%Z" . panicaddress
call add(errors, { " Match and ignore panic address without being part of a multi-line message.
\ "filename" : file, " This is to catch those lines that come after the top most non-standard
\ "lnum" : tokens[2], " library line in stack traces.
\ "text" : test . out, let format .= ",%-G" . readyaddress
\ })
elseif paniced
call add(errors, {"text": line})
elseif !empty(errors)
" Preserve indented lines. This comes up especially with multi-line test output.
if match(line, '^ *\t\+') >= 0
call add(errors, {"text": line})
endif
endif
endfor
return errors " Match and ignore exit status lines (produced when go test panics) whether
" part of a multi-line message or not, because these lines sometimes come
" before and sometimes after panic stacktraces.
let format .= ",%-Cexit status %[0-9]%\\+"
"let format .= ",exit status %[0-9]%\\+"
" Match and ignore exit failure lines whether part of a multi-line message
" or not, because these lines sometimes come before and sometimes after
" panic stacktraces.
let format .= ",%-CFAIL%\\t%.%#"
"let format .= ",FAIL%\\t%.%#"
" Match and ignore everything else in multi-line messages.
let format .= ",%-C%.%#"
" Match and ignore everything else not in a multi-line message:
let format .= ",%-G%.%#"
let s:efm = format
return s:efm
endfunction endfunction
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View file

@ -0,0 +1,121 @@
func! Test_GoTest() abort
let expected = [
\ {'lnum': 12, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'log message'},
\ {'lnum': 13, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'sub badness'},
\ {'lnum': 15, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'badness'},
\ {'lnum': 16, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'helper badness'},
\ {'lnum': 20, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is an error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 21, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': ''},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is another error'},
\ {'lnum': 26, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 6, 'bufnr': 3, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'another package badness'},
\ {'lnum': 43, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'}
\ ]
call s:test('play/play_test.go', expected)
endfunc
func! Test_GoTestConcurrentPanic()
let expected = [
\ {'lnum': 50, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: concurrent fail'}
\ ]
call s:test('play/play_test.go', expected, "-run", "TestConcurrentPanic")
endfunc
func! Test_GoTestVerbose() abort
let expected = [
\ {'lnum': 12, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'log message'},
\ {'lnum': 13, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'sub badness'},
\ {'lnum': 15, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'badness'},
\ {'lnum': 16, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'helper badness'},
\ {'lnum': 20, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is an error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 21, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': ''},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is another error'},
\ {'lnum': 26, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 32, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'goodness'},
\ {'lnum': 6, 'bufnr': 3, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'another package badness'},
\ {'lnum': 43, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'}
\ ]
call s:test('play/play_test.go', expected, "-v")
endfunc
func! Test_GoTestCompilerError() abort
let expected = [
\ {'lnum': 6, 'bufnr': 6, 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'syntax error: unexpected newline, expecting comma or )'}
\ ]
call s:test('compilerror/compilerror_test.go', expected)
endfunc
func! Test_GoTestTimeout() abort
let expected = [
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: test timed out after 500ms'}
\ ]
let g:go_test_timeout="500ms"
call s:test('timeout/timeout_test.go', expected)
unlet g:go_test_timeout
endfunc
func! Test_GoTestShowName() abort
let expected = [
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld'},
\ {'lnum': 6, 'bufnr': 9, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'so long'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld/sub'},
\ {'lnum': 9, 'bufnr': 9, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
\ ]
let g:go_test_show_name=1
call s:test('showname/showname_test.go', expected)
let g:go_test_show_name=0
endfunc
func! s:test(file, expected, ...) abort
if has('nvim')
" nvim mostly shows test errors correctly, but the the expected errors are
" slightly different; buffer numbers are not the same and stderr doesn't
" seem to be redirected to the job, so the lines from the panic aren't in
" the output to be parsed, and hence are not in the quickfix lists. Once
" those two issues are resolved, this early return should be removed so
" the tests will run for Neovim, too.
return
endif
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/test'
silent exe 'e ' . $GOPATH . '/src/' . a:file
" clear the quickfix lists
call setqflist([], 'r')
let args = [1,0]
if a:0
let args += a:000
endif
" run the tests
call call(function('go#test#Test'), args)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
for item in actual
let item.text = s:normalize_durations(item.text)
endfor
for item in a:expected
let item.text = s:normalize_durations(item.text)
endfor
call gotest#assert_quickfix(actual, a:expected)
endfunc
func! s:normalize_durations(str) abort
return substitute(a:str, '[0-9]\+\(\.[0-9]\+\)\?s', '0.000s', 'g')
endfunc
" vim: sw=2 ts=2 et

View file

@ -20,10 +20,10 @@ fun! gotest#write_file(path, contents) abort
" Set cursor. " Set cursor.
let l:lnum = 1 let l:lnum = 1
for l:line in a:contents for l:line in a:contents
let l:m = match(l:line, '') let l:m = match(l:line, "\x1f")
if l:m > -1 if l:m > -1
call setpos('.', [0, l:lnum, l:m, 0]) call setpos('.', [0, l:lnum, l:m, 0])
call setline('.', substitute(getline('.'), '', '', '')) call setline('.', substitute(getline('.'), "\x1f", '', ''))
break break
endif endif
@ -102,5 +102,29 @@ fun! gotest#assert_fixture(path) abort
call gotest#assert_buffer(0, l:want) call gotest#assert_buffer(0, l:want)
endfun endfun
func! gotest#assert_quickfix(got, want) abort
call assert_equal(len(a:want), len(a:got), "number of errors")
if len(a:want) != len(a:got)
call assert_equal(a:want, a:got)
return
endif
let i = 0
while i < len(a:want)
let want_item = a:want[i]
let got_item = a:got[i]
let i += 1
call assert_equal(want_item.bufnr, got_item.bufnr, "bufnr")
call assert_equal(want_item.lnum, got_item.lnum, "lnum")
call assert_equal(want_item.col, got_item.col, "col")
call assert_equal(want_item.vcol, got_item.vcol, "vcol")
call assert_equal(want_item.nr, got_item.nr, "nr")
call assert_equal(want_item.pattern, got_item.pattern, "pattern")
call assert_equal(want_item.text, got_item.text, "text")
call assert_equal(want_item.type, got_item.type, "type")
call assert_equal(want_item.valid, got_item.valid, "valid")
endwhile
endfunc
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View file

@ -128,7 +128,7 @@ The following plugins are supported for use with vim-go:
* Real-time completion (Vim): * Real-time completion (Vim):
https://github.com/Shougo/neocomplete.vim https://github.com/Shougo/neocomplete.vim
* Real-time completion (Neovim): * Real-time completion (Neovim and Vim 8):
https://github.com/Shougo/deoplete.nvim and https://github.com/Shougo/deoplete.nvim and
https://github.com/zchee/deoplete-go https://github.com/zchee/deoplete-go
@ -797,6 +797,9 @@ CTRL-t
Toggles |'g:go_metalinter_autosave'|. Toggles |'g:go_metalinter_autosave'|.
By default, `gometalinter` messages will be shown in the |location-list|
window. The list to use can be set using |'g:go_list_type_commands'|.
*:GoTemplateAutoCreateToggle* *:GoTemplateAutoCreateToggle*
:GoTemplateAutoCreateToggle :GoTemplateAutoCreateToggle
@ -1102,12 +1105,12 @@ into the statusline. This function is also used for |'g:go_auto_type_info'|.
============================================================================== ==============================================================================
SETTINGS *go-settings* SETTINGS *go-settings*
*'g:go_test_prepend_name'* *'g:go_test_show_name'*
Prepend the name of the failed test to each test message generated by a failed Show the name of each failed test before the errors and logs output by the
test. By default it is disabled. test. By default it is disabled.
> >
let g:go_test_prepend_name = 0 let g:go_test_show_name = 0
< <
*'g:go_test_timeout'* *'g:go_test_timeout'*
@ -1371,8 +1374,12 @@ function when using the `af` text object. By default it's enabled. >
*'g:go_metalinter_autosave'* *'g:go_metalinter_autosave'*
Use this option to auto |:GoMetaLinter| on save. Only linter messages for Use this option to auto |:GoMetaLinter| on save. Only linter messages for
the active buffer will be shown. By default it's disabled > the active buffer will be shown.
By default, `gometalinter` messages will be shown in the |location-list|
window. The list to use can be set using |'g:go_list_type_commands'|.
By default it's disabled >
let g:go_metalinter_autosave = 0 let g:go_metalinter_autosave = 0
< <
*'g:go_metalinter_autosave_enabled'* *'g:go_metalinter_autosave_enabled'*
@ -1384,17 +1391,17 @@ default it's using `vet` and `golint`.
< <
*'g:go_metalinter_enabled'* *'g:go_metalinter_enabled'*
Specifies the currently enabled linters for the |:GoMetaLinter| command. By Specifies the linters to enable for the |:GoMetaLinter| command. By default
default it's using `vet`, `golint` and `errcheck`. it's using `vet`, `golint` and `errcheck`.
> >
let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck'] let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']
< <
*'g:go_metalinter_excludes'* *'g:go_metalinter_disabled'*
Specifies the linters to be excluded from the |:GoMetaLinter| command. By Specifies the linters to disable for the |:GoMetaLinter| command. By default
default it's empty it's empty
> >
let g:go_metalinter_excludes = [] let g:go_metalinter_disabled = []
< <
*'g:go_metalinter_command'* *'g:go_metalinter_command'*
@ -1437,9 +1444,9 @@ Specifies the type of list to use for command outputs (such as errors from
builds, results from static analysis commands, etc...). When an expected key builds, results from static analysis commands, etc...). When an expected key
is not present in the dictionary, |'g:go_list_type'| will be used instead. is not present in the dictionary, |'g:go_list_type'| will be used instead.
Supported keys are "GoBuild", "GoErrCheck", "GoFmt", "GoInstall", "GoLint", Supported keys are "GoBuild", "GoErrCheck", "GoFmt", "GoInstall", "GoLint",
"GoMetaLinter", "GoModifyTags" (used for both :GoAddTags and :GoRemoveTags), "GoMetaLinter", "GoMetaLinterAutoSave", "GoModifyTags" (used for both
"GoRename", "GoRun", and "GoTest". Supported values for each command are :GoAddTags and :GoRemoveTags), "GoRename", "GoRun", and "GoTest". Supported
"quickfix" and "locationlist". values for each command are "quickfix" and "locationlist".
> >
let g:go_list_type_commands = {} let g:go_list_type_commands = {}
< <
@ -1725,7 +1732,7 @@ Highlight operators such as `:=` , `==`, `-=`, etc.
< <
*'g:go_highlight_functions'* *'g:go_highlight_functions'*
Highlight function names. Highlight function and method declarations.
> >
let g:go_highlight_functions = 0 let g:go_highlight_functions = 0
< <
@ -1737,11 +1744,11 @@ declarations. Setting this implies the functionality from
> >
let g:go_highlight_function_arguments = 0 let g:go_highlight_function_arguments = 0
< <
*'g:go_highlight_methods'* *'g:go_highlight_function_calls'*
Highlight method names. Highlight function and method calls.
> >
let g:go_highlight_methods = 0 let g:go_highlight_function_calls = 0
< <
*'g:go_highlight_types'* *'g:go_highlight_types'*

View file

@ -1,3 +1,5 @@
" vint: -ProhibitAutocmdWithNoGroup
" We take care to preserve the user's fileencodings and fileformats, " We take care to preserve the user's fileencodings and fileformats,
" because those settings are global (not buffer local), yet we want " because those settings are global (not buffer local), yet we want
" to override them for loading Go files, which are defined to be UTF-8. " to override them for loading Go files, which are defined to be UTF-8.
@ -18,17 +20,15 @@ function! s:gofiletype_post()
let &g:fileencodings = s:current_fileencodings let &g:fileencodings = s:current_fileencodings
endfunction endfunction
augroup vim-go-filetype " Note: should not use augroup in ftdetect (see :help ftdetect)
autocmd! au BufNewFile *.go setfiletype go | setlocal fileencoding=utf-8 fileformat=unix
au BufNewFile *.go setfiletype go | setlocal fileencoding=utf-8 fileformat=unix au BufRead *.go call s:gofiletype_pre("go")
au BufRead *.go call s:gofiletype_pre("go") au BufReadPost *.go call s:gofiletype_post()
au BufReadPost *.go call s:gofiletype_post()
au BufNewFile *.s setfiletype asm | setlocal fileencoding=utf-8 fileformat=unix au BufNewFile *.s setfiletype asm | setlocal fileencoding=utf-8 fileformat=unix
au BufRead *.s call s:gofiletype_pre("asm") au BufRead *.s call s:gofiletype_pre("asm")
au BufReadPost *.s call s:gofiletype_post() au BufReadPost *.s call s:gofiletype_post()
au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl
augroup end
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View file

@ -128,21 +128,21 @@ abbr if err := ...; err != nil { ... }
# error snippet # error snippet
snippet errn snippet errn
abbr if err != nil { ... } abbr if err != nil { return err }
if err != nil { if err != nil {
return err return err
} }
${0} ${0}
# error snippet in TestFunc # error snippet in TestFunc
snippet errt snippet errt
abbr if err != nil { ... } abbr if err != nil { t.Fatal(err) }
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
# error snippet in log.Fatal # error snippet in log.Fatal
snippet errl snippet errl
abbr if err != nil { ... } abbr if err != nil { log.Fatal(err) }
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -157,7 +157,7 @@ abbr if err != nil { return [...], err }
# error snippet handle and return # error snippet handle and return
snippet errh snippet errh
abbr if err != nil { return } abbr if err != nil { ... return }
if err != nil { if err != nil {
${1} ${1}
return return
@ -166,7 +166,7 @@ abbr if err != nil { return }
# error snippet with panic # error snippet with panic
snippet errp snippet errp
abbr if err != nil { ... } abbr if err != nil { panic(...) }
if err != nil { if err != nil {
panic(${1}) panic(${1})
} }

View file

@ -69,7 +69,7 @@ find "$vimgodir" -name '*_test.vim' | while read test_file; do
"$vimgodir/scripts/run-vim" $coverage $vim -e \ "$vimgodir/scripts/run-vim" $coverage $vim -e \
+"silent e $test_file" \ +"silent e $test_file" \
+"let g:test_verbose=$verbose" \ +"let g:test_verbose=$verbose" \
-S ./scripts/runtest.vim || ( -S ./scripts/runtest.vim < /dev/null || (
# If Vim exits with non-0 it's almost certainly a bug in the test runner; # If Vim exits with non-0 it's almost certainly a bug in the test runner;
# should very rarely happen in normal usage. # should very rarely happen in normal usage.
echo 'test runner failure' echo 'test runner failure'

View file

@ -42,8 +42,8 @@ if !exists("g:go_highlight_function_arguments")
let g:go_highlight_function_arguments = 0 let g:go_highlight_function_arguments = 0
endif endif
if !exists("g:go_highlight_methods") if !exists("g:go_highlight_function_calls")
let g:go_highlight_methods = 0 let g:go_highlight_function_calls = 0
endif endif
if !exists("g:go_highlight_fields") if !exists("g:go_highlight_fields")
@ -202,7 +202,19 @@ else
endif endif
if g:go_highlight_format_strings != 0 if g:go_highlight_format_strings != 0
syn match goFormatSpecifier /\([^%]\(%%\)*\)\@<=%[-#0 +]*\%(\*\|\d\+\)\=\%(\.\%(\*\|\d\+\)\)*[vTtbcdoqxXUeEfgGsp]/ contained containedin=goString " [n] notation is valid for specifying explicit argument indexes
" 1. Match a literal % not preceded by a %.
" 2. Match any number of -, #, 0, space, or +
" 3. Match * or [n]* or any number or nothing before a .
" 4. Match * or [n]* or any number or nothing after a .
" 5. Match [n] or nothing before a verb
" 6. Match a formatting verb
syn match goFormatSpecifier /\
\([^%]\(%%\)*\)\
\@<=%[-#0 +]*\
\%(\%(\%(\[\d\+\]\)\=\*\)\|\d\+\)\=\
\%(\.\%(\%(\%(\[\d\+\]\)\=\*\)\|\d\+\)\=\)\=\
\%(\[\d\+\]\)\=[vTtbcdoqxXUeEfFgGsp]/ contained containedin=goString,goRawString
hi def link goFormatSpecifier goSpecialString hi def link goFormatSpecifier goSpecialString
endif endif
@ -348,7 +360,6 @@ hi def link goOperator Operator
" Functions; " Functions;
if g:go_highlight_functions isnot 0 || g:go_highlight_function_arguments isnot 0 if g:go_highlight_functions isnot 0 || g:go_highlight_function_arguments isnot 0
syn match goFunctionCall /\w\+\ze(/ contains=goBuiltins,goDeclaration
syn match goDeclaration /\<func\>/ nextgroup=goReceiver,goFunction,goSimpleArguments skipwhite skipnl syn match goDeclaration /\<func\>/ nextgroup=goReceiver,goFunction,goSimpleArguments skipwhite skipnl
syn match goReceiverVar /\w\+\ze\s\+\(\w\|\*\)/ nextgroup=goPointerOperator,goReceiverType skipwhite skipnl contained syn match goReceiverVar /\w\+\ze\s\+\(\w\|\*\)/ nextgroup=goPointerOperator,goReceiverType skipwhite skipnl contained
syn match goPointerOperator /\*/ nextgroup=goReceiverType contained skipwhite skipnl syn match goPointerOperator /\*/ nextgroup=goReceiverType contained skipwhite skipnl
@ -367,13 +378,12 @@ else
syn keyword goDeclaration func syn keyword goDeclaration func
endif endif
hi def link goFunction Function hi def link goFunction Function
hi def link goFunctionCall Type
" Methods; " Function calls;
if g:go_highlight_methods != 0 if g:go_highlight_function_calls != 0
syn match goMethodCall /\.\w\+\ze(/hs=s+1 syn match goFunctionCall /\w\+\ze(/ contains=goBuiltins,goDeclaration
endif endif
hi def link goMethodCall Type hi def link goFunctionCall Type
" Fields; " Fields;
if g:go_highlight_fields != 0 if g:go_highlight_fields != 0

View file

@ -0,0 +1,2 @@
build
/doc/tags

View file

@ -0,0 +1,34 @@
language: vim
os:
- linux
- osx
env:
- TEST=package
- TEST=latest
before_script: |
if [ "$TEST" = "package" ]; then
if [ "$TRAVIS_OS_NAME" = "linux" ]; then
sudo apt-get -y update
sudo apt-get -y install vim
fi
else
cd ..
git clone --depth 1 https://github.com/vim/vim
cd vim
./configure --with-features=huge
make
sudo make install
export PATH="/usr/local/bin:$PATH"
cd "$TRAVIS_BUILD_DIR"
fi
if [ "$TRAVIS_OS_NAME" = "osx" ]; then
sudo -H easy_install pip
fi
sudo -H pip install virtualenv
script:
- make test
- make doc

View file

@ -0,0 +1,58 @@
# CONTRIBUTING
These contributing guidelines were accepted rather late in the history of this plugin, after much code had already been written.
If you find any existing behavior which does not conform to these guidelines, please correct it and send a pull request.
## General Rules
Every non local identifier must start with `g:vim_markdown_`.
## Documentation
Every new feature must be documented under in the [README.md](README.md). Documentation must be written in [GFM](https://help.github.com/articles/github-flavored-markdown) since GitHub itself is the primary to HTML converter used. In particular, remember that GFM adds line breaks at single newlines, so just forget about the 70 characters wide rule.
Vim help file [doc/vim-markdown.txt](doc/vim-markdown.txt) will be generated from [README.md](README.md) by `make doc` using [vim-tools](https://github.com/xolox/vim-tools).
## Markdown Flavors
There are many flavors of markdown, each one with an unique feature set. This plugin uses the following strategy to deal with all those flavors:
- Features from the [original markdown](http://daringfireball.net/projects/markdown/syntax) are turned on by default. They may not even have an option that turns them off.
- Features from other markdown flavors *must* have an option that turns them on or off. If the feature is common enough across multiple versions of markdown, it may be turned on by default. This shall be decided by the community when the merge request is done.
- If possible, cite the exact point in the documentation of the flavor where a feature is specified. If the feature is not documented, you may also reference the source code itself of the implementation. This way, people who do not know that flavor can check if your implementation is correct.
- Do not use the name of a flavor for a feature that is used across multiple flavors. Instead, create a separate flavor option, that automatically sets each feature.
For example, fenced code blocks (putting code between pairs of three backticks) is not part of the original markdown, but is supported by [GFM](https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks) and [Jekyll](http://jekyllrb.com/docs/configuration/).
Therefore, instead of creating an option `g:vim_markdown_gfm_fenced_code_block`, and an option `g:vim_markdown_jekyll_fenced_code_block`, create a single option `g:vim_markdown_fenced_code_block`.
Next, if there are many more than one Jekyll feature options, create a `g:vim_markdown_jekyll` option that turns them all on at once.
## Style
When choosing between multiple valid Markdown syntaxes, the default behavior must be that specified at: <http://www.cirosantilli.com/markdown-styleguide>
If you wish to have a behavior that differs from that style guide, add an option to turn it on or off, and leave it off by default.
## Tests
All new features must have unit tests.
## Issues
Issues are tracked within GitHub.
When reporting issues, your report is more effective if you include a minimal example file that reproduces the problem. Try to trim out as much as possible, until you have the smallest possible file that still reproduces the issue. Paste the example inline into your issue report, quoted using four spaces at the beginning of each line, like this example from issue [#189](https://github.com/plasticboy/vim-markdown/issues/189):
```
Minimal example:
```
=
```
bad!
```

View file

@ -0,0 +1,82 @@
VIMDIR=$(DESTDIR)/usr/share/vim
ADDONS=${VIMDIR}/addons
REGISTRY=${VIMDIR}/registry
all:
install:
mkdir -pv ${ADDONS}/ftdetect
cp -v ftdetect/markdown.vim ${ADDONS}/ftdetect/markdown.vim
mkdir -pv ${ADDONS}/ftplugin
cp -v ftplugin/markdown.vim ${ADDONS}/ftplugin/markdown.vim
mkdir -pv ${ADDONS}/syntax
cp -v syntax/markdown.vim ${ADDONS}/syntax/markdown.vim
mkdir -pv ${ADDONS}/after/ftplugin
cp -v after/ftplugin/markdown.vim ${ADDONS}/after/ftplugin/markdown.vim
mkdir -pv ${ADDONS}/indent
cp -v indent/markdown.vim ${ADDONS}/indent/markdown.vim
mkdir -pv ${ADDONS}/doc
cp -v doc/vim-markdown.txt ${ADDONS}/doc/vim-markdown.txt
mkdir -pv ${REGISTRY}
cp -v registry/markdown.yaml ${REGISTRY}/markdown.yaml
test: build/tabular build/vim-toml build/vim-json build/vader.vim
test/run-tests.sh
.PHONY: test
update: build/tabular build/vim-toml build/vim-json build/vader.vim
cd build/tabular && git pull
cd build/vim-toml && git pull
cd build/vim-json && git pull
cd build/vader.vim && git pull
.PHONY: update
build/tabular: | build
git clone https://github.com/godlygeek/tabular build/tabular
build/vim-toml: | build
git clone https://github.com/cespare/vim-toml build/vim-toml
build/vim-json: | build
git clone https://github.com/elzr/vim-json build/vim-json
build/vader.vim: | build
git clone https://github.com/junegunn/vader.vim build/vader.vim
build:
mkdir build
doc: build/html2vimdoc build/vim-tools
sed -e '/^\[!\[Build Status\]/d' \
-e '/^1\. \[/d' README.md > doc/tmp.md # remove table of contents
build/html2vimdoc/bin/python build/vim-tools/html2vimdoc.py -f vim-markdown \
doc/tmp.md | \
sed -E -e "s/[[:space:]]*$$//" -e "# remove trailing spaces" \
-e "/^.{79,}\|$$/ {" -e "# wrap table of contents over 79" \
-e "h" -e "# save the matched line to the hold space" \
-e "s/^(.*) (\|[^|]*\|)$$/\1/" -e "# make content title" \
-e "p" -e "# print title" \
-e "g" -e "# restore the matched line" \
-e "s/^.* (\|[^|]*\|)$$/ \1/" -e "# make link" \
-e ":c" -e "s/^(.{1,78})$$/ \1/" -e "tc" -e "# align right" \
-e "}" \
-e "/^- '[^']*':( |$$)/ {" \
-e "h" -e "# save the matched line to the hold space" \
-e "s/^- '([^']{3,})':.*/ \*\1\*/" -e "# make command reference" \
-e "s/^- '([^']{1,2})':.*/ \*vim-markdown-\1\*/" -e "# short command" \
-e ":a" -e "s/^(.{1,78})$$/ \1/" -e "ta" -e "# align right" \
-e "G" -e "# append the matched line after the command reference" \
-e "}" > doc/vim-markdown.txt && rm -f doc/tmp.md
.PHONY: doc
# Prerequire Python and virtualenv.
# $ sudo pip install virtualenv
# Create the virtual environment.
# Install the dependencies.
build/html2vimdoc: | build
virtualenv build/html2vimdoc
build/html2vimdoc/bin/pip install beautifulsoup coloredlogs==4.0 markdown
build/vim-tools: | build
git clone https://github.com/xolox/vim-tools.git build/vim-tools

View file

@ -1,34 +0,0 @@
# Vim Markdown runtime files
This is the development version of Vim's included syntax highlighting and
filetype plugins for Markdown. Generally you don't need to install these if
you are running a recent version of Vim.
One difference between this repository and the upstream files in Vim is that
the former forces `*.md` as Markdown, while the latter detects it as Modula-2,
with an exception for `README.md`. If you'd like to force Markdown without
installing from this repository, add the following to your vimrc:
autocmd BufNewFile,BufReadPost *.md set filetype=markdown
If you want to enable fenced code block syntax highlighting in your markdown
documents you can enable it in your `.vimrc` like so:
let g:markdown_fenced_languages = ['html', 'python', 'bash=sh']
To disable markdown syntax concealing add the following to your vimrc:
let g:markdown_syntax_conceal = 0
Syntax highlight is synchronized in 50 lines. It may cause collapsed
highlighting at large fenced code block.
In the case, please set larger value in your vimrc:
let g:markdown_minlines = 100
Note that setting too large value may cause bad performance on highlighting.
## License
Copyright © Tim Pope. Distributed under the same terms as Vim itself.
See `:help license`.

View file

@ -0,0 +1,397 @@
# Vim Markdown
[![Build Status](https://travis-ci.org/plasticboy/vim-markdown.svg)](https://travis-ci.org/plasticboy/vim-markdown)
Syntax highlighting, matching rules and mappings for [the original Markdown](http://daringfireball.net/projects/markdown/) and extensions.
1. [Installation](#installation)
1. [Options](#options)
1. [Mappings](#mappings)
1. [Commands](#commands)
1. [Credits](#credits)
1. [License](#license)
## Installation
If you use [Vundle](https://github.com/gmarik/vundle), add the following line to your `~/.vimrc`:
```vim
Plugin 'godlygeek/tabular'
Plugin 'plasticboy/vim-markdown'
```
The `tabular` plugin must come *before* `vim-markdown`.
Then run inside Vim:
```vim
:so ~/.vimrc
:PluginInstall
```
If you use [Pathogen](https://github.com/tpope/vim-pathogen), do this:
```sh
cd ~/.vim/bundle
git clone https://github.com/plasticboy/vim-markdown.git
```
To install without Pathogen using the Debian [vim-addon-manager](http://packages.qa.debian.org/v/vim-addon-manager.html), do this:
```sh
git clone https://github.com/plasticboy/vim-markdown.git
cd vim-markdown
sudo make install
vim-addon-manager install markdown
```
If you are not using any package manager, download the [tarball](https://github.com/plasticboy/vim-markdown/archive/master.tar.gz) and do this:
```sh
cd ~/.vim
tar --strip=1 -zxf vim-markdown-master.tar.gz
```
## Options
### Disable Folding
Add the following line to your `.vimrc` to disable the folding configuration:
```vim
let g:vim_markdown_folding_disabled = 1
```
This option only controls Vim Markdown specific folding configuration.
To enable/disable folding use Vim's standard folding configuration.
```vim
set [no]foldenable
```
### Change fold style
To fold in a style like [python-mode](https://github.com/klen/python-mode), add the following to your `.vimrc`:
```vim
let g:vim_markdown_folding_style_pythonic = 1
```
Level 1 heading which is served as a document title is not folded.
`g:vim_markdown_folding_level` setting is not active with this fold style.
To prevent foldtext from being set add the following to your `.vimrc`:
```vim
let g:vim_markdown_override_foldtext = 0
```
### Set header folding level
Folding level is a number between 1 and 6. By default, if not specified, it is set to 1.
```vim
let g:vim_markdown_folding_level = 6
```
Tip: it can be changed on the fly with:
```vim
:let g:vim_markdown_folding_level = 1
:edit
```
### Disable Default Key Mappings
Add the following line to your `.vimrc` to disable default key mappings:
```vim
let g:vim_markdown_no_default_key_mappings = 1
```
You can also map them by yourself with `<Plug>` mappings.
### Enable TOC window auto-fit
Allow for the TOC window to auto-fit when it's possible for it to shrink.
It never increases its default size (half screen), it only shrinks.
```vim
let g:vim_markdown_toc_autofit = 1
```
### Text emphasis restriction to single-lines
By default text emphasis works across multiple lines until a closing token is found. However, it's possible to restrict text emphasis to a single line (ie, for it to be applied a closing token must be found on the same line). To do so:
```vim
let g:vim_markdown_emphasis_multiline = 0
```
### Syntax Concealing
Concealing is set for some syntax.
For example, conceal `[link text](link url)` as just `link text`.
Also, `_italic_` and `*italic*` will conceal to just _italic_.
Similarly `__bold__`, `**bold**`, `___italic bold___`, and `***italic bold***`
will conceal to just __bold__, **bold**, ___italic bold___, and ***italic bold*** respectively.
To enable conceal use Vim's standard conceal configuration.
```vim
set conceallevel=2
```
To disable conceal regardless of `conceallevel` setting, add the following to your `.vimrc`:
```vim
let g:vim_markdown_conceal = 0
```
To disable math conceal with LaTeX math syntax enabled, add the following to your `.vimrc`:
```vim
let g:tex_conceal = ""
let g:vim_markdown_math = 1
```
### Fenced code block languages
You can use filetype name as fenced code block languages for syntax highlighting.
If you want to use different name from filetype, you can add it in your `.vimrc` like so:
```vim
let g:vim_markdown_fenced_languages = ['csharp=cs']
```
This will cause the following to be highlighted using the `cs` filetype syntax.
```csharp
...
```
Default is `['c++=cpp', 'viml=vim', 'bash=sh', 'ini=dosini']`.
### Follow named anchors
This feature allows ge to follow named anchors in links of the form
`file#anchor` or just `#anchor`, where file may omit the `.md` extension as
usual. Two variables control its operation:
```vim
let g:vim_markdown_follow_anchor = 1
```
This tells vim-markdown whether to attempt to follow a named anchor in a link or
not. When it is 1, and only if a link can be split in two parts by the pattern
'#', then the first part is interpreted as the file and the second one as the
named anchor. This also includes urls of the form `#anchor`, for which the first
part is considered empty, meaning that the target file is the current one. After
the file is opened, the anchor will be searched.
Default is `0`.
```vim
let g:vim_markdown_anchorexpr = "'<<'.v:anchor.'>>'"
```
This expression will be evaluated substituting `v:anchor` with a quoted string
that contains the anchor to visit. The result of the evaluation will become the
real anchor to search in the target file. This is useful in order to convert
anchors of the form, say, `my-section-title` to searches of the form `My Section
Title` or `<<my-section-title>>`.
Default is `''`.
### Syntax extensions
The following options control which syntax extensions will be turned on. They are off by default.
#### LaTeX math
Used as `$x^2$`, `$$x^2$$`, escapable as `\$x\$` and `\$\$x\$\$`.
```vim
let g:vim_markdown_math = 1
```
#### YAML Front Matter
Highlight YAML front matter as used by Jekyll or [Hugo](https://gohugo.io/content/front-matter/).
```vim
let g:vim_markdown_frontmatter = 1
```
#### TOML Front Matter
Highlight TOML front matter as used by [Hugo](https://gohugo.io/content/front-matter/).
TOML syntax highlight requires [vim-toml](https://github.com/cespare/vim-toml).
```vim
let g:vim_markdown_toml_frontmatter = 1
```
#### JSON Front Matter
Highlight JSON front matter as used by [Hugo](https://gohugo.io/content/front-matter/).
JSON syntax highlight requires [vim-json](https://github.com/elzr/vim-json).
```vim
let g:vim_markdown_json_frontmatter = 1
```
### Adjust new list item indent
You can adjust a new list indent. For example, you insert a single line like below:
```
* item1
```
Then if you type `o` to insert new line in vim and type `* item2`, the result will be:
```
* item1
* item2
```
vim-markdown automatically insert the indent. By default, the number of spaces of indent is 4. If you'd like to change the number as 2, just write:
```vim
let g:vim_markdown_new_list_item_indent = 2
```
### Do not require .md extensions for Markdown links
If you want to have a link like this `[link text](link-url)` and follow it for editing in vim using the `ge` command, but have it open the file "link-url.md" instead of the file "link-url", then use this option:
```vim
let g:vim_markdown_no_extensions_in_markdown = 1
```
This is super useful for GitLab and GitHub wiki repositories.
Normal behaviour would be that vim-markup required you to do this `[link text](link-url.md)`, but this is not how the Gitlab and GitHub wiki repositories work. So this option adds some consistency between the two.
### Auto-write when following link
If you follow a link like this `[link text](link-url)` using the `ge` shortcut, this option will automatically save any edits you made before moving you:
```vim
let g:vim_markdown_autowrite = 1
```
### Change default file extension
If you would like to use a file extension other than `.md` you may do so using the `vim_markdown_auto_extension_ext` variable:
```vim
let g:vim_markdown_auto_extension_ext = 'txt'
```
## Mappings
The following work on normal and visual modes:
- `gx`: open the link under the cursor in the same browser as the standard `gx` command. `<Plug>Markdown_OpenUrlUnderCursor`
The standard `gx` is extended by allowing you to put your cursor anywhere inside a link.
For example, all the following cursor positions will work:
[Example](http://example.com)
^ ^ ^^ ^ ^
1 2 34 5 6
<http://example.com>
^ ^ ^
1 2 3
Known limitation: does not work for links that span multiple lines.
- `ge`: open the link under the cursor in Vim for editing. Useful for relative markdown links. `<Plug>Markdown_EditUrlUnderCursor`
The rules for the cursor position are the same as the `gx` command.
- `]]`: go to next header. `<Plug>Markdown_MoveToNextHeader`
- `[[`: go to previous header. Contrast with `]c`. `<Plug>Markdown_MoveToPreviousHeader`
- `][`: go to next sibling header if any. `<Plug>Markdown_MoveToNextSiblingHeader`
- `[]`: go to previous sibling header if any. `<Plug>Markdown_MoveToPreviousSiblingHeader`
- `]c`: go to Current header. `<Plug>Markdown_MoveToCurHeader`
- `]u`: go to parent header (Up). `<Plug>Markdown_MoveToParentHeader`
This plugin follows the recommended Vim plugin mapping interface, so to change the map `]u` to `asdf`, add to your `.vimrc`:
map asdf <Plug>Markdown_MoveToParentHeader
To disable a map use:
map <Plug> <Plug>Markdown_MoveToParentHeader
## Commands
The following requires `:filetype plugin on`.
- `:HeaderDecrease`:
Decrease level of all headers in buffer: `h2` to `h1`, `h3` to `h2`, etc.
If range is given, only operate in the range.
If an `h1` would be decreased, abort.
For simplicity of implementation, Setex headers are converted to Atx.
- `:HeaderIncrease`: Analogous to `:HeaderDecrease`, but increase levels instead.
- `:SetexToAtx`:
Convert all Setex style headers in buffer to Atx.
If a range is given, e.g. hit `:` from visual mode, only operate on the range.
- `:TableFormat`: Format the table under the cursor [like this](http://www.cirosantilli.com/markdown-style-guide/#tables).
Requires [Tabular](https://github.com/godlygeek/tabular).
The input table *must* already have a separator line as the second line of the table.
That line only needs to contain the correct pipes `|`, nothing else is required.
- `:Toc`: create a quickfix vertical window navigable table of contents with the headers.
Hit `<Enter>` on a line to jump to the corresponding line of the markdown file.
- `:Toch`: Same as `:Toc` but in an horizontal window.
- `:Toct`: Same as `:Toc` but in a new tab.
- `:Tocv`: Same as `:Toc` for symmetry with `:Toch` and `:Tocv`.
## Credits
The main contributors of vim-markdown are:
- **Ben Williams** (A.K.A. **plasticboy**). The original developer of vim-markdown. [Homepage](http://plasticboy.com/).
If you feel that your name should be on this list, please make a pull request listing your contributions.
## License
The MIT License (MIT)
Copyright (c) 2012 Benjamin D. Williams
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,160 @@
" folding for Markdown headers, both styles (atx- and setex-)
" http://daringfireball.net/projects/markdown/syntax#header
"
" this code can be placed in file
" $HOME/.vim/after/ftplugin/markdown.vim
"
" original version from Steve Losh's gist: https://gist.github.com/1038710
function! s:is_mkdCode(lnum)
let name = synIDattr(synID(a:lnum, 1, 0), 'name')
return (name =~ '^mkd\%(Code$\|Snippet\)' || name != '' && name !~ '^\%(mkd\|html\)')
endfunction
if get(g:, "vim_markdown_folding_style_pythonic", 0)
function! Foldexpr_markdown(lnum)
let l1 = getline(a:lnum)
" keep track of fenced code blocks
if l1 =~ '````*' || l1 =~ '\~\~\~\~*'
if b:fenced_block == 0
let b:fenced_block = 1
elseif b:fenced_block == 1
let b:fenced_block = 0
endif
elseif g:vim_markdown_frontmatter == 1
if b:front_matter == 1 && a:lnum > 2
let l0 = getline(a:lnum-1)
if l0 == '---'
let b:front_matter = 0
endif
elseif a:lnum == 1
if l1 == '---'
let b:front_matter = 1
endif
endif
endif
if b:fenced_block == 1 || b:front_matter == 1
if a:lnum == 1
" fold any 'preamble'
return '>1'
else
" keep previous foldlevel
return '='
endif
endif
let l2 = getline(a:lnum+1)
if l2 =~ '^==\+\s*' && !s:is_mkdCode(a:lnum+1)
" next line is underlined (level 1)
return '>0'
elseif l2 =~ '^--\+\s*' && !s:is_mkdCode(a:lnum+1)
" next line is underlined (level 2)
return '>1'
endif
if l1 =~ '^#' && !s:is_mkdCode(a:lnum)
" current line starts with hashes
return '>'.(matchend(l1, '^#\+') - 1)
elseif a:lnum == 1
" fold any 'preamble'
return '>1'
else
" keep previous foldlevel
return '='
endif
endfunction
function! Foldtext_markdown()
let line = getline(v:foldstart)
let has_numbers = &number || &relativenumber
let nucolwidth = &fdc + has_numbers * &numberwidth
let windowwidth = winwidth(0) - nucolwidth - 6
let foldedlinecount = v:foldend - v:foldstart
let line = strpart(line, 0, windowwidth - 2 -len(foldedlinecount))
let line = substitute(line, '\%("""\|''''''\)', '', '')
let fillcharcount = windowwidth - len(line) - len(foldedlinecount) + 1
return line . ' ' . repeat("-", fillcharcount) . ' ' . foldedlinecount
endfunction
else
function! Foldexpr_markdown(lnum)
if (a:lnum == 1)
let l0 = ''
else
let l0 = getline(a:lnum-1)
endif
" keep track of fenced code blocks
if l0 =~ '````*' || l0 =~ '\~\~\~\~*'
if b:fenced_block == 0
let b:fenced_block = 1
elseif b:fenced_block == 1
let b:fenced_block = 0
endif
elseif g:vim_markdown_frontmatter == 1
if b:front_matter == 1
if l0 == '---'
let b:front_matter = 0
endif
elseif a:lnum == 2
if l0 == '---'
let b:front_matter = 1
endif
endif
endif
if b:fenced_block == 1 || b:front_matter == 1
" keep previous foldlevel
return '='
endif
let l2 = getline(a:lnum+1)
if l2 =~ '^==\+\s*' && !s:is_mkdCode(a:lnum+1)
" next line is underlined (level 1)
return '>1'
elseif l2 =~ '^--\+\s*' && !s:is_mkdCode(a:lnum+1)
" next line is underlined (level 2)
if s:vim_markdown_folding_level >= 2
return '>1'
else
return '>2'
endif
endif
let l1 = getline(a:lnum)
if l1 =~ '^#' && !s:is_mkdCode(a:lnum)
" fold level according to option
if s:vim_markdown_folding_level == 1 || matchend(l1, '^#\+') > s:vim_markdown_folding_level
if a:lnum == line('$')
return matchend(l1, '^#\+') - 1
else
return -1
endif
else
" headers are not folded
return 0
endif
endif
if l0 =~ '^#' && !s:is_mkdCode(a:lnum-1)
" previous line starts with hashes
return '>'.matchend(l0, '^#\+')
else
" keep previous foldlevel
return '='
endif
endfunction
endif
let b:fenced_block = 0
let b:front_matter = 0
let s:vim_markdown_folding_level = get(g:, "vim_markdown_folding_level", 1)
if !get(g:, "vim_markdown_folding_disabled", 0)
setlocal foldexpr=Foldexpr_markdown(v:lnum)
setlocal foldmethod=expr
if get(g:, "vim_markdown_folding_style_pythonic", 0) && get(g:, "vim_markdown_override_foldtext", 1)
setlocal foldtext=Foldtext_markdown()
endif
endif

View file

@ -0,0 +1,492 @@
*vim-markdown* Vim Markdown
===============================================================================
Contents ~
1. Introduction |vim-markdown-introduction|
2. Installation |vim-markdown-installation|
3. Options |vim-markdown-options|
1. Disable Folding |vim-markdown-disable-folding|
2. Change fold style |vim-markdown-change-fold-style|
3. Set header folding level |vim-markdown-set-header-folding-level|
4. Disable Default Key Mappings |vim-markdown-disable-default-key-mappings|
5. Enable TOC window auto-fit |vim-markdown-enable-toc-window-auto-fit|
6. Text emphasis restriction to single-lines
|vim-markdown-text-emphasis-restriction-to-single-lines|
7. Syntax Concealing |vim-markdown-syntax-concealing|
8. Fenced code block languages |vim-markdown-fenced-code-block-languages|
9. Follow named anchors |vim-markdown-follow-named-anchors|
10. Syntax extensions |vim-markdown-syntax-extensions|
1. LaTeX math |vim-markdown-latex-math|
2. YAML Front Matter |vim-markdown-yaml-front-matter|
3. TOML Front Matter |vim-markdown-toml-front-matter|
4. JSON Front Matter |vim-markdown-json-front-matter|
11. Adjust new list item indent |vim-markdown-adjust-new-list-item-indent|
12. Do not require .md extensions for Markdown links
|vim-markdown-do-not-require-.md-extensions-for-markdown-links|
13. Auto-write when following link
|vim-markdown-auto-write-when-following-link|
14. Change default file extension |vim-markdown-auto-extension-ext|
4. Mappings |vim-markdown-mappings|
5. Commands |vim-markdown-commands|
6. Credits |vim-markdown-credits|
7. License |vim-markdown-license|
8. References |vim-markdown-references|
===============================================================================
*vim-markdown-introduction*
Introduction ~
Syntax highlighting, matching rules and mappings for the original Markdown [1]
and extensions.
===============================================================================
*vim-markdown-installation*
Installation ~
If you use Vundle [2], add the following line to your '~/.vimrc':
>
Plugin 'godlygeek/tabular'
Plugin 'plasticboy/vim-markdown'
<
The 'tabular' plugin must come _before_ 'vim-markdown'.
Then run inside Vim:
>
:so ~/.vimrc
:PluginInstall
<
If you use Pathogen [3], do this:
>
cd ~/.vim/bundle
git clone https://github.com/plasticboy/vim-markdown.git
<
To install without Pathogen using the Debian vim-addon-manager [4], do this:
>
git clone https://github.com/plasticboy/vim-markdown.git
cd vim-markdown
sudo make install
vim-addon-manager install markdown
<
If you are not using any package manager, download the tarball [5] and do this:
>
cd ~/.vim
tar --strip=1 -zxf vim-markdown-master.tar.gz
<
===============================================================================
*vim-markdown-options*
Options ~
-------------------------------------------------------------------------------
*vim-markdown-disable-folding*
Disable Folding ~
Add the following line to your '.vimrc' to disable the folding configuration:
>
let g:vim_markdown_folding_disabled = 1
<
This option only controls Vim Markdown specific folding configuration.
To enable/disable folding use Vim's standard folding configuration.
>
set [no]foldenable
<
-------------------------------------------------------------------------------
*vim-markdown-change-fold-style*
Change fold style ~
To fold in a style like python-mode [6], add the following to your '.vimrc':
>
let g:vim_markdown_folding_style_pythonic = 1
<
Level 1 heading which is served as a document title is not folded.
'g:vim_markdown_folding_level' setting is not active with this fold style.
To prevent foldtext from being set add the following to your '.vimrc':
>
let g:vim_markdown_override_foldtext = 0
<
-------------------------------------------------------------------------------
*vim-markdown-set-header-folding-level*
Set header folding level ~
Folding level is a number between 1 and 6. By default, if not specified, it is
set to 1.
>
let g:vim_markdown_folding_level = 6
<
Tip: it can be changed on the fly with:
>
:let g:vim_markdown_folding_level = 1
:edit
<
-------------------------------------------------------------------------------
*vim-markdown-disable-default-key-mappings*
Disable Default Key Mappings ~
Add the following line to your '.vimrc' to disable default key mappings:
>
let g:vim_markdown_no_default_key_mappings = 1
<
You can also map them by yourself with '<Plug>' mappings.
-------------------------------------------------------------------------------
*vim-markdown-enable-toc-window-auto-fit*
Enable TOC window auto-fit ~
Allow for the TOC window to auto-fit when it's possible for it to shrink. It
never increases its default size (half screen), it only shrinks.
>
let g:vim_markdown_toc_autofit = 1
<
-------------------------------------------------------------------------------
*vim-markdown-text-emphasis-restriction-to-single-lines*
Text emphasis restriction to single-lines ~
By default text emphasis works across multiple lines until a closing token is
found. However, it's possible to restrict text emphasis to a single line (ie,
for it to be applied a closing token must be found on the same line). To do so:
>
let g:vim_markdown_emphasis_multiline = 0
<
-------------------------------------------------------------------------------
*vim-markdown-syntax-concealing*
Syntax Concealing ~
Concealing is set for some syntax.
For example, conceal '[link text](link url)' as just 'link text'. Also,
'_italic_' and '*italic*' will conceal to just _italic_. Similarly '__bold__',
'**bold**', '___italic bold___', and '***italic bold***' will conceal to just
**bold**, **bold**, **_italic bold_**, and **_italic bold_** respectively.
To enable conceal use Vim's standard conceal configuration.
>
set conceallevel=2
<
To disable conceal regardless of 'conceallevel' setting, add the following to
your '.vimrc':
>
let g:vim_markdown_conceal = 0
<
To disable math conceal with LaTeX math syntax enabled, add the following to
your '.vimrc':
>
let g:tex_conceal = ""
let g:vim_markdown_math = 1
<
-------------------------------------------------------------------------------
*vim-markdown-fenced-code-block-languages*
Fenced code block languages ~
You can use filetype name as fenced code block languages for syntax
highlighting. If you want to use different name from filetype, you can add it
in your '.vimrc' like so:
>
let g:vim_markdown_fenced_languages = ['csharp=cs']
<
This will cause the following to be highlighted using the 'cs' filetype syntax.
>
```csharp
...
```
<
Default is "['c++=cpp', 'viml=vim', 'bash=sh', 'ini=dosini']".
-------------------------------------------------------------------------------
*vim-markdown-follow-named-anchors*
Follow named anchors ~
This feature allows ge to follow named anchors in links of the form
'file#anchor' or just '#anchor', where file may omit the '.md' extension as
usual. Two variables control its operation:
>
let g:vim_markdown_follow_anchor = 1
<
This tells vim-markdown whether to attempt to follow a named anchor in a link
or not. When it is 1, and only if a link can be split in two parts by the
pattern '#', then the first part is interpreted as the file and the second one
as the named anchor. This also includes urls of the form '#anchor', for which
the first part is considered empty, meaning that the target file is the current
one. After the file is opened, the anchor will be searched.
Default is '0'.
>
let g:vim_markdown_anchorexpr = "'<<'.v:anchor.'>>'"
<
This expression will be evaluated substituting 'v:anchor' with a quoted string
that contains the anchor to visit. The result of the evaluation will become the
real anchor to search in the target file. This is useful in order to convert
anchors of the form, say, 'my-section-title' to searches of the form 'My
Section Title' or '<<my-section-title>>'.
Default is "''".
-------------------------------------------------------------------------------
*vim-markdown-syntax-extensions*
Syntax extensions ~
The following options control which syntax extensions will be turned on. They
are off by default.
-------------------------------------------------------------------------------
*vim-markdown-latex-math*
LaTeX math ~
Used as '$x^2$', '$$x^2$$', escapable as '\$x\$' and '\$\$x\$\$'.
>
let g:vim_markdown_math = 1
<
-------------------------------------------------------------------------------
*vim-markdown-yaml-front-matter*
YAML Front Matter ~
Highlight YAML front matter as used by Jekyll or Hugo [7].
>
let g:vim_markdown_frontmatter = 1
<
-------------------------------------------------------------------------------
*vim-markdown-toml-front-matter*
TOML Front Matter ~
Highlight TOML front matter as used by Hugo [7].
TOML syntax highlight requires vim-toml [8].
>
let g:vim_markdown_toml_frontmatter = 1
<
-------------------------------------------------------------------------------
*vim-markdown-json-front-matter*
JSON Front Matter ~
Highlight JSON front matter as used by Hugo [7].
JSON syntax highlight requires vim-json [9].
>
let g:vim_markdown_json_frontmatter = 1
<
-------------------------------------------------------------------------------
*vim-markdown-adjust-new-list-item-indent*
Adjust new list item indent ~
You can adjust a new list indent. For example, you insert a single line like
below:
>
* item1
<
Then if you type 'o' to insert new line in vim and type '* item2', the result
will be:
>
* item1
* item2
<
vim-markdown automatically insert the indent. By default, the number of spaces
of indent is 4. If you'd like to change the number as 2, just write:
>
let g:vim_markdown_new_list_item_indent = 2
<
-------------------------------------------------------------------------------
*vim-markdown-do-not-require-.md-extensions-for-markdown-links*
Do not require .md extensions for Markdown links ~
If you want to have a link like this '[link text](link-url)' and follow it for
editing in vim using the 'ge' command, but have it open the file "link-url.md"
instead of the file "link-url", then use this option:
>
let g:vim_markdown_no_extensions_in_markdown = 1
<
This is super useful for GitLab and GitHub wiki repositories.
Normal behaviour would be that vim-markup required you to do this '[link text
](link-url.md)', but this is not how the Gitlab and GitHub wiki repositories
work. So this option adds some consistency between the two.
-------------------------------------------------------------------------------
*vim-markdown-auto-write-when-following-link*
Auto-write when following link ~
If you follow a link like this '[link text](link-url)' using the 'ge' shortcut,
this option will automatically save any edits you made before moving you:
>
let g:vim_markdown_autowrite = 1
<
-------------------------------------------------------------------------------
*vim-markdown-auto-extension-ext*
Change default file extension ~
If you would like to use a file extension other than '.md' you may do so using
the 'vim_markdown_auto_extension_ext' variable:
>
let g:vim_markdown_auto_extension_ext = 'txt'
<
===============================================================================
*vim-markdown-mappings*
Mappings ~
The following work on normal and visual modes:
*vim-markdown-gx*
- 'gx': open the link under the cursor in the same browser as the standard
'gx' command. '<Plug>Markdown_OpenUrlUnderCursor'
The standard 'gx' is extended by allowing you to put your cursor anywhere
inside a link.
For example, all the following cursor positions will work:
>
[Example](http://example.com)
^ ^ ^^ ^ ^
1 2 34 5 6
<http://example.com>
^ ^ ^
1 2 3
<
Known limitation: does not work for links that span multiple lines.
*vim-markdown-ge*
- 'ge': open the link under the cursor in Vim for editing. Useful for
relative markdown links. '<Plug>Markdown_EditUrlUnderCursor'
The rules for the cursor position are the same as the 'gx' command.
*vim-markdown-]]*
- ']]': go to next header. '<Plug>Markdown_MoveToNextHeader'
*vim-markdown-[[*
- '[[': go to previous header. Contrast with ']c'.
'<Plug>Markdown_MoveToPreviousHeader'
*vim-markdown-][*
- '][': go to next sibling header if any.
'<Plug>Markdown_MoveToNextSiblingHeader'
*vim-markdown-[]*
- '[]': go to previous sibling header if any.
'<Plug>Markdown_MoveToPreviousSiblingHeader'
*vim-markdown-]c*
- ']c': go to Current header. '<Plug>Markdown_MoveToCurHeader'
*vim-markdown-]u*
- ']u': go to parent header (Up). '<Plug>Markdown_MoveToParentHeader'
This plugin follows the recommended Vim plugin mapping interface, so to change
the map ']u' to 'asdf', add to your '.vimrc':
>
map asdf <Plug>Markdown_MoveToParentHeader
<
To disable a map use:
>
map <Plug> <Plug>Markdown_MoveToParentHeader
<
===============================================================================
*vim-markdown-commands*
Commands ~
The following requires ':filetype plugin on'.
*:HeaderDecrease*
- ':HeaderDecrease':
Decrease level of all headers in buffer: 'h2' to 'h1', 'h3' to 'h2', etc.
If range is given, only operate in the range.
If an 'h1' would be decreased, abort.
For simplicity of implementation, Setex headers are converted to Atx.
*:HeaderIncrease*
- ':HeaderIncrease': Analogous to ':HeaderDecrease', but increase levels
instead.
*:SetexToAtx*
- ':SetexToAtx':
Convert all Setex style headers in buffer to Atx.
If a range is given, e.g. hit ':' from visual mode, only operate on the
range.
*:TableFormat*
- ':TableFormat': Format the table under the cursor like this [10].
Requires Tabular [11].
The input table _must_ already have a separator line as the second line of
the table. That line only needs to contain the correct pipes '|', nothing
else is required.
*:Toc*
- ':Toc': create a quickfix vertical window navigable table of contents with
the headers.
Hit '<Enter>' on a line to jump to the corresponding line of the markdown
file.
*:Toch*
- ':Toch': Same as ':Toc' but in an horizontal window.
*:Toct*
- ':Toct': Same as ':Toc' but in a new tab.
*:Tocv*
- ':Tocv': Same as ':Toc' for symmetry with ':Toch' and ':Tocv'.
===============================================================================
*vim-markdown-credits*
Credits ~
The main contributors of vim-markdown are:
- **Ben Williams** (A.K.A. **plasticboy**). The original developer of vim-
markdown. Homepage [12].
If you feel that your name should be on this list, please make a pull request
listing your contributions.
===============================================================================
*vim-markdown-license*
License ~
The MIT License (MIT)
Copyright (c) 2012 Benjamin D. Williams
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
===============================================================================
*vim-markdown-references*
References ~
[1] http://daringfireball.net/projects/markdown/
[2] https://github.com/gmarik/vundle
[3] https://github.com/tpope/vim-pathogen
[4] http://packages.qa.debian.org/v/vim-addon-manager.html
[5] https://github.com/plasticboy/vim-markdown/archive/master.tar.gz
[6] https://github.com/klen/python-mode
[7] https://gohugo.io/content/front-matter/
[8] https://github.com/cespare/vim-toml
[9] https://github.com/elzr/vim-json
[10] http://www.cirosantilli.com/markdown-style-guide/#tables
[11] https://github.com/godlygeek/tabular
[12] http://plasticboy.com/
vim: ft=help

View file

@ -1,6 +1,3 @@
autocmd BufNewFile,BufRead *.markdown,*.md,*.mdown,*.mkd,*.mkdn " markdown filetype file
\ if &ft =~# '^\%(conf\|modula2\)$' | au BufRead,BufNewFile *.{md,mdown,mkd,mkdn,markdown,mdwn} set filetype=markdown
\ set ft=markdown | au BufRead,BufNewFile *.{md,mdown,mkd,mkdn,markdown,mdwn}.{des3,des,bf,bfa,aes,idea,cast,rc2,rc4,rc5,desx} set filetype=markdown
\ else |
\ setf markdown |
\ endif

View file

@ -1,75 +1,773 @@
" Vim filetype plugin "TODO print messages when on visual mode. I only see VISUAL, not the messages.
" Language: Markdown
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" Last Change: 2013 May 30
if exists("b:did_ftplugin") " Function interface phylosophy:
finish "
endif " - 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-+
runtime! ftplugin/html.vim ftplugin/html_*.vim ftplugin/html/*.vim " 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^######[^#]@='
\ }
setlocal comments=fb:*,fb:-,fb:+,n:> commentstring=<!--%s--> " Maches any header level of any type.
setlocal formatoptions+=tcqln formatoptions-=r formatoptions-=o "
setlocal formatlistpat=^\\s*\\d\\+\\.\\s\\+\\\|^[-*+]\\s\\+\\\|^\\[^\\ze[^\\]]\\+\\]: " This could be deduced from `s:levelRegexpDict`, but it is more
" efficient to have a single regexp for this.
"
let s:headersRegexp = '\v^(#|.+\n(\=+|-+)$)'
if exists('b:undo_ftplugin') " Returns the line number of the first header before `line`, called the
let b:undo_ftplugin .= "|setl cms< com< fo< flp<" " current header.
else "
let b:undo_ftplugin = "setl cms< com< fo< flp<" " If there is no current header, return `0`.
endif "
" @param a:1 The line to look the header of. Default value: `getpos('.')`.
function! MarkdownFold() "
let line = getline(v:lnum) function! s:GetHeaderLineNum(...)
if a:0 == 0
" Regular headers let l:l = line('.')
let depth = match(line, '\(^#\+\)\@<=\( .*$\)\@=')
if depth > 0
return ">" . depth
endif
" Setext style headings
let nextline = getline(v:lnum + 1)
if (line =~ '^.\+$') && (nextline =~ '^=\+$')
return ">1"
endif
if (line =~ '^.\+$') && (nextline =~ '^-\+$')
return ">2"
endif
return "="
endfunction
function! MarkdownFoldText()
let hash_indent = s:HashIndent(v:foldstart)
let title = substitute(getline(v:foldstart), '^#\+\s*', '', '')
let foldsize = (v:foldend - v:foldstart + 1)
let linecount = '['.foldsize.' lines]'
return hash_indent.' '.title.' '.linecount
endfunction
function! s:HashIndent(lnum)
let hash_header = matchstr(getline(a:lnum), '^#\{1,6}')
if len(hash_header) > 0
" hashtag header
return hash_header
else else
" == or -- header let l:l = a:1
let nextline = getline(a:lnum + 1)
if nextline =~ '^=\+\s*$'
return repeat('#', 1)
elseif nextline =~ '^-\+\s*$'
return repeat('#', 2)
endif 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 endif
endfunction endfunction
if has("folding") && exists("g:markdown_folding") " Move cursor to previous header (before current) of any level.
setlocal foldexpr=MarkdownFold() "
setlocal foldmethod=expr " If it does not exist, print a warning.
setlocal foldtext=MarkdownFoldText() "
let b:undo_ftplugin .= " foldexpr< foldmethod< foldtext<" 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, ':.'))
execute 'edit' l:url
endif
if l:anchor != ''
silent! execute '/'.l:anchor
endif
else
echomsg 'The cursor is not on a link.'
endif
endfunction
endif endif
" vim:set sw=2: 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) ? ' 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!
au BufWinEnter * call s:MarkdownRefreshSyntax(1)
au BufUnload * call s:MarkdownClearSyntaxVariables()
au BufWritePost * call s:MarkdownRefreshSyntax(0)
au InsertEnter,InsertLeave * call s:MarkdownRefreshSyntax(0)
au CursorHold,CursorHoldI * call s:MarkdownRefreshSyntax(0)
augroup END

View file

@ -0,0 +1,75 @@
if exists("b:did_indent") | finish | endif
let b:did_indent = 1
setlocal indentexpr=GetMarkdownIndent()
setlocal nolisp
setlocal autoindent
" Automatically insert bullets
setlocal formatoptions+=r
" Do not automatically insert bullets when auto-wrapping with text-width
setlocal formatoptions-=c
" Accept various markers as bullets
setlocal comments=b:*,b:+,b:-
" Automatically continue blockquote on line break
setlocal comments+=b:>
" Only define the function once
if exists("*GetMarkdownIndent") | finish | endif
function! s:IsMkdCode(lnum)
let name = synIDattr(synID(a:lnum, 1, 0), 'name')
return (name =~ '^mkd\%(Code$\|Snippet\)' || name != '' && name !~ '^\%(mkd\|html\)')
endfunction
function! s:IsLiStart(line)
return a:line !~ '^ *\([*-]\)\%( *\1\)\{2}\%( \|\1\)*$' &&
\ a:line =~ '^\s*[*+-] \+'
endfunction
function! s:IsHeaderLine(line)
return a:line =~ '^\s*#'
endfunction
function! s:IsBlankLine(line)
return a:line =~ '^$'
endfunction
function! s:PrevNonBlank(lnum)
let i = a:lnum
while i > 1 && s:IsBlankLine(getline(i))
let i -= 1
endwhile
return i
endfunction
function GetMarkdownIndent()
if v:lnum > 2 && s:IsBlankLine(getline(v:lnum - 1)) && s:IsBlankLine(getline(v:lnum - 2))
return 0
endif
let list_ind = get(g:, "vim_markdown_new_list_item_indent", 4)
" Find a non-blank line above the current line.
let lnum = s:PrevNonBlank(v:lnum - 1)
" At the start of the file use zero indent.
if lnum == 0 | return 0 | endif
let ind = indent(lnum)
let line = getline(lnum) " Last line
let cline = getline(v:lnum) " Current line
if s:IsLiStart(cline)
" Current line is the first line of a list item, do not change indent
return indent(v:lnum)
elseif s:IsHeaderLine(cline) && !s:IsMkdCode(v:lnum)
" Current line is the header, do not indent
return 0
elseif s:IsLiStart(line)
if s:IsMkdCode(lnum)
return ind
else
" Last line is the first line of a list item, increase indent
return ind + list_ind
end
else
return ind
endif
endfunction

View file

@ -0,0 +1,9 @@
addon: markdown
description: "Markdown syntax highlighting"
files:
- ftdetect/markdown.vim
- ftplugin/markdown.vim
- syntax/markdown.vim
- after/ftplugin/markdown.vim
- indent/markdown.vim
- doc/vim-markdown.txt

View file

@ -1,169 +1,173 @@
" Vim syntax file " Vim syntax file
" Language: Markdown " Language: Markdown
" Maintainer: Tim Pope <vimNOSPAM@tpope.org> " Maintainer: Ben Williams <benw@plasticboy.com>
" Filenames: *.markdown " URL: http://plasticboy.com/markdown-vim-mode/
" Last Change: 2013 May 30 " Remark: Uses HTML syntax file
" TODO: Handle stuff contained within stuff (e.g. headings within blockquotes)
if exists("b:current_syntax")
" 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 finish
endif endif
if !exists('main_syntax') " don't use standard HiLink, it will not work with included syntax files
let main_syntax = 'markdown' if version < 508
command! -nargs=+ HtmlHiLink hi link <args>
else
command! -nargs=+ HtmlHiLink hi def link <args>
endif endif
runtime! syntax/html.vim syn spell toplevel
unlet! b:current_syntax
if !exists('g:markdown_fenced_languages')
let g:markdown_fenced_languages = []
endif
let s:done_include = {}
for s:type in map(copy(g:markdown_fenced_languages),'matchstr(v:val,"[^=]*$")')
if has_key(s:done_include, matchstr(s:type,'[^.]*'))
continue
endif
if s:type =~ '\.'
let b:{matchstr(s:type,'[^.]*')}_subtype = matchstr(s:type,'\.\zs.*')
endif
exe 'syn include @markdownHighlight'.substitute(s:type,'\.','','g').' syntax/'.matchstr(s:type,'[^.]*').'.vim'
unlet! b:current_syntax
let s:done_include[matchstr(s:type,'[^.]*')] = 1
endfor
unlet! s:type
unlet! s:done_include
if !exists('g:markdown_minlines')
let g:markdown_minlines = 50
endif
execute 'syn sync minlines=' . g:markdown_minlines
syn case ignore syn case ignore
syn sync linebreaks=1
syn match markdownValid '[<>]\c[a-z/$!]\@!' let s:conceal = ''
syn match markdownValid '&\%(#\=\w*;\)\@!'
syn match markdownLineStart "^[<@]\@!" nextgroup=@markdownBlock,htmlSpecialChar
syn cluster markdownBlock contains=markdownH1,markdownH2,markdownH3,markdownH4,markdownH5,markdownH6,markdownBlockquote,markdownListMarker,markdownOrderedListMarker,markdownCodeBlock,markdownRule
syn cluster markdownInline contains=markdownLineBreak,markdownLinkText,markdownItalic,markdownBold,markdownCode,markdownEscape,@htmlTop,markdownError
syn match markdownH1 "^.\+\n=\+$" contained contains=@markdownInline,markdownHeadingRule,markdownAutomaticLink
syn match markdownH2 "^.\+\n-\+$" contained contains=@markdownInline,markdownHeadingRule,markdownAutomaticLink
syn match markdownHeadingRule "^[=-]\+$" contained
syn region markdownH1 matchgroup=markdownH1Delimiter start="##\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
syn region markdownH2 matchgroup=markdownH2Delimiter start="###\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
syn region markdownH3 matchgroup=markdownH3Delimiter start="####\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
syn region markdownH4 matchgroup=markdownH4Delimiter start="#####\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
syn region markdownH5 matchgroup=markdownH5Delimiter start="######\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
syn region markdownH6 matchgroup=markdownH6Delimiter start="#######\@!" end="#*\s*$" keepend oneline contains=@markdownInline,markdownAutomaticLink contained
syn match markdownBlockquote ">\%(\s\|$\)" contained nextgroup=@markdownBlock
syn region markdownCodeBlock start=" \|\t" end="$" contained
" TODO: real nesting
syn match markdownListMarker "\%(\t\| \{0,4\}\)[-*+]\%(\s\+\S\)\@=" contained
syn match markdownOrderedListMarker "\%(\t\| \{0,4}\)\<\d\+\.\%(\s\+\S\)\@=" contained
syn match markdownRule "\* *\* *\*[ *]*$" contained
syn match markdownRule "- *- *-[ -]*$" contained
syn match markdownLineBreak " \{2,\}$"
syn region markdownIdDeclaration matchgroup=markdownLinkDelimiter start="^ \{0,3\}!\=\[" end="\]:" oneline keepend nextgroup=markdownUrl skipwhite
syn match markdownUrl "\S\+" nextgroup=markdownUrlTitle skipwhite contained
syn region markdownUrl matchgroup=markdownUrlDelimiter start="<" end=">" oneline keepend nextgroup=markdownUrlTitle skipwhite contained
syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+"+ end=+"+ keepend contained
syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+'+ end=+'+ keepend contained
syn region markdownUrlTitle matchgroup=markdownUrlTitleDelimiter start=+(+ end=+)+ keepend contained
syn region markdownLinkText matchgroup=markdownLinkTextDelimiter start="!\=\[\%(\_[^]]*]\%( \=[[(]\)\)\@=" end="\]\%( \=[[(]\)\@=" nextgroup=markdownLink,markdownId skipwhite contains=@markdownInline,markdownLineStart
syn region markdownLink matchgroup=markdownLinkDelimiter start="(" end=")" contains=markdownUrl keepend contained
syn region markdownId matchgroup=markdownIdDelimiter start="\[" end="\]" keepend contained
syn region markdownAutomaticLink matchgroup=markdownUrlDelimiter start="<\%(\w\+:\|[[:alnum:]_+-]\+@\)\@=" end=">" keepend oneline
let s:concealends = '' let s:concealends = ''
if has('conceal') && get(g:, 'markdown_syntax_conceal', 1) == 1 if has('conceal') && get(g:, 'vim_markdown_conceal', 1)
let s:conceal = ' conceal'
let s:concealends = ' concealends' let s:concealends = ' concealends'
endif endif
exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\S\@<=\*\|\*\S\@=" end="\S\@<=\*\|\*\S\@=" keepend contains=markdownLineStart,@Spell' . s:concealends
exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\S\@<=_\|_\S\@=" end="\S\@<=_\|_\S\@=" keepend contains=markdownLineStart,@Spell' . s:concealends
exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\S\@<=\*\*\|\*\*\S\@=" end="\S\@<=\*\*\|\*\*\S\@=" keepend contains=markdownLineStart,markdownItalic,@Spell' . s:concealends
exe 'syn region markdownBold matchgroup=markdownBoldDelimiter start="\S\@<=__\|__\S\@=" end="\S\@<=__\|__\S\@=" keepend contains=markdownLineStart,markdownItalic,@Spell' . s:concealends
exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\S\@<=\*\*\*\|\*\*\*\S\@=" end="\S\@<=\*\*\*\|\*\*\*\S\@=" keepend contains=markdownLineStart,@Spell' . s:concealends
exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\S\@<=___\|___\S\@=" end="\S\@<=___\|___\S\@=" keepend contains=markdownLineStart,@Spell' . s:concealends
syn region markdownCode matchgroup=markdownCodeDelimiter start="`" end="`" keepend contains=markdownLineStart " additions to HTML groups
syn region markdownCode matchgroup=markdownCodeDelimiter start="`` \=" end=" \=``" keepend contains=markdownLineStart if get(g:, 'vim_markdown_emphasis_multiline', 1)
syn region markdownCode matchgroup=markdownCodeDelimiter start="^\s*````*.*$" end="^\s*````*\ze\s*$" keepend 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
syn match markdownFootnote "\[^[^\]]\+\]" " [link](URL) | [link][id] | [link][] | ![image](URL)
syn match markdownFootnoteDefinition "^\[^[^\]]\+\]:" 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
if main_syntax ==# 'markdown' " Autolink without angle brackets.
let s:done_include = {} " mkd inline links: protocol optional user:pass@ sub/domain .com, .co.uk, etc optional port path/querystring/hash fragment
for s:type in g:markdown_fenced_languages " ------------ _____________________ ----------------------------- _________________________ ----------------- __
if has_key(s:done_include, matchstr(s:type,'[^.]*')) syn match mkdInlineURL /https\?:\/\/\(\w\+\(:\w\+\)\?@\)\?\([A-Za-z0-9][-_0-9A-Za-z]*\.\)\{1,}\(\w\{2,}\.\?\)\{1,}\(:[0-9]\{1,5}\)\?\S*/
continue
endif " Autolink with parenthesis.
exe 'syn region markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\..*','','').' matchgroup=markdownCodeDelimiter start="^\s*````*\s*'.matchstr(s:type,'[^=]*').'\S\@!.*$" end="^\s*````*\ze\s*$" keepend contains=@markdownHighlight'.substitute(matchstr(s:type,'[^=]*$'),'\.','','g') 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=")"
let s:done_include[matchstr(s:type,'[^.]*')] = 1
endfor " Autolink with angle brackets.
unlet! s:type syn region mkdInlineURL matchgroup=mkdDelimiter start="\\\@<!<\ze[a-z][a-z0-9,.-]\{1,22}:\/\/[^> ]*>" end=">"
unlet! s:done_include
" 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 start="^\s*#" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn region htmlH2 start="^\s*##" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn region htmlH3 start="^\s*###" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn region htmlH4 start="^\s*####" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn region htmlH5 start="^\s*#####" end="$" contains=mkdLink,mkdInlineURL,@Spell
syn region htmlH6 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
syn region mkdCode start=/\(\([^\\]\|^\)\\\)\@<!`/ end=/\(\([^\\]\|^\)\\\)\@<!`/
syn region mkdCode start=/\s*``[^`]*/ end=/[^`]*``\s*/
syn region mkdCode start=/^\s*\z(`\{3,}\)[^`]*$/ end=/^\s*\z1`*\s*$/
syn region mkdCode start=/\s*\~\~[^\~]*/ end=/[^\~]*\~\~\s*/
syn region mkdCode start=/^\s*\z(\~\{3,}\)\s*[0-9A-Za-z_+-]*\s*$/ end=/^\s*\z1\~*\s*$/
syn region mkdCode start="<pre[^>]*\\\@<!>" end="</pre>"
syn region mkdCode start="<code[^>]*\\\@<!>" end="</code>"
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 endif
syn match markdownEscape "\\[][\\`*_{}()<>#+.!-]" if get(g:, 'vim_markdown_toml_frontmatter', 0)
syn match markdownError "\w\@<=_\w\@=" try
syn include @tomlTop syntax/toml.vim
hi def link markdownH1 htmlH1 syn region Comment matchgroup=mkdDelimiter start="\%^+++$" end="^+++$" transparent contains=@tomlTop keepend
hi def link markdownH2 htmlH2 unlet! b:current_syntax
hi def link markdownH3 htmlH3 catch /E484/
hi def link markdownH4 htmlH4 syn region Comment matchgroup=mkdDelimiter start="\%^+++$" end="^+++$"
hi def link markdownH5 htmlH5 endtry
hi def link markdownH6 htmlH6
hi def link markdownHeadingRule markdownRule
hi def link markdownH1Delimiter markdownHeadingDelimiter
hi def link markdownH2Delimiter markdownHeadingDelimiter
hi def link markdownH3Delimiter markdownHeadingDelimiter
hi def link markdownH4Delimiter markdownHeadingDelimiter
hi def link markdownH5Delimiter markdownHeadingDelimiter
hi def link markdownH6Delimiter markdownHeadingDelimiter
hi def link markdownHeadingDelimiter Delimiter
hi def link markdownOrderedListMarker markdownListMarker
hi def link markdownListMarker htmlTagName
hi def link markdownBlockquote Comment
hi def link markdownRule PreProc
hi def link markdownFootnote Typedef
hi def link markdownFootnoteDefinition Typedef
hi def link markdownLinkText htmlLink
hi def link markdownIdDeclaration Typedef
hi def link markdownId Type
hi def link markdownAutomaticLink markdownUrl
hi def link markdownUrl Float
hi def link markdownUrlTitle String
hi def link markdownIdDelimiter markdownLinkDelimiter
hi def link markdownUrlDelimiter htmlTag
hi def link markdownUrlTitleDelimiter Delimiter
hi def link markdownItalic htmlItalic
hi def link markdownItalicDelimiter markdownItalic
hi def link markdownBold htmlBold
hi def link markdownBoldDelimiter markdownBold
hi def link markdownBoldItalic htmlBoldItalic
hi def link markdownBoldItalicDelimiter markdownBoldItalic
hi def link markdownCodeDelimiter Delimiter
hi def link markdownEscape Special
hi def link markdownError Error
let b:current_syntax = "markdown"
if main_syntax ==# 'markdown'
unlet main_syntax
endif endif
" vim:set sw=2: 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
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 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

View file

@ -0,0 +1,5 @@
You can run the tests using the Makefile from the top directory:
make test
To run them manually please refer to the instructions/commands in the Makefile.

View file

@ -0,0 +1,178 @@
" Tests atx and setext folding, and :Toc.
Before:
source ../after/ftplugin/markdown.vim
After:
setlocal foldexpr=0
setlocal foldmethod=manual
Given markdown;
# chap 1
hello
world
```bash
# some bash scripting
pwd
# this is another comment
# other
echo "foo"
```
## chap 1.1
- dog
- cat
~~~~bash
mkdir foo
# comment in ~
~~~~
### chap 1.1.1
- dragons
- fenixs
# chap 2
another
## chap 2.1
- uk
- japan
- china
# chap 3
nothing here
chap 4
======
setext are evil
chap 4.1
--------
evil indeed
````bash
# get system info
uname -a
````
Execute (fold level):
AssertEqual foldlevel(1), 0, '# chap 1'
AssertEqual foldlevel(3), 1, 'hello'
AssertEqual foldlevel(6), 1, '```bash'
AssertEqual foldlevel(7), 1, '# some bash scripting'
AssertEqual foldlevel(15), 1, '## chap 1.1'
AssertEqual foldlevel(21), 2, 'mkdir foo'
AssertEqual foldlevel(22), 2, 'comment in ~'
AssertEqual foldlevel(25), 2, '### chap 1.1.1'
AssertEqual foldlevel(27), 3, '- dragons'
AssertEqual foldlevel(30), 1, '# chap 2'
AssertEqual foldlevel(32), 1, 'another'
AssertEqual foldlevel(34), 1, '# chap 2.1'
AssertEqual foldlevel(37), 2, '- japan'
AssertEqual foldlevel(41), 1, '# chap 3'
AssertEqual foldlevel(45), 1, 'chap 4\n======'
AssertEqual foldlevel(48), 1, 'setext are evil'
AssertEqual foldlevel(50), 2, 'chap 4.1\n------'
Execute (fold text result):
AssertEqual foldtextresult(2), '+-- 28 lines: hello'
AssertEqual foldtextresult(31), '+-- 10 lines: another'
AssertEqual foldtextresult(42), '+-- 3 lines: nothing here'
AssertEqual foldtextresult(45), '+-- 14 lines: chap 4'
Execute (fold level with setting):
let g:vim_markdown_folding_level = 2
source ../after/ftplugin/markdown.vim
AssertEqual foldlevel(1), 0, '# chap 1'
AssertEqual foldlevel(3), 1, 'hello'
AssertEqual foldlevel(6), 1, '```bash'
AssertEqual foldlevel(7), 1, '# some bash scripting'
AssertEqual foldlevel(15), 0, '## chap 1.1'
AssertEqual foldlevel(21), 2, 'mkdir foo'
AssertEqual foldlevel(22), 2, 'comment in ~'
AssertEqual foldlevel(25), 2, '### chap 1.1.1'
AssertEqual foldlevel(27), 3, '- dragons'
AssertEqual foldlevel(30), 0, '# chap 2'
AssertEqual foldlevel(32), 1, 'another'
AssertEqual foldlevel(34), 0, '# chap 2.1'
AssertEqual foldlevel(37), 2, '- japan'
AssertEqual foldlevel(41), 0, '# chap 3'
AssertEqual foldlevel(45), 1, 'chap 4\n======'
AssertEqual foldlevel(48), 1, 'setext are evil'
AssertEqual foldlevel(50), 1, 'chap 4.1\n------'
let g:vim_markdown_folding_level = 0
Execute (check TOC):
:Toc
:lclose
let res = getloclist(0)
let elem = res[0]
AssertEqual elem.lnum, 1
AssertEqual elem.text, '# chap 1'
let elem = res[1]
AssertEqual elem.lnum, 15
AssertEqual elem.text, '## chap 1.1'
let elem = res[2]
AssertEqual elem.lnum, 25
AssertEqual elem.text, '### chap 1.1.1'
let elem = res[3]
AssertEqual elem.lnum, 30
AssertEqual elem.text, '# chap 2'
let elem = res[4]
AssertEqual elem.lnum, 34
AssertEqual elem.text, '## chap 2.1'
let elem = res[5]
AssertEqual elem.lnum, 41
AssertEqual elem.text, '# chap 3'
let elem = res[6]
AssertEqual elem.lnum, 45
AssertEqual elem.text, 'chap 4'
let elem = res[7]
AssertEqual elem.lnum, 50
AssertEqual elem.text, 'chap 4.1'
Given markdown;
---
layout: article
title: A test of the heading folding when there is YAML frontmatter
tags: markdown yaml vim-markdown
---
body
heading
-------
Execute (fold level of yaml front matter):
let g:vim_markdown_frontmatter = 1
source ../after/ftplugin/markdown.vim
AssertEqual foldlevel(1), 0, '---'
AssertEqual foldlevel(2), 0, 'layout: article'
AssertEqual foldlevel(4), 0, 'tags: markdown yaml vim-markdown'
AssertEqual foldlevel(5), 0, '---'
AssertEqual foldlevel(6), 0, 'body'
AssertEqual foldlevel(8), 2, 'heading'
AssertEqual foldlevel(9), 2, '-------'
unlet g:vim_markdown_frontmatter
Execute (check Toc of yaml front matter):
let g:vim_markdown_frontmatter = 1
:Toc
:lclose
let res = getloclist(0)
AssertEqual len(res), 1
let elem = res[0]
AssertEqual elem.lnum, 8
AssertEqual elem.text, 'heading'
unlet g:vim_markdown_frontmatter

View file

@ -0,0 +1,53 @@
Before:
source ../after/ftplugin/markdown.vim
After:
setlocal foldexpr=0
setlocal foldmethod=manual
Given markdown;
# Title
## Chapter 1
```
This is code block
# This is just a comment
```
## Capter 2
foobar
Execute (fold level # in code block):
AssertEqual foldlevel(1), 0, '# Title'
AssertEqual foldlevel(3), 1, '## Chapter 1'
AssertEqual foldlevel(7), 2, '# This is just a comment'
AssertEqual foldlevel(8), 2, '```'
AssertEqual foldlevel(10), 1, '## Chapter 2'
AssertEqual foldlevel(12), 2, 'foobar'
Given markdown;
Fold Level 1
============
Fold Level 2
------------
Execute (fold level ==, --):
AssertEqual foldlevel(2), 1, '=='
AssertEqual foldlevel(4), 2, '--'
Given markdown;
# H1
## H1.1
## H1.2
# H2
Execute (fold level # in last line):
AssertEqual foldlevel(1), 0, '# H1'
AssertEqual foldlevel(3), 1, '## H1.1'
AssertEqual foldlevel(5), 1, '## H1.2'
AssertEqual foldlevel(7), 0, '# H2'

View file

@ -0,0 +1 @@
ge test

View file

@ -0,0 +1,15 @@
Before:
let g:vim_markdown_new_list_item_indent = 2
After:
unlet g:vim_markdown_new_list_item_indent
Given markdown;
* item1
Do (new line from the first item of the list and add the second item):
o* item2
Expect (insert 2 spaces to the head of second item):
* item1
* item2

View file

@ -0,0 +1,26 @@
1. Confirm indent with new line insert after list items
'\' is not list item.
\ foo
If only space and three '*' or '-' character are in the line,
this line means horizontal item.
If current line is below horizontal item, it need not to indent.
Following example is horizontal item.
---
***
- - -
* * *
And list item must be specified space after [*-+].
Following example is list item.
* foo
- bar
+ baz
But following example is not list item.
*foo
-bar
+baz

View file

@ -0,0 +1,73 @@
Given markdown;
* item1
Do (insert enter at list end):
A\<cr>item2
Expect (auto insert * and indent level is same):
* item1
* item2
Given markdown;
Execute:
syntax off
Do (insert enter at list end with syntax off):
i* item1\<cr>item2
Expect (auto insert * and indent level is same):
* item1
* item2
Execute:
syntax on
Given markdown;
```
* item1
Do (insert after list items in code block):
jotext
Expect (no autoindent in code block):
```
* item1
text
Given markdown;
* item1
a
Do (insert enter after list):
jji\<cr>b
Expect (no autoindent outside list):
* item1
ba
Given markdown;
- a
# b
Do (insert header after list):
jjwi#
Expect (no indent header after list):
- a
## b
Given markdown;
* item1
Do (new line from the first item of the list and add the second item):
o* item2
Expect (insert 4 spaces to the head of second item):
* item1
* item2

View file

@ -0,0 +1,153 @@
Given markdown;
a <http://b> c
Execute (gx autolink):
let b:url = 'http://b'
let b:line = getline(1)
let b:func = Markdown_GetFunc('vim-markdown/ftplugin/markdown.vim', 'Markdown_GetUrlForPosition')
AssertEqual b:func(1, match(b:line, 'a') + 1), ''
AssertEqual b:func(1, match(b:line, '<') + 1), b:url
AssertEqual b:func(1, match(b:line, 'h') + 1), b:url
AssertEqual b:func(1, match(b:line, '>') + 1), b:url
AssertEqual b:func(1, match(b:line, 'c') + 1), ''
Given markdown;
a http://b.bb c
Execute (gx implicit autolink):
let b:url = 'http://b.bb'
let b:line = getline(1)
let b:func = Markdown_GetFunc('vim-markdown/ftplugin/markdown.vim', 'Markdown_GetUrlForPosition')
AssertEqual b:func(1, match(b:line, 'a') + 1), ''
AssertEqual b:func(1, match(b:line, 'h') + 1), b:url
AssertEqual b:func(1, match(b:line, 'c') + 1), ''
Given markdown;
[a]: http://b "c"
Execute (gx link reference definition):
let b:url = 'http://b'
let b:line = getline(1)
let b:func = Markdown_GetFunc('vim-markdown/ftplugin/markdown.vim', 'Markdown_GetUrlForPosition')
" TODO would be cool if all of the following gave the link.
AssertEqual b:func(1, match(b:line, 'a') + 1), ''
AssertEqual b:func(1, match(b:line, 'h') + 1), b:url
AssertEqual b:func(1, match(b:line, 'c') + 1), ''
Given markdown;
a [b](c) d
Execute (gx autolink):
let b:url = 'c'
let b:line = getline(1)
let b:func = Markdown_GetFunc('vim-markdown/ftplugin/markdown.vim', 'Markdown_GetUrlForPosition')
AssertEqual b:func(1, match(b:line, 'a') + 1), ''
AssertEqual b:func(1, match(b:line, '[') + 1), b:url
AssertEqual b:func(1, match(b:line, 'b') + 1), b:url
AssertEqual b:func(1, match(b:line, ']') + 1), b:url
AssertEqual b:func(1, match(b:line, '(') + 1), b:url
AssertEqual b:func(1, match(b:line, 'c') + 1), b:url
AssertEqual b:func(1, match(b:line, ')') + 1), b:url
AssertEqual b:func(1, match(b:line, 'd') + 1), ''
Given markdown;
[ge_test.md](ge_test.md)
Execute (ge opens file):
normal ge
AssertEqual @%, 'ge_test.md'
AssertEqual getline(1), 'ge test'
Given markdown;
[ge_test](ge_test)
Execute (ge opens file without .md extensions):
let g:vim_markdown_no_extensions_in_markdown = 1
normal ge
AssertEqual @%, 'ge_test.md'
AssertEqual getline(1), 'ge test'
unlet g:vim_markdown_no_extensions_in_markdown
Given markdown;
[ge_test.md](ge_test.md)
Execute (ge does not write before opening file):
normal ia
normal l
normal ge
AssertEqual @%, 'ge_test.md'
AssertEqual getline(1), 'ge test'
Given markdown;
[ge_test.md](ge_test.md)
Execute (ge auto-write before opening file):
let g:vim_markdown_autowrite = 1
normal ia
normal l
AssertThrows normal ge
AssertEqual g:vader_exception, 'Vim(write):E382: Cannot write, ''buftype'' option is set'
unlet g:vim_markdown_autowrite
Given markdown;
# a
b
# c
d
Execute (]] same level):
AssertEqual line('.'), 1
normal ]]
AssertEqual line('.'), 5
normal [[
AssertEqual line('.'), 1
Given markdown;
# a
b
## c
d
Execute (]] different levels level):
AssertEqual line('.'), 1
normal ]]
AssertEqual line('.'), 5
normal [[
AssertEqual line('.'), 1
Given markdown;
# a
b
## c
d
# e
f
Execute (][ different levels level):
AssertEqual line('.'), 1
normal ][
AssertEqual line('.'), 9
normal []
AssertEqual line('.'), 1
Given markdown;
# a
b
Execute (]c):
normal! 3G
AssertEqual line('.'), 3
normal ]c
AssertEqual line('.'), 1

View file

@ -0,0 +1,85 @@
Before:
let g:vim_markdown_folding_style_pythonic = 1
source ../after/ftplugin/markdown.vim
After:
setlocal foldexpr=0
setlocal foldmethod=manual
Given markdown;
# Title
## Chapter 1
```
This is code block
# This is just a comment
```
## Chapter 2
foobar
Execute (fold level # in code block):
AssertEqual foldlevel(1), 0, '# Title'
AssertEqual foldlevel(3), 1, '## Chapter 1'
AssertEqual foldlevel(7), 1, '# This is just a comment'
AssertEqual foldlevel(8), 1, '```'
AssertEqual foldlevel(10), 1, '## Chapter 2'
AssertEqual foldlevel(12), 1, 'foobar'
Execute (fold text of chapters):
let b:width = winwidth(0)
let b:hyphen = repeat('-', b:width - 18 > 2 ? b:width - 18 : b:width - 9 > 0 ? 3 : 2)
AssertEqual foldtextresult(3), strpart('## Chapter 1', 0, b:width - 9) . ' ' . b:hyphen . ' 6'
AssertEqual foldtextresult(10), strpart('## Chapter 2', 0, b:width - 9) . ' ' . b:hyphen . ' 2'
Given markdown;
Fold text 1
===========
Fold text 2
-----------
Execute (fold level ==, --):
AssertEqual foldlevel(2), 0, '=='
AssertEqual foldlevel(4), 1, '--'
Execute (fold text of ==, --):
let b:width = winwidth(0)
let b:hyphen = repeat('-', b:width - 17 > 2 ? b:width - 17 : b:width - 9 > 0 ? 3 : 2)
AssertEqual foldtextresult(3), strpart('Fold text 2', 0, b:width - 9) . ' ' . b:hyphen . ' 1'
Given markdown;
Headline
foobar
# Title
Execute (fold any preamble):
AssertEqual foldlevel(1), 1, 'Headline'
AssertEqual foldlevel(3), 1, 'foobar'
AssertEqual foldlevel(5), 0, '# Title'
Given markdown;
---
layout: article
title: A test of the heading folding when there is YAML frontmatter
tags: markdown yaml vim-markdown
---
body
heading
-------
Execute (fold level of yaml front matter):
let g:vim_markdown_frontmatter = 1
source ../after/ftplugin/markdown.vim
AssertEqual foldlevel(1), 1, '---'
AssertEqual foldlevel(2), 1, 'layout: article'
AssertEqual foldlevel(4), 1, 'tags: markdown yaml vim-markdown'
AssertEqual foldlevel(5), 1, '---'
AssertEqual foldlevel(6), 1, 'body'
AssertEqual foldlevel(8), 1, 'heading'
AssertEqual foldlevel(9), 1, '-------'
unlet g:vim_markdown_frontmatter

View file

@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Exit on error.
set -e
cd "$( dirname "${BASH_SOURCE[0]}" )"
for dep in ../build/tabular ../build/vim-toml ../build/vim-json ../build/vader.vim; do
if [[ ! -d $dep ]]; then
echo "Missing dependency: $dep"
echo "You may just want to use 'make test'."
exit 1
fi
done
vim -Nu vimrc -c 'Vader! *' > /dev/null

View file

@ -0,0 +1,158 @@
Before:
let g:vim_markdown_emphasis_multiline = 0
syn off | syn on
After:
let g:vim_markdown_emphasis_multiline = 1
syn off | syn on
Given markdown;
a **b** c
Execute (bold):
AssertNotEqual SyntaxOf('a'), 'htmlBold'
AssertEqual SyntaxOf('b'), 'htmlBold'
AssertNotEqual SyntaxOf('c'), 'htmlBold'
Given markdown;
a __b__ c
Execute (bold):
AssertNotEqual SyntaxOf('a'), 'htmlBold'
AssertEqual SyntaxOf('b'), 'htmlBold'
AssertNotEqual SyntaxOf('c'), 'htmlBold'
Given markdown;
a *b* c
Execute (italic):
AssertNotEqual SyntaxOf('a'), 'htmlItalic'
AssertEqual SyntaxOf('b'), 'htmlItalic'
AssertNotEqual SyntaxOf('c'), 'htmlItalic'
Given markdown;
a _b_ c
Execute (italic):
AssertNotEqual SyntaxOf('a'), 'htmlItalic'
AssertEqual SyntaxOf('b'), 'htmlItalic'
AssertNotEqual SyntaxOf('c'), 'htmlItalic'
Given markdown;
_a_b_
Execute (italic text has underscores):
AssertEqual SyntaxOf('a'), 'htmlItalic'
AssertEqual SyntaxOf('b'), 'htmlItalic'
Given markdown;
a \*b\* c
Execute (not italic with escaped asterisks):
AssertNotEqual SyntaxOf('a'), 'htmlItalic'
AssertNotEqual SyntaxOf('b'), 'htmlItalic'
AssertNotEqual SyntaxOf('c'), 'htmlItalic'
Given markdown;
a \_b\_ c
Execute (not italic with escaped underscores):
AssertNotEqual SyntaxOf('a'), 'htmlItalic'
AssertNotEqual SyntaxOf('b'), 'htmlItalic'
AssertNotEqual SyntaxOf('c'), 'htmlItalic'
Given markdown;
a _b\_c_ d
Execute (italic with escaped underscores):
AssertNotEqual SyntaxOf('a'), 'htmlItalic'
AssertEqual SyntaxOf('b'), 'htmlItalic'
AssertEqual SyntaxOf('c'), 'htmlItalic'
AssertNotEqual SyntaxOf('d'), 'htmlItalic'
Given markdown;
a_b_c
Execute (not italic underscores within text):
AssertNotEqual SyntaxOf('a'), 'htmlItalic'
AssertNotEqual SyntaxOf('b'), 'htmlItalic'
AssertNotEqual SyntaxOf('c'), 'htmlItalic'
Given markdown;
a *b\*c* d
Execute (italic with escaped asterisks):
AssertNotEqual SyntaxOf('a'), 'htmlItalic'
AssertEqual SyntaxOf('b'), 'htmlItalic'
AssertEqual SyntaxOf('c'), 'htmlItalic'
AssertNotEqual SyntaxOf('d'), 'htmlItalic'
Given markdown;
a __b\_\_c__ d
Execute (bold with escaped underscores):
AssertNotEqual SyntaxOf('a'), 'htmlBold'
AssertEqual SyntaxOf('b'), 'htmlBold'
AssertEqual SyntaxOf('c'), 'htmlBold'
AssertNotEqual SyntaxOf('d'), 'htmlBold'
Given markdown;
_a b
c_ d
Execute (italic with underscores in multiple lines):
AssertNotEqual SyntaxOf('a'), 'htmlItalic'
AssertNotEqual SyntaxOf('b'), 'htmlItalic'
AssertNotEqual SyntaxOf('c'), 'htmlItalic'
AssertNotEqual SyntaxOf('d'), 'htmlItalic'
Given markdown;
__a b
c__ d
Execute (bold with underscores in multiple lines):
AssertNotEqual SyntaxOf('a'), 'htmlBold'
AssertNotEqual SyntaxOf('b'), 'htmlBold'
AssertNotEqual SyntaxOf('c'), 'htmlBold'
AssertNotEqual SyntaxOf('d'), 'htmlBold'
Given markdown;
___a b
c___ d
Execute (bold italic with underscores in multiple lines):
AssertNotEqual SyntaxOf('a'), 'htmlBoldItalic'
AssertNotEqual SyntaxOf('b'), 'htmlBoldItalic'
AssertNotEqual SyntaxOf('c'), 'htmlBoldItalic'
AssertNotEqual SyntaxOf('d'), 'htmlBoldItalic'
Given markdown;
*a b
c* d
Execute (italic with asterisks in multiple lines):
AssertNotEqual SyntaxOf('a'), 'htmlItalic'
AssertNotEqual SyntaxOf('b'), 'htmlItalic'
AssertNotEqual SyntaxOf('c'), 'htmlItalic'
AssertNotEqual SyntaxOf('d'), 'htmlItalic'
Given markdown;
**a b
c** d
Execute (bold with asterisks in multiple lines):
AssertNotEqual SyntaxOf('a'), 'htmlBold'
AssertNotEqual SyntaxOf('b'), 'htmlBold'
AssertNotEqual SyntaxOf('c'), 'htmlBold'
AssertNotEqual SyntaxOf('d'), 'htmlBold'
Given markdown;
***a b
c*** d
Execute (bold italic with asterisks in multiple lines):
AssertNotEqual SyntaxOf('a'), 'htmlBoldItalic'
AssertNotEqual SyntaxOf('b'), 'htmlBoldItalic'
AssertNotEqual SyntaxOf('c'), 'htmlBoldItalic'
AssertNotEqual SyntaxOf('d'), 'htmlBoldItalic'

View file

@ -0,0 +1,89 @@
# Fenced code living in an indented environment is correctly highlighted
1. run this command to do this:
```
some command
```
2. Subsequent list items are correctly highlighted.
Fenced code block with language:
```ruby
def f
0
end
```
# Links
[a](b "c")
[a]()
[good spell](a)
[badd spell](a)
[a](b "c")
[a]( b
"c" )
a (`a`) b. Fix: <https://github.com/plasticboy/vim-markdown/issues/113>
Escaped:
\[a](b)
[a\]b](c)
## Known failures
Escape does not work:
[a\](b)
Should not be links because of whitespace:
[a] (b)
[a](a
b)
[a](a b)
# Reference links
Single links:
[a][b]
[good spell][a]
[badd spell][a]
[a][]
[a] []
[a][b] c [d][e]
Reference link followed by inline link:
[a] [b](c)
## Known failures
Should be shortcut reference links:
[a]
[a] b [c]
Should be a single link:
[a] [b]
[a] b [c](d)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,44 @@
Before:
let &gdefault = 1
After:
let &gdefault = 0
Given markdown;
| normal |no space| 2 spaces ||
| - |-| --- ||
| normal |no space| 2 spaces ||
Execute (format unformatted table):
TableFormat
Expect (table is formatted):
| normal | no space | 2 spaces | |
|--------|----------|----------|--|
| normal | no space | 2 spaces | |
Given markdown;
| a | b |
|---|---|
| c | d |
Execute (format well formatted table):
TableFormat
Expect (table is not modified):
| a | b |
|---|---|
| c | d |
Given markdown;
| left |right| center ||
| :- | --: |:---:|:|
| left |right| center ||
Execute (format table with colons):
TableFormat
Expect (preserve colons to align text):
| left | right | center | |
|:-----|------:|:------:|:--|
| left | right | center | |

View file

@ -0,0 +1,53 @@
" Tests toc window auto-fit to longest header, but without exceeding half screen.
Given markdown;
# chap 1
# chap 2
# chap 3
# chap 4
# chap 5
# chap 6
# chap 7
# chap 8
# chap 9
# chap 10
# chap 11
# chap 12
## chap 12.1
### chap 12.1.1
#### chap 12.1.1.1
##### chap 12.1.1.1.1
###### chap 12.1.1.1.1.1
# chap 13
Execute (toc window autofit width):
set number
let g:vim_markdown_toc_autofit = 1
let line = '###### chap 12.1.1.1.1.1'
AssertEqual getline('33'), line
:Toc
let real_width = winwidth(0)
:lclose
let expected_width = len(line) + 2*5 + 1 + 3 - 7
AssertEqual real_width, expected_width
set nonumber
" 2 spaces * 5 additional header levels + 1 space for first header +
" 3 spaces for line numbers - 7 chars ('###### ') that don't show up on the TOC

View file

@ -0,0 +1,185 @@
Given markdown;
# a
Execute (Toc does not set nomodifiable on other files):
" Sanity check.
Assert &modifiable
:Toc
:lclose
:edit a
Assert &modifiable
Given markdown;
header 1
========
test
header 2
--------
test
### header 3
test
Execute (Toc setex headers):
:Toc
Expect (setex headers):
header 1
header 2
header 3
Given markdown;
# header 1
test
## header 2
test
### header 3
test
Execute (Toc atx headers):
:Toc
Expect (atx headers):
header 1
header 2
header 3
Given markdown;
ATX tests.
# h1 space
#h1 nospace
# h1 2 spaces
# h1 trailing hash #
## h2 space
##h2 nospace
## h2 trailing hash ##
### h3 space
###h3 nospace
### h3 trailing hash ###
#### h4
##### h5
###### h6
---
Relative positions.
# h1 before h2
## h2 between h1s
# h1 after h2
---
Setex tests.
setex h1
========
setex h2
--------
setex h1 single punctuation
=
setex h1 punctuation longer than header
================================
Prevent list vs Setex confusion:
- not Setex
- because list
---
Mixed tests.
setex h1 before atx
===================
## atx h2
### atx h3
# atx h1
setex h2
------------------
### atx h3 2
Execute (Toc multiple headers):
:Toc
Expect (multiple headers):
h1 space
h1 nospace
h1 2 spaces
h1 trailing hash
h2 space
h2 nospace
h2 trailing hash
h3 space
h3 nospace
h3 trailing hash
h4
h5
h6
h1 before h2
h2 between h1s
h1 after h2
setex h1
setex h2
setex h1 single punctuation
setex h1 punctuation longer than header
setex h1 before atx
atx h2
atx h3
atx h1
setex h2
atx h3 2
Execute:
:lclose
Given markdown;
# header 1
## header 2
### header 3
Execute (Toc cursor on the current header):
normal! 4G
:Toc
AssertEqual line('.'), 2
:lclose
normal! G
:Toc
AssertEqual line('.'), 3
:lclose

View file

@ -0,0 +1,27 @@
set nocompatible
set rtp+=../
set rtp+=../build/tabular/
set rtp+=../build/vim-toml/
set rtp+=../build/vim-json/
set rtp+=../build/vader.vim/
let $LANG='en_US'
filetype on
filetype plugin on
filetype indent on
syntax on
function! Markdown_GetScriptID(fname) abort
let a:snlist = ''
redir => a:snlist
silent! scriptnames
redir END
let a:mx = '^\s*\(\d\+\):\s*\(.*\)$'
for a:line in split(a:snlist, "\n")
if stridx(substitute(a:line, '\\', '/', 'g'), a:fname) >= 0
return substitute(a:line, a:mx, '\1', '')
endif
endfor
endfunction
function! Markdown_GetFunc(fname, funcname) abort
return function('<SNR>' . Markdown_GetScriptID(a:fname) . '_' . a:funcname)
endfunction

View file

@ -1,6 +1,6 @@
" repeat.vim - Let the repeat command repeat plugin maps " repeat.vim - Let the repeat command repeat plugin maps
" Maintainer: Tim Pope " Maintainer: Tim Pope
" Version: 1.1 " Version: 1.2
" GetLatestVimScripts: 2136 1 :AutoInstall: repeat.vim " GetLatestVimScripts: 2136 1 :AutoInstall: repeat.vim
" Installation: " Installation:
@ -111,7 +111,8 @@ endfunction
function! repeat#wrap(command,count) function! repeat#wrap(command,count)
let preserve = (g:repeat_tick == b:changedtick) let preserve = (g:repeat_tick == b:changedtick)
exe 'norm! '.(a:count ? a:count : '').a:command . (&foldopen =~# 'undo\|all' ? 'zv' : '') call feedkeys((a:count ? a:count : '').a:command, 'n')
exe (&foldopen =~# 'undo\|all' ? 'norm! zv' : '')
if preserve if preserve
let g:repeat_tick = b:changedtick let g:repeat_tick = b:changedtick
endif endif

View file

@ -1,7 +1,7 @@
# more can be found in snippets/html_minimal.snippets # more can be found in snippets/html_minimal.snippets
# these UltiSnips override snippets because nested placeholders are being used # these UltiSnips override snippets because nested placeholders are being used
priority -50 priority -49
snippet id snippet id
id="$1"$2 id="$1"$2

View file

@ -608,6 +608,12 @@ returning ${1:variable} do${2/(^(?<var>\s*[a-z_][a-zA-Z0-9_]*\s*)(,\g<var>)*,?\s
end end
endsnippet endsnippet
snippet cmm "Create Migration Model Class"
class Migration${1/(?:^|_)(\w)/\u$1/g} < ApplicationRecord
self.table_name = :${1:model_name}s
end
endsnippet
snippet t. "t.binary (tcbi)" snippet t. "t.binary (tcbi)"
t.binary :${1:title}${2:, :limit => ${3:2}.megabytes} t.binary :${1:title}${2:, :limit => ${3:2}.megabytes}
t.$0 t.$0

View file

@ -8,7 +8,7 @@ priority -50
# Built In library # # Built In library #
###################### ######################
snippet cat "Catenate" snippet cat "Catenate"
Catenate SEPARATOR=${1:---} ${2:Hello} ${3:world} \${${1:name}}= Catenate SEPARATOR=${2:---} ${3:Hello} ${4:world}
endsnippet endsnippet
snippet eval "Evaluate" snippet eval "Evaluate"
@ -109,6 +109,10 @@ snippet sgv "Set Global Variable"
Set Global Variable \${${1:name}} ${2:${value}} Set Global Variable \${${1:name}} ${2:${value}}
endsnippet endsnippet
snippet sbe "Should Be Equal"
Should Be Equal "\${${1:var}}" "${2:expected value}" ${3:"optional error msg"} ${4:ignore_case=True}
endsnippet
snippet sleep "Sleep" snippet sleep "Sleep"
Sleep ${1:2 minutes 10 seconds} Sleep ${1:2 minutes 10 seconds}
endsnippet endsnippet
@ -142,6 +146,12 @@ Import library Dialogs
Pause execution Pause execution
endsnippet endsnippet
snippet tpause "Teardown Pause - pause test execution only on failure"
[Teardown] Run Keyword If Test Failed Run Keywords
... Import library Dialogs AND
... Pause execution
endsnippet
############################## ##############################
# Selenium2Library library # # Selenium2Library library #

View file

@ -2,6 +2,48 @@ priority -50
extends texmath extends texmath
global !p
def create_table(snip):
rows = snip.buffer[snip.line].split('x')[0]
cols = snip.buffer[snip.line].split('x')[1]
int_val = lambda string: int(''.join(s for s in string if s.isdigit()))
rows = int_val(rows)
cols = int_val(cols)
offset = cols + 1
old_spacing = snip.buffer[snip.line][:snip.buffer[snip.line].rfind('\t') + 1]
snip.buffer[snip.line] = ''
final_str = old_spacing + "\\begin{tabular}{|" + "|".join(['$' + str(i + 1) for i in range(cols)]) + "|}\n"
for i in range(rows):
final_str += old_spacing + '\t'
final_str += " & ".join(['$' + str(i * cols + j + offset) for j in range(cols)])
final_str += " \\\\\\\n"
final_str += old_spacing + "\\end{tabular}\n$0"
snip.expand_anon(final_str)
def add_row(snip):
row_len = int(''.join(s for s in snip.buffer[snip.line] if s.isdigit()))
old_spacing = snip.buffer[snip.line][:snip.buffer[snip.line].rfind('\t') + 1]
snip.buffer[snip.line] = ''
final_str = old_spacing
final_str += " & ".join(['$' + str(j + 1) for j in range(row_len)])
final_str += " \\\\\\"
snip.expand_anon(final_str)
endglobal
snippet "b(egin)?" "begin{} / end{}" br snippet "b(egin)?" "begin{} / end{}" br
\begin{${1:something}} \begin{${1:something}}
${0:${VISUAL}} ${0:${VISUAL}}
@ -20,6 +62,14 @@ $0${2/(?<=.)(c|l|r)|./(?1: & )/g}
\end{$1${1/(t)$|(a)$|(.*)/(?1:abular)(?2:rray)/}} \end{$1${1/(t)$|(a)$|(.*)/(?1:abular)(?2:rray)/}}
endsnippet endsnippet
pre_expand "create_table(snip)"
snippet "gentbl(\d+)x(\d+)" "Generate table of *width* by *height*" r
endsnippet
pre_expand "add_row(snip)"
snippet "tr(\d+)" "Add table row of dimension ..." r
endsnippet
snippet table "Table environment" b snippet table "Table environment" b
\begin{table}[${1:htpb}] \begin{table}[${1:htpb}]
\centering \centering

View file

@ -0,0 +1,38 @@
snippet mod
module ${0:`expand('%:t:r')`}
snippet imp
import ${0:http}
snippet impt
import type ${0:option.option}
snippet exp
export ${0}
snippet expt
export ${0}
snippet fn
val ${1:fn} ${2}: fn ${3:'a} -> ${4:'a}
let $1 ${5} =
${0:${VISUAL}}
snippet mat
match ${1} with
| ${2} -> ${0}
snippet -
| ${1} -> ${0}
snippet let
let ${1} = ${2:${VISUAL}} in
${0}
snippet letf
let ${1} =
${0:${VISUAL}}
snippet ty
type ${1:msg}
= ${0}
snippet test
test "${1}" =
${0:${VISUAL}}
snippet doc
{-| ${0}
-}
snippet p
|> ${0}
snippet ae
assert.equal ${0}

View file

@ -48,6 +48,17 @@ snippet once
${0} ${0}
#endif /* end of include guard: $1 */ #endif /* end of include guard: $1 */
# Disable C++ name mangling in C headers
snippet nocxx
#ifdef __cplusplus
extern "C" {
#endif
${0}
#ifdef __cplusplus
} /* extern "C" */
#endif
## ##
## Control Statements ## Control Statements
# if # if

Some files were not shown because too many files have changed in this diff Show more