Validate input from an inputbox without leaving the inputbox - regex

I created a function with an inputdialog to move lines conditionally (tnx to Romainl).
First thing to do is a search, then to invoke the code below.
My Code:
if !exists("char")
let char = "Move Lines with search match after/before?
\ \n
\ \nMove Line Backwards: Start input with: '?'
\ \nMove Line Forwards: Start input with: '/'
\ \n
\ \np.e.
\ \n?=\\s*$
\"
endif
let a = inputdialog(char)
if a == ""
return
endif
if matchstr(a, '^?') != ''
let minplus = '-'
elseif matchstr(a, '^/') != ''
let minplus = '+'
else
echo "wrong input: input does not start with '?' or '/'"
return
endif
I would like to change the "return" command in a "return back to inputdialog" command:
I would like to check the input entered in the inputbox immediately without leaving the inputbox, is that possible?

The call to inputdialog() is a single, blocking call in Vimscript. None of your code can run while it's open. No events (that can be hooked into with :autocmd are fired. In general, there's no parallelism in Vim.
The best you can do is re-launch the inputdialog() (possibly initialized with the previously entered text) when the validation fails.
Alternatively, you'd have to implement your own input control (e.g. using getchar()). There, you can run validation while waiting for the next pressed character.

Related

Debugging Nim source filter

Can someone point out what's syntactically wrong with this source filter (as documented here - https://nim-lang.org/docs/filters.html) as it refuses to compile with an "invalid indentation" error message
#? stdtmpl | standard
#proc greet(name = "world"): string =
# result = ""
<h1>Hello $name</h1>
#end proc
echo greet()
Since echo greet() is Nim code, you need to prefix it with #. Also, be aware that you may not have empty lines outside the proc, because Nim would then try to append them to a result variable, which does not exist outside the proc.
#? stdtmpl | standard
#proc greet(name = "world"): string =
# result = ""
<h1>Hello $name</h1>
#end proc
#echo greet()

CodeMirror electricInput does not match expression with leading whitespace

In my custom mode for CodeMirror, I want the electricInput event to fire when the user types a line starting with the word bank (with optional leading whitespace).
I have electricInput set up like this: electricInput: /\s*bank$/i
The event DOES fire when the user types bank at the beginning of a line. It does NOT fire when there are spaces before the word bank. Why?
(the RegEx seems to be fine. I have a grammar rule in that mode with the same RegEx, and it matches the token as expected, regardless of leading white spaces:
CodeMirror.defineSimpleMode("myMode", {
start: [
{regex: /\s*bank$/i, token: 'bank', sol: true, indent: true}
Thanks to Marijn's kind help on the CodeMirror discussions forum, I was able to solve this: a custom indent function needs to be passed to defineSimpleMode. Then, we still need to set electricInput (because otherwise the indent function does not get called when typing bank). But no event handler for onElectricInput is required.
CodeMirror.defineSimpleMode("myMode", {
start: [
...
],
meta: {
electricInput: /\s*bank$/i,
indent: function (state, textAfter, line) {
if (textAfter.substring(0, 4).toLowerCase() === 'bank') return 0
return 2;
}
}
});

Simple AHK script not working

I had read lots of pages on AHK but haven't found any that explains how to make a script that enables me to replace "for" when it's typed by the following:
for(int i=0;i<CONDITION;i++)
{
}
I would like it to set cursor focus on inside the brackets to start writing the loop-code right away.
Here is what I have came up until now:
::for::for(int i=0;i<CONDITION;i++),
{,
,
}
Should replace "for" with the code at top of the post but gets the following error:
Error at line 2.
linetext: ,,
Error: this line does not contain recognised action.
The program will exit.
A hotkey (or hotstring) that executes more than one line must list its first line beneath the hotkey (or hotstring).
https://autohotkey.com/docs/FAQ.htm#autoexec
Comma, semicolon, and other characters such as {}^!+# have special meaning in AHK and need to be escaped in order to be interpreted differently than it normally would.
https://autohotkey.com/docs/commands/_EscapeChar.htm
::for::for(int i=0`;i<CONDITION`;i`{+`}`{+`})`n`{`{`}`n`n`{`}`}{Up}
The easiest way to send such a text is this:
; #If WinActive("ahk_class Notepad")
::for::
ClipSaved := ClipboardAll ; save clipboard
clipboard := "" ; empty clipboard
clipboard = ; send this text to the clipboard:
(
for(int i=0;i<CONDITION;i++)
{
}
)
ClipWait, 1 ; wait for the clipboard to contain data
Send, ^v
Send, {Up}
clipboard := ClipSaved ; restore original clipboard
return
; #If
Also fairly simple is this approach, which works well in Scite and Notepad++ which handles tabbing automatically:
::for::
SendRaw,
(
For(int i=0;i<CONDITION;i++)
{
}
)
Send, {Up}{End}
return

Regex for GString Token

I have an ID in this format:
1.5.31.14.${CHANNEL}.${SLOT}
It's read in from a file as a string and I'm trying to replace ${} tokens with other variables.
However, when I run:
id.replaceAll("\${CHANNEL}", "3")
it will attempt to replace ${CHANNEL} with a value called
CHANNEL
within the function and throw an error.
I've been going crazy trying to figure out what RegEx I can use to replace
${CHANNEL} and ${SLOT}
in the ID.
If anybody has any suggestions, please let me know.
EDIT:
Here is the error:
Exception thrown
groovy.lang.MissingPropertyException: No such property: CARD for class: ConsoleScript91
EDIT 2
Here is the code I ran:
def id = '1.5.31.14.${CHANNEL}.${SLOT}'
id = id.replaceAll('\${CHANNEL}', '3')
replaced = id.replaceAll('\${SLOT}', '2')
print replaced
Here is the error:
Exception thrown
java.util.regex.PatternSyntaxException: Illegal repetition near index 0
${CHANNEL}
^
at java_lang_String$replaceAll$1.call(Unknown Source)
at ConsoleScript5.run(ConsoleScript5:2)
You need to escape both dollar sign $ and curly braces {}. Mind the fact that \ is a special character so it should be escaped itself:
def id = '1.5.31.14.${CHANNEL}.${SLOT}'
replaced = id.replaceAll('\\$\\{CHANNEL\\}', '3').replaceAll('\\$\\{SLOT\\}', '2')
replaced2 = id.replaceAll(/\$\{CHANNEL\}/, '3').replaceAll(/\$\{SLOT\}/, '2')
assert replaced == '1.5.31.14.3.2'
assert replaced == replaced2

c++, cscope, ctags, and vim: Finding classes that inherit from this one

In a rather large code base with a few layers is there a way in vim or from the command line to find all classes that are derived from a base class? grep is an option but can be slow since grep does not index.
Neither cscope nor ctags allow us to deal with inheritance directly but it's relatively easy to work around that limitation because derived classes are also indexed.
cscope
In cscope, looking for "C symbol" Foobar usually lists the original class and classes inheriting from it. Since the search is done against a database, it is lightning fast.
Alternatively, you could use cscope's egrep searching capabilities with a pattern like :.*Foobar to list only classes inheriting from Foobar.
So, even if we don't have a dedicated "Find classes inheriting from this class" command, we can get the work done without much effort.
ctags
While ctags allows you to include inheritance information with --fields=+i, that information can't be used directly in Vim. The inherits field is parsed by Vim, though, so it might be possible to build a quick and dirty solution using taglist().
ack, ag
Those two programs work more or less like grep but they are targeted toward searching in source code so they are really faster than grep.
In my Vim config, :grep is set to run the ag program instead of the default grep so, searching for classes derived from the class under the cursor would look like:
:grep :.*<C-r><C-w><CR>
Here are the relevant lines from my ~/.vimrc:
if executable("ag")
set grepprg=ag\ --nogroup\ --nocolor\ --ignore-case\ --column
set grepformat=%f:%l:%c:%m,%f:%l:%m
endif
If you build your tags files with Exuberant CTags using inheritance information (see the --fields option), then the following script will work. It adds an :Inherits command which takes either the name of a class (e.g. :Inherits Foo) or a regular expression.
Like the :tag command, you indicate that you want the search with a regex by preceding it with a '\' character, e.g. :Inherits \Foo.*.
The results are put into the window's location list, which you browse with :ll, :lne, :lp, etc. VIM doesn't seem to allow scripts to modify the tag list which is what I'd prefer.
If you're wondering why I don't use taglist() for this, it's because taglist() is incredibly slow on large tag files. The original post had a version using taglist(), if you're curious you can browse the edit history.
" Parse an Exuberant Ctags record using the same format as taglist()
"
" Throws CtagsParseErr if there is a general problem parsing the record
function! ParseCtagsRec(record, tag_dir)
let tag = {}
" Parse the standard fields
let sep_pos = stridx(a:record, "\t")
if sep_pos < 1
throw 'CtagsParseErr'
endif
let tag['name'] = a:record[:sep_pos - 1]
let tail = a:record[sep_pos + 1:]
let sep_pos = stridx(tail, "\t")
if sep_pos < 1
throw 'CtagsParseErr'
endif
" '/' will work as a path separator on most OS's, but there
" should really be an OS independent way to build paths.
let tag['filename'] = a:tag_dir.'/'.tail[:sep_pos - 1]
let tail = tail[sep_pos + 1:]
let sep_pos = stridx(tail, ";\"\t")
if sep_pos < 1
throw 'CtagsParseErr'
endif
let tag['cmd'] = tail[:sep_pos - 1]
" Parse the Exuberant Ctags extension fields
let extensions = tail[sep_pos + 3:]
for extension in split(extensions, '\t')
let sep_pos = stridx(extension, ':')
if sep_pos < 1
if has_key(tag, 'kind')
throw 'CtagsParseErr'
endif
let tag['kind'] = extension
else
let tag[extension[:sep_pos - 1]] = extension[sep_pos + 1:]
endif
endfor
return tag
endfunction
" Find all classes derived from a given class, or a regex (preceded by a '/')
" The results are placed in the current windows location list.
function! Inherits(cls_or_regex)
if a:cls_or_regex[0] == '/'
let regex = a:cls_or_regex[1:]
else
let regex = '\<'.a:cls_or_regex.'\>$'
endif
let loc_list = []
let tfiles = tagfiles()
let tag_count = 0
let found_count = 0
for file in tfiles
let tag_dir = fnamemodify(file, ':p:h')
try
for line in readfile(file)
let tag_count += 1
if tag_count % 10000 == 0
echo tag_count 'tags scanned,' found_count 'matching classes found. Still searching...'
redraw
endif
if line[0] == '!'
continue
endif
let tag = ParseCtagsRec(line, tag_dir)
if has_key(tag, 'inherits')
let baselist = split(tag['inherits'], ',\s*')
for base in baselist
if match(base, regex) != -1
let location = {}
let location['filename'] = tag['filename']
let cmd = tag['cmd']
if cmd[0] == '/' || cmd[0] == '?'
let location['pattern'] = cmd[1:-2]
else
let location['lnum'] = str2nr(cmd)
endif
call add(loc_list, location)
let found_count += 1
endif
endfor
endif
endfor
catch /^OptionErr$/
echo 'Parsing error: Failed to parse an option.'
return
catch /^CtagsParseErr$/
echo 'Parsing error: Tags files does not appear to be an Exuberant Ctags file.'
return
catch
echo 'Could not read tag file:' file
return
endtry
endfor
call setloclist(0, loc_list)
echo tag_count 'tags scanned,' found_count 'matching classes found.'
endfunction
command! -nargs=1 -complete=tag Inherits call Inherits('<args>')
In lh-cpp, I define the command :Children. It relies on a ctags database, and as a consequence, it is quite limited.
It takes two optional parameters: the namespace where to look for (I haven't found a way to avoid that), and the name of the parent class -> :Children [!] {namespace} {parent-class}.
The command tries to cache as much information as possible. Hence, when pertinent information changes in the ctags database, the cache must be updated. It is done by banging the command -> :Children!
I don't think vim is the correct tool to list all child classes. Instead, we'd better use the doxygen to generate documentation for the source code. Although the doxygen needs some time, we can use the document/diagrams for all classes, which is clear and fast.