vimscript get number of first and last visible line - vim

I know I can go to the first and last visible line of current buffer with H and L respectively. But how I can pass the line numbers to variables ?

let topline = line("w0")
let botline = line("w$")

There might be a better way, but if nothing else, you can use H and L to move there and `` to get back and get. Something like
norm 'H'
let top=line('.')
norm '``L'
let bottom=line('.')
norm '``'
or you can use getpos() to store and setpos() to restore the position, but I am not sure you can avoid destroying the previous position mark (the :keepmarks command should do that, but it's documentation says it only works in some special case).

Related

Resize vim split based on number of visible lines

I am trying to write a function in my vimrc which lets me create a small split which looks as though it is inside the current file but is, in fact, a different file.
To do this cleanly, I want this split to appear immediately below the cursor which means I need to resize my splits appropriately. Unfortunately, I cannot find a way to measure the number of visible lines between my cursor and the top of the window. In files where I have folded code, the approach of using line(".") - line("w0") ("." gives line number of cursor; "w0" gives line number of topmost visible line) does not work since this calculation includes the hidden lines inside the folds. Does anybody know how this could be achieved?
Images for reference before and after inserting the split:
line(".") - line("w0")
gives you the number of physical lines between the top of the window and the current line. As you figured out, this method doesn't account for folds.
You can count the number of folds within a range with something like this:
function! CountFolds(top_line, bottom_line)
let folded_lines = []
for line_nr in range(a:top_line, a:bottom_line)
if foldlevel(line_nr) == 0
call add(folded_lines, 0)
else
call add(folded_lines, 1)
endif
endfor
return count(uniq(folded_lines), 1)
endfunction
and then remove it from your initial line count:
let top_line = line("w0")
let bottom_line = line(".")
let physical_lines = bottom_line - top_line
let screen_lines = physical_lines - CountFolds(top_line, bottom_line)
Reference:
:help range()
:help foldlevel()
:help add()
:help uniq()
:help count()
Note that you may need your script to account for soft-wrapped lines as well, a topic that is well worth another question.

How to quickly edit determinate part of code inside different similar lines

I have this problem I'm adjusting a code I've made I have a structure like this:
Apple1 = Fruit("ss","ss",[0.1,0.4],'w')
PineApple = Fruit("ss","ss",[0.315,0.4],'w')
Banana = Fruit("ss","ss",[0.315,0.280],'w')
...
...
Instead of "ss"I would like to type further information like "Golden Delicious". For the moment I'm simply deleting "ss"clicking over it and then replacing it with the information I want to insert. I'm sure there is a faster way to do it, I've tried something with VIM macros but I can't figure out how to "Raw input" my data.
I've try simply to substitute it with Spyder, but is slow because I have to click substitute every time, with VIM for what I've try is the same.
Then I wonder how insert something else after 'w'...
This is an example of an final output only to understand better the question :
Apple1 = Fruit("Golden Delicous","red",[0.1,0.4],'w')
PineApple = Fruit("Ananas comosus","green",[0.315,0.4],'w')
Banana = Fruit(" Cavendish banana","yellow",[0.315,0.280],'w')
...
...
I reformulate the question: which is the faster way to change "ss", for the moment I'm clicking over "ss" delate "ss" and write e.g "Golden Delicous" but is very slow. What I would like is that for every single ss the editor ask me to insert something to replace the single ss.
e.g. first ssin the fist line: I want to replace it typing something else e.g. "Golden Delicous" second ssin the first line I want to replace it typing somethingelse e.g. red. First ssin the second line I want to replace it with s.e. e.g. Ananas comosussecond ssin the second line I want to replace with s.e. e.g. green and so on.
I'm sure there is an answer for this somewhere but I can't find it!
Please if you down vote explain me why so I can improve it!
As far as I understand, the data that you want to substitute for "ss" does not have regular structure, so you will need to enter it by hand.
In Vim you would do it like this:
Place the cursor over the first "ss", then press * and then N.
Press ce, enter the new data (e.g. "Golden Delicious"), then leave Insert mode by pressing Escape.
Press n to jump to the next instance of "ss".
Repeat steps 2 and 3 ad libitum.
Look up :h * and :h n for more information.
I would do it like that:
:%s/ss/\=input('Replacement: ')/gc
This queries you for each occurrence. With the /c flag, the display is even updated during the loop (at the cost of having to additionally answer y for each occurrence); without the flag, you would need to keep track of where you are yourself.
You can use a function that searches the whole file substituting all "ss" strings with values from arrays populated with the replacement data:
function! ChangeSS()
let ss1 = ['Golden Delicous', 'Ananas comosus', 'Cavendish banana']
let ss2 = ['red', 'green', 'yellow']
call cursor(1, 1)
let l = "ss2"
while search('"ss"', 'W') > 0
if l == "ss1"
let l = "ss2"
else
let l = "ss1"
endif
execute 'normal ci"' . remove({l}, 0)
endwhile
endfunction
It uses a reference variable (l) that exchanges which array you want to extract data from. ss1 is for first appearance of "ss" in the line and ss2 for the second one.
Run it like:
:call ChangeSS()
That (in my test) yields:
Apple1 = Fruit("Golden Delicous","red",[0.1,0.4],'w')
PineApple = Fruit("Ananas comosus","green",[0.315,0.4],'w')
Banana = Fruit("Cavendish banana","yellow",[0.315,0.280],'w')

How to repeatedly add text on both sides of a word in vim?

I have a bunch of local variable references in a Python script that I want to pull from a dictionary instead. So, I need to essentially change foo, bar, and others into env['foo'], env['bar'] and so on. Do I need to write a regular expression and match each variable name to transform, or is there a more direct approach that I could just repeat with the . command?
You can use a macro: type these commands in one go (with spacing just to insert comments)
" first move to start of the relevant word (ie via search)
qa " record macro into the a register.
ienv['<esc> " insert relevant piece
ea'] " move to end of word and insert relevant piece
q " stop recording
then, when you're on the next word, just hit #a to replay the macro (or even ## to repeat the last replay after that).
There's an easier way - you can use a regex search and replace. Go into cmdline mode by typing a colon and then run this command:
%s/\\(foo\|bar\|baz\\)/env['\1']/
Replacing foo, bar, and baz with whatever your actual variable names are. You can add as many additional variables as you'd like, just be sure to escape your OR pipes with a backslash. Hope that helps.
you could write a function that would do this pretty well, add this to your .vimrc file:
function! s:surround()
let word = expand("<cword>")
let command = "%s/".word."/env[\'".word."\']/g"
execute command
endfunction
map cx :call <SID>surround()<CR>
This will surround every occurance of the word currently under the cursor.
If you wanted to specify what went before and after each instance you could use this:
function! s:surround()
let word = expand("<cword>")
let before = input("what should go before? ")
let after = input("what should go after? ")
let command = "%s/".word."/".before.word.after."/g"
execute command
endfunction
map cx :call <SID>surround()<CR>
If you only want to confirm each instance of the variable you could use this:
function! s:surround()
let word = expand("<cword>")
let before = input("what should go before? ")
let after = input("what should go after? ")
let command = "%s/".word."/".before.word.after."/c"
execute command
endfunction
map cx :call <SID>surround()<CR>
I figured out one way to do what I need. Use q{0-9a-zA-Z"} to record key strokes into a buffer. Position the cursor at the begging of the variable name, then cw and type env['']. Next move the cursor back one space to the last quote and paste the buffer filled from the cw command with P. Finally, reuse the recording with #{0-9a-z".=*} for each variable.

How to append to the clipboard

I know how to copy to the clipboard but how can I append to it?
I use this in my code:
let #+ = my_expression
but that overwrites the clipboard.
I know that I can use the registers a-z to append to:
let #B = my_expression
which appends to register b, but what do I when I want to append to the clipboard?
use:
let #+ = #+ . my_expression
or shorter:
let #+ .= my_expression
Reference: :help :let.=
If you're not macro-ing, it's probably worth checking out registers as well. :help registers was mind-blowing.
In an oversimplified nutshell, there are 26 additional "customizable clipboards", called registers, where you can store text, starting with a and going through z.
You add text to a register in command mode by hitting ", naming the register (say f), and then typing the "motion" you want to select text.
NOTE: We're using the named f register here b/c it's probably under your left index finger. That is, f is picked just b/c it's handy. You could replace f with a or u or z or whatever throughout if you wanted.
Copying with a register (cursor at [T]):
Initial File State
This is my first line.
[T]his is my second line.
This is my third line.
Type "fyy in command mode to fill the register with one line (yy).
Type p (* see below) to immediately paste it from the default register.
Type "f to pick the f register and then p to paste from the f register directly. Right now f and default as the same.
So the result of typing "fyyp is exactly the same as having typed yyp with the default clipboard.
Result
This is my first line.
This is my second line.
[T]his is my second line.
This is my third line.
Appending to a register:
Use the capital letter to append to your existing register.
In the above example after pasting, press j to go down a line and then "Fyy. Then type p to paste. You've appended "This is my third line." to f's contents.
Result
This is my first line.
This is my second line.
This is my second line.
This is my third line.
This is my second line.
[T]his is my third line.
(Using a lower case f would have cleared out f's contents and ended up with it only holding "This is my third line.")
Why does p paste what's in register f immediately after you yanked into f? Because your default register holds a pointer to the last selection, and apparently doesn't simply hold what you added to f, but pulls everything that's in f when you append. It might be more expository to say, in the first case, "the result of typing "fyy"fp is exactly the same as having typed yyp with the default clipboard."
But if you were now to yy a new line into the default register, you can hit "f to select the f register and then p to paste that previous value.

How do you select the entire PHP function definition?

In PHP, if I have a function such as:
function test($b) {
var $a = 0;
while ($a < b) {
$a += 3;
}
return $a;
}
and the cursor is on the $a += 3 line, is it possible to quickly select the entire function?
"v2aB" would select everything including the function braces but not the declaration function test($b)
Press V after the selection command you post, to convert the selection to line selection, and it will select the function declaration:
v2aBV
It's been a long time since this question was asked and answered, but I will add my own answer because it's the one I was looking for and none of the others work exactly like this one:
nnoremap vaf ?func.*\n*\s*{<cr>ma/{<cr>%mb`av`b
vmap af o<esc>kvaf
The first mapping, "Visual around function" or vaf, will jump back to the start of the function definition, regardless that the { is in the same line or the next one, and even if it's a lambda function, and visually select it characterwise to it's ending bracket. This works in PHP, Javascript and Go.
The user can then press V to turn to linewise select mode if she wants to.
The only problem that I found is that when I am in the body of a big function, but below a line that uses a lambda (let's say "small") function, this will stop searching at the beginning of the small function and select it's body instead of reaching the start of the big function and select all of its body.
function show_video_server(v_server) {
// this whole function should get selected
var something = function(){ /* this function gets selected */ };
// | the cursor is here when I type "vaf"
}
As a workaround I use the second mapping: vmap af o<esc>kvaf. It feels like a repetition or expansion of the selection. What it really does is abandon the selection and go to the line before it, and then try it agan. If the "big" function uses several lambda functions the user has to repeat the af several times to reach the big one.
Usually, vaf es enough. Sometimes vaf af or vaf af af is needed. Anyway, it's the closest I could get to what I wanted, so this is the version I'm using.
Here's a mapping that seems to work very well, no matter the nesting level.
:map t ? function <CR>f{vaBV
Here's another method that will work if you have function-level folding turned on: z c v
That closes the current fold and selects it, but it leaves it closed. If you want it to remain open: z c v $
If you have block-level folding turned on, you would have to close twice, since you're inside the while loop, so: 2 z c v
To enable PHP class/function folding: let php_folding = 1
simple way
nmap vaf va}V
I like this
nmap vaf [{?function<CR>:nohl<CR>vf{]}
if ‘{’ is in new line
nmap vaF [{?function<CR>:nohl<CR>v/{<CR>]}
Yet another way. This should select the entire function definition regardless of your cursor position within the definition, not just when you're at the $a += 3 line.
Use this in normal mode (<CR> means press enter)
?func<CR>V/{%
Explanation of each part:
?func search backward for the word "func" (the idea is to get to the first line of the function definition)
V go to visual line mode
/{ search forward for the opening brace (I didn't use f{ because the opening brace might be on a separate line)
% go to the matching brace
If you are using OOP programming this works (it looks for extra words before function[public, private, protected])
nmap vaf [{?\S* function<CR>:nohl<CR>v/{<CR>]}
As a bonus here is a wrapper around if
nmap vai [{?if<CR>:nohl<CR>v/{<CR>]}

Resources