diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2020-01-03 16:41:21 +1300 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2020-01-03 16:41:21 +1300 |
commit | 25c6bc5b82a70ab210b6bc226ca62bb9633b29fc (patch) | |
tree | 086daaf68598c5e2eeacd34bf96666088ed7641c | |
parent | Merge branch 'release/v8.8.0' (diff) | |
parent | Update dotfiles(7) manual page (diff) | |
download | dotfiles-25c6bc5b82a70ab210b6bc226ca62bb9633b29fc.tar.gz dotfiles-25c6bc5b82a70ab210b6bc226ca62bb9633b29fc.zip |
Merge branch 'release/v8.9.0'v8.9.0
* release/v8.9.0: (32 commits)
Update dotfiles(7) manual page
Fix up some blank lines and comments
Remove duplicate function definitions
Rename and move HTML URL anchoring
Rename HTML tidy function
Correct wildcard escaping in option esc function
Correct unmaps for changed mail importance maps
Wrap a long comment line
Remove unneeded exclamations for :command
Reimplement mail header importance flagging
Add trailing commas
Add missing undo for strict quote map defs
Move strict quote mappings beneath var guard
Move ft=mail start suggestion to autoload
Specify encoding of file with ZALGO comment
Add abort attribute for markdown#Fold()
Move Markdown folding into function
Remove unneeded map#() autoload function
Put a common string into a variable
...
53 files changed, 481 insertions, 241 deletions
@@ -171,6 +171,7 @@ /games/rot13 /games/squ /games/strik +/games/uuu /games/xyzzy /games/zs /git/gitconfig @@ -265,6 +265,7 @@ GAMES = games/aaf \ games/pks \ games/rndn \ games/rot13 \ + games/uuu \ games/squ \ games/strik \ games/xyzzy \ @@ -572,15 +573,20 @@ install-vim-after-syntax: install-vim-autoload: mkdir -p $(VIMDIR)/autoload - cp -p -- vim/autoload/*.vim $(VIMDIR)/autoload + cd vim && find autoload \ + -type d -exec sh -c \ + 'mkdir -p -- $(VIMDIR)/"$$1"' _ {} \; \ + -o \ + -type f -exec sh -c \ + 'cp -p -- "$$1" $(VIMDIR)/"$$1"' _ {} \; install-vim-bundle: install-vim-config - find vim/bundle/*/* \ + cd vim/bundle && find */* \ -type d -exec sh -c \ - 'mkdir -p -- $(VIMDIR)/"$${1#vim/bundle/*/}"' _ {} \; - find vim/bundle/*/*/* \ + 'mkdir -p -- $(VIMDIR)/"$${1#*/}"' _ {} \; + cd vim/bundle && find */*/* \ -type f -exec sh -c \ - 'cp -p -- "$$1" $(VIMDIR)/"$${1#vim/bundle/*/}"' _ {} \; + 'cp -p -- "$$1" $(VIMDIR)/"$${1#*/}"' _ {} \; $(VIM) -e -u NONE -c 'helptags $(VIMDIR)/doc' -c quit install-vim-cache: @@ -607,6 +607,7 @@ There's some silly stuff in `install-games`: * `rndn(6df)` implements an esoteric random number generation algorithm. * `strik(6df)` outputs s̶t̶r̶i̶k̶e̶d̶ ̶o̶u̶t̶ struck out text. * `rot13(6df)` rotates the Latin letters in its input. +* `uuu(6df)` uuuuu uuuu uu uuuuuu uuuuuuu u uuu uuuuu. * `xyzzy(6df)` teleports to a marked location on the filesystem. * `zs(6df)` prefixes "z" case-appropriately to every occurrence of "s" in the text on its standard input. @@ -1,2 +1,2 @@ -tejr dotfiles v8.8.0 -Thu, 19 Dec 2019 05:30:10 +0000 +tejr dotfiles v8.9.0 +Fri, 03 Jan 2020 03:41:18 +0000 diff --git a/games/uuu.sed b/games/uuu.sed new file mode 100644 index 00000000..ab8927be --- /dev/null +++ b/games/uuu.sed @@ -0,0 +1,2 @@ +# uuuuuu uu +s/[[:alpha:]]/u/g diff --git a/man/man6/uuu.6df b/man/man6/uuu.6df new file mode 100644 index 00000000..dd3fcc5a --- /dev/null +++ b/man/man6/uuu.6df @@ -0,0 +1,18 @@ +.TH UUU 6df "December 2019" "Manual page for uuu" +.SH NAME +.B uuu +\- uuuuu u uuuuuu uuuuu uuu uuuuu uuuuu +.SH USAGE +.B uuu +\< uuuu +.br +.B uuuuuuu +| uuu +.SH DESCRIPTION +.B uuu +uuuuuuuuu u uuuuuu uuuuu uuu uuuuu[1] uuuuu uuuuu, uuuuuuuuuu uu uuuu uuu uuuu +uuuu, uuu uuuuuu uu uu uuuuuu. +.SH SEE ALSO +[1]: <https://github.com/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu> +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man7/dotfiles.7df b/man/man7/dotfiles.7df index 7d5a65db..3e70c142 100644 --- a/man/man7/dotfiles.7df +++ b/man/man7/dotfiles.7df @@ -971,6 +971,8 @@ algorithm. .IP \[bu] 2 \f[C]rot13(6df)\f[] rotates the Latin letters in its input. .IP \[bu] 2 +\f[C]uuu(6df)\f[] uuuuu uuuu uu uuuuuu uuuuuuu u uuu uuuuu. +.IP \[bu] 2 \f[C]xyzzy(6df)\f[] teleports to a marked location on the filesystem. .IP \[bu] 2 \f[C]zs(6df)\f[] prefixes \[lq]z\[rq] case\-appropriately to every diff --git a/vim/after/ftplugin/c.vim b/vim/after/ftplugin/c.vim index f082ca18..d67e3654 100644 --- a/vim/after/ftplugin/c.vim +++ b/vim/after/ftplugin/c.vim @@ -1,4 +1,5 @@ -" Set 'commentstring', 'define', and 'include' back to their default C-friendly values +" Set 'commentstring', 'define', and 'include' back to their default +" C-friendly values setlocal commentstring&vim define&vim include&vim let b:undo_ftplugin .= '|setlocal commentstring< define< include<' @@ -6,10 +7,9 @@ let b:undo_ftplugin .= '|setlocal commentstring< define< include<' setlocal complete+=d let b:undo_ftplugin .= '|setlocal complete<' -" Fold based on indent level, but start with all folds open +" Fold based on indent level setlocal foldmethod=indent -setlocal foldlevel=99 -let b:undo_ftplugin .= '|setlocal foldmethod< foldlevel<' +let b:undo_ftplugin .= '|setlocal foldmethod<' " Include system headers on UNIX if has('unix') diff --git a/vim/after/ftplugin/html.vim b/vim/after/ftplugin/html.vim index 7866f31b..7e2ba439 100644 --- a/vim/after/ftplugin/html.vim +++ b/vim/after/ftplugin/html.vim @@ -19,14 +19,14 @@ let b:undo_ftplugin .= '|unlet b:current_compiler' " it; we map \= to do the former, but don't actually set 'equalprg' for the " latter, instead falling back on the good-enough built-in Vim indentation " behavior -nnoremap <buffer> <Leader>= :<C-U>call html#TidyBuffer()<CR> +nnoremap <buffer> <Leader>= :<C-U>call html#Tidy()<CR> let b:undo_ftplugin .= '|nunmap <buffer> <Leader>=' " Set up hooks for timestamp updating augroup html_timestamp autocmd BufWritePre <buffer> \ if exists('b:html_timestamp_check') - \| call html#TimestampUpdate() + \| call html#timestamp#Update() \|endif augroup END let b:undo_ftplugin .= '|execute ''autocmd! html_timestamp''' @@ -39,5 +39,5 @@ endif " Transform URLs to HTML anchors nnoremap <buffer> <LocalLeader>r - \ :<C-U>call html#UrlLink()<CR> + \ :<C-U>call html#url#Anchor()<CR> let b:undo_ftplugin .= '|nunmap <buffer> <LocalLeader>r' diff --git a/vim/after/ftplugin/mail.vim b/vim/after/ftplugin/mail.vim index 9f8fc608..738f17ec 100644 --- a/vim/after/ftplugin/mail.vim +++ b/vim/after/ftplugin/mail.vim @@ -3,59 +3,20 @@ let b:quote_space = 0 let b:undo_ftplugin .= '|unlet b:quote_space' -" Attempt to move to a good spot to start writing -function! s:SuggestStart() abort - - " Move to top of buffer - call setpos('.', [0, 1, 1, 0]) - - " Move to body text - call search('\m^$', 'c') | + - - " Start by trying to move to the first quoted line; this may fail if there's - " no quote, which is fine - call search('\m^>', 'c') - - " Delete quoted blank lines or quoted greetings until we get to something - " with substance. Yes, I like Perl, how could you tell? - while getline('.') =~? '^> *' - \ . '\%(' - \ . '\%(' - \ . 'g[''\u2019]\=day' - \ . '\|\%(good \)\=\%(morning\|afternoon\|evening\)' - \ . '\|h[eu]\%(ll\|rr\)o\+' - \ . '\|hey\+' - \ . '\|hi\+' - \ . '\|sup' - \ . '\|what[''\u2019]\=s up' - \ . '\|yo' - \ . '\)' - \ . '[[:punct:] ]*' - \ . '\%(' - \ . '\a\+' - \ . '[[:punct:] ]*' - \ . '\)\=' - \ . '\)\=$' - delete - endwhile - - " Now move to the first quoted or unquoted blank line - call search('\m^>\= *$', 'c') -endfunction -command! -bar -buffer SuggestStart - \ call s:SuggestStart() +command -bar -buffer SuggestStart + \ call mail#SuggestStart() let b:undo_ftplugin .= '|delcommand SuggestStart' SuggestStart " Normalise quoting -command -buffer -bar -range=% StrictQuote +command -bar -buffer -range=% StrictQuote \ call mail#StrictQuote(<q-line1>, <q-line2>) -nnoremap <LocalLeader>s - \ :StrictQuote<CR> -xnoremap <LocalLeader>s - \ :StrictQuote<CR> let b:undo_ftplugin .= '|delcommand StrictQuote' +command -bar -buffer -nargs=1 SetImportance + \ call mail#importance#Set(<f-args>) +let b:undo_ftplugin .= '|delcommand SetImportance' + " Add a space to the end of wrapped lines for format-flowed mail setlocal formatoptions+=w let b:undo_ftplugin .= '|setlocal formatoptions<' @@ -78,12 +39,15 @@ if exists('no_plugin_maps') || exists('no_mail_maps') endif " Flag messages as important/unimportant -nnoremap <buffer> <LocalLeader>h - \ :<C-U>call mail#FlagImportant()<CR> -let b:undo_ftplugin .= '|nunmap <buffer> <LocalLeader>h' -nnoremap <buffer> <LocalLeader>l - \ :<C-U>call mail#FlagUnimportant()<CR> -let b:undo_ftplugin .= '|nunmap <buffer> <LocalLeader>l' +nnoremap <buffer> <LocalLeader>ih + \ :<C-U>SetImportance high<CR> +let b:undo_ftplugin .= '|nunmap <buffer> <LocalLeader>ih' +nnoremap <buffer> <LocalLeader>il + \ :<C-U>SetImportance low<CR> +let b:undo_ftplugin .= '|nunmap <buffer> <LocalLeader>il' +nnoremap <buffer> <LocalLeader>in + \ :<C-U>SetImportance normal<CR> +let b:undo_ftplugin .= '|nunmap <buffer> <LocalLeader>in' " Quote operator nnoremap <buffer> <expr> <LocalLeader>q @@ -101,6 +65,14 @@ xnoremap <buffer> <expr> <LocalLeader>Q let b:undo_ftplugin .= '|nunmap <buffer> <LocalLeader>Q' \ . '|xunmap <buffer> <LocalLeader>Q' +" Mappings for enforcing strict quoting +nnoremap <LocalLeader>s + \ :StrictQuote<CR> +xnoremap <LocalLeader>s + \ :StrictQuote<CR> +let b:undo_ftplugin .= '|nunmap <buffer> <LocalLeader>s' + \ . '|xunmap <buffer> <LocalLeader>s' + " Maps using autoloaded function for quoted paragraph movement nnoremap <buffer> <silent> <LocalLeader>[ \ :<C-U>call mail#NewBlank(v:count1, 1, 0)<CR> diff --git a/vim/after/ftplugin/perl.vim b/vim/after/ftplugin/perl.vim index 611af62c..9333e234 100644 --- a/vim/after/ftplugin/perl.vim +++ b/vim/after/ftplugin/perl.vim @@ -7,10 +7,9 @@ if executable('perltidy') let b:undo_ftplugin .= '|setlocal equalprg<' endif -" Fold based on indent level, but start with all folds open +" Fold based on indent level setlocal foldmethod=indent -setlocal foldlevel=99 -let b:undo_ftplugin .= '|setlocal foldmethod< foldlevel<' +let b:undo_ftplugin .= '|setlocal foldmethod<' " Add angle brackets to pairs of matched characters for q<...> setlocal matchpairs+=<:> diff --git a/vim/after/ftplugin/php.vim b/vim/after/ftplugin/php.vim index 20d8d42e..b747a14e 100644 --- a/vim/after/ftplugin/php.vim +++ b/vim/after/ftplugin/php.vim @@ -12,10 +12,9 @@ setlocal comments=s1:/*,m:*,ex:*/,://,:# setlocal formatoptions+=or let b:undo_ftplugin .= '|setlocal comments< formatoptions<' -" Fold based on indent level, but start with all folds open +" Fold based on indent level setlocal foldmethod=indent -setlocal foldlevel=99 -let b:undo_ftplugin .= '|setlocal foldmethod< foldlevel<' +let b:undo_ftplugin .= '|setlocal foldmethod<' " Use pman as 'keywordprg' setlocal keywordprg=pman diff --git a/vim/after/ftplugin/sh.vim b/vim/after/ftplugin/sh.vim index 39b8d0d6..2c68d83a 100644 --- a/vim/after/ftplugin/sh.vim +++ b/vim/after/ftplugin/sh.vim @@ -3,10 +3,9 @@ setlocal comments=:# setlocal formatoptions+=or let b:undo_ftplugin .= '|setlocal comments< formatoptions<' -" Fold based on indent level, but start with all folds open +" Fold based on indent level setlocal foldmethod=indent -setlocal foldlevel=99 -let b:undo_ftplugin .= '|setlocal foldmethod< foldlevel<' +let b:undo_ftplugin .= '|setlocal foldmethod<' " If subtype is Bash, set 'keywordprg' to han(1df) if exists('b:is_bash') diff --git a/vim/after/ftplugin/vim.vim b/vim/after/ftplugin/vim.vim index 481af0ec..01f971b9 100644 --- a/vim/after/ftplugin/vim.vim +++ b/vim/after/ftplugin/vim.vim @@ -9,10 +9,9 @@ endif let b:regex_escape_flavor = 'vim' let b:undo_ftplugin .= '|unlet b:regex_escape_flavor' -" Fold based on indent level, but start with all folds open +" Fold based on indent level setlocal foldmethod=indent -setlocal foldlevel=99 -let b:undo_ftplugin .= '|setlocal foldmethod< foldlevel<' +let b:undo_ftplugin .= '|setlocal foldmethod<' " Use :help as 'keywordprg' if not already set; this is the default since Vim " v8.1.1290 diff --git a/vim/after/ftplugin/zsh.vim b/vim/after/ftplugin/zsh.vim index 880c2c39..c361c859 100644 --- a/vim/after/ftplugin/zsh.vim +++ b/vim/after/ftplugin/zsh.vim @@ -5,5 +5,4 @@ let b:undo_ftplugin .= '|unlet b:current_compiler' " Fold based on indent level, but start with all folds open setlocal foldmethod=indent -setlocal foldlevel=99 -let b:undo_ftplugin .= '|setlocal foldmethod< foldlevel<' +let b:undo_ftplugin .= '|setlocal foldmethod<' diff --git a/vim/after/indent/awk.vim b/vim/after/indent/awk.vim index 61f09a1e..bff1b904 100644 --- a/vim/after/indent/awk.vim +++ b/vim/after/indent/awk.vim @@ -1,2 +1,2 @@ " Use four spaces for indentation -call indent#spaces(4) +call indent#Spaces(4) diff --git a/vim/after/indent/css.vim b/vim/after/indent/css.vim index 61f09a1e..bff1b904 100644 --- a/vim/after/indent/css.vim +++ b/vim/after/indent/css.vim @@ -1,2 +1,2 @@ " Use four spaces for indentation -call indent#spaces(4) +call indent#Spaces(4) diff --git a/vim/after/indent/html.vim b/vim/after/indent/html.vim index c043f620..4cb4a678 100644 --- a/vim/after/indent/html.vim +++ b/vim/after/indent/html.vim @@ -1,5 +1,5 @@ " Use four spaces for indentation -call indent#spaces(4) +call indent#Spaces(4) " Clear away the flag we set to indent after paragraphs unlet html_indent_inctags diff --git a/vim/after/indent/javascript.vim b/vim/after/indent/javascript.vim index 61f09a1e..bff1b904 100644 --- a/vim/after/indent/javascript.vim +++ b/vim/after/indent/javascript.vim @@ -1,2 +1,2 @@ " Use four spaces for indentation -call indent#spaces(4) +call indent#Spaces(4) diff --git a/vim/after/indent/mail.vim b/vim/after/indent/mail.vim index 61f09a1e..bff1b904 100644 --- a/vim/after/indent/mail.vim +++ b/vim/after/indent/mail.vim @@ -1,2 +1,2 @@ " Use four spaces for indentation -call indent#spaces(4) +call indent#Spaces(4) diff --git a/vim/after/indent/markdown.vim b/vim/after/indent/markdown.vim index 61f09a1e..bff1b904 100644 --- a/vim/after/indent/markdown.vim +++ b/vim/after/indent/markdown.vim @@ -1,2 +1,2 @@ " Use four spaces for indentation -call indent#spaces(4) +call indent#Spaces(4) diff --git a/vim/after/indent/perl.vim b/vim/after/indent/perl.vim index 61f09a1e..bff1b904 100644 --- a/vim/after/indent/perl.vim +++ b/vim/after/indent/perl.vim @@ -1,2 +1,2 @@ " Use four spaces for indentation -call indent#spaces(4) +call indent#Spaces(4) diff --git a/vim/after/indent/php.vim b/vim/after/indent/php.vim index 61f09a1e..bff1b904 100644 --- a/vim/after/indent/php.vim +++ b/vim/after/indent/php.vim @@ -1,2 +1,2 @@ " Use four spaces for indentation -call indent#spaces(4) +call indent#Spaces(4) diff --git a/vim/after/indent/sh.vim b/vim/after/indent/sh.vim index 61f09a1e..bff1b904 100644 --- a/vim/after/indent/sh.vim +++ b/vim/after/indent/sh.vim @@ -1,2 +1,2 @@ " Use four spaces for indentation -call indent#spaces(4) +call indent#Spaces(4) diff --git a/vim/after/indent/vim.vim b/vim/after/indent/vim.vim index ce99f713..da34eb75 100644 --- a/vim/after/indent/vim.vim +++ b/vim/after/indent/vim.vim @@ -1,5 +1,5 @@ " Use two (not four!) spaces for indentation, per convention -call indent#spaces(2) +call indent#Spaces(2) " Remove inapplicable defaults from 'indentkeys'; we should only need to undo " this if the stock plugin didn't already arrange that (before v7.3.539) diff --git a/vim/autoload/argument.vim b/vim/autoload/argument.vim new file mode 100644 index 00000000..85d75eb1 --- /dev/null +++ b/vim/autoload/argument.vim @@ -0,0 +1,8 @@ +" Escape a single argument for use on an Ex command line; essentially +" a backport of fnameescape() for versions before v7.1.299 +" +function! argument#Escape(argument) abort + return exists('*fnameescape') + \ ? fnameescape(a:argument) + \ : escape(a:argument, "\n\r\t".' *?[{`$\%#''"|!<') +endfunction diff --git a/vim/autoload/colorscheme.vim b/vim/autoload/colorscheme.vim index db965d99..349ee374 100644 --- a/vim/autoload/colorscheme.vim +++ b/vim/autoload/colorscheme.vim @@ -9,6 +9,7 @@ function! colorscheme#UpdateCursorline(colors_name, list) abort " on if the current colorscheme is in the whitelist, and off otherwise; fire " the WinEnter and WinLeave events so any other 'cursorline' related hooks " can run too + " let l:cursorline = index(a:list, a:colors_name) >= 0 tabdo windo let &g:cursorline = l:cursorline \| silent doautocmd WinEnter,WinLeave diff --git a/vim/autoload/escape.vim b/vim/autoload/escape.vim deleted file mode 100644 index 0fdfba99..00000000 --- a/vim/autoload/escape.vim +++ /dev/null @@ -1,13 +0,0 @@ -function! escape#Arg(arg) abort - return exists('*fnameescape') - \ ? fnameescape(a:arg) - \ : escape(a:arg, "\n\r\t".' *?[{`$\%#''"|!<') -endfunction - -function! escape#Item(item) abort - return escape(a:item, ',') -endfunction - -function! escape#Wild(string) abort - return escape(a:string, '\*?[{`''$~') -endfunction diff --git a/vim/autoload/fortune.vim b/vim/autoload/fortune.vim index 6bbe6b3b..da6e2fa3 100644 --- a/vim/autoload/fortune.vim +++ b/vim/autoload/fortune.vim @@ -1,23 +1,32 @@ +" Declare paths to check for fortune files let s:paths = [ \ $HOME.'/.fortunes', \ $HOME.'/.local/share/games/fortunes', \] +" List of executables for which we need to check let s:executables = [ \ 'fortune', \ 'timeout', \] +" Entry point for plugin function! fortune#() abort + " Check we have all of the executables we need for executable in s:executables if !executable(executable) echoerr 'Missing executable "'.executable.'"' endif endfor + " Maximum length of fortunes is the width of the screen minus 1; characters + " wider than one column will break this + " let limit = &columns - 1 + " Some implementations of fortune(6) thrash the disk if they can't meet the + " length limit, so we need to rap this invocation in a timeout(1) call let command = [ \ 'timeout', \ '0.3s', @@ -27,6 +36,7 @@ function! fortune#() abort \ limit, \] + " Find a path for custom fortunes and add it on to the command if found for path in s:paths if isdirectory(path) call add(command, path) @@ -34,6 +44,8 @@ function! fortune#() abort endif endfor + " Run the command and condense any control or space character groups into + " just one space let fortune = substitute( \ system(join(command)), \ '[[:cntrl:][:space:]]\+', @@ -41,6 +53,7 @@ function! fortune#() abort \ 'g', \) + " Show the fortune message! echomsg fortune endfunction diff --git a/vim/autoload/gitcommit.vim b/vim/autoload/gitcommit.vim index 7aba1c5b..72d2b9ff 100644 --- a/vim/autoload/gitcommit.vim +++ b/vim/autoload/gitcommit.vim @@ -3,6 +3,7 @@ function! gitcommit#CursorColumn() abort " If we can find a line after the first that isn't a comment, we're " composing the message + " for num in range(1, line('$')) if num == 1 continue diff --git a/vim/autoload/has.vim b/vim/autoload/has.vim index 0100e913..162e4929 100644 --- a/vim/autoload/has.vim +++ b/vim/autoload/has.vim @@ -1,16 +1,34 @@ +" Wrapper to backport the nicer has() syntax for simultaneous version and +" patch level checking that was introduced in v7.4.236 and fixed in v7.4.237. +" +" * <https://github.com/vim/vim/releases/tag/v7.4.236> +" * <https://github.com/vim/vim/releases/tag/v7.4.237> +" function! has#(feature) abort + + " If we're new enough, we can just run the native has() if has('patch-7.4.237') return has(a:feature) endif + + " Otherwise, we have to break down the pattern and do manual version and + " patch level checks; if it doesn't match the patch syntax, just return what + " the native has() does + " let feature = a:feature - let pattern = 'patch-\(\d\+\)\.\(\d\+\)\.\(\d\+\)' + let pattern = '^patch-\(\d\+\)\.\(\d\+\)\.\(\d\+\)$' let matchlist = matchlist(feature, pattern) if empty(matchlist) return has(a:feature) endif let [major, minor, patch] = matchlist[1:3] + + " The v:version variable looks like e.g. 801 for v8.1 let l:version = major * 100 + minor + + " Compare the version numbers, and then the patch level if they're the same return v:version != l:version \ ? v:version > l:version \ : has('patch-'.patch) + endfunction diff --git a/vim/autoload/html.vim b/vim/autoload/html.vim index 7ac4f9d3..2ce5288d 100644 --- a/vim/autoload/html.vim +++ b/vim/autoload/html.vim @@ -1,39 +1,6 @@ -" Make a bare URL into a link to itself -function! html#UrlLink() abort - - " Yank this whole whitespace-separated word - normal! yiW - " Open a link tag - normal! i<a href=""> - " Paste the URL into the quotes - normal! hP - " Move to the end of the link text URL - normal! E - " Close the link tag - normal! a</a> - -endfunction - " Tidy the whole buffer -function! html#TidyBuffer() abort +function! html#Tidy() abort let view = winsaveview() %!tidy -quiet call winrestview(view) endfunction - -" Update a timestamp -function! html#TimestampUpdate() abort - if !&modified - return - endif - let cv = winsaveview() - call cursor(1,1) - let li = search('\m\C^\s*<em>Last updated: .\+</em>$', 'n') - if li - let date = substitute(system('date -u'), '\n$', '', '') - let line = getline(li) - call setline(li, substitute(line, '\S.*', - \ '<em>Last updated: '.date.'</em>', '')) - endif - call winrestview(cv) -endfunction diff --git a/vim/autoload/html/timestamp.vim b/vim/autoload/html/timestamp.vim new file mode 100644 index 00000000..ad79ad23 --- /dev/null +++ b/vim/autoload/html/timestamp.vim @@ -0,0 +1,74 @@ +" Keys and date formats for return value of s:Timestamp() +let s:formats = { + \ 'human': '%a, %d %b %Y %T %Z', + \ 'machine': '%Y-%m-%dT%H:%M:%S.000Z', + \} + +" Get UTC timestamp string dictionary with layout in s:formats +function! s:Timestamp(time) abort + + " Force UTC, recording previous timezone, if any + if exists('$TZ') + let tz = $TZ + endif + let $TZ = 'UTC' + + " Get current time + let time = localtime() + + " Fill out timestamp dictionary + let timestamp = {} + for key in keys(s:formats) + let timestamp[key] = strftime(s:formats[key], time) + endfor + + " Clear UTC and restore previous timezone, if any + unlet $TZ + if exists('tz') + let $TZ = tz + endif + + " Return filled-out timestamp dictionary + return timestamp + +endfunction + +" Define timestamp prefix string +let s:prefix = 'Last updated: ' + +" Define pattern to match date timestamps; no ZALGO, please +let s:pattern = '\m\C' + \.s:prefix + \.'<time datetime="[^"]\+">' + \.'[^<]\+' + \.'</time>' + +" Update a timestamp +function! html#timestamp#Update() abort + + " Do nothing if the buffer hasn't been modified + if !&modified + return + endif + + " Find the first occurrence of the timestamp pattern, bail if none + let lnum = search(s:pattern, 'nw') + if !lnum + return + endif + + " Get timestamp dictionary + let timestamp = s:Timestamp(localtime()) + + " Fill out updated timestamp string with dictionary values + let update = s:prefix + \.'<time datetime="'.timestamp['machine'].'">' + \.timestamp['human'] + \.'</time>' + + " Apply the updated timestamp + let line = getline(lnum) + let line = substitute(line, s:pattern, update, '') + call setline(lnum, line) + +endfunction diff --git a/vim/autoload/html/url.vim b/vim/autoload/html/url.vim new file mode 100644 index 00000000..99b4409e --- /dev/null +++ b/vim/autoload/html/url.vim @@ -0,0 +1,15 @@ +" Make a bare URL into a link to itself +function! html#url#Anchor() abort + + " Yank this whole whitespace-separated word + normal! yiW + " Open a link tag + normal! i<a href=""> + " Paste the URL into the quotes + normal! hP + " Move to the end of the link text URL + normal! E + " Close the link tag + normal! a</a> + +endfunction diff --git a/vim/autoload/indent.vim b/vim/autoload/indent.vim index 77403658..d597653f 100644 --- a/vim/autoload/indent.vim +++ b/vim/autoload/indent.vim @@ -1,5 +1,5 @@ " Set the current buffer to space indent -function! indent#spaces(...) abort +function! indent#Spaces(...) abort setlocal expandtab " If an argument was provided, use that for the number of spaces; otherwise, @@ -14,19 +14,19 @@ function! indent#spaces(...) abort \ ? -1 \ : &l:shiftwidth - call indent#undo() + call indent#Undo() endfunction " Set the current buffer to tab indent -function! indent#tabs() abort +function! indent#Tabs() abort setlocal noexpandtab setlocal shiftwidth< softtabstop< - call indent#undo() + call indent#Undo() endfunction " Add commands to b:undo_indent to clean up buffer-local indentation changes " on a change of filetype -function! indent#undo() abort +function! indent#Undo() abort " Check and set a flag so that we only do this once per buffer if exists('b:undo_indent_type_set') diff --git a/vim/autoload/mail.vim b/vim/autoload/mail.vim index 3fbba860..cd585af4 100644 --- a/vim/autoload/mail.vim +++ b/vim/autoload/mail.vim @@ -1,35 +1,3 @@ -" Add a header to a mail message -function! mail#AddHeaderField(name, body) abort - let num = 0 - while num < line('$') && getline(num + 1) !=# '' - let num += 1 - endwhile - call append(num, a:name.': '.a:body) -endfunction - -" Add a set of headers to a mail message -function! mail#AddHeaderFields(fields) abort - for name in sort(keys(a:fields)) - call mail#AddHeaderField(name, a:fields[name]) - endfor -endfunction - -" Flag a message as important -function! mail#FlagImportant() abort - call mail#AddHeaderFields({ - \ 'Importance': 'High', - \ 'X-Priority': 1 - \ }) -endfunction - -" Flag a message as unimportant -function! mail#FlagUnimportant() abort - call mail#AddHeaderFields({ - \ 'Importance': 'Low', - \ 'X-Priority': 5 - \ }) -endfunction - " Move through quoted paragraphs like normal-mode `{` and `}` function! mail#NewBlank(count, up, visual) abort @@ -102,3 +70,43 @@ function! mail#StrictQuote(start, end) abort endfor endfunction + +" Attempt to move to a good spot to start writing +function! mail#SuggestStart() abort + + " Move to top of buffer + call setpos('.', [0, 1, 1, 0]) + + " Move to body text + call search('\m^$', 'c') | + + + " Start by trying to move to the first quoted line; this may fail if there's + " no quote, which is fine + call search('\m^>', 'c') + + " Delete quoted blank lines or quoted greetings until we get to something + " with substance. Yes, I like Perl, how could you tell? + while getline('.') =~? '^> *' + \ . '\%(' + \ . '\%(' + \ . 'g[''\u2019]\=day' + \ . '\|\%(good \)\=\%(morning\|afternoon\|evening\)' + \ . '\|h[eu]\%(ll\|rr\)o\+' + \ . '\|hey\+' + \ . '\|hi\+' + \ . '\|sup' + \ . '\|what[''\u2019]\=s up' + \ . '\|yo' + \ . '\)' + \ . '[[:punct:] ]*' + \ . '\%(' + \ . '\a\+' + \ . '[[:punct:] ]*' + \ . '\)\=' + \ . '\)\=$' + delete + endwhile + + " Now move to the first quoted or unquoted blank line + call search('\m^>\= *$', 'c') +endfunction diff --git a/vim/autoload/mail/header.vim b/vim/autoload/mail/header.vim new file mode 100644 index 00000000..7a360a2c --- /dev/null +++ b/vim/autoload/mail/header.vim @@ -0,0 +1,59 @@ +" Parse a mail header into a list of dictionaries with 'name' and 'body' keys; +" preserve case of the name and whitespace in body, including leading space +" +function! mail#header#Read() abort + let fields = [] + for lnum in range(1, line('$')) + let line = getline(lnum) + let matchlist = matchlist( + \ line, + \ '^\([a-zA-Z0-9-]\+\):\s*\(\_.*\)', + \) + if !empty(matchlist) + let field = { + \ 'name': matchlist[1], + \ 'body': matchlist[2], + \} + call add(fields, field) + elseif line =~ '^\s' && exists('field') + let field['body'] .= "\n" . line + elseif line ==# '' + break + else + throw 'Parse error' + endif + endfor + let header = { + \'fields': fields, + \} + return header +endfunction + +" Flatten a header data structure into a string +function! mail#header#String(header) abort + let fields = copy(a:header['fields']) + return join( + \ map( + \ copy(a:header['fields']), + \ 'v:val[''name''] . '': '' . v:val[''body''] . "\n"'), + \ '', + \) +endfunction + +" Replace existing mail header with the provided one +function! mail#header#Write(header) abort + let start = 1 + for lnum in range(1, line('$')) + if getline(lnum) ==# '' + break + endif + let end = lnum + endfor + let curpos = getpos('.') + if exists('end') + let range = join([start, end], ',') + execute join(['silent', range, 'delete']) + endif + silent 0 put =mail#header#String(a:header) + call setpos('.', curpos) +endfunction diff --git a/vim/autoload/mail/header/field.vim b/vim/autoload/mail/header/field.vim new file mode 100644 index 00000000..e27d13c0 --- /dev/null +++ b/vim/autoload/mail/header/field.vim @@ -0,0 +1,45 @@ +" Add a field to a header, regardless of whether a field by the same name is +" already present +function! mail#header#field#Add(header, name, body) abort + let new = { + \ 'name': a:name, + \ 'body': a:body, + \} + call add(a:header['fields'], new) +endfunction + +" Set a field in a header, replacing the first one with the same name (if +" any), and and removing any others +" +function! mail#header#field#Set(header, name, body) abort + let fields = [] + let new = { + \ 'name': a:name, + \ 'body': a:body, + \} + for field in a:header['fields'] + if field['name'] ==? a:name + if exists('new') + let field = new | unlet new + else + continue + endif + endif + call add(fields, field) + endfor + if exists('new') + call add(fields, new) | unlet new + endif + let a:header['fields'] = fields +endfunction + +" Remove a header field +function! mail#header#field#Clear(header, name) abort + let fields = [] + for field in a:header['fields'] + if field['name'] !=? a:name + call add(fields, field) + endif + endfor + let a:header['fields'] = fields +endfunction diff --git a/vim/autoload/mail/importance.vim b/vim/autoload/mail/importance.vim new file mode 100644 index 00000000..78fb53ac --- /dev/null +++ b/vim/autoload/mail/importance.vim @@ -0,0 +1,26 @@ +" Define header fields for high, low, and normal priorities +let s:fields = { + \ 'high': { + \ 'Importance': 'High', + \ 'X-Priority': '1', + \}, + \ 'low': { + \ 'Importance': 'Low', + \ 'X-Priority': '5', + \}, + \ 'normal': {}, + \} + +" Set the priority headers; pass in "high", "low", or "normal" +function! mail#importance#Set(level) abort + let header = mail#header#Read() + let fields = s:fields[a:level] + for name in ['Importance', 'X-Priority'] + if has_key(fields, name) + call mail#header#field#Set(header, name, fields[name]) + else + call mail#header#field#Clear(header, name) + endif + endfor + call mail#header#Write(header) +endfunction diff --git a/vim/autoload/map.vim b/vim/autoload/map.vim deleted file mode 100644 index d4bd90a2..00000000 --- a/vim/autoload/map.vim +++ /dev/null @@ -1,12 +0,0 @@ -" We declare a wrapper around map() to allow us always to call it with -" a Funcref as the second function parameter, which isn't directly supported -" by map() until Vim v7.4.1989. If the running version is older than that, -" apply string() to the Funcref to use the older calling convention. -" -" <https://github.com/vim/vim/releases/tag/v7.4.1989> -" -function! map#(list, Func) abort - return has('patch-7.4.1989') - \ ? map(a:list, a:Func) - \ : map(a:list, string(a:Func).'(0, v:val)') -endfunction diff --git a/vim/autoload/markdown.vim b/vim/autoload/markdown.vim index 8bac8045..0f03961c 100644 --- a/vim/autoload/markdown.vim +++ b/vim/autoload/markdown.vim @@ -1,3 +1,26 @@ +" Let's try this heading-based fold method out (Tim Pope) +function! markdown#Fold() abort + let line = getline(v:lnum) + + " Regular headers + let depth = match(line, '\(^#\+\)\@<=\( .*$\)\@=') + if depth > 0 + return '>' . depth + endif + + " Setext style headings + if line =~# '^.\+$' + let nextline = getline(v:lnum + 1) + if nextline =~# '^=\+$' + return '>1' + elseif nextline =~# '^-\+$' + return '>2' + endif + endif + + return '=' +endfunction + " Add an underline under a heading function! markdown#Heading(char) abort @@ -9,10 +32,12 @@ function! markdown#Heading(char) abort " Build underline string by repeating character by the string length of the " heading text + " let underline = repeat(a:char, strlen(heading)) " If the line after this one looks like it's already an underline, replace " it; otherwise, create a new underline + " if getline(pos[1] + 1) =~# '^[-=]\{2,}$' call setline(pos[1] + 1, underline) else diff --git a/vim/autoload/option.vim b/vim/autoload/option.vim new file mode 100644 index 00000000..5ff44ced --- /dev/null +++ b/vim/autoload/option.vim @@ -0,0 +1,11 @@ +" Split a comma-separated option value into parts, accounting for escaped +" commas and leading whitespace as Vim itself does internally +" +function! option#Split(expr, ...) abort + if a:0 > 1 + echoerr 'Too many arguments' + endif + let keepempty = a:0 ? a:1 : 0 + let parts = split(a:expr, '\\\@<!,[, ]*', keepempty) + return map(parts, 'substitute(v:val, ''\\,'', '','', ''g'')') +endfunction diff --git a/vim/autoload/option/item.vim b/vim/autoload/option/item.vim new file mode 100644 index 00000000..ee92101f --- /dev/null +++ b/vim/autoload/option/item.vim @@ -0,0 +1,14 @@ +" Escape a single item for a comma-separated list, optionally escaping any +" filename wildcards +" +function! option#item#Escape(item, ...) abort + if a:0 > 1 + echoerr 'Too many arguments' + endif + let item = a:item + let wild = a:0 ? a:1 : 0 + if wild + let item = escape(item, '\*?[{`''$~') + endif + return escape(item, ',') +endfunction diff --git a/vim/autoload/path.vim b/vim/autoload/path.vim index 83102138..e230cab2 100644 --- a/vim/autoload/path.vim +++ b/vim/autoload/path.vim @@ -1,3 +1,5 @@ +" Create all the directories needed for a path, with optional flag for +" owner-only permissions function! path#Create(name, ...) abort if a:0 > 2 echoerr 'Too many arguments' diff --git a/vim/autoload/plugin.vim b/vim/autoload/plugin.vim index 68e3d54b..629a4367 100644 --- a/vim/autoload/plugin.vim +++ b/vim/autoload/plugin.vim @@ -1,3 +1,6 @@ +" Check whether plugins are enabled and a specific named plugin (excluding +" extension .vim) is available somewhere within 'runtimepath' +" function! plugin#Ready(name) abort return &loadplugins \ && globpath(&runtimepath, 'plugin/'.a:name.'.vim') !=# '' diff --git a/vim/autoload/put_date.vim b/vim/autoload/put_date.vim index c9f52c12..b0b0b548 100644 --- a/vim/autoload/put_date.vim +++ b/vim/autoload/put_date.vim @@ -1,5 +1,10 @@ +" RFC2822 format string for strftime() let s:rfc_2822 = '%a, %d %b %Y %T %z' +" Write a date to the buffer, UTC or local, in the specified format, +" defaulting to RFC2822; formats are provided without the leading % signs +" before each letter, like PHP date() +" function! put_date#(line, utc, format) abort let line = a:line let utc = a:utc diff --git a/vim/autoload/reload.vim b/vim/autoload/reload.vim index 558f24d6..322c44d2 100644 --- a/vim/autoload/reload.vim +++ b/vim/autoload/reload.vim @@ -1,9 +1,13 @@ +" Re-run filetype detection, if it's run before function! reload#FileType() abort if exists('g:did_load_filetypes') doautocmd filetypedetect BufRead endif endfunction +" Re-read .vimrc file, reloading filetypes afterwards to avoid masking +" filetype plugin settings +" function! reload#Vimrc() abort noautocmd source $MYVIMRC call reload#FileType() diff --git a/vim/autoload/split.vim b/vim/autoload/split.vim deleted file mode 100644 index 44065094..00000000 --- a/vim/autoload/split.vim +++ /dev/null @@ -1,12 +0,0 @@ -if v:version < 702 || v:version == 702 && !has('patch-61') - runtime autoload/unescape.vim -endif - -function! split#Option(expr, ...) abort - if a:0 > 2 - echoerr 'Too many arguments' - endif - let keepempty = a:0 ? a:1 : 0 - let parts = split(a:expr, '\\\@<!,[, ]*', keepempty) - return map#(parts, function('unescape#Item')) -endfunction diff --git a/vim/autoload/unescape.vim b/vim/autoload/unescape.vim deleted file mode 100644 index a809827d..00000000 --- a/vim/autoload/unescape.vim +++ /dev/null @@ -1,3 +0,0 @@ -function! unescape#Item(key, val) abort - return substitute(a:val, '\\,', ',', 'g') -endfunction diff --git a/vim/ftplugin/awk.vim b/vim/ftplugin/awk.vim index dbefa5cd..76637486 100644 --- a/vim/ftplugin/awk.vim +++ b/vim/ftplugin/awk.vim @@ -11,8 +11,7 @@ let b:undo_ftplugin = 'setlocal comments< formatoptions<' " Fold based on indent level, but start with all folds open setlocal foldmethod=indent -setlocal foldlevel=99 -let b:undo_ftplugin .= '|setlocal foldmethod< foldlevel<' +let b:undo_ftplugin .= '|setlocal foldmethod<' " Specify ERE flavor for regex_escape.vim let b:regex_escape_flavor = 'ere' diff --git a/vim/ftplugin/markdown.vim b/vim/ftplugin/markdown.vim index da2228b3..3f41cf8d 100644 --- a/vim/ftplugin/markdown.vim +++ b/vim/ftplugin/markdown.vim @@ -18,32 +18,9 @@ let &l:formatlistpat = '^\s*\d\+\.\s\+\|^[-*+]\s\+\|^\[^\ze[^\]]\+\]:' let b:undo_ftplugin .= '|setlocal formatoptions< formatlistpat<' " Let's try this heading-based fold method out (Tim Pope) -function! MarkdownFold() - let line = getline(v:lnum) - - " Regular headers - let depth = match(line, '\(^#\+\)\@<=\( .*$\)\@=') - if depth > 0 - return '>' . depth - endif - - " Setext style headings - if line =~# '^.\+$' - let nextline = getline(v:lnum + 1) - if nextline =~# '^=\+$' - return '>1' - elseif nextline =~# '^-\+$' - return '>2' - endif - endif - - return '=' -endfunction -let b:undo_ftplugin .= '|delfunction MarkdownFold' -setlocal foldexpr=MarkdownFold() +setlocal foldexpr=markdown#Fold() setlocal foldmethod=expr -setlocal foldlevel=99 -let b:undo_ftplugin .= '|setlocal foldexpr< foldmethod< foldlevel<' +let b:undo_ftplugin .= '|setlocal foldexpr< foldmethod<' " Spellcheck documents we're actually editing (not just viewing) if &modifiable && !&readonly @@ -80,7 +57,7 @@ let b:undo_ftplugin .= '|nunmap <buffer> <LocalLeader>Q' \ . '|xunmap <buffer> <LocalLeader>Q' " Autoformat headings -command! -buffer -nargs=1 MarkdownHeading +command -buffer -nargs=1 MarkdownHeading \ call markdown#Heading(<f-args>) nnoremap <buffer> <LocalLeader>= \ :<C-U>MarkdownHeading =<CR> diff --git a/vim/ftplugin/sed.vim b/vim/ftplugin/sed.vim index 35e705a4..04d86d7d 100644 --- a/vim/ftplugin/sed.vim +++ b/vim/ftplugin/sed.vim @@ -11,5 +11,4 @@ let b:undo_ftplugin = 'setlocal comments< formatoptions<' " Fold based on indent level, but start with all folds open setlocal foldmethod=indent -setlocal foldlevel=99 -let b:undo_ftplugin .= '|setlocal foldmethod< foldlevel<' +let b:undo_ftplugin .= '|setlocal foldmethod<' @@ -91,7 +91,7 @@ scriptencoding utf-8 " test it with some values of your own, if you want to understand why. Vim, " I love you, but you are really weird sometimes. " -" We do all this with an autoloaded function split#Option(). +" We do all this with an autoloaded function option#Split(). " " If an environment variable MYVIM exists, and it isn’t blank, apply its value " as the first value of 'runtimepath', after escaping it appropriately. @@ -99,9 +99,9 @@ scriptencoding utf-8 " list becomes MYVIM. " if exists('$MYVIM') && $MYVIM !=# '' - execute 'set runtimepath^='.escape#Arg(escape#Item(escape#Wild($MYVIM))) + execute 'set runtimepath^='.argument#Escape(option#item#Escape($MYVIM, 1)) elseif &runtimepath !=# '' - let $MYVIM = split#Option(&runtimepath)[0] + let $MYVIM = option#Split(&runtimepath)[0] endif " We need a command to reliably establish a full path, whether or not the @@ -133,7 +133,7 @@ command! -bang -bar -complete=dir -nargs=1 CreatePath " " <https://github.com/vim/vim/releases/tag/v8.1.0716> " -execute 'set viminfo+='.escape#Arg('n'.$MYVIM.'/viminfo') +execute 'set viminfo+='.argument#Escape('n'.$MYVIM.'/viminfo') CreatePath $MYVIM " Speaking of recorded data in viminfo files, the default Vim limit of a mere @@ -179,7 +179,7 @@ set history=10000 " 'backupfullname', 'swapfilefullname' would have been clearer. " set backup -execute 'set backupdir^='.escape#Arg(escape#Item( +execute 'set backupdir^='.argument#Escape(option#item#Escape( \ $MYVIM.'/backup'.(has#('patch-8.1.251') ? '//' : ''), \)) CreatePath! $MYVIM/backup @@ -218,7 +218,8 @@ endif " option has supported that hint for much longer than 'backupdir' has. We " apply path#Create() to attempt to create the path, if needed. " -execute 'set directory^='.escape#Arg(escape#Item($MYVIM.'/swap//')) +execute 'set directory^=' + \.argument#Escape(option#item#Escape($MYVIM.'/swap//')) CreatePath! $MYVIM/swap " Keep tracked undo history for files permanently, in a dedicated cache @@ -236,7 +237,8 @@ CreatePath! $MYVIM/swap " if has#('persistent_undo') set undofile - execute 'set undodir^='.escape#Arg(escape#Item($MYVIM.'/undo//')) + execute 'set undodir^=' + \.argument#Escape(option#item#Escape($MYVIM.'/undo//')) CreatePath! $MYVIM/undo endif @@ -354,8 +356,10 @@ set spellcapcheck=[.?!]\\%(\ \ \\\|[\\n\\r\\t]\\) set dictionary^=/usr/share/dict/words let s:ref = $MYVIM.'/ref' try - execute 'set dictionary^='.escape#Arg(escape#Item(s:ref.'/dictionary.txt')) - execute 'set thesaurus^='.escape#Arg(escape#Item(s:ref.'/thesaurus.txt')) + execute 'set dictionary^=' + \.argument#Escape(option#item#Escape(s:ref.'/dictionary.txt')) + execute 'set thesaurus^=' + \.argument#Escape(option#item#Escape(s:ref.'/thesaurus.txt')) catch /^Vim\%((\a\+)\)\=:E474:/ endtry @@ -462,6 +466,11 @@ set confirm " set noesckeys +" Always start with 'foldlevel' set high enough to have all folds of any +" practical depth open by default. +" +set foldlevel=256 + " Automatic text wrapping options using flags in the 'formatoptions' option " begin here. I rely on the filetype plugins to set the ‘t’ and ‘c’ flags for " this option to configure whether text or comments should be wrapped, as |