To go to the next line with a (lowercase) mark, I can do:
]'
And to go to the previous mark I can do:
['
However, let's say I have 10 marks in the file and I am at the last one. Is there a way to cycle through to the first mark from the last one (like how search does "Search hit bottom. Continuing from Top".)?
Is there a way to cycle through to the first mark from the last one
Supply big count. E.g. 26['. To wrap it into a script:
" our mappings with counter support
nnoremap <silent>[' :<C-U>call NextMark(-v:count1)<CR>
nnoremap <silent>]' :<C-U>call NextMark(v:count1)<CR>
function! NextMark(incr) abort
" save current line number
let l:lnum = line('.')
" go to the next lowercase mark
execute printf("normal! %d%s'", abs(a:incr), a:incr > 0 ? ']' : '[')
if l:lnum == line('.')
" no move, assume it's the last (or the first) one
if stridx(&shortmess, 's') < 0
" print message as with n/N
echohl WarningMsg
echo printf("search hit %s, continuing at %s",
\ a:incr > 0 ? 'BOTTOM' : 'TOP', a:incr > 0 ? 'TOP' : 'BOTTOM')
echohl NONE
endif
" jump to the first (or last) lowercase mark
execute printf("normal! 26%s'", a:incr > 0 ? '[' : ']')
endif
endfunction
Related
Is there a way to run the script as if it were typed into the interpeter? The benefits are that I don't need echos everywhere, the work done is saved as a file, and I can use vim to do the editing. Example:
example.ijs
x =. 1
x + 3
terminal
x =. 1
x + 3
4
If not, I'll write a vimscript that can do the above then share them here. The advantage of a vimscript solution is that I could have commands to run the entire file, the current line, the current selection, everything up to and including the current line, or whatever else is useful.
Related but not a duplicate: How to call J (ijconsole) with a script automatically
It sounds like you are asking for this to be solved for the jconsole interface. I don't have answer for that, but would point out that that functionality is available for both the JHS and jQt interfaces. If you don't mind switching to a different interface then that would be a quick and easy solution.
The easiest way to interactively run a script is to use labs command:
load'labs/lab'
lab'myscript.ijt'
1 of 2 in myscript.ijt
x =. 1
NB. press Ctrl+'.' to advance.
x + 3
4
NB. Run the whole script.
lab 1 _
x =. 1
x + 3
4
More info about labs here
If you want to non-interactively run a script as if typed in the console, you can just feed the script to j (in linux):
j < myscript.ijs
4
Alternatively, to
see the lines on the screen just as though they had been typed from the keyboard
from a script, you can use 0!:1:
0!:1 < 'myscript.ijs'
x =. 1
x + 3
4
Use loadd rather than load to run the script and display the lines and results.
loadd 'example.ijs'
x =. 1
x + 3
4
This is the vimscript solution I mentioned. It's as far as I can tell language agnostic as well.
" Global variable dictates whether new terminals are opened
" horizontally ('h') or vertically ('v').
let g:terminalsplit = 'h'
" Add execution strings for each language you use.
augroup terminalcommands
autocmd!
autocmd Filetype j let g:cmdstr = 'jconsole.cmd'
autocmd Filetype python let g:cmdstr = 'python'
augroup END
" Close all terminals
nnoremap <silent> <leader>p :call CloseTerminal()<cr>
" Run file
nnoremap <leader>h :call Run(g:cmdstr, 'script')<cr>
" Run as if file were entered line-by-line into the interpreter
" Mappings for: Line, selection, file up to line, entire file
nnoremap <leader>j yy:call Run(g:cmdstr, 'interpreter')<cr>
vnoremap <leader>j ygv<esc>:call Run(g:cmdstr, 'interpreter')<cr>
nnoremap <leader>k Vggy<c-O>:call Run(g:cmdstr, 'interpreter')<cr>
nnoremap <leader>l mzggVGy'z:call Run(g:cmdstr, 'interpreter')<cr>
function! Run(cmdstr, mode)
let filepath = expand('%:p') " Copy filepath before switch to terminal
call CloseTerminal()
call OpenTerminal()
echo g:clear . " & " . a:cmdstr
call feedkeys(g:clear . " & " . a:cmdstr) " Begin run command
call RunCode(filepath, a:mode)
call feedkeys("\<c-w>p") " Switch back to file window
endfunction
function! CloseTerminal()
if has('nvim')
let terminals = split(execute('filter/term:/ls'), '\n')
else
let terminals = split(execute('filter/!/ls'), '\n')
endif
for i in range(len(terminals))
silent! exe "bd! " . split(terminals[i], ' ')[0]
endfor
endfunction
function! OpenTerminal()
if g:terminalsplit == 'h'
terminal
elseif g:terminalsplit == 'v'
vertical terminal
else
echo 'g:terminalsplit=' . &g:terminalsplit . '. Must be "h" or "v".'
endif
endfunction
function! RunCode(filepath, mode)
if a:mode == 'script'
call feedkeys(" " . a:filepath . "\<cr>")
elseif a:mode == 'interpreter'
call feedkeys("\<cr>")
call feedkeys("\<c-w>\"\"")
else
echo 'a:mode=' . a:mode . '. Must be "script" or "interpreter".'
endif
endfunction
" Use to clear the terminal window before running the script
if has('unix')
let g:clear = 'clear'
else
let g:clear = 'cls'
endif
In Vim, as far as I know, the numbered register saves only the deleted history.
Is it possible to make it save also the yanked one?
(without plugin)
Thanks
Is it possible to make it save also the yanked one?
In fact, the registers 2-9 are rarely useful, as we can save all the important pieces into "letter" registers as needed. And there's "undo" for anything lost anyway.
without plugin
Plugin is just a piece of code. You can do without a plugin, but you can't do without code.
" Shift previously yanked text through the numbered registers 0, 2-9
" note:
" the register 1 is reserved for deletion
" there's no "small yank" register
" can break :h redo-register
" $-blocks are broken
nnoremap <silent>y :set opfunc=YankeeDoodle<CR>g#
nnoremap <silent>Y :k] \| k[ \| call YankeeDoodle("line")<CR>
nnoremap <silent>yy :k] \| k[ \| call YankeeDoodle("line")<CR>
vnoremap <silent>y :<C-U>call YankeeDoodle(visualmode())<CR>
vnoremap <silent>Y :<C-U>call YankeeDoodle("V")<CR>
let s:ydict = #{char: "v", line: "V", block: "\<C-V>"}
function! YankeeDoodle(type)
if has_key(s:ydict, a:type)
let l:type = s:ydict[a:type]
let l:mark = ['[', ']']
else
let l:type = a:type
let l:mark = ['<', '>']
endif
if v:register is '"'
for l:regno in range(8, 2, -1)
call setreg(l:regno + 1, getreg(l:regno), getregtype(l:regno))
endfor
call setreg(2, getreg(0), getregtype(0))
endif
" go to start mark; register override; yank; forced-motion; move to end mark
call execute(printf('normal! g`%s"%sy%sg`%s', l:mark[0], v:register, l:type, l:mark[1]), '')
endfunction
UPD. As #PeterRincker suggests we can use TextYankPost instead. The following code must be more robust, but still not 100%, as we must keep track of the register 0 manually (TextYankPre will be much better here, but, alas, we don't have it).
" note:
" the register 1 is reserved for deletion
" there's no "small yank" register
" can break :h redo-register
" still misses any manual register 0 change
augroup YankShift | au!
let s:regzero = [getreg(0), getregtype(0)]
autocmd TextYankPost * call <SID>yankshift(v:event)
augroup end
function! s:yankshift(event)
if a:event.operator ==# 'y' && (empty(a:event.regname) || a:event.regname == '"')
for l:regno in range(8, 2, -1)
call setreg(l:regno + 1, getreg(l:regno), getregtype(l:regno))
endfor
call setreg(2, s:regzero[0], s:regzero[1])
let s:regzero = [a:event.regcontents, a:event.regtype]
elseif a:event.regname == '0'
let s:regzero = [a:event.regcontents, a:event.regtype]
endif
endfunction
Is it possible to do this with a Vim command?
[] = Cursor Normal Mode
[ = Cursor Insert Mode
Before
Text []
After
Text
[
Before
Text []
After
[
Text
I've changed the [count] behavior of o / O with the following mapping. I think this does what you want:
" o/O Start insert mode with [count] blank lines.
" The default behavior repeats the insertion [count]
" times, which is not so useful.
function! s:NewLineInsertExpr( isUndoCount, command )
if ! v:count
return a:command
endif
let l:reverse = { 'o': 'O', 'O' : 'o' }
" First insert a temporary '$' marker at the next line (which is necessary
" to keep the indent from the current line), then insert <count> empty lines
" in between. Finally, go back to the previously inserted temporary '$' and
" enter insert mode by substituting this character.
" Note: <C-\><C-n> prevents a move back into insert mode when triggered via
" |i_CTRL-O|.
return (a:isUndoCount && v:count ? "\<C-\>\<C-n>" : '') .
\ a:command . "$\<Esc>m`" .
\ v:count . l:reverse[a:command] . "\<Esc>" .
\ 'g``"_s'
endfunction
nnoremap <silent> <expr> o <SID>NewLineInsertExpr(1, 'o')
nnoremap <silent> <expr> O <SID>NewLineInsertExpr(1, 'O')
do these two mapping help?
nnoremap <leader>O O<ESC>O
nnoremap <leader>o o<cr>
the first by pressing <leader>O will add two empty lines above current line, and bring you to INSERT mode. The 2nd one by pressing <leader>o will add two lines after your current.
I'd like to search for text in all files currently open in vim and display all results in a single place. There are two problems, I guess:
I can't pass the list of open files to :grep/:vim, especially the names of files that aren't on the disk;
The result of :grep -C 1 text doesn't look good in the quickfix window.
Here is a nice example of multiple file search in Sublime Text 2:
Any ideas?
Or
:bufdo vimgrepadd threading % | copen
The quickfix window may not look good for you but it's a hell of a lot more functional than ST2's "results panel" if only because you can keep it open and visible while jumping to locations and interact with it if it's not there.
ack and Ack.vim handle this problem beautifully. You can also use :help :vimgrep. For example:
:bufdo AckAdd -n threading
will create a nice quickfix window that lets you hop to the cursor position.
Like the answer of Waz, I have written custom commands for that, published in my GrepCommands plugin. It allows to search over buffers (:BufGrep), visible windows (:WinGrep), tabs, and arguments.
(But like all the other answers, it doesn't handle unnamed buffers yet.)
I really liked romainl's answer, but there were a few sticky edges that made it awkward to use in practice.
The following in your .vimrc file introduces a user command Gall (Grep all) that addresses the issues that I found irksome.
funct! GallFunction(re)
cexpr []
execute 'silent! noautocmd bufdo vimgrepadd /' . a:re . '/j %'
cw
endfunct
command! -nargs=1 Gall call GallFunction(<q-args>)
This will allow case-sensitive searches like this:
:Gall Error\C
and case-insensitive:
:Gall error
and with spaces:
:Gall fn run
Pros
It will only open the Quickfix window, nothing else.
It will clear the Quickfix window first before vimgrepadd-ing results from each buffer.
The Quickfix window will contain the locations of all matches throughout the open buffers, not just the last visited.
Use :Gall repeatedly without any special housekeeping between calls.
Doesn't wait on errors and displays results immediately.
Doesn't allow any autocmd to run, speeding up the overall operation.
Ambivalent features
Doesn't preemptively jump to any occurrence in the list. :cn gets second result or CTRL-w b <enter> to get to the first result directly.
Cons
If there's only one result, you'll have to navigate to it manually with CTRL-w b <enter>.
To navigate to a result in any buffer quickly:
:[count]cn
or
:[count]cp
E.g. :6cn to skip 6 results down the list, and navigate to the correct buffer and line in the "main" window.
Obviously, window navigation is essential:
Ctrl-w w "next window (you'll need this at a bare minimum)
Ctrl-w t Ctrl-w o "go to the top window then close everything else
Ctrl-w c "close the current window, i.e. usually the Quickfix window
:ccl "close Quickfix window
If you close the Quickfix window, then need the results again, just use:
:cw
or
:copen
to get it back.
I made this function a long time ago, and I'm guessing it's probably not the cleanest of solutions, but it has been useful for me:
" Looks for a pattern in the open buffers.
" If list == 'c' then put results in the quickfix list.
" If list == 'l' then put results in the location list.
function! GrepBuffers(pattern, list)
let str = ''
if (a:list == 'l')
let str = 'l'
endif
let str = str . 'vimgrep /' . a:pattern . '/'
for i in range(1, bufnr('$'))
let str = str . ' ' . fnameescape(bufname(i))
endfor
execute str
execute a:list . 'w'
endfunction
" :GrepBuffers('pattern') puts results into the quickfix list
command! -nargs=1 GrepBuffers call GrepBuffers(<args>, 'c')
" :GrepBuffersL('pattern') puts results into the location list
command! -nargs=1 GrepBuffersL call GrepBuffers(<args>, 'l')
An improved (on steroids) version of Waz's answer, with better buffer searching and special case handling, can be found below (The moderators wouldn't let me update Waz's answer anymore :D).
A more fleshed out version with binds for arrow keys to navigate the QuickFix list and F3 to close the QuickFix window can be found here: https://pastebin.com/5AfbY8sm
(When i feel like figuring out how to make a plugin i'll update this answer. I wanted to expedite sharing it for now)
" Looks for a pattern in the buffers.
" Usage :GrepBuffers [pattern] [matchCase] [matchWholeWord] [prefix]
" If pattern is not specified then usage instructions will get printed.
" If matchCase = '1' then exclude matches that do not have the same case. If matchCase = '0' then ignore case.
" If prefix == 'c' then put results in the QuickFix list. If prefix == 'l' then put results in the location list for the current window.
function! s:GrepBuffers(...)
if a:0 > 4
throw "Too many arguments"
endif
if a:0 >= 1
let l:pattern = a:1
else
echo 'Usage :GrepBuffers [pattern] [matchCase] [matchWholeWord] [prefix]'
return
endif
let l:matchCase = 0
if a:0 >= 2
if a:2 !~ '^\d\+$' || a:2 > 1 || a:2 < 0
throw "ArgumentException: matchCase value '" . a:2 . "' is not in the bounds [0,1]."
endif
let l:matchCase = a:2
endif
let l:matchWholeWord = 0
if a:0 >= 3
if a:3 !~ '^\d\+$' || a:3 > 1 || a:3 < 0
throw "ArgumentException: matchWholeWord value '" . a:3 . "' is not in the bounds [0,1]."
endif
let l:matchWholeWord = a:3
endif
let l:prefix = 'c'
if a:0 >= 4
if a:4 != 'c' && a:4 != 'l'
throw "ArgumentException: prefix value '" . a:4 . "' is not 'c' or 'l'."
endif
let l:prefix = a:4
endif
let ignorecase = &ignorecase
let &ignorecase = l:matchCase == 0
try
if l:prefix == 'c'
let l:vimgrep = 'vimgrep'
elseif l:prefix == 'l'
let l:vimgrep = 'lvimgrep'
endif
if l:matchWholeWord
let l:pattern = '\<' . l:pattern . '\>'
endif
let str = 'silent ' . l:vimgrep . ' /' . l:pattern . '/'
for buf in getbufinfo()
if buflisted(buf.bufnr) " Skips unlisted buffers because they are not used for normal editing
if !bufexists(buf.bufnr)
throw 'Buffer does not exist: "' . buf.bufnr . '"'
elseif empty(bufname(buf.bufnr)) && getbufvar(buf.bufnr, '&buftype') != 'quickfix'
if len(getbufline(buf.bufnr, '2')) != 0 || strlen(getbufline(buf.bufnr, '1')[0]) != 0
echohl warningmsg | echomsg 'Skipping unnamed buffer: [' . buf.bufnr . ']' | echohl normal
endif
else
let str = str . ' ' . fnameescape(bufname(buf.bufnr))
endif
endif
endfor
try
execute str
catch /^Vim\%((\a\+)\)\=:E\%(683\|480\):/ "E683: File name missing or invalid pattern --- E480: No match:
" How do you want to handle this exception?
echoerr v:exception
return
endtry
execute l:prefix . 'window'
"catch /.*/
finally
let &ignorecase = ignorecase
endtry
endfunction
I often use visual block then inserting on multiple lines when for example commenting out a lot of code. This is great for inserting text in the same position on multiple lines but I can't figure out how to delete this text later using visual block mode, Backspace, Del and d all don't work. I am using MacVim.
You're looking for x:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
Then visual-block-select, x:
root:/root:/bin/bash
daeaemon:/usr/sbin:/bin/sh
bin/bin:/bin/sh
sys/dev:/bin/sh
I use this frequently, for exactly the same reason -- commenting and uncommenting large blocks of code.
This isn't directly answering the question (sarnold has already done so), but I would suggest there are more efficient ways of (un-)commenting code blocks. I have a CommentToggle function which either comments or uncomments the current line, depending on whether or not it begins with the "comchar".
function! CommentToggle(comchar)
let firstchar = matchstr(getline("."),"[^ ]")
if firstchar == a:comchar
sil exe 'normal ^xx'
else
sil exe 'normal ^i' . a:comchar . ' '
endif
endfunction
So, for perl files you can map:
nnoremap <silent> <leader>c :call CommentToggle('#')<CR>
and pressing 3 \ c (un-)comments three lines from the cursor position.
You can also write a visual-mode mapping:
vnoremap <silent> <leader>c :call CommentToggle('#')<CR>
allowing you to select a visual region and press \c to (un-)comment them all.
This particular function only works for one-character comments ("#", "%", etc.), but it is straightforward to extend it to longer strings (e.g. "//"), and even more complex replacements, such as HTML comments.
Hope this helps.
Prince Goulash's answer doesn't work in lines with leading tabs.
I changed it, adding the tab character to the pattern, although lines lose their indent after comment and uncomment.
function! CommentToggle( comchar )
let firstchar = matchstr( getline( "." ), "[^ \t]" )
if firstchar == a:comchar
sil exe 'normal ^2x'
else
sil exe 'normal ^i' . a:comchar . ' '
endif
endfunction
I like more adding the comment char to first position in line, this modification to Prince Goulash's function does the trick:
function! CommentToggle( comchar )
let firstchar = matchstr( getline( "." ), "[^ \t]" )
if firstchar == a:comchar
sil exe 'normal ^2x'
else
sil exe 'normal gI' . a:comchar . ' '
endif
endfunction