I am using vim to edit a shell script (did not use the right coding standard). I need to change all of my variables from camel-hum-notation startTime to caps-and-underscore-notation START_TIME.
I do not want to change the way method names are represented.
I was thinking one way to do this would be to write a function and map it to a key. The function could do something like generating this on the command line:
s/<word under cursor>/<leave cursor here to type what to replace with>
I think that this function could be applyable to other situations which would be handy. Two questions:
Question 1: How would I go about creating that function.
I have created functions in vim before the biggest thing I am clueless about is how to capture movement. Ie if you press dw in vim it will delete the rest of a word. How do you capture that?
Also can you leave an uncompleted command on the vim command line?
Question 2: Got a better solution for me? How would you approach this task?
Use a plugin
Check the COERCION section at the bottom of the page:
http://www.vim.org/scripts/script.php?script_id=1545
Get the :s command to the command line
:nnoremap \c :%s/<C-r><C-w>/
<C-r><C-w> gets the word under the cursor to command-line
Change the word under the cursor with :s
:nnoremap \c lb:s/\%#<C-r><C-w>/\=toupper(substitute(submatch(0), '\<\#!\u', '_&', 'g'))/<Cr>
lb move right, then to beginning of the word. We need to do this to get
the cursor before the word we wish to change because we want to change only
the word under the cursor and the regex is anchored to the current cursor
position. The moving around needs to be done because b at the
start of a word moves to the start of the previous word.
\%# match the current cursor position
\= When the substitute string starts with "\=" the remainder is interpreted as an expression. :h sub-replace-\=
submatch(0) Whole match for the :s command we are dealing with
\< word boundary
\#! do not match the previous atom (this is to not match at the start of a
word. Without this, FooBar would be changed to _FOO_BAR)
& in replace expressions, this means the whole match
Change the word under the cursor, all matches in the file
:nnoremap \a :%s/<C-r><C-w>/\=toupper(substitute(submatch(0), '\<\#!\u', '_&', 'g'))/g<Cr>
See 3. for explanation.
Change the word under the cursor with normal mode commands
/\u<Cr> find next uppercase character
i_ insert an underscore.
nn Search the last searched string twice (two times because after exiting insert mode, you move back one character).
. Repeat the last change, in this case inserting the underscore.
Repeat nn. until all camelcases have an underscore added before them, that is, FooBarBaz has become Foo_Bar_Baz
gUiw uppercase current inner word
http://vim.wikia.com/wiki/Converting_variables_to_camelCase
I am not sure what you understand under 'capturing movements'. That
said, for a starter, I'd use something like this for the function:
fu! ChangeWord()
let l:the_word = expand('<cword>')
" Modify according to your rules
let l:new_var_name = toupper(l:the_word)
normal b
let l:col_b = col(".")
normal e
let l:col_e = col(".")
let l:line = getline(".")
let l:line = substitute(
\ l:line,
\ '^\(' . repeat('.', l:col_b-1) . '\)' . repeat('.', l:col_e - l:col_b+1),
\ '\1' . l:new_var_name,
\ '')
call setline(".", l:line)
endfu
As to leaving an uncompleted command on the vim command line, I think you're after
:map ,x :call ChangeWord(
which then can be invoked in normal mode by pressing ,x.
Update
After thinking about it, this following function is a bit shorter:
fu! ChangeWordUnderCursor()
let l:the_word = expand('<cword>')
"" Modify according to your rules
let l:new_var_name = '!' . toupper(l:the_word) . '!'
normal b
let l:col_b = col(".")
normal e
let l:col_e = col(".")
let l:line = getline(".")
exe 's/\%' . l:col_b . 'c.*\%' . (l:col_e+1) .'c/' . l:new_var_name . '/'
endfu
Related
I normally use the United States-International keyboard layout. This layout has several keys set as "dead" keys for diacritic marks - for example, pressing ^ is a dead key; it appears to do nothing until the next key is hit; if that key is one that the circumflex is an allowable diacritic, it replaces it with the marked key - that is, if I press ^ then a, I will get â - but if I press a key that it's not an allowed diacritic for, I will get the circumflex followed by the letter, e.g., ^ follows by h gives me ^h.
I wrote a AHK script that adds the diacriticalized characters for Esperanto (see below). It used to work "transparently" and matched the behavior described above. However, recently, the behavior seems to have changed: it no longer "swallows" the diacritic, and inserts a backspace before inserting the character desired.
In other words, if I type "The Esperanto character that sounds like English 'ch' is " and then type ^ then c, it replaces the space following "is" with the ĉ, and on the next keystroke, whatever it is, acts like I had hit ^ then that key.
Why? and How do I fix this?
#Hotstring ? C *
; Esperanto diacriticalized characters
::^c::ĉ
::^C::Ĉ
::^g::ĝ
::^G::Ĝ
::^h::ĥ
::^H::Ĥ
::^j::ĵ
::^J::Ĵ
::^s::ŝ
::^S::Ŝ
::~u::ŭ
::~U::Ŭ
Don't know if I maybe missed something simple with hotstrings, but I couldn't really make it work without trying to do some even further trickery.
I figured an InputHook(docs) implementation could work pretty well.
It might be overkill/stupid though, since it basically just creates a custom implementation for a hotstring. But well, at least it works.
key_map := { "c": "ĉ"
, "g": "ĝ"
, "h": "ĥ"
, "j": "ĵ"
, "s": "ŝ"
, "u": "ŭ" }
ih := InputHook("V")
ih.OnChar := Func("OnChar")
ih.Start()
OnChar(_, char)
{
if (StrLen(char) != 2 || SubStr(char, 1, 1) != "^" || !(key := diacriticalize(SubStr(char, 2))))
return
fObj := Func("SendReplacement").Bind(key)
SetTimer, % fObj, -0
}
diacriticalize(key)
{
global key_map
if key is upper
return Format("{:U}", key_map[key])
else
return key_map[key]
}
SendReplacement(key)
{
SendInput, % "{BS 2}" key
}
So what's happening?
First a map for the key replacements is defined.
Adding any extra dead key combinations for ^ will work just fine.
The input hook is created with only the V(docs) option.
This makes it so that it doesn't consume input while processing it.
Then, with .OnChar(docs) we define a function that runs every time the input receives a new character.
The functions always receives just one character, except when a dead key is used it'll receive e.g ^c.
This is why we check if the input length is two and why we use SubStr()(docs) to transform ^c to just c.
SubStr(char, 1, 1) != "^" also ensures that the pressed deadkey was ^, and not e.g ¨. Otherwise ¨c would produce ĉ.
Then in the user defined function diacriticalize() we return the corresponding diacriticalized key from the key_map (if possible). If the input key was in uppercase, return the diacriticalized key in uppercase as well.
If there is no matching key in the key_map, nothing is returned. Which makes the || !(key := ...) part do its trick to also return if the input key wasn't valid.
Then the timer(docs) trickery is done just to execute the replacement outside of the OnChar() function in another thread to avoid problems with send command running too early.
Basically the period -0 just means to run once immediately.
The function which the timer will is defined as a function object that has a parameter (the key) bound to it with .Bind()(docs).
OK, I’m not sure why it works this way, but I was able to get it working by turning off the automatic backspacing and manually adding my own. The revised AHK script is as follows:
#Hotstring ? C * B0
; Acts only as a supplement to a keyboard that (a) does not
; have these characters defined _and_ uses ^ and ~ as "dead"
; keys to apply accents.
::^c::{bs 2}ĉ
::^C::{bs 2}Ĉ
::^g::{bs 2}ĝ
::^G::{bs 2}Ĝ
::^h::{bs 2}ĥ
::^H::{bs 2}Ĥ
::^j::{bs 2}ĵ
::^J::{bs 2}Ĵ
::^s::{bs 2}ŝ
::^S::{bs 2}Ŝ
::~u::{bs 2}ŭ
::~U::{bs 2}Ŭ
::^::^
::~::~
The B0 in the #Hotstring directive turns off the automatic backspacing. With that option in effect in the original script, typing ^c would result in ^cĉ, so by inserting two backspaces before it ({bs 2}), I get rid of the extraneous ^c before inserting the ĉ.
The last two lines, replacing the caret and tilde with themselves, don’t have an obvious explanation for the reason that they're needed, but they ensure that the behavior is consistent with the standard deadkey usage, so that if I type ^spacec I get the expected ^c instead of an unexpected ĉ.
As Word does not have a mechanism for searching based on a regular expression, I was trying to write a simple macro that would, in this case, search in my currently active document for a period (.) WITHOUT a space following it. Here is my first pass on this:
Sub TestREG()
'
' TestREG Macro
'
'
Set objRegExp1 = CreateObject("vbscript.regexp")
objRegExp1.Global = True
objRegExp1.IgnoreCase = True
objRegExp1.Pattern = "\.[A-Z]"
MyDOC = ActiveDocument
objRegExp1.Execute (MyDOC)
End Sub
I know that I'm missing a lot here, but was trying to remember how to do this in an open Word doc. Every test I try, as I step through this is returning False.
Could anyone suggest how I might do this?
Looks like Word can do regular expressions since 2007:
Find and replace text by using regular expressions (Advanced)
Is there a way to do line break in AutoHotKey souce code? My code is getting longer than 80 characters and I would like to separate them neatly. I know we can do this in some other language, such as VBA for example below:
http://www.excelforum.com/excel-programming-vba-macros/564301-how-do-i-break-vba-code-into-two-or-more-lines.html
If Day(Date) > 10 _
And Hour(Time) > 20 Then _
MsgBox "It is after the tenth " & _
"and it is evening"
Is there a souce code line break in AutoHotKey? I use a older version of the AutoHotKey, ver 1.0.47.06
There is a Splitting a Long Line into a Series of Shorter Ones section in the documentation:
Long lines can be divided up into a collection of smaller ones to
improve readability and maintainability. This does not reduce the
script's execution speed because such lines are merged in memory the
moment the script launches.
Method #1: A line that starts with "and", "or", ||, &&, a comma, or a
period is automatically merged with the line directly above it (in
v1.0.46+, the same is true for all other expression operators except
++ and --). In the following example, the second line is appended to the first because it begins with a comma:
FileAppend, This is the text to append.`n ; A comment is allowed here.
, %A_ProgramFiles%\SomeApplication\LogFile.txt ; Comment.
Similarly, the following lines would get merged into a single line
because the last two start with "and" or "or":
if (Color = "Red" or Color = "Green" or Color = "Blue" ; Comment.
or Color = "Black" or Color = "Gray" or Color = "White") ; Comment.
and ProductIsAvailableInColor(Product, Color) ; Comment.
The ternary operator is also a good candidate:
ProductIsAvailable := (Color = "Red")
? false ; We don't have any red products, so don't bother calling the function.
: ProductIsAvailableInColor(Product, Color)
Although the indentation used in the examples above is optional, it might improve
clarity by indicating which lines belong to ones above them. Also, it
is not necessary to include extra spaces for lines starting with the
words "AND" and "OR"; the program does this automatically. Finally,
blank lines or comments may be added between or at the end of any of
the lines in the above examples.
Method #2: This method should be used to merge a large number of lines
or when the lines are not suitable for Method #1. Although this method
is especially useful for auto-replace hotstrings, it can also be used
with any command or expression. For example:
; EXAMPLE #1:
Var =
(
Line 1 of the text.
Line 2 of the text. By default, a line feed (`n) is present between lines.
)
; EXAMPLE #2:
FileAppend, ; The comma is required in this case.
(
A line of text.
By default, the hard carriage return (Enter) between the previous line and this one will be written to the file as a linefeed (`n).
By default, the tab to the left of this line will also be written to the file (the same is true for spaces).
By default, variable references such as %Var% are resolved to the variable's contents.
), C:\My File.txt
In the examples above, a series of lines is bounded at
the top and bottom by a pair of parentheses. This is known as a
continuation section. Notice that the bottom line contains
FileAppend's last parameter after the closing parenthesis. This
practice is optional; it is done in cases like this so that the comma
will be seen as a parameter-delimiter rather than a literal comma.
Please read the documentation link for more details.
So your example can be rewritten as the following:
If Day(Date) > 10
And Hour(Time) > 20 Then
MsgBox
(
It is after the tenth
and it is evening
)
I'm not aware of a general way of doing this, but it seems you can break a line and start the remainder of the broken line (e.g. the next real line) with an operator. As long as the second line (and the third, fourth, etc., as applicable) starts with (optional whitespace plus) an operator, AHK will treat the whole thing as one line.
For instance:
hello := "Hello, "
. "world!"
MsgBox %hello%
The presence of the concatenation operator . at the logical beginning of the second line here makes AHK treat both lines as one.
(I also tried leaving the operator and the end of the first line and starting the second off with a double-quoted string; that didn't work.)
Assuming I have the following Perl code open in Vim:
if (#arr = shomething()) {
for (#arr) {
some_function($_->{some_key});
# some
# more
# code
while (some_other_funtion($_)) {
write_log('working');
}
}
}
and the cursor at the beginning of the line with some_function, how I can move the cursor to any of:
the start of the while
the { of the while
the first line inside the while block (with the call to write_log)
Searching for { is not an option, because there could be many of { that do not start new inner code block - for example, see parameter of some_function.
It seems you are defining a “code block” to be { } that contain at least one line. You can most easily search for those just by searching for a { at the end of a line:
/{$
/{ means search for a {, and $ represents an anchor to the end of the line.
There might be cases where a { opens a block, but is not the last character of a line:
while (some_other_funtion($_)) { # this while is very important
write_log('working');
}
To take this into account, do the following search for a { that is not closed on the same line:
/{[^}]*$
/ – search for
{ – a { character
[^}] – followed by any character that is not a }
* – repeated 0 or more times
$ – until the end of the line
(Vim regexes are not always the same as in Perl, but this particular one is.)
You could define a mapping for that second search by putting this in your .vimrc:
noremap <Leader>nb /{[^}]*$<CR>
That would let you jump to the next block by pressing <Leader> (\ by default) n b.
Since it uses :noremap, it affects Select mode too. You won’t want that if your <Leader> is a printable character (which it is by default). In that case, add the line sunmap <Leader>nb below the previous line to fix Select mode.
% , $, and ^ are your best friends. (cursor to matching enclosure, end of line, beginning of line).
At the beginning of your code block there ':1$' , will put your cursor at the first bracket.
% will advance you to the next 'matching' end of your code block, assuming it is balanced. If your code is out of balance, the cursor won't move. It actually counts matching-type opening and closing braces which follow and if there is an imbalance, the cursor will not move. Usually the terminal will beep: as in 'Doh! You have a problem.' It's very useful and it works with '{}[]()'
Good way to check your code and ensure that the end of the block exists. It will skip as many lines as exist between the braces (or parens or brackets) to place the cursor on the matching enclosure.
This file is small but assuming you're on line 1 (:1)
:1$ - end of line first code block
:2 - puts the cursor at the 'f' in 'for' on line 2 rather than the white space preceding.
% - jumps you to the closing ')' on that line.
% - jumps you to the opening '(' on that line.
$ - takes you to the '{' which opens the for loop code
% - jumps the cursor to the ending '}' of the for loop
% - takes you back to the top (% is bi-directional. )
Play with it. There's a reason that Intellij's text editor has a vim mode. It's powerful.
Also, pretty good vim manual here that covers some of this stuff and much more.
https://www.pks.mpg.de/~mueller/docs/suse10.1/suselinux-manual_en/manual/sec.suse.vim.html
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