" curcmdmode.vim: extends the notion of Vim's mode() function " Author: Hari Krishna " Last Change: 14-Jul-2003 @ 20:00 " Created: 08-Jul-2003 " Requires: Vim-6.0, genutils.vim (1.7) " Version: 1.0.1 " Description: " This plugin provides a small library of functions and mappings and a " framework to extend Vim's command-line mode in a way that is not possible " by using Vim's built-in features alone. " " - Maps "/", ":" and "?" in normal mode to keep track of the current " command mode. It also remaps CCMCCM to point to the current " command mode, to be used in your mappings. " - Defines the following plugin mappings in all the modes to the " corresponding key shown. Use these in your mappings whenever you want to " by pass any mappings for these keys and directly access the " corresponding key from your mapping. E.g., if you need to include ":" in " your mapping and want to bypass the mapping provided by the plugin " itself (say you are only temporarily switching to ":" to come back to " the current mode through CCMCCM), then you should use CCM: " instead of ":" in your mapping, to avoid any side effects (events in " this case). " CCM: => : " CCM/ => / " CCM? => ? " CCMC-U => " CCMC-C => " CCMC-R => " CCMC-O => " CCMEsc => " CCMBS => " CCMCR => " - The built-in function mode() is useful to know that it is a command " mode, but you can't know if you are on a ":" or "/" prompt etc. To know " the exact mode, use the CCMGetCCM() function. Use CCMSetCCM() function " to change the mode that is stored internally by this plugin (not the " actual command mode in Vim). Useful if you are bypassing the plugin by " using one of the CCM[:/?] mappings to change the mode. " - Defines functions CCMAddCmdModeListener(), CCMRemoveCmdModeListener() to " register and deregister call back listener functions that will be called " whenever the plugin is aware of the changes in the command-mode. There " can be multiple such functions registered at anytime and the function " should accept exactly one declared argument, which is passed a value of " g:CCM_START, g:CCM_RESET, g:CCM_ABORT or g:CCM_END. Only the g:CCM_START " events are notified by default. To start/stop listening to the rest of " the events, you need to call the CCMStMon()/CCMEnMon() functions. The " CCMStMon() function needs to be called everytime a new mode is started. " - For additional help and usage ideas, see the following plugins: " chcmdmode.vim: http://www.vim.org/script.php?script_id=144 " cmdalias.vim: http://www.vim.org/script.php?script_id=746 " smartctrl-w.vim: " http://mywebpage.netscape.com/haridara/vim/smartctrl-w.vim " Limitations: " - Implementation of CCMStMon()/CCMEnMon() is incomplete. The events may " not be captured in all the instances and there could be some " interference with maps executed through :normal command without " suffixing it with a '!'. " TODO: " - It should be possible to map the in expr mode to followed by an " examination into the 'expr' history to know if it is going to be the " last and automatically EndExprMode(). I should also be able to " position the cursor correctly, following the way cmdalias does it. " - I need to capture the same way as " - I need to take care of the commands that continue in the : mode such as " multi-statement commands (function, if etc.) and gQ. " if exists("loaded_curcmdmode") finish endif let loaded_curcmdmode = 1 " Make sure genutils.vim is available for initialization. if !exists("loaded_genutils") runtime plugin/genutils.vim endif " Make sure line-continuations won't cause any problem. This will be restored " at the end let s:save_cpo = &cpo set cpo&vim function! s:MyScriptId() map xx xx let s:sid = maparg("xx") unmap xx return substitute(s:sid, "xx$", "", "") endfunction let s:myScriptId = s:MyScriptId() delfunction s:MyScriptId " This is not needed anymore. " Use this to avoid disturbing the command modes. nnoremap CCM: : nnoremap CCM/ / nnoremap CCM? ? noremap CCMC-U noremap! CCMC-U noremap CCMC-C noremap! CCMC-C noremap CCMC-R noremap! CCMC-R noremap CCMC-O noremap! CCMC-O noremap CCMEsc noremap! CCMEsc noremap CCMBS noremap! CCMBS noremap CCMCR noremap! CCMCR let g:CCM_START = 'start' let g:CCM_RESET = 'reset' let g:CCM_ABORT = 'abort' let g:CCM_END = 'end' " Save the started mode by mapping :, / and ?. " These may already be mapped (e.g., I have hlsearch related mappings), so use " MapAppendCascaded to avoid overwriting them. call MapAppendCascaded(":", "=".s:myScriptId. \ "CCMSetCCM(\":\", 1)", "nn") call MapAppendCascaded("/", "=".s:myScriptId. \ "CCMSetCCM(\"/\", 1)", "nn") call MapAppendCascaded("?", "=".s:myScriptId. \ "CCMSetCCM(\"?\", 1)", "nn") " Initialize script variables. let s:curCmdMode = ":" let s:originLineNo = 0 let s:monitoring = 0 aug CCMEnMon | aug END " Listeners that will be called when one of the command modes " starts/resets/ends. let s:cmdModeListeners = '' function! CCMGetCCM() return s:curCmdMode endfunction function! CCMSetCCM(cmdMode) return s:CCMSetCCM(a:cmdMode, 0) endfunction function! CCMStMon() if !s:monitoring let s:monitoring = 1 " NOTE: Observe the use of versions of the BS, CR etc. to avoid " getting recursively mapped. " NOTE: Observe the use of :=Func() instead of :call Func() " to avoid the call showing up (and the hit return prompt thereafter). " " Normally, simplified versions such as this would suffice, but for " applications such as chcmdmode.vim, which needs to reset the current " line, the callback needs to be run from : mode. "cmap CCMC-U=ResetLine()CCMCR cmap CCMC-UCCMBSCCM:CCMC-R=ResetLine()CCMCRCCMBSCCMCCM cmap CCMC-CCCM: call AbortCmdMode()CCMCR cmap CCMEscCCM: call EscCmdMode()CCMCR cmap CCMCRCCM:CCMC-R=EndCmdMode()CCMCRCCMBS cmap = CCMC-R=StartExprCmdMode()CCMCRCCMC-R= " This is a trick that works quite nicely. For the last , the " CCMHdlrBS gets executed in the normal or insert mode. For the rest " of the 's, it is executed in the command mode and is ignored. cmap CCMBSCCMHdlrBS "cmap CCMHdlrBS " This doesn't update selection for some reason. cnoremap CCMHdlrBS a nmap CCMHdlrBS CCM:CCMC-R=ResetLine()CCMCRCCMBS imap CCMHdlrBS CCMC-OCCM:CCMC-R=ResetLine()CCMCRCCMBS au CCMEnMon CursorHold * :call CCMEnMon() endif endfunction function CCMEnMon() if s:monitoring silent! cunmap silent! cunmap silent! cunmap silent! cunmap silent! cunmap = silent! cunmap silent! cunmap CCMHdlrBS silent! nunmap CCMHdlrBS silent! iunmap CCMHdlrBS au! CCMEnMon let s:monitoring = 0 endif endfunction " The Expr mode started from the command mode. function! s:StartExprCmdMode() cmap CCMC-U cmap CCMC-CCCM: call EndExprCmdMode()CCMCR cmap CCMEscCCM: call EndExprCmdMode()CCMCR cmap CCMCRCCMC-R=EndExprCmdMode()CCMCR cmap = CCMC-R= " FIXME: I need to somehow capture the last also. return "" endfunction " Return back to the command mode from expr mode. function! s:EndExprCmdMode() cunmap cunmap cunmap cunmap cunmap = return "" endfunction function! s:CCMSetCCM(cmdMode, start) let s:curCmdMode = a:cmdMode " Decho "CCMSetCCM: curCmdMode = " . CCMGetCCM() . " start = " . a:start . " line = " . CCMOriginLineNo() if a:start call s:StartCmdMode() endif exec "noremap! CCMCCM" s:curCmdMode exec "noremap CCMCCM" s:curCmdMode return "" endfunction function! s:StartCmdMode() call CCMEnMon() " In case we missed it last time. let s:originLineNo = line(".") call s:FireCmdModeListeners(g:CCM_START) return "" endfunction function! s:EndCmdMode() call CCMEnMon() call s:FireCmdModeListeners(g:CCM_END) return "" endfunction function! s:ResetLine() call s:FireCmdModeListeners(g:CCM_RESET) return "" endfunction function! s:AbortCmdMode() call CCMEnMon() call s:FireCmdModeListeners(g:CCM_ABORT) return "" endfunction function! s:EscCmdMode() " If the was entered through a map, then it would result in an " g:CCM_END instead of an g:CCM_ABORT, but there is no way to distinguish " it. if &cpoptions =~ '.*x.*' call s:EndCmdMode() else call CCMEnMon() call s:FireCmdModeListeners(g:CCM_ABORT) endif return "" endfunction function! CCMOriginLineNo() return s:originLineNo endfunction function! CCMAddCmdModeListener(listener) let s:cmdModeListeners = s:cmdModeListeners . "\ncall ".a:listener.'()' endfunction function! CCMRemoveCmdModeListener(listener) let s:cmdModeListeners = substitute(s:cmdModeListeners, \ "\ncall ".a:listener.'()', '', '') endfunction function! s:FireCmdModeListeners(action) let listenerCalls = substitute(s:cmdModeListeners, '', "'".a:action."'", \ 'g') exec listenerCalls endfunction " Restore cpo. let &cpo = s:save_cpo unlet s:save_cpo " vim6:fdm=marker sw=2