I need to do the following:
Set a variable temp to some value (say 100).
Go somewhere to increase a number by temp for a range of lines.
Update temp to be +100.
Repeat
I started by setting the value 100 to temp using let temp=100.
I then figure out the following ex command that I can apply to a line: :execute "normal! ". temp. "^A" this will take temp and increase the number by temp for the current line.
Unfortunately, this will not work when I visually select a range of lines and then hit : to apply a command to all the lines selected.
How can I achieve the same but for a range of lines?
Here is an example:
1
2
3
4
5
Should become
101
102
103
104
105
Then I will update the temp to let temp=temp + 100 and repeat for the next block and so on.
Thanks!
To answer your question directly, :help :execute is what is blocking you for two reasons:
:execute doesn't accept a range,
:execute is not necessary to begin with.
The following command does the job without :execute:
:[range]normal! <C-r>=temp<CR><C-v><C-a><CR>
Breakdown:
[range] would be '<,'> after a visual selection.
:help :normal executes the given macro in normal mode.
:help c_ctrl-r inserts the content of the given register in the command line.
:help "= is the expression register, which returns an evaluated expression.
temp is the expression to evaluate, so <C-r>=temp<CR> inserts the content of the variable temp.
<C-v><C-a> inserts a literal ^A.
<CR> executes the command.
But that's a lot to type so a simple mapping seems more appropriate in this case:
xnoremap <expr> <key> temp . '<C-a>'
Breakdown:
:help :xnoremap creates a visual mode mapping.
:help <expr> makes it an expression mapping, where the actual RHS is evaluated at runtime.
<key> is what key you want to press.
temp . '<C-a>' is your expression, which concatenates the current value of temp with <C-a> to obtain 100<C-a>, 200<C-a>, etc..
Usage:
Set temp to the desired value:
:let temp = 100
Select some lines:
v<motion>
Increment the first number of each line:
<key>
Change the value of temp:
:let temp += 100
Move to next block and select some lines:
<motion>
v<motion>
Increment the first number of each line:
<key>
However, the manual way would go like this:
v<motion> " visually select the desired lines
100<C-a> " increment the first number on each line by 100
then:
<motion>
v<motion>
200<C-a> " increment the first number on each line by 200
and so on… so I'm not sure what's be the benefit of introducing variables, :normal, etc., here.
I just found a workaround, if no one knows a better way.
I recorded a macro b to run execute "normal! " . temp . "^A" on the current line. Then recorded another macro a that will go and visually select all the lines in the group interested and run :'<,'>norm #b this will apply that operation on every line, then before ending the macro #a, I also set let temp=temp+100.
I think we can use global command to do the work, it accepts the range.
:'<,'>g/./execute "normal! ". temp. "^A"
Related
I am pretty new to vim/gvim. This is my first attempt at modifying the ~/.vimrc file.
I am trying to map F11 to multiple commands.
What I am trying is
inoremap jj <ESC>
map <F11> h e a , jj && l && x
While manually entering h e a , jj l x is working for me, using F11 key is not.
What I am trying to achieve is-
1. get to the end of the current word
2. append ','
3. move right one place and delete that space between ',' and the start of next word.
I do not understand what's wrong with my mapping.
Please help me here.
First, let's build a proper representation of your macro
get to the end of the current word
e
append ','
ea,<Esc>
move right one place…
ea,<Esc>l
… and delete that space between ',' and the start of next word.
ea,<Esc>lx
Second, let's use it in the right-hand part of a proper non-recursive mapping:
nnoremap <F11> ea,<Esc>lx
Of note:
<Space> is a legitimate normal mode command so any <Space> in your macro is interpreted as "move the cursor one cell to the right", see :help l.
& is also a legitimate normal mode command, see :help &.
In a macro, every character is meaningful so h e a , jj && l && x is interpreted as:
move the cursor one cell to the left,
move the cursor one cell to the right,
move the cursor to the end of the current word,
move the cursor one cell to the right,
append a space, a comma, a space,
move the cursor one cell to the right,
repeat last substitution,
repeat last substitution,
move the cursor one cell to the right,
move the cursor one cell to the right,
move the cursor one cell to the right,
repeat last substitution,
repeat last substitution,
move the cursor one cell to the right,
cut the character under the cursor.
Your jj insert mode mapping provides zero value over plain <Esc> so there is no reason to use it in any mapping.
Your mapping thus becomes a non-recursive mapping so nmap becomes nnoremap.
Intro
In gVim under Windows i have to replace insert and mod in each string of file some character in a specifics position.
Example
QWAER;PA 0X000055;123WAAAA
TYUIO;PA 0Y000056;123VAAAA
need to become
QWAE#;PAX000055;123;WAAAA
TYUI#;PAY000056;123;VAAAA
modify char 5 in #
delete char 9,10
insert ; in original pos 22 or after delete in pos 20
More Info
Usually I do
Put cursor and beginning of text to select
Press CTRL-V (CTRL-Q in gVim) to begin select of the column
keep press SHIFT and selecte the interested area
the go at the end of file
then do the replace insert or modification.
(This is where I learn about Keyboard-only column block selection in GVim Win32, or why does Ctrl-Q not emulate Ctrl-V when mswin.vim is included?
and here i learn how to do the insert(http://pivotallabs.com/column-edit-mode-in-vi/)
It's not a correct way to do the things.
In vim i can do the replaceof a fange of rows, and so on using commands.
I think that should be possible to use commands to replace a portion of each string but i have no Knoledge about those command.
this is the main idea
Replacing alternate line in vi text file with a particular string
Question
Is there a way to do the operations using commands with absolute position and not selection.
Thanks
:{range}normal 5|r#9|2x20|i;
Does what you want on the lines covered by {range}:
5|r# " go to column 5 and replace the character with #
9|2x " go to column 9 and cut 2 characters
20|i; " go to column 20 and insert a ; to the right
So…
:5,25norm 5|r#9|2x20|i; would apply that transformation to lines 5 to 25,
:.,$norm 5|r#9|2x20|i; would apply that transformation from the current line to the last,
:'<,'>norm 5|r#9|2x20|i; would apply that transformation to the current (or last) visual selection,
:'{,'}norm 5|r#9|2x20|i; would apply that transformation to the current "paragraph",
:%norm 5|r#9|2x20|i; would apply that transformation to the whole buffer,
and so on…
You could also record it, let's say in register q:
qq
5|r#
9|2x
20|i;<Esc>
q
and play it back on {range}:
:{range}#q
Or spend 30 minutes trying to come up with the right :s// command…
Reference:
:help range
:help :normal
:help |
:help r
:help x
:help i
:h recording
I want to indent an entire text file with n spaces in VIM.
My current method is:
Go to the beginning of the file
Type Ctrl + V
Press the down key ↓ or the j key to select the lines to indent
Type Shift + I
Type the Space Bar key n times
Press Esc
Is there a way to accomplish this without using visual mode and having to manually go through the entire file?
Use a Global Substitution
Assuming you want to indent four spaces, you can do this:
:%s/^/ /
This will effectively insert four spaces at the start of each line. Adjust the number of spaces on the right-hand side of the substitution expression to suit your indentation needs.
Using Visual Mode
Alternatively, you can go into normal mode and then:
gg
SHIFT+V
SHIFT+G
SHIFT+>
to indent the entire file by the value of shiftwidth.
how about:
:%s/^/(you count n spaces here)/
A :normal variant that adds two spaces at the beginning of every line:
:%norm 0i<space><space><CR>
Another :normal variant that adds two spaces before the first printable character of every line:
:%norm I<space><space><CR>
You can indent a set of lines like this:
:1,44>
If you press ctrl-g, it will give you the last line of the file.
I am dealing with a block of comments like:
//this is comment 1
//this is comment 2
//this is comment 3
//this is comment 4
I would like to make it look like:
//this is comment 1
//this is comment 2
//this is comment 3
//this is comment 4
Is there a Vim shortcut to make this transformation on selected lines while staying in command mode?
You can use the :substitute command. With the cursor anywhere on the
first of the lines:
:,+3s/$/\r
This inserts an additional newline at the end of each line.
You can also use the :global command. With the cursor anywhere on
the first of the lines, run:
:,+3g//norm o
For each of the next four lines, this executes the o Normal-mode
command, adding a new blank line.
In both of the commands, the ,+3 prefix is a range for the
command, see :help range. Briefly, the comma separates the addresses
of the starting and ending lines of the range, where the current line
is used if we omit the former of the two addresses. The +3 address
refers to the line that is three lines below from the current line.
Rather than specifying a range, e.g., ,+3, for either of these
commands, you can use the V Normal-mode command to make a Visual
block across all the lines that you want. Then typing : to begin the
command will auto-fill the range specifying the visual block, and you
can then enter either of the two commands starting with s or g:
:'<,'>s/$/\r
You can use a macro:
qao<esc>jq
then use 3#a to apply the macro 3 times over the last lines.
where:
qa "Start recording a macro named a
o "Insert new line under current line
<esc> "Exit insert mode
j " Move down next line
q " end macro
Select your visual selection with V
Then run a regex replace to replace one line break with two
:s/\n/\r\r/g
One can use the command
:g/^/pu_
on the whole buffer (by default) or on a selected range of lines.
Select the lines you want with V
Then type : and s/\ze/\r
How can I fill the remainder of a line with the specified character up to a certain column using Vim? For example, imagine that the cursor is on column four and I want to fill the remainder of the current line with dashes up to column 80. How would I do that?
You can do 80Ax<Esc>d80| for a simpler solution.
Here's a function to implement what you ask, and slightly more.
It fills the line from its current end of line, rather than the cursor position
It forces a single space between what's currently on the line and the repeated chars
It allows you to specify any string to fill the rest of the line with
It uses vim's textwidth setting to decide how long the line should be
(rather than just assuming 80 chars)
The function is defined as follows:
" fill rest of line with characters
function! FillLine( str )
" set tw to the desired total length
let tw = &textwidth
if tw==0 | let tw = 80 | endif
" strip trailing spaces first
.s/[[:space:]]*$//
" calculate total number of 'str's to insert
let reps = (tw - col("$")) / len(a:str)
" insert them, if there's room, removing trailing spaces (though forcing
" there to be one)
if reps > 0
.s/$/\=(' '.repeat(a:str, reps))/
endif
endfunction
Insert that into your .vimrc, and make a mapping to it, e.g.
map <F12> :call FillLine( '-' )
Then you can press F12 to apply the hyphens to the current line
Note: this could probably be easily extended to act on a selection in VISUAL mode, but currently works for single lines only.*
If I understand the question correctly, this can be accomplished like this: in normal mode subtract the cursor's current column position from the desired ending column, then type the result followed by 'i' to enter insert mode, then the character you want to fill the space with. End by returning to normal mode. For example, with the cursor at column four in normal mode, if you wanted to fill the rest of the line up to column 80 with dashes, type 76i- then Esc or Ctrl-[ to return to normal mode. This should result in 76 dashes starting in column 4 and ending in column 79.
If you have the virtualedit option set to block or all, you can create a visual selection (even over empty space) up to the desired column:
v80| (if virtualedit=all) or
<c-v>80| (if virtualedit=block)
Then replace the selected area with dashes:
r-
It's probably helpful to start visual mode after the last character in the line by hitting l to avoid overwriting the last character in the line. If you are not using virtualedit=all, then you need to set virtualedit+=onemore so you can move one character beyond the end of line in normal mode.
One of the other answers here is: 80Ax<Esc>d80|. I initially started using it as a key mapping like this:
nnoremap <leader>- 80A-<Esc>d80<bar>
...but I didn't like how it leaves the cursor at the end of the line. Also, for narrow windows (e.g. just a little wider than 80 cells), it causes the entire window to scroll horizontally because the cursor briefly jumps to the end of the long line before it's trimmed back to 80. This is partially resolved by returning to the beginning of the line:
nnoremap <leader>- 80A-<Esc>d80<bar>0
...but then the screen will "flash" briefly while the cursor jumps offscreen and back (thus scrolling the window briefly to the right and back). To prevent this, we can temporarily use reverse insert mode (:h revins or :h ri) to keep the cursor onscreen while appending. Here's the full command as a key mapping:
nnoremap <leader>- :set ri<cr>80A-<esc>81<bar>d$0:set nori<cr>
This answer answers your question. Just replace len computation with your desired column number (+/- 1 may be, I never remember), and remove the enclosing double-quotes added by the substitution.
Using the textwidth value is also possible. It allows for different maximum line widths depending on filetype (check :h 'tw'). Here is what I now use, it appends a space after existing line content if present and will prompt for the string to use for the pattern:
function! FillLine() abort
if &textwidth
let l:str = input('FillLine>')
.s/\m\(\S\+\)$/\1 /e " Add space after content (if present).
" Calculate how many repetitions will fit.
let l:lastcol = col('$')-1 " See :h col().
if l:lastcol > 1
let l:numstr = float2nr(floor((&textwidth-l:lastcol)/len(l:str)))
else
let l:numstr = float2nr(floor(&textwidth/len(l:str)))
endif
if l:numstr > 0
.s/\m$/\=(repeat(l:str, l:numstr))/ " Append repeated pattern.
endif
else
echohl WarningMsg
echom "FillLine requires nonzero textwidth setting"
echohl None
endif
endfunction
You can map it for quick access of course. I like:
nnoremap <Leader>' :call FillLine()<Cr>
Note that the calculation assumes simple ASCII characters are being inserted. For more complicated strings, len(l:str) might not work. From :h strlen():
If you want to count the number of multi-byte characters use strchars().
Also see len(), strdisplaywidth() and strwidth().
You can insert first your dashes and then go to the first character and enter your text in replace mode: 80i-Esc0R
if you don't want to type the text, first delete the line with 0D, use 80i-Esc to insert the dashes and 0RCTRL+r " to paste the contents of the unamed register in replace mode.
I actually stumbled across this looking to align columns. Just in case anyone else does the same, this thread might be useful: How to insert spaces up to column X to line up things in columns?