vimwiki style links without vimwiki? - vim

One of my favorite features of vimwiki is the way it handles links. Unfortunately, I use something else (vimoutliner) for my main note-taking, and it doesn't have anywhere near the linking capability that vimwiki does.
Is there a plugin that adds the linking functionality of vimwiki (or at least something similar) when editing any arbitrary file, and not just a *.wiki file?

I think you could just write one for your simple use case, something like:
function! LinkForward()
let g:fromFile=expand('%:p')
let fn= substitute(getline('.'),'^.*\[\[\([^\]]*\)\].*$',"\\1",'g')
execute "e ".fn
endfunction
nnoremap <cr> :call LinkForward()<cr>
function! LinkBackward()
execute "e ".g:fromFile
endf
nnoremap <bs> :call LinkBackward()<cr>
source this will do some similiar action, e.g pressing Enter on [[path/to/file/foo]]foo will go to that foo file, and within that file pressing <Backspace> will get you back.
note the codes above are just example, it won't work perfect. You could make it work better, e.g:
add a list/(stack) to store the file jump history.
define that this kind of action works only for certain filetype
check if the line under cursor doesn't match [[...]] then do normal <Enter>
etc.. if you like you could take a look vimwiki's codes and "borrow" some snippets for your personal usage.
hope it helps you.. & good luck.

Related

Vim key binding to call a function with input from user

There might be a simple solution for this. I couldn't find any solution to it (I might be searching with wrong context)
Here is my requirement.
I wrote the below key mapping in vimrc. It should print the line "Hello user_name." n times, where n and user_name are the user input once the key is pressed.
autocmd FileType ruby nnoremap <expr> <C-h> :call FuncPrnt(<-syntax to pass input from user->)
function! FuncPrnt(count, uname)
let c=a:count
let i=0
while i<c
call append(line("."), "Hello ".a:uname.".")
let i+=1
endwhile
endfunction
On Pressing the key user enters 3 and 'Ironman'. The output would be like
Hello Ironman.
Hello Ironman.
Hello Ironman.
Thanks in advance
A simpler approach is to just use a non-<expr> mapping to prepare the Ex command without executing it:
nnoremap <C-h> :call FuncPrnt(,"")<left><left><left><left>
If you associate that with a filetype (such as "ruby" in your case), make sure you create a local mapping using <buffer>. Otherwise the mapping will be global and will work on every buffer and not only those with Ruby source files.
If you use an autocmd in your vimrc, make sure you wrap it in an augroup to prevent getting duplicated commands if you reload your vimrc.
Alternatively, you can add filetype mappings for Ruby in a file ~/.vim/ftplugin/ruby.vim which gets automatically loaded by Vim whenever a file of type Ruby is loaded. (That way you don't need to use explicit autocmds, Vim will take care of those details on your behalf.)
Got the answer. Just in case someone else is searching for similar solution
autocmd FileType ruby nnoremap <expr> <C-h> input("", ":call FuncPrnt(,\"\")<left><left><left><left>")
will print the command and waits for the user to edit. Once the user edits and enters it is executed
Thinking outside of the box a little bit (questioning the premise), perhaps the best in this case is to define a user-defined command instead of a mapping.
You can define one here with:
command! -buffer -bar -count=1 -nargs=1 FuncPrnt
\ call FuncPrnt(<count>, <q-args>)
That way you can use it with:
:3FuncPrnt Ironman
If you omit the count, 1 will be used. (You can pick a different default as the argument to -count=N.)
You can use Tab completion for FuncPrnt, so perhaps :3Fu<Tab> or even :3F<Tab> might be enough to complete the command.
This might end up being quicker or more convenient to type than <C-H>3<right><right>, since it doesn't involve moving your hand to the arrow keys.

Get Vim to open new blocks like Sublime Text 2

In sublime text 2 when you:
BLOCK { <Return>
It generates (where the pipe is the cursor):
BLOCK {
|
}
How can I get Vim to behave this way?
I have autoindent on, and smartindent off because with smartindent it does this on return:
BLOCK {
|}
To be more clear, I'm specifically looking for 2 returns, moving up a line, and tabbing in (2 soft tabs to be specific). I already have it auto-matching characters like {, (, [ etc.
A simple mapping will work for most purposes:
imap {<cr> {<cr>}<c-o>O
Depending on plugins, some users may need inoremap instead of imap.
Before it was with TextMate, now it's with ST2.
You have basically two paths before you.
The "dumb" path
One could come up with dozens of variations of this method: you simply create a mapping that executes the series of key presses needed to reach your goal:
inoremap {<CR> {<cr><cr>}<C-o>k<tab>
I called it "dumb" but it doesn't mean that you would be dumb to use it: it's low-tech, has no dependencies, is easy to customize and it can be mapped to anything you like.
The "smart" method
This method involves the use of a plugin. I use DelimitMate but there are many others, choose your poison.
I did some quick digging for vim addons (which are often the solution to this sort of problem). I don't think I've found what you want: there are a few addons that come close, but nothing that inserts the extra newline before the closing brace.
You could do something like
imap { {<return><return>}<up><tab>
but this will get awkward if you are working in a language that uses braces in other situations. You could instead react to the newline:
inoremap <return> <return><return>}<up><tab>
Of course this will trigger on EVERY entered newline, rather than just those following an opening brace. To get it to check that the brace is the last character of the current line, you can:
Have a function (in ~/.vimrc or somewhere in ~/.vim/plugin) that looks like
function! CloseBraceIfOpened()
if getline(".")[-1:] == '{'
" insert a space and then delete it to preserve autoindent level
exec "normal o "
normal x
normal o}
normal k
else
normal o
endif
endfunction
also do
inoremap <buffer> <enter> <esc>:call CloseBraceIfOpened()<enter>A
Note that this imap is buffer-specific, so that mapping will only apply to the buffer you are in when you run it. To have it apply to all buffers, remove <buffer>.
If you are really ambitious/particular, you can do tests in the function to see if the code in the current line really opens a block.
To get the indentation working the way you want it, turn on the 'autoindent' and 'smartindent' settings.
: set autoindent smartindent
To have it on by default, add
set autoindent smartindent
to ~/.vimrc.
I use the following map:
inoremap {{ {<CR><CR>}<ESC>kcc
so instead of using {<CR> I use this mapping. Besides that I also use the plugin mentioned by romainl, DelimitMate for other mappings with braces.
I had the same problem and delimitMate solves it. After installing it you can enable it with:
let g:delimitMate_expand_cr = 1
There are lot's of hacks that delivers the SublimeText experience. Because I got frustrated I've created a project that includes all those features in a single vim distribution (without the need of installing/compiling external plugins/tools).
You can check it out from here: https://github.com/fatih/subvim

Vim inline remap to check first character

I am trying to do a comment remap in Vim with an inline if to check if it's already commented or not. This is what I have already and of course it's not working ha ha:
imap <c-c> <Esc>^:if getline(".")[col(".")-1] == '/' i<Delete><Delete> else i// endif
What I want to do is check the first character if it's a / or not. If it's a / then delete the first two characters on that line, if it's not a / then add two // in front of the line.
What I had originally was this:
imap <c-c> <Esc>^i//
And that worked perfectly, but what I want is to be able to comment/uncomment at a whim.
I completely agree with #Peter Rincker's answer warning against doing this in insert mode, and pointing you to fully-featured plugins.
However, I couldn't resist writing this function to do precisely what you ask for. I find it easier to deal with this kind of mapping with functions. As an added bonus, it returns you to insert mode in the same position on the line as you started (which has been shifted by inserting or deleting the characters).
function! ToggleComment()
let pos=getpos(".")
let win=winsaveview()
if getline(".") =~ '\s*\/\/'
normal! ^2x
let pos[2]-=1
else
normal! ^i//
let pos[2]+=3
endif
call winrestview(win)
call setpos(".",pos)
startinsert
endfunction
inoremap <c-c> <Esc>:call ToggleComment()<CR>
Notice the modifications to pos to ensure the cursor is returned to the correct column. The command startinsert is useful in this type of function to return to insert mode. It is always safer to use noremap for mappings, unless there is a very good reason not to.
This seems to work well, but it is not very Vim-like, and you might find other plugins more flexible in the long run.
There are many commenting plugins for vim:
commentary.vim
tComment
EnhCommentify
NERD Commenter
and many more at www.vim.org
I would highly suggest you take a look at some these plugins first before you decide to roll your own. It will save you great effort.
As a side note you typically would want to comment/uncomment in normal mode not insert mode. This is not only the vim way, but will also provide a nicer undo history.
If you are dead set on creating your own mappings I suggest you create a function to do all the hard work and have your mapping call that function via :call. If you think you can get by with simple logic that doesn't need a function then you can use an expression mapping (see :h map-<expr>). You may want organize into a plugin as it could be large. If that is the case look at :h write-plugin to give you a feel
for writing plugins the proper way.
Example of a simple expression mapping for toggling comments:
nnoremap <expr> <leader>c getline(".") =~ '\m^\s*\/\/' ? '^"_2x' : 'I//<esc>`['
there's also this vimtip! http://vim.wikia.com/wiki/Comment/UnComment_visually_selected_text
i use the bottom one with the
...
noremap <silent> ,c :<C-B>sil <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:noh<CR>
noremap <silent> ,u :<C-B>sil <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:noh<CR>
,c comments out a region
,u uncomments a region

How to remove the highlight from a search inside an `operator-pending` mapping in vim ?

I'm trying to define an operator-pending mapping (using onoremap) to match
all text between the previous { and the next }. This is for use in a css
file, so I can easily delete, change or copy rules with ir.
I've used the syntax used in "Learn vimscript the hard way", which is a combination
of execute and normal. What I have been unable to do is clear the highlight
search after my last search. After using the mapping, all the } in the file
get highlighted.
So far, here's what I got :
au Filetype css noremap <buffer> ir :<C-U>execute "normal! ?{\rjV/}\rk"<CR>
I know I need to call :nohlsearch but I don't know how to fit that in the
mapping. The mapping results in a text being visually selected, so when
:nohlsearch gets called it's applied to the selection, which does not work.
I need a way to clear the '<,'> markers but I couldn't find how to write
<C-U> in the normal command to get it executed right. Another way might be
to leave Visual mode, execute the :nohlsearch and then reselect the last
selected text with gv, but even for that I can't find how to write <Esc> in
my normal statement.
In this particular case, it's probably easier to use something like:
au Filetype css noremap <buffer> ir :<C-U>call search('{', 'Wb')\|call search('}', 'Ws')\|normal! v''<CR>
Note that this does what your example does, not the right thing. Nested braces for example won't be handled correctly.
Calling the search function without the 's' flag won't set any marks or change the search register so you don't need to reset anything.
Note that this isn't really the best way of accomplishing what you want - you probably want the iB or i{ (they're the same) text object instead.

Vim: Resolve ambiguity of key mappings in a specific buffer to avoid timeout

I use plugin "Buffet", and there's local-to-buffer mapping "d" to delete buffer under cursor.
I also use plugun Surround, and there's global mapping "ds" that means "delete surround".
So, when i press "d" in the Buffet's window, Vim waits for a second before execute mapping "d". I know about &timeoutlen, but i don't want to change it. So that I want to resolve ambiguity of key mappings for "d" in the Buffet's window to avoid timeout to delete a buffer.
To resolve the problem, I want to unmap in Buffet window all the mappings that start with "d", but except Buffet's own mappings. How can i do that?
P.S. I have read about maparg() and mapcheck(), but they seem not to be what i need, unfortunately.
It seems like i found the solution myself:
au BufEnter buflisttempbuffer* nunmap ds
au BufLeave buflisttempbuffer* nmap ds <Plug>Dsurround
I hoped that there's more universal approach (to remove really all mappings starting from "d"), but at this moment i failed to find it.
Even if i found out how to get all these mappings, unfortunately i can't do unmap <buffer> ds, because ds is a global mapping. I'm sure that i should be able to disable global mapping for some buffer, though. Vim is great but not perfect.
Well, it works for me now.
Now that the question has been "rephrased", this solution is no longer relevant, but I'll post it anyway since I spent a few minutes on it.
Here's a function that grabs the output of map <letter> and extracts the individual maps. Then it unmaps them all.
function! Unmap(leader)
redir => maps
sil exe "map " . a:leader
redir END
let maps_list = split(strtrans(maps),'\^#')
if len(maps_list) > 1
for this in maps_list
let mapn = matchstr(this,"^\\w\\s*\\zsd\\w*\\>")
exe "unmap " . mapn
endfor
endif
endfunction
Example usage: call Unmap("d"). This will remove all mappings that begin with d, leaving only Vim's defaults.
Disclaimer: this has not been rigorously tested. In particular I don't know how portable the \^# character is, but that's how it looks on my (Win32) machine.
The easiest way to do it is:
:e /WHERE/YOU/HAD/INSTALLED/buffet.vim
:%s:map <buffer> <silent> d:"&:
:wq
$ vim # Restart Vim to take effect...
Generally you can't unmap based on a pattern.
If you want to use another key (e.g. with <leader>, just change this line in the plugin:
map <buffer> <silent> d :call <sid>deletebuffer(0)<cr>
This question is rather old, but if you're still interested, you might want to give Bufstop a try.
This issue is handled by the plugin, you can press the d key to delete a buffer, and you won't get any timeout if you installed other plugins which have global mappings.
A cheap trick that worked for me was to make the timeoutlen so short it becomes pretty much instantaneous. As long as you don't use multiple key mappings yourself, that will cover all plugins in one shot.
We don't want that setting to stay however, so we remove it every time we leave the buffer.
Add this so that it runs inside your custom buffer:
augroup no_map_chords
autocmd!
autocmd BufEnter <buffer> let g:bak_timeoutlen = &timeoutlen | set timeoutlen=1
autocmd BufLeave <buffer> let &timeoutlen = g:bak_timeoutlen | unlet g:bak_timeoutlen
augroup END
A similar technique could be used for a specific file type, or other such "global" settings.
Buffet is a very young plugin, I don't think it's used by as many people as Command-T or NERDTree so you may not receive lots of answers. Its author has been very responsive on the numerous threads he created there about it you should contact him directly or create an issue on Buffet's github.

Resources