Vim script for automatic function insertion - code-generation

Say I have a class evilLord declared in the file evil_lair.hh and is implemented in the file evil_lair.cc. Now, I want to add the function bool minionDo(std::string command). Is there any script which will put the declaration and empty function definition in the respective files automatically?
I am using c-support vim-plugin which I find useful. Maybe this can be added as a functionality to this script...

The task is not that trivial -- if we want to correctly report the scope of the function. I've already done the work in my :GOTOIMPL (and :MOVEIMPL) command, from my lh-cpp ftplugin suite.

Here is a script which will work:
:let lines = ["bool minionDo(std::string command)"]
:e evil_lair.hh
:call append( line('$'), lines )
:wq
:e evil_lair.cc
:call append( line('$'), lines )
:call append( line('$'), "{}" )
:wq

Related

Variable not being recognized after "read"

-- Edit : Resolved. See answer.
Background:
I'm writing a shell that will perform some extra actions required on our system when someone resizes a database.
The shell is written in ksh (requirement), the OS is Solaris 5.10 .
The problem is with one of the checks, which verifies there's enough free space on the underlying OS.
Problem:
The check reads the df -k line for root, which is what I check in this step, and prints it to a file. I then "read" the contents into variables which I use in calculations.
Unfortunately, when I try to run an arithmetic operation on one of the variables, I get an error indicating it is null. And a debug output line I've placed after that line verifies that it is null... It lost it's value...
I've tried every method of doing this I could find online, they work when I run it manually, but not inside the shell file.
(* The file does have #!/usr/bin/ksh)
Code:
df -k | grep "rpool/ROOT" > dftest.out
RPOOL_NAME=""; declare -i TOTAL_SIZE=0; USED_SPACE=0; AVAILABLE_SPACE=0; AVAILABLE_PERCENT=0; RSIGN=""
read RPOOL_NAME TOTAL_SIZE USED_SPACE AVAILABLE_SPACE AVAILABLE_PERCENT RSIGN < dftest.out
\rm dftest.out
echo $RPOOL_NAME $TOTAL_SIZE $USED_SPACE $AVAILABLE_SPACE $AVAILABLE_PERCENT $RSIGN
((TOTAL_SIZE=$TOTAL_SIZE/1024))
This is the result:
DBResize.sh[11]: TOTAL_SIZE=/1024: syntax error
I'm pulling hairs at this point, any help would be appreciated.
The code you posted cannot produce the output you posted. Most obviously, the error is signalled at line 11 but you posted fewer than 11 lines of code. The previous lines may matter. Always post complete code when you ask for help.
More concretely, the declare command doesn't exist in ksh, it's a bash thing. You can achieve the same result with typeset (declare is a bash equivalent to typeset, but not all options are the same). Either you're executing this script with bash, or there's another error message about declare, or you've defined some additional commands including declare which may change the behavior of this code.
None of this should have an impact on the particular problem that you're posting about, however. The variables created by read remain assigned until the end of the subshell, i.e. until the code hits a ), the end of a pipe (left-hand side of the pipe only in ksh), etc.
About the use of declare or typeset, note that you're only declaring TOTAL_SIZE as an integer. For the other variables, you're just assigning a value which happens to consist exclusively of digits. It doesn't matter for the code you posted, but it's probably not what you meant.
One thing that may be happening is that grep matches nothing, and therefore read reads an empty line. You should check for errors. Use set -e in scripts to exit at the first error. (There are cases where set -e doesn't catch errors, but it's a good start.)
Another thing that may be happening is that df is splitting its output onto multiple lines because the first column containing the filesystem name is too large. To prevent this splitting, pass the option -P.
Using a temporary file is fragile: the code may be executed in a read-only directory, another process may want to access the same file at the same time... Here a temporary file is useless. Just pipe directly into read. In ksh (unlike most other sh variants including bash), the right-hand side of a pipe runs in the main shell, so assignments to variables in the right-hand side of a pipe remain available in the following commands.
It doesn't matter in this particular script, but you can use a variable without $ in an arithmetic expression. Using $ substitutes a string which can have confusing results, e.g. a='1+2'; $((a*3)) expands to 7. Not using $ uses the numerical value (in ksh, a='1+2'; $((a*3)) expands to 9; in some sh implementations you get an error because a's value is not numeric).
#!/usr/bin/ksh
set -e
typeset -i TOTAL_SIZE=0 USED_SPACE=0 AVAILABLE_SPACE=0 AVAILABLE_PERCENT=0
df -Pk | grep "rpool/ROOT" | read RPOOL_NAME TOTAL_SIZE USED_SPACE AVAILABLE_SPACE AVAILABLE_PERCENT RSIGN
echo $RPOOL_NAME $TOTAL_SIZE $USED_SPACE $AVAILABLE_SPACE $AVAILABLE_PERCENT $RSIGN
((TOTAL_SIZE=TOTAL_SIZE/1024))
Strange...when I get rid of your "declare" line, your original code seems to work perfectly well (at least with ksh on Linux)
The code :
#!/bin/ksh
df -k | grep "/home" > dftest.out
read RPOOL_NAME TOTAL_SIZE USED_SPACE AVAILABLE_SPACE AVAILABLE_PERCENT RSIGN < dftest.out
\rm dftest.out
echo $RPOOL_NAME $TOTAL_SIZE $USED_SPACE $AVAILABLE_SPACE $AVAILABLE_PERCENT $RSIGN
((TOTAL_SIZE=$TOTAL_SIZE/1024))
print $TOTAL_SIZE
The result :
32962416 5732492 25552588 19% /home
5598
Which are the value a simple df -k is returning. The variables seem to last.
For those interested, I have figured out that it is not possible to use "read" the way I was using it.
The variable values assigned by "read" simply "do not last".
To remedy this, I have applied the less than ideal solution of using the standard "while read" format, and inside the loop, echo selected variables into a variable file.
Once said file was created, I just "loaded" it.
(pseudo code:)
LOOP START
echo "VAR_A="$VAR_A"; VAR_B="$VAR_B";" > somefile.out
LOOP END
. somefile.out

Uppercasing filename in Makefile using sed

I try to convert a filename such as foo/bar/baz.proto into something like foo/bar/Baz.java in my Makefile. For this purpose, I thought I could use sed. However, it seems that the command does not work as expected:
uppercase_file = $(shell echo "$(1)" | sed 's/\(.*\/\)\(.*\)/\1\u\2/')
# generated Java sources
PROTO_JAVA_TARGETS := ${PROTO_SPECS:$(SRCDIR)/%.proto=$(JAVAGEN)/$(call uppercase_file,%).java}
When I try to run the sed command on the command line it seems to work:
~$ echo "foo/bar/baz" | sed 's/\(.*\/\)\(.*\)/\1\u\2/'
foo/bar/Baz
Any ideas why this does not work inside the Makefile?
UPDATE:
The java files are generated with the following target:
$(JAVAGEN)/%.java: $(SRCDIR)/%.proto
How can I apply the substitution also for targets?
GNU Make does not replace % character in the replacement part of a substitution reference (which is basically a syntactic sugar for patsubst) if it is part of a variable reference. I have not found this behavior described in the documentation, but you can look it implemented in the source code (the relevant function I believe is find_char_unquote).
I suggest moving the call out of the substitution reference, since uppercase_file obviously works properly on any file path:
PROTO_JAVA_TARGETS := $(call uppercase_file,${PROTO_SPECS:$(SRCDIR)/%.proto=$(JAVAGEN)/%.java})
If $(PROTO_SPECS) resolves not to a single element, but rather to a list of elements, you can use foreach to call the function on every elements of a processed list:
PROTO_JAVA_TARGETS := $(foreach JAVA,${PROTO_SPECS:$(SRCDIR)/%.proto=$(JAVAGEN)/%.java},$(call uppercase_file,$(JAVA)))
The java files are generated with the following target: $(JAVAGEN)/%.java: $(SRCDIR)/%.proto
How can I apply the substitution also for targets?
Since Make matches targets first, and there is no way to run sed backwards, what you need here is either define an inverse function, or generate multiple explicit rules. I will show the latter approach.
define java_from_proto
$(call uppercase_file,$(1:$(SRCDIR)/%.proto=$(JAVAGEN)/%.java)): $1
# Whatever recipe you use.
# Use `$$#`, `$$<` and so on instead of `$#` or `$<`.
endef
$(foreach PROTO,$(PROTO_SPECS),$(eval $(call java_from_proto,$(PROTO))))
We basically generate one rule per file in $(PROTO_SPEC) using a multiline variable syntax, and then use eval to install that rule. There is also a very similar example on this documentation page that can be helpful.

In vim, how do I redirect the output of a vimscript function?

I have this vimscript function
function! Env()
redir => s
sil! exe "norm!:ec$\<c-a>'\<c-b>\<right>\<right>\<del>'\<cr>"
redir END
return split(s)
endfunction
This function was obtained from this question:
How to list all the environment variables in Vim?
When I do :call Env() I don't see any output, but :echo Env() displays as output the names of all environment variables.
I'd rather copy and paste this output somehow. I know about :redir. However this doesn't work:
:redir #A
:echo Env()
:redir END
"ap
Instead, a blank line is pasted.
I have tried many combinations of the :redir command (to registers and/or files) and variations on call Env() without success. Is this because the output generated is from calling a function? I thought it might be because the function returns a list, but :echo string(Env()) isn't captured by :redir either.
Edit:
This is the modified solution I used from the answer below.
function! Env()
redir => s
sil! exe "norm!:ec$\<c-a>'\<c-b>\<right>\<right>\<del>'\<cr>"
redir END
let #s = string(split(s))
endfunction
One can then execute :call Env() and then "sp to paste.
related question:
How to redirect ex command output into current buffer or file?
As I said in the comment, this is a problem with :redir that's being discussed on vim_dev. In short, nested redirection with :redir isn't possible.
But of course you can simply modify your Env() function to redirect output to a register or global variable.
Just change redir => s to either redir => g:s (global variable g:s) or redir #s (register s), and remove the return statement. Let's use the register:
function! Env()
redir #s
sil! exe "norm!:ec$\<c-a>'\<c-b>\<right>\<right>\<del>'\<cr>"
redir END
endfunction
After calling the function, the output is stored in register s and you can put it with "sp, of course.
:let #a+=Env()
instead of
:redir #A
:echo Env()
:redir END
is much more concise, but doesn't print Env()'s output to the screen.
(The asker #Ein's modified solution does neither.)

Displaying List of AutoHotkey Hotkeys

I’ve written script that contains numerous hotkeys (general structure is as below). I would like to create another one that when pressed displays a list of all of the hotkeys and their corresponding descriptions that the script contains in a nice, formatted table.
The formatting and display are tenuous since AutoHotkey’s output is limited to message-boxes, but possible. More problematic is getting the hotkeys and corresponding descriptions.
The hotkeys all call the same function with different arguments. I considered adding a variable to the function so that depending on the value, the function either performs the normal function when triggered by the normal hotkeys, or builds a string or something when triggered from the special display hotkey.
I cannot figure out a way to programmatically access the script’s hotkeys at all. I checked the docs and there don’t seem to be any A_ variables that can be used for this purpose, nor does the Hotkey command lend itself well (it can be used to test if a hotkey exists, but looping through the innumerable combinations is, at best, tedious).
Failed attempts:
I tried using Elliot’s suggestion of parsing the script itself (replacing the path with %A_ScriptFullPath%, and while it does work for a raw script, it does not when the script is compiled
I tried assigning the entire hotkey section of the script to a variable as a continuation section and then parsing the variable and creating hotkeys using the Hotkey command. This worked well right up until the last part because the Hotkey command cannot take arbitrary commands as the destination and requires existing labels.
The ListHotkeys command is not applicable because it only displays the hotkeys as plain text in the control window.
Does anyone know how I can display a list of the hotkeys and either their corresponding arguments or comments?
Example script:
SomeFunc(foobar)
{
MsgBox %foobar%
}
!^#A::SomeFunc("a") ; blah
^+NumpadMult::SomeFunc("c") ; blivet
^+!#`::SomeFunc("b") ; baz
^#Space::SomeFunc("d") ; ermahgerd
…
Example desired “outputs”:
C+A+ W+ A a | C+ S+ NumpadMult b
------------------+----------------------
C+A+S+W+ ` c | C+ W+ Space d
    or
Ctrl Alt Shift Win Key Action
-----------------------------------------
× × × A blah
× × NumpadMult baz
× × × × ` blivet
× × Space ermahgerd
etc.
The only thing I can think of is to read each line of your script individually and parse it. This code reads your script (script.ahk) one line at a time and parses it. This should get you started. Additionally, you could parse the line to check for the modifiers as well.
Loop
{
FileReadLine, line, C:\script.ahk, %A_Index%
if ErrorLevel
break
If Instr(line, "::")
{
StringSplit, linearray, line, ::,
key := linearray1
StringSplit, commandarray, linearray3, `;
action := commandarray2
hotkeyline := "key: " . key . "`tAction: " . action
final .= hotkeyline . "`r"
}
}
msgbox % final
return
I found a solution. It is not perfect (or ideal), and hopefully a proper, built-in method will become available in the future, but it works well (enough) and for raw and compiled scripts.
What I did was to use the FileInstall command which tells the compiler to add a file to the executable (and extract it when run).
Sadly, the FileInstall command will not allow the use of variables for the source file, so I cannot simply include the script itself (FileInstall, %A_ScriptFullPath%, %A_Temp%\%A_ScriptName%, 1).
As a work-around, I ended up extracting all of the desired hotkeys to a second file which I then parse as Elliot suggested, then delete, and #Include at the end of my script (it must be at the end since hotkeys will terminate the autoexecute section).
;;;;; Test.ahk ;;;;;
; Add hotkey file to executable and extract to Temp directory at runtime
FileInstall, Hotkeys.ahk, %A_Temp%\Hotkeys.ahk, 1
Loop
{
;Read a line from the extracted hotkey script and quit if error
FileReadLine, line, %A_Temp%\Hotkeys.ahk, %A_Index%
if ErrorLevel
break
;Trim whitespace
line=%line%
; Parse the line as in Elliot’s answer, but with tweaks as necessary
ParseHotkey(line)
…
}
FileDelete, %A_Temp%\Hotkeys.ahk ; Delete the extracted script
DisplayHotkeys() ; I ended up bulding and using a GUI instead
#Include, Hotkeys.ahk ; It is included at compile-time, so no A_Temp
;;;;; Hotkeys.ahk ;;;;;
z::MsgBox foo
y::MsgBox bar

Powershell variable preceded by = does not evaluate

I'm writing some PowerShell scripts to work with our source control software (which is not a PowerShell cmdlet) and I'm running into a problem using variables as command line arguments when they are preceded by an =, like this:
cm mklabel lb:BL$baseline -c=$comment
This ends up create a label in with the comment of "$comment". If I put a space after the =, it looks like it evaluates the variable properly, but the command does not associate the comment with -c argument anymore. Is there a way to force the variable to be evaluated despite the =?
Try:
cm mklabel lb:BL$baseline -c=($comment)
Try
cm mklabel lb:BL$baseline "-c=$comment"