The company I work for uses special headers for source files, which contain the date of last modification.
I wrote a vim script that updates this date automatically on each buffer write.
I am using the search/substitue feature to do the trick.
Now the problem is that the replace does move the cursor at the beginning of the file, which is very annoying because at each buffer write, the user has to jump back manually to the previous editing position.
Does anyone know a way to prevent vim from jumping when updating the date, or at least to make it jump back to the previous position?
Interactively, you can use <C-O> or the change mark `` to move back to the original position, as shown at vim replace all without cursor moving.
In a Vimscript, especially when this runs on every buffer write, I would wrap the code:
let l:save_view = winsaveview()
%substitute///
call winrestview(l:save_view)
The former move commands might still affect the window view (i.e. which exact lines and columns are shown in the viewport), whereas this solution restores everything as it was.
Bonus stuff
Also, be aware that the {pattern} used in the :substitute is added to the search history. To avoid that, append
call histdel('search', -1)
(The search pattern itself isn't affected when you have this in a :function.)
or use the :keeppatterns command introduced in Vim 8:
keeppatterns %substitute///
Related plugins
I've implemented similar functionality in my AutoAdapt plugin. You'll see all these tricks used there, too.
I think using the function substitute() will keep your changelist and jumplist. In neovim I am using a function to change the date of file modification this way:
M.changeheader = function()
-- We only can run this function if the file is modifiable
local bufnr = vim.api.nvim_get_current_buf()
if not vim.api.nvim_buf_get_option(bufnr, "modifiable") then
require("notify")("Current file not modifiable!")
return
end
-- if not vim.api.nvim_buf_get_option(bufnr, "modified") then
-- require("notify")("Current file has not changed!")
-- return
-- end
if vim.fn.line("$") >= 7 then
os.setlocale("en_US.UTF-8") -- show Sun instead of dom (portuguese)
local time = os.date("%a, %d %b %Y %H:%M:%S")
local l = 1
while l <= 7 do
vim.fn.setline(l, vim.fn.substitute(vim.fn.getline(l), 'last (change|modified): \\zs.*', time , 'gc'))
l = l + 1
end
require("notify")("Changed file header!")
end
end
Related
For my LaTeX editing in vim, I recorded a handful of useful macros and wrapped them into functions/command. I have one, that change the type of a Latex environment e.g., when I have:
\begin{itemize}
\item First
\item Second
\end{itemize}
I just enter :ChangeEnv enumerate with the cursor somewhere in the environment to change from itemize to enumerate.
The code in my ftplugin/tex.vim looks like this:
function! ChangeEnv(newenv)
let l:save = #e
let #e = a:newenv
let l:line = getline('.')
" Fake change to restore undo
normal ix
normal x
if match(l:line, '\\begin{') != -1
normal mz_%f{lci}e'zf{l.`z:delma z
else
normal my?\\begin{^M_mz%f{lci}^Re^['zf{l.`y:delma yz
endif
let #e = l:save
endfunction
command -nargs=1 ChangeEnv :silent call ChangeEnv(<f-args>)
The first part (after if match(...) intended if the cursor is on the \begin{...} part of the environment works perfectly so far, I can make the change and undo it, cursor stays where it should.
The second part, intended for inside the environment, also works great, but when the change is undone, the cursor jumps to the first charachter of the \begin line.
The normal ix and normal x part is intended to ensure the cursor position is restored after the und (I have this from here: Restor Cursor Position)
My question is, why doesn't it work for the second macro? Is there some error?
To spare you deconstructing the macro, this are the steps:
my - set y mark at current position
?\\begin{^M - Search backward for the beginning of the environment and jump there
_mz - go to first character of that line and set the z mark
% - Jump to the matching \end{... of the environment (This is part of the matchit vim plugin, delivered with vim but not active per default).
f{l- Jump forward to { and one character right
ci} - Change inner {...}
^Re^[ - Insert the content of the e register, where the new environment name is stored and return to normal mode
'z' - Jump to the line beginning of the z mark (The \begin{...)
f{l. - Forward to {, one step right and repeat the last change
`y - Jump to the y mark, the initial position
:delma yz - Delete the y and z mark
The undo behavior is not a deal breaker, nevertheless, I'd at least like to know why it behaves that way.
Thank you in advance.
Usually, when you make multiple changes, each one is undone separately. But inside a function, everything is lumped together as one single change.
Your intention of doing this dummy change at the beginning (when the cursor hasn't yet been moved) is that the cursor returns to that point after undo.
However, I think as Vim treats the whole set of changes done within ChangeEnv() as one big modification, it returns to the beginning of the range of changed lines (mark '[), as it does when undoing built-in commands. Which change command was executed "first" doesn't matter, all that matters is the range of lines that got changed. As your second branches searches backwards to the \begin and makes a change there, that's where the cursor will be after undo. I don't think there's a way around this; undo cannot be influenced by scripts, as this could potentially introduce inconsistencies.
What you could do is setting a mark after doing the changes (at the end of your function: :normal! m'), so that you can quickly jump back there (via `` or <C-o>).
I'm currently writing a vim script that runs my code through the gofmt linter. Once I have the output from the command, I run the following.
let view = winsaveview()
1,$d
undojoin | call setline(1, gofmt_output)
call winrestview(view)
This works great, however, when the user presses u to undo, the file scrolls back to the top. Is there a way around this? Below is a gif of the behavior.
Here's a hack I just found that seems to be working somewhat.
All I do is to make a bogus, noop change on the current line
before I replace the buffer contents.
In my case I simply read the new content from a file so it looks like this:
let winview = winsaveview()
:s/$//
:%!cat /tmp/.vimfmtnew
call winrestview(winview)
The s/$// bit is the noop change on the current line, and :%!cat /tmp/.vimfmtnew replaces the whole buffer with my tmpfile.
My theory about this is that vim collapses all changes from my function into a single undo action and puts the cursor position to the first action's first changed line. This now happens to be an action where my cursor was when I called the function so this puts the cursor onto the correct line.
It's not perfect though:
if you have the same buffer open in a split view,
the unfocused split views will still reset on undo/redo.
I think the correct solution is this:
diff the old and new content
and only replace the changed lines.
I think that would retain the positions in the split views too
but it's a bit more effort to implement this nicely.
(Doing the replace from the first changed line doesn't always work for me
because I use goimports as my formatter and that can change lines near the beginning too so that would almost be equivalent to a full replace.)
Is it possible to reuse the range of ex commands in VIM?
As an example, I can write (copy) lines 4 to 10 from my current file to a new file using the following command:
:4,10w foo/bar.txt
But what I really want to do is move the lines to a new file. I can do this like so:
:4,10w foo/bar.txt
:4,10d
But it's a bit annoying to have to type 4,10 both times.
So I have two questions:
Generally, Is there a way to reference the previously used range in ex commands?
Specifically, if there is not a way to do (1), is there any easier way to cut and paste a number of lines from one file into a new one.
I usually use cat to do this:
:4,10!cat > foo/bar.txt
This works because you're piping the lines through cat and replacing them with the resulting output, which is nothing. And of course you can append to the end of an existing file by doing >> instead of >.
I am unaware of an answer to (1), but to answer (2), there are a number of different ways of doing it that don't require reselecting the lines by hand. In visual mode this will work:
4GV10G
:w foo/bar.txt
gvd
because gv reselects the previous selection, which is almost what you want, without using an ex range.
But you could just turn the problem on its head, and try:
:4,10d
:sp foo/bar.txt
pZZ
to cut, then paste into a new file, then close it.
Other than using the Vim history (:Cursor Up, q:) and removing the previous command so that just the range is kept, there's no way to re-use the last range, no magic variable.
If I used this move lines combination more often, I would write a custom command for it:
command! -bang -range -nargs=1 -complete=file MoveWrite <line1>,<line2>write<bang> <args> | <line1>,<line2>delete _
You need to specify the range only once and save typing.
You can write something like this for other combinations, too. The main challenge is specifying all the command attributes (bang, range, completion), and, later, remembering the custom command name.
Generally, what I do is delete the lines from the one file, switch to the other file, and paste.
Also, I generally use marks. Instead of typing the actual numbers, I hit mb to mark the beginning line, then go to the end line and hit d'b to delete back to the line marked as b. But you can use mb to mark a begin line, and me to mark an end line, then run an ex command:
:'b,'e w somefile.txt<Enter>
Of course you can use any letters from a through z for your marks; I usually use b and e but you can use what you like.
How I would move the lines:
m'b
<navigate to end line>
d'b
:n somefile.txt<Enter>
p
Ctrl+^
Ctrl+^ switches from the current open file to the previous open file. (You could also just open a pane and switch panes, if you prefer. Panes don't work in plain vi but do work in vim.)
The above assumes that you have set the autowrite option on. With autowrite, the :n command and Ctrl+^ both just write the current file and then switch files, instead of complaining that the file has been changed without you saving it. You can also do the above and just explicitly write the file before using :n or Ctrl+^.
By the way, I use Ctrl+^ so much that I mapped it onto K. Easier to type, but I got in that habit long ago when I used to have to sometimes use a dumb terminal that couldn't type Ctrl+^.
By the way, when you delete lines, they go into the "unnamed buffer". In vim, the unnamed buffer is preserved when you switch files. In original vi, the unnamed buffer is cleared. So the above won't work with old vi. You can make it work by deleting into a named buffer, then pasting from the named buffer; that works in any version of vi.
m'b
<navigate to end line>
"ad'b
:n somefile.txt<Enter>
"ap
Ctrl+^
The above deletes into the buffer named a, then pastes from a in the other file. This does work in vim of course; it's just that you don't need it.
Here's a command-line mapping that achieves this. I've bound it to CTRL-G CTRL-U, since it performs a similar action as CTRL-U. (But you can change that, of course!)
" c_CTRL-G_CTRL-U Remove all characters between the cursor position and
" the closest previous |:range| given to a command. When
" directly after a range, remove it.
" Useful to repeat a recalled command line with the same
" range, but a different command.
let s:singleRangeExpr = '\%(\d\+\|[.$%]\|''\S\|\\[/?&]\|/[^/]*/\|?[^?]*?\)\%([+-]\d*\)\?'
let s:rangeExpr = s:singleRangeExpr.'\%([,;]'.s:singleRangeExpr.'\)\?'
let s:upToRangeExpr = '^\%(.*\\\#<!|\)\?\s*' . s:rangeExpr . '\ze\s*\h'
" Note: I didn't take over the handling of command prefixes (:verbose, :silent,
" etc.) to avoid making this overly complex.
function! s:RemoveAllButRange()
let l:cmdlineBeforeCursor = strpart(getcmdline(), 0, getcmdpos() - 1)
let l:cmdlineAfterCursor = strpart(getcmdline(), getcmdpos() - 1)
let l:upToRange = matchstr(l:cmdlineBeforeCursor, s:upToRangeExpr)
if empty(l:upToRange)
return getcmdline()
else
call setcmdpos(strlen(l:upToRange) + 1)
return l:upToRange . l:cmdlineAfterCursor
endif
endfunction
cnoremap <C-g><C-u> <C-\>e(<SID>RemoveAllButRange())<CR>
as a plugin
My CmdlineSpecialEdits plugin has (among many others) this mapping as well.
You can also do something like this to write the contents of the anonymous register to file2.txt
:4,10d | :call writefile(split(##, "\n", 1), 'file2.txt')
You can do the deleting first, and then open a new tab and paste the contents - so :4,10d, then :tabe foo/bar.txt, followed by p... does that sound better?
In Vim 8 and NVIM 0.3.7 as of writing, you can actually edit your command list and hit enter to execute.
:4,10w foo/bar.txt
q:
q: is to enter interactive ex command
Once you open the interactive command list, you can then edit it and press enter to execute.
I love moopet's answer though, it's efficient.
Is it possible to reuse the range of ex commands in VIM?
As an example, I can write (copy) lines 4 to 10 from my current file to a new file using the following command:
:4,10w foo/bar.txt
But what I really want to do is move the lines to a new file. I can do this like so:
:4,10w foo/bar.txt
:4,10d
But it's a bit annoying to have to type 4,10 both times.
So I have two questions:
Generally, Is there a way to reference the previously used range in ex commands?
Specifically, if there is not a way to do (1), is there any easier way to cut and paste a number of lines from one file into a new one.
I usually use cat to do this:
:4,10!cat > foo/bar.txt
This works because you're piping the lines through cat and replacing them with the resulting output, which is nothing. And of course you can append to the end of an existing file by doing >> instead of >.
I am unaware of an answer to (1), but to answer (2), there are a number of different ways of doing it that don't require reselecting the lines by hand. In visual mode this will work:
4GV10G
:w foo/bar.txt
gvd
because gv reselects the previous selection, which is almost what you want, without using an ex range.
But you could just turn the problem on its head, and try:
:4,10d
:sp foo/bar.txt
pZZ
to cut, then paste into a new file, then close it.
Other than using the Vim history (:Cursor Up, q:) and removing the previous command so that just the range is kept, there's no way to re-use the last range, no magic variable.
If I used this move lines combination more often, I would write a custom command for it:
command! -bang -range -nargs=1 -complete=file MoveWrite <line1>,<line2>write<bang> <args> | <line1>,<line2>delete _
You need to specify the range only once and save typing.
You can write something like this for other combinations, too. The main challenge is specifying all the command attributes (bang, range, completion), and, later, remembering the custom command name.
Generally, what I do is delete the lines from the one file, switch to the other file, and paste.
Also, I generally use marks. Instead of typing the actual numbers, I hit mb to mark the beginning line, then go to the end line and hit d'b to delete back to the line marked as b. But you can use mb to mark a begin line, and me to mark an end line, then run an ex command:
:'b,'e w somefile.txt<Enter>
Of course you can use any letters from a through z for your marks; I usually use b and e but you can use what you like.
How I would move the lines:
m'b
<navigate to end line>
d'b
:n somefile.txt<Enter>
p
Ctrl+^
Ctrl+^ switches from the current open file to the previous open file. (You could also just open a pane and switch panes, if you prefer. Panes don't work in plain vi but do work in vim.)
The above assumes that you have set the autowrite option on. With autowrite, the :n command and Ctrl+^ both just write the current file and then switch files, instead of complaining that the file has been changed without you saving it. You can also do the above and just explicitly write the file before using :n or Ctrl+^.
By the way, I use Ctrl+^ so much that I mapped it onto K. Easier to type, but I got in that habit long ago when I used to have to sometimes use a dumb terminal that couldn't type Ctrl+^.
By the way, when you delete lines, they go into the "unnamed buffer". In vim, the unnamed buffer is preserved when you switch files. In original vi, the unnamed buffer is cleared. So the above won't work with old vi. You can make it work by deleting into a named buffer, then pasting from the named buffer; that works in any version of vi.
m'b
<navigate to end line>
"ad'b
:n somefile.txt<Enter>
"ap
Ctrl+^
The above deletes into the buffer named a, then pastes from a in the other file. This does work in vim of course; it's just that you don't need it.
Here's a command-line mapping that achieves this. I've bound it to CTRL-G CTRL-U, since it performs a similar action as CTRL-U. (But you can change that, of course!)
" c_CTRL-G_CTRL-U Remove all characters between the cursor position and
" the closest previous |:range| given to a command. When
" directly after a range, remove it.
" Useful to repeat a recalled command line with the same
" range, but a different command.
let s:singleRangeExpr = '\%(\d\+\|[.$%]\|''\S\|\\[/?&]\|/[^/]*/\|?[^?]*?\)\%([+-]\d*\)\?'
let s:rangeExpr = s:singleRangeExpr.'\%([,;]'.s:singleRangeExpr.'\)\?'
let s:upToRangeExpr = '^\%(.*\\\#<!|\)\?\s*' . s:rangeExpr . '\ze\s*\h'
" Note: I didn't take over the handling of command prefixes (:verbose, :silent,
" etc.) to avoid making this overly complex.
function! s:RemoveAllButRange()
let l:cmdlineBeforeCursor = strpart(getcmdline(), 0, getcmdpos() - 1)
let l:cmdlineAfterCursor = strpart(getcmdline(), getcmdpos() - 1)
let l:upToRange = matchstr(l:cmdlineBeforeCursor, s:upToRangeExpr)
if empty(l:upToRange)
return getcmdline()
else
call setcmdpos(strlen(l:upToRange) + 1)
return l:upToRange . l:cmdlineAfterCursor
endif
endfunction
cnoremap <C-g><C-u> <C-\>e(<SID>RemoveAllButRange())<CR>
as a plugin
My CmdlineSpecialEdits plugin has (among many others) this mapping as well.
You can also do something like this to write the contents of the anonymous register to file2.txt
:4,10d | :call writefile(split(##, "\n", 1), 'file2.txt')
You can do the deleting first, and then open a new tab and paste the contents - so :4,10d, then :tabe foo/bar.txt, followed by p... does that sound better?
In Vim 8 and NVIM 0.3.7 as of writing, you can actually edit your command list and hit enter to execute.
:4,10w foo/bar.txt
q:
q: is to enter interactive ex command
Once you open the interactive command list, you can then edit it and press enter to execute.
I love moopet's answer though, it's efficient.
I'm a Ruby programming trying to switch from Textmate to MacVim, and I'm having trouble wading through the gargantuan lists of things you can do in VIM and all of the keypresses for them. I'm tired of hearing "You can use 'I' for inserting text, or 'a' for appending text after the character, or 'A' for appending text at the end of the line, or…" I can't imagine everyone uses all 20 different keypresses to navigate text, 10 or so keys to start adding text, and 18 ways to visually select an inner block. Or do you!?
My ideal cheat sheet would be the 30-40 most-used keypresses or commands that everyone uses for writing code on a daily basis, along with the absolute essential plugins that rubyists use daily and the 10 most-used commands for them. In theory, once I have that and start becoming as proficient in VIM as I am in Textmate, then I can start learning the thousands of other VIM commands that will make me more efficient.
Or, am I learning VIM the wrong way altogether?
Here's a tip sheet I wrote up once, with the commands I actually use regularly:
References
vim documentation online
advanced vim tips
more useful tips and graphical cheat sheet
General
Nearly all commands can be preceded by a number for a repeat count. eg. 5dd delete 5 lines
<Esc> gets you out of any mode and back to command mode
Commands preceded by : are executed on the command line at the bottom of the screen
:help help with any command
Navigation
Cursor movement: ←h ↓j ↑k l→
By words:
w next word (by punctuation); W next word (by spaces)
b back word (by punctuation); B back word (by spaces)
e end word (by punctuation); E end word (by spaces)
By line:
0 start of line; ^ first non-whitespace
$ end of line
By paragraph:
{ previous blank line; } next blank line
By file:
gg start of file; G end of file
123G go to specific line number
By marker:
mx set mark x; 'x go to mark x
'. go to position of last edit
' ' go back to last point before jump
Scrolling:
^F forward full screen; ^B backward full screen
^D down half screen; ^U up half screen
^E scroll one line up; ^Y scroll one line down
zz centre cursor line
Editing
u undo; ^R redo
. repeat last editing command
Inserting
All insertion commands are terminated with <Esc> to return to command mode.
i insert text at cursor; I insert text at start of line
a append text after cursor; A append text after end of line
o open new line below; O open new line above
Changing
r replace single character; R replace multiple characters
s change single character
cw change word; C change to end of line; cc change whole line
c<motion> changes text in the direction of the motion
ci( change inside parentheses (see text object selection for more examples)
Deleting
x delete char
dw delete word; D delete to end of line; dd delete whole line
d<motion> deletes in the direction of the motion
Cut and paste
yy copy line into paste buffer; dd cut line into paste buffer
p paste buffer below cursor line; P paste buffer above cursor line
xp swap two characters (x to delete one character, then p to put it back after the cursor position)
Blocks
v visual block stream; V visual block line; ^V visual block column
most motion commands extend the block to the new cursor position
o moves the cursor to the other end of the block
d or x cut block into paste buffer
y copy block into paste buffer
> indent block; < unindent block
gv reselect last visual block
Global
:%s/foo/bar/g substitute all occurrences of "foo" to "bar"
% is a range that indicates every line in the file
/g is a flag that changes all occurrences on a line instead of just the first one
Searching
/ search forward; ? search backward
* search forward for word under cursor; # search backward for word under cursor
n next match in same direction; N next match in opposite direction
fx forward to next character x; Fx backward to previous character x
; move again to same character in same direction; , move again to same character in opposite direction
Files
:w write file to disk
:w name write file to disk as name
ZZ write file to disk and quit
:n edit a new file; :n! edit a new file without saving current changes
:q quit editing a file; :q! quit editing without saving changes
:e edit same file again (if changed outside vim)
:e . directory explorer
Windows
^Wn new window
^Wj down to next window; ^Wk up to previous window
^W_ maximise current window; ^W= make all windows equal size
^W+ increase window size; ^W- decrease window size
Source Navigation
% jump to matching parenthesis/bracket/brace, or language block if language module loaded
gd go to definition of local symbol under cursor; ^O return to previous position
^] jump to definition of global symbol (requires tags file); ^T return to previous position (arbitrary stack of positions maintained)
^N (in insert mode) automatic word completion
Show local changes
Vim has some features that make it easy to highlight lines that have been changed from a base version in source control. I have created a small vim script that makes this easy: http://github.com/ghewgill/vim-scmdiff
http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html
This is the greatest thing ever for learning VIM.
Here is a great cheat sheet for vim:
Have you run through Vim's built-in tutorial? If not, drop to the command-line and type vimtutor. It's a great way to learn the initial commands.
Vim has an incredible amount of flexibility and power and, if you're like most vim users, you'll learn a lot of new commands and forget old ones, then relearn them. The built-in help is good and worthy of periodic browsing to learn new stuff.
There are several good FAQs and cheatsheets for vim on the internet. I'd recommend searching for vim + faq and vim + cheatsheet. Cheat-Sheets.org#vim is a good source, as is Vim Tips wiki.
What most people do is start out with the bare basics, like maybe i, yw, yy, and p. You can continue to use arrow keys to move around, selecting text with the mouse, using the menus, etc. Then when something is slowing you down, you look up the faster way to do it, and gradually add more and more commands. You might learn one new command per day for a while, then it will trickle to one per week. You'll feel fairly productive in a month. After a year you will have a pretty solid repertoire, and after 2-3 years you won't even consciously think what your fingers are typing, and it will look weird if you have to spell it out for someone. I learned vi in 1993 and still pick up 2 or 3 new commands a year.
#Greg Hewgill's cheatsheet is very good. I started my switch from TextMate a few months ago. Now I'm as productive as I was with TM and constantly amazed by Vim's power.
Here is how I switched. Maybe it can be useful to you.
Grosso modo, I don't think it's a good idea to do a radical switch. Vim is very different and it's best to go progressively.
And to answer your subquestion, yes, I use all of iaIAoO everyday to enter insert mode. It certainly seems weird at first but you don't really think about it after a while.
Some commands incredibly useful for any programming related tasks:
r and R to replace characters
<C-a> and <C-x>to increase and decrease numbers
cit to change the content of an HTML tag, and its variants (cat, dit, dat, ci(, etc.)
<C-x><C-o> (mapped to ,,) for omnicompletion
visual block selection with <C-v>
and so on…
Once you are accustomed to the Vim way it becomes really hard to not hit o or x all the time when editing text in some other editor or textfield.
I can't imagine everyone uses all 20 different keypresses to navigate text, 10 or so keys to start adding text, and 18 ways to visually select an inner block. Or do you!?
I do.
In theory, once I have that and start becoming as proficient in VIM as I am in Textmate, then I can start learning the thousands of other VIM commands that will make me more efficient.
That's the right way to do it. Start with basic commands and then pick up ones that improve your productivity. I like following this blog for tips on how to improve my productivity with vim.
tuxfiles.org holds a pretty good cheat sheet. I think there are a couple of points to learning the commands:
Read the cheat sheet regularly. Don't worry about using all of them, or remembering all the keys, just know that the command exists. Look up the command and use it when you find yourself doing something repetitive.
If you find yourself doing something regularly (like deleting an entire line after a particular character d$), go a quick google search to see if you can find a command for it.
Write down commands you think you'll find useful and keep that list where you can see it while you're writing your code. I would argue against printing something out and instead encourage you to use post it notes for just a few commands at a time.
If possible, watch other programmers use vim, and ask them what commands they are using as you see them do something interesting.
Besides these tips, there are some basic concepts you should understand.
vim will use the same character to represent the same function. For example, to delete a line after a character use d$. To highlight a line after a particular character use v$. So notice that $ indicates you will be doing something to the end of the line from where your cursor currently is.
u is undo, and ctrl+r is redo.
putting a number in front of a command will execute it repeatedly. 3dd will delete the line your cursor is on and the two lines that follow, similarly 3yy will copy the line your cursor is on and the two lines that follow.
understand how to navigate through the buffers use :ls to list the buffers, and :bn, :bp to cycle through them.
read through the tutorial found in :help This is probably the best way to 'learn the ropes', and the rest of the commands you will learn through usage.
Go to Efficient Editing with vim and learn what you need to get started. Not everything on that page is essential starting off, so cherry pick what you want.
From there, use vim for everything. "hjkl", "y", and "p" will get you a long way, even if it's not the most efficient way. When you come up against a task for which you don't know the magic key to do it efficiently (or at all), and you find yourself doing it more than a few times, go look it up. Little by little it will become second nature.
I found vim daunting many moons ago (back when it didn't have the "m" on the end), but it only took about a week of steady use to get efficient. I still find it the quickest editor in which to get stuff done.
Put this in your .bashrc to open vim with last edited file at last edited line
alias vil="vim +\"'\"0"