toggle to disable the timeout, something smart that extends it while using cgn to replace occurences
https://github.com/wincent/wincent/blob/2aa44544fe9e8fc466fea42391e66fff3583054c/roles/dotfiles/files/.vim/plugin/autocmds.vim#L14
https://github.com/wincent/wincent/blob/2aa44544fe9e8fc466fea42391e66fff3583054c/roles/dotfiles/files/.vim/autoload/autocmds.vim
https://github.com/wincent/wincent/blob/2aa44544fe9e8fc466fea42391e66fff3583054c/roles/dotfiles/files/.vim/plugin/statusline.vim
https://github.com/wincent/wincent/blob/2aa44544fe9e8fc466fea42391e66fff3583054c/roles/dotfiles/files/.vim/autoload/statusline.vim
This started showing up when I configured iTerm to send +Esc for the left-option key, in order to get Meta keys working in Neovim. This also meant changing the way I defined option-key bindings from hard-coding the special character macOS was sending, e.g.
nnoremap ∆ :cnext<CR>
to a more universal form
nnoremap <M-j> :cnext<CR>
That was enough to get Meta keys working in Neovim, but for Vim I had to configure it to recognize the escape sequences being sent by iTerm:
set <F21>=^[j
map <F21> <M-j>
map! <F21> <M-j>
That put me at the mercy of ttimeoutlen
, which I had set to 100 milliseconds. When I hit esc j
quickly from Insert mode, Vim recognized this as ^[j
(were I in Normal mode it would’ve mapped to <M-j>
).
The solution was to reduce `ttimeoutlen` considerably (to 5 milliseconds).
I’ve noticed that with ‘foldmethod’ set to syntax, folds don’t automatically recompute as I edit the file. This is probably fine, as I’d rather not waste lots of cycles recomputing folds constantly, but I would like to manually trigger recomputing of folds from time to time. How can I do this?
zx
I recently discovered this construct in shell-scripting:
{ # Prevent execution if this script was only partially downloaded
foo() {
:
}
}
I’ve since found that in addition to providing I/O redirection en-masse, an { expression-group; }
can be a nice way of organizing code.
Given a snippet like the example above, with ‘foldmethod’ set to syntax
, I want to be able to fold like so,
{ # <- fold here
foo() { # <- and here
:
}
}
The root of the problem lies in the runtime file that defines shell syntax, $VIMRUNTIME/syntax/sh.vim
, but before tackling the main problem, set a couple variables that script looks for:
let g:is_bash=1
let g:sh_fold_enabled=1
The first, g:is_bash
is self-explanatory. sh.vim
checks for a few different shells, and some of the syntax definitions vary accordingly.
The second, g:sh_fold_enabled
, should be a number from 0 to 7. It’s treated as a bitmask that determines which syntax groups are defined with folding.
┌────── if/do/for │ ┌─── heredocs │ │ ┌ functions 2² 2¹ 2⁰ (4)(2)(1)
To enable folds for functions and if/do/for constructs, you’d set it to 5 (i.e. 1+4). I’ve found that enabling folds for if/do/for leads to more folds than I want, and feels cluttered. And while I like the idea of folding heredocs, in practice I prefer to just fold function definitions. Thus g:sh_fold_enabled=1
.
To ensure that shell files always use syntax-based folding regardless what’s in your vimrc, put the following in $HOME/.vim/ftplugin/sh.vim
setlocal foldmethod=syntax
Now to get Vim to recognize the { expression-group; }
and functions nested within it. Actually, Vim already recognizes expression-groups, and has a shExpr
syntax item defined:
syn region shExpr transparent matchgroup=shExprRegion start="{" end="}" contains=@shExprList2 nextgroup=shSpecialNxt
There’s a lot going on there, but the main things to focus on are contains=@shExprList2
and the lack of any mention of folding. The contains
argument specifies which syntax groups can nest within this one. It takes a comma-separated list of groups, but to avoid a lot of repetition, you can “cluster” a list of syntax groups under a single name and refer to it with a @
prefix. So, contains=@shExprList2
refers to a cluster defined earlier in the file,
syn cluster shExprList2 contains=@shExprList1,@shCaseList,shTest
This in turn references the previously-defined shExprList1
syn cluster shExprList1 contains=shCharClass,shNumber,shOperator,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq
None of the shell function syntax groups are included in the above clusters, so Vim won’t recognize a shell function that’s nested within an expression-group. Fortunately it’s easy to add to an existing cluster:
syn cluster shExprList2 add=shFunctionOne,shFunctionTwo,shFunctionThree,shFunctionFour
This is a safe modification to make because shExpr
is the only syntax item that references shExprList2
.
Vim now recognizes the nested functions, and with g:sh_fold_enabled
set to 1 (or any odd number), you can fold them:
{
+--- 3 lines: foo() {
}
To fold the command-group itself, redefine shExpr
, passing it the fold
argument:
syn region shExpr transparent matchgroup=shExprRegion start="{" end="}" contains=@shExprList2 nextgroup=shSpecialNxt fold
Like magic:
+-- 5 lines: {
Now that you know what modifications you need to make, where do you put them?
You don’t want to modify the runtime file directly, as you’d lose your changes when updating. You could copy it to $HOME/.vim/syntax/sh.vim
and modify that, but that would shadow the orignal file, since Vim only loads the first syntax file found (assuming it sets b:current_syntax
), and your user runtime directory ($HOME/.vim/
) is earlier in the runtimepath
than $VIMRUNTIME
is), so you’d lose the benefit of any future updates it receives.
Instead, use put just the relevant modifications in $HOME/.vim/after/syntax/sh.vim
" Recognize functions nested within an { expression-group; }
syn cluster shExprList2 add=shFunctionOne,shFunctionTwo,shFunctionThree,shFunctionFour
" Allow { expression-list; } to fold
syn region shExpr transparent matchgroup=shExprRegion start="{" end="}" contains=@shExprList2 nextgroup=shSpecialNxt fold
This will get sourced after $VIMRUNTIME/syntax/sh.vim
, applying just the necessary modifications to the syntax groups the original file defined.
Ideally, these modifications could be merged into the original. I’ve emailed the following patches to the original file’s maintainer, and hope to hear back.
From fb65475d2449838fc3c84dc7c80512794bc99e71 Mon Sep 17 00:00:00 2001
From: ivanbrennan <[email protected]>
Date: Mon, 3 Jul 2017 13:21:17 -0400
Subject: [PATCH 1/2] runtime sh syntax: { expression-list; } folding
Add support for folding compound expressions, for example:
{ # <- fold here
echo 'Inside a compound group'
echo 'doing more stuff...'
}
---
runtime/doc/syntax.txt | 1 +
runtime/syntax/sh.vim | 11 ++++++++++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index 6606524ab..022c3d117 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -2908,6 +2908,7 @@ The syntax/sh.vim file provides several levels of syntax-based folding: >
let g:sh_fold_enabled= 1 (enable function folding)
let g:sh_fold_enabled= 2 (enable heredoc folding)
let g:sh_fold_enabled= 4 (enable if/do/for folding)
+ let g:sh_fold_enabled= 8 (enable { expression-list; } folding)
>
then various syntax items (ie. HereDocuments and function bodies) become
syntax-foldable (see |:syn-fold|). You also may add these together
diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim
index f97299cde..7c6e12f5c 100644
--- a/runtime/syntax/sh.vim
+++ b/runtime/syntax/sh.vim
@@ -81,6 +81,9 @@ endif
if !exists("s:sh_fold_ifdofor")
let s:sh_fold_ifdofor = and(g:sh_fold_enabled,4)
endif
+if !exists("s:sh_fold_expressions")
+ let s:sh_fold_expressions = and(g:sh_fold_enabled,8)
+endif
if g:sh_fold_enabled && &fdm == "manual"
" Given that the user provided g:sh_fold_enabled
" AND g:sh_fold_enabled is manual (usual default)
@@ -114,6 +117,11 @@ if s:sh_fold_ifdofor
else
com! -nargs=* ShFoldIfDoFor <args>
endif
+if s:sh_fold_expressions
+ com! -nargs=* ShFoldExpr <args> fold
+else
+ com! -nargs=* ShFoldExpr <args>
+endif
" sh syntax is case sensitive {{{1
syn case match
@@ -213,7 +221,7 @@ syn match shPattern "\<\S\+\())\)\@=" contained contains=shExSingleQuote,shSin
" Subshells: {{{1
" ==========
-syn region shExpr transparent matchgroup=shExprRegion start="{" end="}" contains=@shExprList2 nextgroup=shSpecialNxt
+ShFoldExpr syn region shExpr transparent matchgroup=shExprRegion start="{" end="}" contains=@shExprList2 nextgroup=shSpecialNxt
syn region shSubSh transparent matchgroup=shSubShRegion start="[^(]\zs(" end=")" contains=@shSubShList nextgroup=shSpecialNxt
" Tests: {{{1
@@ -711,6 +719,7 @@ endif
delc ShFoldFunctions
delc ShFoldHereDoc
delc ShFoldIfDoFor
+delc ShFoldExpr
" Set Current Syntax: {{{1
" ===================
--
2.11.1
From 1631f02d2dd84c3cf337e4d6f9a743710b315547 Mon Sep 17 00:00:00 2001
From: ivanbrennan <[email protected]>
Date: Mon, 3 Jul 2017 13:30:39 -0400
Subject: [PATCH 2/2] sh.vim syntax: let shExpr contain shFunction
Allow functions nested within a { expression-list; } to be recognized as
such. For example,
{ # Prevent execution if this script was only partially downloaded
foo() {
...
}
bar() {
...
}
}
---
runtime/syntax/sh.vim | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim
index 7c6e12f5c..ab1dc2df5 100644
--- a/runtime/syntax/sh.vim
+++ b/runtime/syntax/sh.vim
@@ -143,7 +143,7 @@ syn cluster shDerefList contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial
syn cluster shDerefVarList contains=shDerefOff,shDerefOp,shDerefVarArray,shDerefOpError
syn cluster shEchoList contains=shArithmetic,shCommandSub,shDeref,shDerefSimple,shEscape,shExpr,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote
syn cluster shExprList1 contains=shCharClass,shNumber,shOperator,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq
-syn cluster shExprList2 contains=@shExprList1,@shCaseList,shTest
+syn cluster shExprList2 contains=@shExprList1,@shCaseList,shTest,shFunctionOne,shFunctionTwo,shFunctionThree,shFunctionFour
syn cluster shFunctionList contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shOption,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shOperator,shCtrlSeq
if exists("b:is_kornshell") || exists("b:is_bash")
syn cluster shFunctionList add=shRepeat
--
2.11.1
Ruby allows method names to end in ! and ? characters. If I use command-line mode to search for such a method’s tag (i.e. :tag mymethod!
), Vim finds it correctly, but if I try to use the <C-]>
normal-mode command (jump to the tag for the word under cursor), Vim omits the trailing punctuation char from its tag search. How can I fix this?
I initially tried set iskeyword+=!
, but this causes other problems, such as including a leading ! in tag lookups.
Instead, a better workaround is to put the following in ~/.vim/ftplugin/ruby.vim:
nnoremap <buffer><silent> <C-]> :<C-U>exe v:count1."tag <Plug><cword>"<CR>
nnoremap <buffer><silent> g] :<C-U>tselect <Plug><cword><CR>
nnoremap <buffer><silent> g<C-]> :<C-U>tjump <Plug><cword><CR>
This makes use of a <Plug><cword>
mapping provided by vim-ruby (and included in Vim’s runtime files by default) provides to correctly identify the Ruby cursor identifierr.
See:
https://github.com/vim-ruby/vim-ruby/commit/deb3490a0ecca3d2163863bb49e5a3adff875387
https://github.com/vim-ruby/vim-ruby/commit/2322c368736156413b7fac9f13521ed0e851fe70
https://github.com/vim-ruby/vim-ruby/commit/37ab22005b44605c1c5385d6551644c49199b691
https://github.com/vim-ruby/vim-ruby/blob/master/ftplugin/ruby.vim
https://github.com/vim/vim/blob/master/runtime/ftplugin/ruby.vim