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

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')

Related

Lua: Line breaks in strings

I've been working on a formatter that will take a long string and format it into a series of lines broken off at the word within a certain character limit. For instance, He eats the bread, broken off at every 8 characters, would return something like:
He eats
the
bread
This is because "He eats" contains 7 characters, and "the bread" contains 9, so it has to break off at "the" and continue with "bread".
The script itself has been working wonderfully, thanks to the help of other members before. However, now I have a new challenge.
I'm utilizing a free-entry box for entering the series of lines. It's for a program called MUSHclient, if anyone is familiar with it. The utils.editbox opens up an editor text box, and allows free form, and returns the result as a string. For example:
result = utils.editbox("What are you typing?")
saves the typed response into result which is a string type. I want to put line breaks in properly, but I can't figure out how to do that. If I print(result), it returns something like the following:
This is the result of three separate paragraphs.
As you can see, there is a line break between each paragraph.
So the result contains line breaks, or control characters.
Now, I've managed to extrapolate the individual lines, but not the linebreaks in between, using the following code:
for line in result:gmatch("[^%c]+%c?") do
Send(note_wrap(line))
end
How can I extrapolate for linebreaks as well as textual content?
Edit For reference, the note_wrap function is as follows:
function note_wrap(str, limit, indent, indent1)
indent = indent or ""
indent1 = indent1 or indent
limit = limit or 79
local here = 1-#indent1
local last_color = ''
return indent1..str:gsub("(%s+)()(%S+)()",
function(sp, st, word, fi)
local delta = 0
local color_before_current_word = last_color
word:gsub('()#([#%a])',
function(pos, c)
if c == '#' then
delta = delta + 1
elseif c == 'x' then
delta = delta + 5
last_color = word:sub(pos, pos+4)
else
delta = delta + 2
last_color = word:sub(pos, pos+1)
end
end)
here = here + delta
if fi-here > limit then
here = st - #indent + delta
return "\n"..indent..color_before_current_word..word
end
end)
end
The reason for this code is to be able to take #rHe eats #x123the bread, ignore the color codes (indicated by #<letter> or #x<digits>) and return the aforementioned result of:
He eats
the
bread
but insert the color codes so it then returns:
#rHe eats
#x123the
bread
If this can be modified to recognize new lines and put an actual new line in, that would be fabulous.
Edit 2 Tried Paul's solution, and it's not doing what I'm needing it to. I may not have been clear enough, so I'll try to clear it up in this edit.
When I type into the free form box, I want the information presented exactly as is, but formatted to break correctly AND maintain entered newlines. For example, if I write this in the free form box:
Mary had a little lamb, little lamb, little lamb
Mary had a little lamb
It's fleece was white as snow
using Paul's code, and setting string to 79, I get:
Mary had a little lamb, little lamb, little lamb
Mary had a little lamb
It's fleece was white as snow
And that's not what I want. I'd want it to return it just as it was written, and breaking line as necessary. So if I had a 20 character limit, it'd return:
Mary had a little
lamb, little lamb,
little lamb
Mary had a little
lamb
It's fleece was
white as snow
If I added manual line breaks, I'd want it to respect those, so if I hit return twice after the first line, it'd have a line break under the first line as well. For example, if I wrote this post in the free form box, I'd want it to respect every new paragraph and every proper line break as well. I hope this clears things up.
I use something like this in my code (this assumes that \n is the line separator):
local function formatUpToX(s, x, indent)
x = x or 79
indent = indent or ""
local t = {""}
local function cleanse(s) return s:gsub("#x%d%d%d",""):gsub("#r","") end
for prefix, word, suffix, newline in s:gmatch("([ \t]*)(%S*)([ \t]*)(\n?)") do
if #(cleanse(t[#t])) + #prefix + #cleanse(word) > x and #t > 0 then
table.insert(t, word..suffix) -- add new element
else -- add to the last element
t[#t] = t[#t]..prefix..word..suffix
end
if #newline > 0 then table.insert(t, "") end
end
return indent..table.concat(t, "\n"..indent)
end
print(formatUpToX(result, 20))
print(formatUpToX("#rHe eats #x123the bread", 8, " "))
The cleanse function removes any markup that needs to be included in the string, but doesn't need to count against the limit.
For the example you have, I get the following output (using 20 as the limit for the first fragment and 8 for the second):
This is the result
of three separate
paragraphs.
As you can see,
there is a line
break between each
paragraph.
So the result
contains line
breaks, or control
characters.
#rHe eats
#x123the
bread

vim - non rectangular visual block

Is there a way to select the second column in the following code,
which turns out to be non rectangular.
I tried "CTRLv 3jE" , but that doesn't work.
int var_one = 1;
int var_two = 2;
int var_three = 3;
int var_very_long = 4;
You could use one of the Align plugins to align your column, select and copy it and afterwards undo the alignment (or leave it aligned)
Based on the comments, I think the way to go is writing a custom function that
passes the task to awk. It could be done with some regex also, splitting each
line on spaces, but awk should be easier. Here is my first try:
function! ExtractColToRegister(reg, ...) range
let input = join(getline(a:firstline, a:lastline), "\n")
if a:1 | let column = a:1
else | let column = 1 | endif
exec "let #". a:reg . " = system(\"awk '{ print $" .
\ column . " }'\", input)"
endfunction
You should have no trouble understanding it if you're already writing Vim
scripts :-) however let me know if some part of it is unclear, and if there is
something to improve as well.
Basically what the function does is saving a specific column to a register. If
you visually select the example code given in the question, and then:
:'<,'>call ExtractColToRegister("a", 2)
Register a will now contain:
var_one
var_two
var_three
var_very_long
And you can easily "ap somewhere else. Notice the column defaults to 1 if the
argument was omitted.
Creating a custom command "Column to Register" makes the process even nicier to
use outside of Vim scripts:
:command! -range -nargs=+ CTR <line1>,<line2>call ExtractColToRegister(<f-args>)
use the CopyMatches function from http://vim.wikia.com/wiki/Copy_the_search_results_into_clipboard
then select the lines and do something like
:'<,'>CopyMatches .*=

vimscript get number of first and last visible line

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).

How can I quickly add something to a few lines in vim?

I'm trying to get away from my arrow use but there is one thing I've yet to solve without using the arrow keys. Take this example:
var1 = "1"
var2 = "2"
var3 = "3"
var4 = "4"
Now I want this to be:
var_1 = "1"
var_2 = "2"
var_3 = "3"
var_4 = "4"
Using arrows I would just goto the var1, insert and add the underscore and then arrow down and do the same thing. The problem with using hjkl is I can't be in insert mode so I have to esc out, move down, insert...rinse repeat which required more work. Is there another way to accomplish this?
You can also use a visual block insert:
go to the "1" in "var1"
press CTRL+V
go down with j to select all the rows you wish to affect
I (that's capital i)
_
<ESC>
The underscore should now be inserted at the correct place in all the rows selected (for some reason it takes a second for it to happen on my machine)
There are many ways to do this. Using movement commands for example:
1G0 → Go to the start of the first line
f1 → go to the first occurence of "1"
i_<ESC> → insert "_" and go back to normal mode
j. → go down a line and repeat the insert command
j. → go down a line and repeat the insert command
...
Or, better yet, use an "ex" command:
:%s/var/var_/
Or even with the visual block command, as johusman notes.
Assuming you're at line 1, character 1...
Using a macro:
qqfra_<Esc>+q3#q
q Record macro
q Into register q
f find
r 'r'
a append
_ underscore
Esc Normal mode
+ Start of next line
3 Three times
# Play macro
q from register q
Par 11.
Or (better) using substitute:
:%s!r!&_<CR>
Par 9!
[Sorry... too much VimGolf!]
I tend to prefer :substitute over the visual block mode.
%s/var\zs\ze\d/_/
I always have line numbers turned on, so I'd do e.g.
1,4 s/var/var_/
This is similar to the
% s/var/var_/
answer, but it only functions on the named lines. You can use visual mode to mark the lines, if you don't like typing the range (the 1,4 prefix) in your command.

Reordering methods using vim

I have 2 methods in a source file:
def Foo
puts "hello"
puts "bar"
end
def Bar
puts "hello"
puts "bar"
end
I would like to swap the order of the methods, so Bar is first.
Assuming the cursor is on the d in def Foo, the simple way is to:
shift v -> jjjj -> d -> jjj -> p -> O -> esc
But that feels a little long winded and does not account well for arbitrarily long methods:
What is the most efficient way to do this in Vim, keystroke wise?
EDIT
Keep in mind, I would like the solution to account for a situation where the methods are in a context of a big class, so G is probably best avoided
Assuming the cursor is somewhere in the first method, press dap}p and they should be swapped.
What dap does is simply "delete a paragraph". Try :help object-select to learn other way of deleting/selecting text objects in VIM.
EDIT: Replaced G with } in the command.
Similar to Spatz's
d}}p
delete to the next blank line (below Foo), skip to the next blank line (below Bar), paste.
Found another method ( from godlygeek on #vim ):
with:
def function():
first
first
first
def lol():
second
second
second
From line 1, count up until the 'def lol', which is 5. Then:
:1,5m$
A couple of ways off the top of my head. You could say
5dd/end[enter key]pO
Deletes five lines, searches for end, places the lines underneath, adds a space.
If you have VimRuby installed, I believe you can use % to jump between def and end. In that case, you could say
v%x5jpO
Edit: I defer to spatz on this :P
From line 1, 5ddGp , or 5dd:5p is the most concise/shortest I can think of.
personally, I would go '4dd' then down under bar and press 'p', but I'm not a vim guru

Resources