I want to process a set of lines and apply a template to each line. Let's say I have the following block of lines:
CASE
ESAC
IF
FI
And I would like to get the following output:
<YYINITIAL> {CASE} {
return new Symbol(sym.CASE);
}
...
<YYINITIAL> {FI} {
return new Symbol(sym.FI);
}
So, I thought of having a template with the body to apply for each line, something like this:
<YYINITIAL> {###PLACE_HOLDER###} {
return new Symbol(sym.###PLACE_HOLDER###);
}
And apply it to each line.
What I thought was of selecting the lines, putting then in a register ("a, for instance), then selecting the template putting it in a register ("b) and call an external script that generates the output. However no luck (don't know how to pass the contents of a register to an external script).
Any other approach is welcome.
this command will do that job:
:%s/.*/<YYINITIAL> {&} {\r\treturn new Symbol(sym.&);\r}/
you could remove the %, if you visual selected lines. Also you could create a mapping or user command for it, if it is used often.
Your suggested approach seems overly complex, especially because you still have to implement the template expansion logic as the external script.
Why don't you simply define your templates as :substitute commands?! You can pattern-match your PLACEHOLDER via /\({pattern}\)/ and insert it in the replacement text as \1, \2, etc. (I hope you know about the power of Vim's :substitute command; otherwise, read :help :s.
You can keep and persist your templates in several ways, depending on how often / long you need them:
Just in the command-line history (:s<Up>)
Save in named registers (:let #a = #:), execute with :#a
Save in a Vim script and :source it
Write a custom plugin with :commands
Related
I would like to replace parts of one line with the result of the selection being piped into a command.
For example:
echo "hello $(echo "world" | base64)" | vim -
This will open a buffer with hello d29ybGQK in it. Now press wvw to visually select d29ybGQK.
Then I attempted :!base64 -d and I expected the buffer to contain hello world, which did not happen. Indeed, the whole line was piped into the command, and the whole line was replaced.
Is it possible to replace only the visual selection, and have only that selection piped into the command?
I also attempted c<c-r>=system('base64 -d') but that did not send the visual selection to the command's stdin.
Filtering with ! is always line-wise. Your solution with c and the
expression register is an excellent way to solve this. You only forgot
to pass the input to system(), which is its second optional argument.
Since you just changed the text selected, it went into the " register
automatically. All you need to do is to grab it back and pass it to
system with getreg():
c<C-R>=system('base64 -D', getreg('"'))
Note that base64 may echo a newline at the end. If you want to remove
it, either wrap the whole thing in trim(), a new function in Vim 8, or
use [:-2]:
c<C-R>=trim(system('base64 -D', getreg('"')))
c<C-R>=system('base64 -D', getreg('"'))[:-2]
This is a shorthand for [0:-2], meaning grab everything from character
0 to second-last in the resulting string.
Consider creating a visual map if you use it often:
vnoremap <leader>d c<C-R>=system('base64 -D', getreg('"'))[:-2]<CR>
For historical reasons, the Ex commands are inherently line-based; venerable vi also didn't have visual mode yet. That limitation includes filtering through an external command with :range!; it will always filter complete lines.
manual solution
For simple input like in your example, it's probably easiest to temporarily split the line, filter, and then reassemble. For example:
:substitute/ /\r/ | execute '.!base64 -d' | -1join
plugin solution
For a universal solution, you need to use or implement a plugin that grabs the selected text, filters it (probably through system()), and then replaces the selection with the result.
My SubstituteExpression plugin has a {Visual}g= mapping that can filter through Vimscript expressions, Vim functions and commands, and external commands.
express.vim by Tom McDonald offers an almost identical implementation. It also allows on-the-fly creation of operators via :MapExpress and :MapSubpress, something for which I would use my TextTransform plugin, which you need to install as a dependency, anyway. My plugin offers more advanced (cross-mode) repeats, and the :Ex-command expression variant, but has two large dependencies you also need to install.
Taking the advice of Xaelias I'll modify this post, because the initial question was quite unclear and overcomplicated.
So basically I have multiple script files that need to be edited (too many of them to afford to edit them manually). What I need to do for each script file is to insert a certain code at a specific location inside each of them.
So if my script file was called foo.script, and if the code inside it was as follows:
cut_bar() {
some code...
}
cut_cabbage() {
some code...
}
...
Then I'd like my final look of the foo.script file to be as follows:
cut_bar() {
some code...
message msg_foo_bar
}
cut_cabbage() {
some code...
message msg_foo_cabbage
...
(cut_ is a universal prefix shared by all those functions inside of which the code needs to be added).
Is there any way I can do this in Sublime Text editor? Or is there no other way but to develop a small program that does all of this (in which case, tips would be appreciated likewise!).
We'll try a first solution, which is far from perfect, but might be enough for what you want to do. If it does not work, we'll try another solution.
You want to do a search and replace (⌥⌘F (on mac) or Find→Replace...).
Make sure that the RegEx modifier is active (.* on the left)
Find What: (?<=^cut_)(.*?)(\(\)\h*\{(?:.|\v)*?)(^\}\h*$) (explanations below)
Replace With: \1\2\tmsg_foo_\1\n\3
If it does not work as intended, maybe we'll just need to tweak a little the RegEx. Or maybe we'll need to resort to python and ST plugins!
RegEx explanation:
(?<=^cut_): we want our match to start at the beginning of a line (^) with cut_ this is not captured because as you said, this is a constant, so we don't really care
(.*?): this matches the rest of the name of the method. This is captured and will be \1
(\(\)\h*\{(?:.|\v)*?): we stop the name capture at (), \h is for any horizontal space (spaces or tabs) that could be between the end of the name, and {, then we match every character and \v (vertical space such as new lines) (this is captured as \2)
(^\}\h*$): ... up until we meet a line that starts with }, and might have any kind of horizontal space before the end of the line ($), this is captured as \3
From there, the replace part is kind of straightforward I guess.
\1\2\ is everything we captured but do not want to modify (the name of the method, and its body, except for the last line })
Then we put what you wanted, msg_foo_\1 which will transform into msg_foo_bar for example, and then put back the ending }
I've read vim-wiki about dynamic templates and I want similar, simple "template-system". I've created a function:
function! Read_template(file)
execute '0r /home/zsolt/.vim/skeletons/'.a:file
%substitute#\[:EVAL:\]\(.\{-\}\)\[:END:\]#\=eval(submatch(1))#ge
%substitute#\[:READ:\]\(.\{-\}\)\[:END:\]#??????#ge
endfunction
I want to include a file from a template. The EVAL works well but how can I solve the READ function? It isn't important to eval the included file.
An example:
main.tex:
\documentclass[a4paper]{article}
....
exam.tex:
% Created [:EVAL:]strftime('%Y. %B. %d.')[:END:]
[:READ:]/path/of/main/main.tex[:READ:]
I exec Read_template("exam.tex") and want that exam.tex includes main.tex.
How can I do this?
You'll need to read the file and insert its contents. As you cannot use :read (it will read entire lines and cannot be called from within a substitution), you have to use the lower-level readfile() Vimscript function, like this:
%substitute#\[:READ:\]\(.\{-\}\)\[:END:\]#\=join(readfile(submatch(1)),"\n")/#ge
You'll have to parse each line imported and apply what needs be :
- expression substitution
- inclusion of other templates, etc. (which will mean that you'll have to remove and add lines on the fly. In the last version of mu-template template expansion engine, the expansion is done in-memory)
FYI, my work of mu-template already has this feature: http://code.google.com/p/lh-vim/wiki/muTemplate#Completely_useless_recursive_example
Is there some sort of Vim plugin that would allow me to do something like this, given the code:
function something (arbitraryObject) {
arbitraryObject.something = doesNotMatter;
}
Then let's say I just select the word arbitraryObject in the function body, I'd like an easy way to write a macro that, given a short key combination or command-mode command, could give me something like:
function something (arbitraryObject) {
arbitraryObject.something = doesNotMatter;
console.log(arbitraryObject);
}
or...
function something (arbitraryObject) {
arbitraryObject.something = doesNotMatter;
window.arbitraryObject = arbitraryObject;
}
Note that I'm not asking what this macro would actually look like, I'm curious if there are built-in tools or plugins that make the creation of things like this particularly easy.
I know that you aren't asking for the specific macro, but it's easiest to learn these types of things by example. The first one (console.log) can be achieved through this mapping:
:vmap <leader>il y<esc>oconsole.log(<c-r>");<esc>
Likewise, the second one could look like this:
:vmap <leader>iw y<esc>owindow.<c-r>" = <c-r>";<esc>
Can you spot the similarities? <leader>il means that the command binds to the leader key (usually ,) followed by i followed by l. You can check what the following commands mean by using :help [key] in vim, but the mappings basically yank (copy) the selected text, enters a new line (Esc, o) and then appends some text followed by Ctrl+r and ", which inserts the yanked text.
One option would be to use something like snipMate.vim and have snippets for your various tasks. For example, you could create these snippets:
snippet cons
console.log(${1:variable});${2}
snippet wind
window.${1:attribute} = $1${2}
Then you could do something like yocons<Tab><C-r>"<Tab>, or likewise yowind<Tab><C-r>"<Tab>. You could also use yiw instead of visually selecting too. I like an option like this because then you can easily make it applicable to only a particular type of filetype (e.g. javascript) and continue to extend your already existing snippets.
nmap <Leader>l o<esc>pv^"xygv[ygvdiconsole.log(<esc>a"<esc>pa",<esc>"xpa);<esc>
This is better alternative, since it quotes strings with help of vim-unimpared. Just yank text, you need to log and use this key binging. It converts
this.$el.find("input,select,textarea")
to
console.log("this.$el.find(\"input,select,textarea\")",this.$el.find("input,select,textarea"));
I am trying to set up some useful coding templates in vim, for example I have mapped
map `cl iclass <+CLASSNAME+><CR>{<CR><Esc>Iprotected:<CR><+PROTECTED MEMBERS+><CR><Esc>Ipublic:<CR><+PUBLIC INTERFACE+><CR>};<CR><++><CR><Esc>3kv<0v3k<2k
so that when I type `cl in vim I get
class <+CLASSNAME+>
{
protected:
<+PROTECTED MEMBERS+>
public:
<+PUBLIC INTERFACE+>
};
<++>
(so that I can jump between the <+ +> tags with C-j). This works fine, but I find the above remap pretty obscure. Is there a way to enter what I want vim to type in "verbatim mode"? So I would want to write something like
map `cl i{VERBATIMSTART}class <+CLASSNAME+>
{
protected:
<+PROTECTED MEMBERS+>
public:
<+PUBLIC INTERFACE+>
};
<++>{VERBATIMEND}
?
Thank you
Paul
I don't know if there is such a "verbatim"-mode for mappings.
I, personally, would use one of the snippet-plugins to do this.
See www.vim.org and search
for "snippet". I have not tried all of them (just SnippetsMgr ;-) ),
but I suppose that they are handier to define multi-line-snippets.
Some of the available snippet-plugins on vim.org: snippets.vim ,
snippetsEmu, snipMate, SnippetsMgr, etc.
As Habi has mentioned, one way to go about this is with a snippet plugin.
Another way is to copy that snippet of code into its own file and set up your mapping to insert that file below the cursor:
map `cl :r /path/to/code_snippet<CR>
Kind of obvious (and probably not what you want) is:
map `cl iclass <+CLASSNAME+>
\<CR>{
\<CR>protected:
\<CR> <+PROTECTED MEMBERS+>
\<CR>public:
\<CR> <+PUBLIC INTERFACE+>
\<CR>};
\<CR><++>
\<CR>
\ in beginning of line tells that the line is the continuation of the previous one. But this is rather literal continuation: it doesn't add new lines so one has to add them manually. Since it uses the insert mode, the operation would be also affected by the current indentation mode. (Though one can try to work that around with :set paste/:set nopaste.)
I would have tried to put the text into a temp variable or register then Pput (or :put) it into the buffer. E.g. setreg() allows one to tell that the content of a register are lines and thus Putting it would work regardless of indentation.
Otherwise, looking in :help line-continuation or :help variables I see no way how one can specify a multi-line string or text.