How to delete the surround block delimited by do/end in ruby with vim
For example
(10..20).map do |i| <CURSOR HERE>
(1..10).map do |j|
p j
end
end
I want to do something like dsb (delete surround block) and get
(1..10).map do |j|
p j
end
Maybe you can make nnormap.
Every end/do pair is on the same indent, so firstly you should find pair indent - in this case, next line for the same indent (Cause your cursor is in do line.)
So you can make vimscript function with finding next indent line and delete it.
This is an example of the function. You can customize you want - i.e.) set indent for resting lines.
function! DeleteWithSameIndent(inc)
" Get the cursor current position
let currentPos = getpos('.')
let currentLine = currentPos[1]
let firstLine = currentPos[1]
let matchIndent = 0
d
" Look for a line with the same indent level whithout going out of the buffer
while !matchIndent && currentLine != line('$') + 1 && currentLine != -1
let currentLine += a:inc
let matchIndent = indent(currentLine) == indent('.')
endwhile
" If a line is found go to this line
if (matchIndent)
let currentPos[1] = currentLine
call setpos('.', currentPos)
d
endif
endfunction
nnoremap di :call DeleteWithSameIndent(1)<CR>
Related
I would like to know how to change the position of a line in a file (preferably using sed). For example, consider the file that contains
goal identifier statement
let statement 1
let statement 2
forall statement
other statements
I would like to be able to do this
goal identifier statement
forall statement
let statement 1
let statement 2
other statements
where I change the position of the forall line and bring it after the goal line. forall and goal are regexps that can be used to identify the lines.
you can try, for move line 4 to line 2, I want to move line A to line B, where A>B
sed -n '2{h; :a; n; 4{p;x;bb}; H; ba}; :b; p' file
or A<B
sed -n '2{h; d}; 4{p; x;}; p' file
you get, in first case: move line 4 to line 2
goal identifier statement
forall statement
let statement 1
let statement 2
other statements
you get, in second case: move line 2 to line 4
goal identifier statement
let statement 2
forall statement
let statement 1
other statements
Explanation
sed -n ' #silent option ON
2{ #if is line 2
h #Replace the contents of the hold space with the contents of the pattern space
:a #label "a"
n #fetch the next line
4{ #if is line 4
p #print line 4
x #Exchange the contents of the hold and pattern spaces
bb #goto "b"
}
H #appends line from the pattern space to the hold space, with a newline before it.
ba #goto "a"
}
:b #Label "b"
p #print
' file
EDIT
If You want use regex for identify the lines, you can modify first command
sed -n '/goal/{p;n;h;:a;n;/forall/{p;x;bb};H;ba};:b;p' file
$ cat r.awk
BEGIN {
forall_re = "^forall" # examples of regexps
goal_re = "^goal"
}
function tag(l) { # tag a line
if (l ~ goal_re ) return "goal"
else if (l ~ forall_re) return "forall"
else return "rest"
}
{ # store entire file in array; give a tag to every line
lines[NR] = $0
tags[NR] = tag($0)
}
function swap0(a, i, j, tmp) {
tmp = a[i]; a[i] = a[j]; a[j] = tmp
}
function swap(i, j) {
swap0(lines, i, j); swap0(tags, i, j)
}
function rise(i) {
# TODO: add error check
while (i - 1 > 0 && tags[i - 1] != "goal") {
swap(i, i - 1); i--
}
}
function process( i) {
for (i = 1; i <= NR; i++)
if (tags[i] == "forall") rise(i)
}
function dump( i) { # print the array
for (i = 1; i <= NR; i++)
print lines[i]
}
END {
process()
dump()
}
An example of input file
$ cat r.txt
goal identifier statement
let statement 1
let statement 2
forall statement A
other statements
goal identifier statement
let statement 1
let statement 2
forall statement B
other statements
Usage:
$ awk -f r.awk r.txt
goal identifier statement
forall statement A
let statement 1
let statement 2
other statements
goal identifier statement
forall statement B
let statement 1
let statement 2
other statements
sed is for simple substitutions on individual lines, that is all. For anything else you should use awk for every desirable attribute of software (clarity, simplicity, portability, etc.:
$ awk 'NR==FNR{if (/forall/) {f=FNR; v=$0} next} FNR!=f; /goal/{print v} ' file file
goal identifier statement
forall statement
let statement 1
let statement 2
other statements
sed -r '/goal/{ # if match "goal" line
:X # this is a lable for branch command
N # append next line
/forall[^\n]*$/{ # if match "forall" line move to "goal" line below
s#^([^\n]*)(.*)(\n[^\n]*)$#\1\3\2#
b # after move finished branch to end
}
bX # branch to :X for appending next line
}' file
goal identifier statement
forall statement
let statement 1
let statement 2
other statements
A less terrible way using vim to find a line in $FILENAME using regex $REGEX_EXPRESSION and move that line to $LINE_NUMBER:
vim -c "g:$REGEX_EXPRESSION:m$LINE_NUMBER" -cwq "$FILENAME"
explanation: -c is command in vim, so it goes to the first line that matches that regex and then moves it to the line number specified, and then does the command wq (or write and quit).
I'm trying to grab the first characters before a space.
I know it can be done this way
str = "3 Hello World"
str = Mid(str, 1,2)
But how would i do this after a space?
Edit: Looks like you changed your question to get characters BEFORE the first space instead of AFTER. I've updated my examples.
Here's one way:
strTextBeforeFirstSpace = Split(str, " ")(0)
Assuming a space exists in your string, this would return everything up until the first space.
Another way would be:
strTextBeforeFirstSpace = Left(str, InStr(str, " ") - 1)
You can get the index of the first space with the InStr function
InStr(str, " ")
And use this as a parameter in your Mid function
Dim str, index
str = "3 Hello World"
index = InStr(str," ")
'only neccessary if there is a space
If index > 0 Then
str = Mid(str,1,index - 1)
End If
I'm making a JSON parser and I am looking for an algorithm that can find all of the matching brackets ([]) and braces ({}) and put them into a table with the positions of the pair.
Examples of returned values:
table[x][firstPos][secondPos] = type
table[x] = {firstPos, secondPos, bracketType}
EDIT: Let parse() be the function that returns the bracket pairs. Let table be the value returned by the parse() function. Let codeString be the string containing the brackets that I want to detect. Let firstPos be the position of the first bracket in the Nth pair of brackets. Let secondPos be the position of the second bracket in the Nth pair of brackets. Let bracketType be the type of the bracket pair ("bracket" or "brace").
Example:
If you called:
table = parse(codeString)
table[N][firstPos][secondPos] would be equal to type.
Well, In plain Lua, you could do something like this, also taking into account nested brackets:
function bm(s)
local res ={}
if not s:match('%[') then
return s
end
for k in s:gmatch('%b[]') do
res[#res+1] = bm(k:sub(2,-2))
end
return res
end
Of course you can generalize this easy enough to braces, parentheses, whatever (do keep in mind the necessary escaping of [] in patterns , except behind the %b pattern).
If you're not restricted to plain Lua, you could use LPeg for more flexibility
If you are not looking for the contents of the brackets, but the locations, the recursive approach is harder to implement, since you should keep track of where you are. Easier is just walking through the string and match them while going:
function bm(s,i)
local res={}
res.par=res -- Root
local lev = 0
for loc=1,#s do
if s:sub(loc,loc) == '[' then
lev = lev+1
local t={par=res,start=loc,lev=lev} -- keep track of the parent
res[#res+1] = t -- Add to the parent
res = t -- make this the current working table
print('[',lev,loc)
elseif s:sub(loc,loc) == ']' then
lev = lev-1
if lev<0 then error('too many ]') end -- more closing than opening.
print(']',lev,loc)
res.stop=loc -- save bracket closing position
res = res.par -- revert to the parent.
end
end
return res
end
Now that you have all matched brackets, you can loop through the table, extracting all locations.
I figured out my own algorithm.
function string:findAll(query)
local firstSub = 1
local lastSub = #query
local result = {}
while lastSub <= #self do
if self:sub(firstSub, lastSub) == query then
result[#result + 1] = firstSub
end
firstSub = firstSub + 1
lastSub = lastSub + 1
end
return result
end
function string:findPair(openPos, openChar, closeChar)
local counter = 1
local closePos = openPos
while closePos <= #self do
closePos = closePos + 1
if self:sub(closePos, closePos) == openChar then
counter = counter + 1
elseif self:sub(closePos, closePos) == closeChar then
counter = counter - 1
end
if counter == 0 then
return closePos
end
end
return -1
end
function string:findBrackets(bracketType)
local openBracket = ""
local closeBracket = ""
local openBrackets = {}
local result = {}
if bracketType == "[]" then
openBracket = "["
closeBracket = "]"
elseif bracketType == "{}" then
openBracket = "{"
closeBracket = "}"
elseif bracketType == "()" then
openBracket = "("
closeBracket = ")"
elseif bracketType == "<>" then
openBracket = "<"
closeBracket = ">"
else
error("IllegalArgumentException: Invalid or unrecognized bracket type "..bracketType.."\nFunction: findBrackets()")
end
local openBrackets = self:findAll(openBracket)
if not openBrackets[1] then
return {}
end
for i, j in pairs(openBrackets) do
result[#result + 1] = {j, self:findPair(j, openBracket, closeBracket)}
end
return result
end
Will output:
5 14
6 13
7 12
8 11
9 10
I would like to get a working code to simply remove from a text line a specific part that always begins with "(" and finish with ")".
Sample text : Hello, how are you (it is a question)
I want to remove this part: "(it is a question)" to only keep this message "Hello, how are you"
Lost...
Thanks
One way using Regular Expressions;
input = "Hello, how are you (it is a question)"
dim re: set re = new regexp
with re
.pattern = "\(.*\)\s?" '//anything between () and if present 1 following whitespace
.global = true
input = re.Replace(input, "")
end with
msgbox input
If the part to be removed is always at the end of the string, string operations would work as well:
msg = "Hello, how are you (it is a question)"
pos = InStr(msg, "(")
If pos > 0 Then WScript.Echo Trim(Left(msg, pos-1))
If the sentence always ends with the ( ) section, use the split function:
line = "Hello, how are you (it is a question)"
splitter = split(line,"(") 'splitting the line into 2 sections, using ( as the divider
endStr = splitter(0) 'first section is index 0
MsgBox endStr 'Hello, how are you
If it is in the middle of the sentence, use the split function twice:
line = "Hello, how are you (it is a question) and further on"
splitter = split(line,"(")
strFirst = splitter(0) 'Hello, how are you
splitter1 = split(line,")")
strSecond = splitter1(UBound(Splitter1)) 'and further on
MsgBox strFirst & strSecond 'Hello, how are you and further on
If there is only one instance of "( )" then you could use a '1' in place of the UBound.
Multiple instances I would split the sentence and then break down each section containing the "( )" and concatenate the final sentence.
I have added the following fine function to my status bar to show which function is currently being edited in C-derived languages:
set statusline+=%{WhatFunctionAreWeIn()}
fun WhatFunctionAreWeIn()
let strList = ["while", "foreach", "ifelse", "if else", "for", "if", "else", "try", "catch", "case"]
let foundcontrol = 1
let pos=getpos(".") " This saves the cursor position
let view=winsaveview() " This saves the window view
while (foundcontrol)
let foundcontrol = 0
" Go to the character before the last open {
normal [{
call search('\S','bW')
" If the character is a ) then go to the character
" preceding the () section
let tempchar = getline(".")[col(".") - 1]
if (match(tempchar, ")") >=0 )
normal %
call search('\S','bW')
endif
let tempstring = getline(".")
for item in strList
if( match(tempstring,item) >= 0 )
let foundcontrol = 1
break
endif
endfor
if(foundcontrol == 0)
call cursor(pos)
call winrestview(view)
return tempstring
endif
endwhile
call cursor(pos)
call winrestview(view)
return tempstring
endfun
However, after a few minutes VIM hangs. Disabling the function prevents the hang, so I feel confident that this function is to blame. Is there anything in there that might hang VIM? Is there a better way to accomplish the task of showing the currently-edited function in the status bar?
Thanks.
The issue is that your strategy for determining whether to keep moving to surrounding braces is too aggressive:
Suppose your cursor is on the f in a preprocessor directive #endif between two functions.
Since you're between two functions, there is no unmatched { for [{ to jump to, so the cursor doesn't move.
Your match() against strList hits the if in #endif, causing the loop to continue.
The loop never exits.
I suspect a ctags-based approach, like #Gowtham suggests, will work better, even if it requires some customization.