diff options
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 |