How to create a "new mode" for vim (i.e., loop and get user input, then execute the associated actions) - editor

I'm trying to create something of a "new mode" in vim. The details of the mode are unimportant, but there is one thing I need to be able to do.
I need to do something like the following pseudo-code:
get user input (movement keys like "j" or complex keys like "dd")
while user_input != <esc>
execute the user input
endwhile
In other words, I need a loop that will read what the user is doing, then perform the associated action.
I've already got the following code:
let char = nr2char(getchar())
while char =~ '^\w$'
execute "normal ". char
let char = nr2char(getchar())
endwhile
This works fine for user movements (j, k, etc.), but fails for more complex multi-character commands like dd.
Also, this is a small annoyance, but the cursor disappears during getchar(), meaning you effectively can't see the cursor (this is of less importance because of what I'm trying to do, but hopefully has a solution as well).
Does anyone have any idea how I can get multi-character actions to work?

I think you might be interested in submode.vim, if not to use it, to at least see how they've implemented this feature.

I usually redefine locally (:h map-<buffer>, for instance) the things this new mode is meant to change. And I also override <esc> to unregister those things from the mode.
This is the easier approach IMO.

Just for the record, for creating a new mode that accepts commands of multiple length you usually use something like a parse tree with keys and commands. Here is a simple version in vim:
let g:EvalTree = { 'root': {} }
function! s:AddKeyMapRecursive(root, keys, command) abort
if !has_key(a:root, a:keys[0])
let a:root[a:keys[0]] = { 'command': '', 'children': {} }
endif
if len(a:keys) == 1
let a:root[a:keys[0]].command = a:command
else
call s:AddKeyMapRecursive(a:root[a:keys[0]].children, a:keys[1 : ], a:command)
endif
endfunction
function! g:EvalTree.AddMap(keys, command) abort
call s:AddKeyMapRecursive(l:self.root, a:keys, a:command)
endfunction
function! s:GetNodeRecursive(root, keys) abort
if !has_key(a:root, a:keys[0])
return 0
endif
if len(a:keys) == 1
return a:root[a:keys[0]]
else
return s:GetNodeRecursive(a:root[a:keys[0]].children, a:keys[1 : ])
endif
endfunction
function! g:EvalTree.GetNode(keys) abort
return s:GetNodeRecursive(l:self.root, a:keys)
endfunction
You can insert in this tree like:
call g:EvalTree.AddMap('hw', ":echo 'hello world'<CR>")
call g:EvalTree.AddMap('DA', 'ggdG')
Later you can use this tree to eval things in the loop you mention. Although you will probably need a also a queue to do this. You can define one as:
let g:TextQueue = { 'text': '', 'index': 0 }
function! g:TextQueue.Push(c) abort
let l:self.text .= a:c
endfunction
function! g:TextQueue.Pop(...) abort
let l:self.index += get(a:, 1, 1)
endfunction
function! g:TextQueue.CheckFirst() abort
return l:self.text[l:self.index]
endfunction
function! g:TextQueue.Text() abort
return l:self.text[l:self.index : ]
endfunction
function! g:TextQueue.Empty() abort
return l:self.index >= strlen(l:self.text)
endfunction
function! g:TextQueue.ReInitialize() abort
let [l:self.text, l:self.index] = ['', 0]
endfunction
And using that queue and the tree make the evaluation loop. This is the part were I don't really know how to do it properly myself, but after trying a while a came to a functional (although ugly and very inefficient since eval some things much more than necessary) code that is this:
function! s:Execute(map) abort
execute 'normal! ' . a:map.command
redraw
call g:TextQueue.Pop(strlen(a:map.keys))
let [a:map.keys, a:map.command] = ['', '']
endfunction
function! s:LimitTimeHasElapsed(time) abort
return (a:time + 1 < localtime())
endfunction
function! g:EvalTree.Start() abort
let l:time = localtime()
let l:stored = { 'keys': '', 'command': '' }
while g:TextQueue.CheckFirst() !=# "\<Esc>"
let l:char_code = getchar(0)
if l:char_code
call g:TextQueue.Push(nr2char(l:char_code))
endif
if !g:TextQueue.Empty()
let l:possible_maps = g:EvalTree.GetNode(g:TextQueue.Text())
if type(l:possible_maps) != type({})
if !empty(l:stored.command)
call s:Execute(l:stored)
elseif g:TextQueue.CheckFirst() !=# "\<Esc>"
call g:TextQueue.Pop()
endif
let l:time = localtime()
continue
endif
if l:possible_maps.command !=# ''
let l:stored.keys = g:TextQueue.Text()
let l:stored.command = l:possible_maps.command
if l:possible_maps.children == {} || s:LimitTimeHasElapsed(l:time)
call s:Execute(l:stored)
let l:time = localtime()
endif
elseif s:LimitTimeHasElapsed(l:time)
let [l:stored.keys, l:stored.command] = ['', '']
call g:TextQueue.pop()
let l:time = localtime()
endif
else
call g:TextQueue.ReInitialize()
let l:time = localtime()
endif
sleep 20m
endwhile
endfunction
Now you simply have to define a several mappings. Ie:
call g:EvalTree.AddMap('hw', ":echo 'hello workd'\<CR>")
call g:EvalTree.AddMap('h', 'h')
call g:EvalTree.AddMap('l', 'l')
call g:EvalTree.AddMap('j', 'j')
call g:EvalTree.AddMap('k', 'k')
call g:EvalTree.AddMap('H', '0')
call g:EvalTree.AddMap('L', '$')
call g:EvalTree.AddMap('K', 'H')
call g:EvalTree.AddMap('J', 'L')
call g:EvalTree.AddMap('dd', 'dd')
call g:EvalTree.AddMap('Z', 'yap')
call g:EvalTree.AddMap('D', 'dap')
call g:EvalTree.AddMap('ab', ":echo 'hello'\<CR>")
call g:EvalTree.AddMap('abc', ":echo 'world'\<CR>")
call g:EvalTree.AddMap('abcd', ":echo 'hello world'\<CR>")
And finally, start the evaluation:
call g:EvalTree.Start()
You all may also be interested in watching my plugin 'EXtend.vim' and its ReadLine() function. It doesn't eval multi character commands but does a couple of things that are indeed pretty interesting, like emulating cursors and selections inside its submode.

Related

I made a (very simple) programming language in Lua, but I can only execute one command

Hello people of stackoverflow, I've made a (very) simple programming language, it kind of looks like minecraft commands. Here is the code
function wait(cmdSleepGetNum)-- you can name this function
--what ever you want, it doesnt matter
local start = os.time()
repeat until os.time() > start + cmdSleepGetNum
end
local input = io.read()
if input == "/help" then
print"you'll find it out"
else if input == "/say" then
print"what do you want to say?"
local cmdSay = io.read()
print(cmdSay)
else if input == "/stop" then
os.exit()
else if input == "/sleep" then
print"how long?"
local cmdSleepGetNum = io.read()
wait(cmdSleepGetNum)
else if input == "/rand" then
local randNum = math.random()
print(randNum)
end
end
end
end
end
Now I know what you are thinking "what is the problem here?" the problem is that I can only execute one command and after that command is finished by the Lua interpreter, I cannot execute any other commands.
for example:
/rand
0.84018771715471 (the /rand command is executed and prints out a random number)
/rand
stdin:1: unexpected symbol near '/' (this happens when i try to execute another command)
If you replace else if with elseif, you won't need so many end's,
You can get rid of if's altogether, is you use a table,
As #Egor Skriptunoff said, you need to create a main loop to run many commands,
I suggest that you add an optional argument to /sleep and /say.
local function wait (cmdSleepGetNum) -- you can name this function
-- whatever you want, it doesn't matter.
local start = os.time ()
repeat until os.time () > start + cmdSleepGetNum
end
local commands = {
help = function ()
print "you'll find it out"
end,
say = function (arg)
if arg == '' then
print 'what do you want to say?'
arg = io.read ()
end
print (arg)
end,
stop = function ()
os.exit ()
end,
sleep = function (arg)
if arg == '' then
print 'how long?'
arg = tonumber (io.read ())
end
wait (tonumber (arg))
end,
rand = function ()
local randNum = math.random ()
print (randNum)
end,
[false] = function () -- fallback.
print 'Unknown command'
end
}
-- Main loop:
while true do
io.write '> '
local key, _, arg = io.read ():match '^%s*/(%S+)(%s*(.*))$' -- you can type /sleep 1, etc. in one line.
local command = key and key ~= '' and commands [key] or commands [false]
command (arg)
end

Lua, Modify print function

I am writing a generic Log() function in lua which utilizes lua print function:
Log (variable, 'String: %s ', str, 'Word: %d', w)
Currently I'm using below approach:
print(string.format (variable, 'String: %s ', str, 'Word: %d', w))
I tried something like:
Log = function(...) begin
return print(string.format(...))
end
But it doesn't work, Is this correct approach? Or Is there any better more generic way to get this done?
If you just want to print a sequence of values, you can do that with print:
print(variable, 'String: %s ', str, 'Word: %d', w)
What you seem to want is something more complicated. Your algorithm seems to be:
For each argument:
If the argument is not a string, then convert it to a string and print it.
If the argument is a string, figure out how many % patterns it has (let us call this number k). Pass string.format the current argument string and the following k parameters, printing the resulting string. Advance k parameters.
That's a much more complicated algorithm than can be done in a one-line system.
Using Lua 5.3, here's what such a function would look like (note: barely tested code):
function Log(...)
local values = {}
local params = table.pack(...)
local curr_ix = 1
while (curr_ix <= params.n) do
local value = params[curr_ix]
if(type(value) == "string") then
--Count the number of `%` characters, *except* for
--sequential `%%`.
local num_formats = 0
for _ in value:gmatch("%%[^%%]") do
num_formats = num_formats + 1
end
value = string.format(table.unpack(params, curr_ix, num_formats + curr_ix))
curr_ix = curr_ix + num_formats
end
values[#values + 1] = value
curr_ix = curr_ix + 1
end
print(table.unpack(values))
end
I don't think your current approach works, because the first argument of string.format expects the format specifier, not the rest of the arguments.
Anyway, this is the way to combine formatting and printing together:
Log = function(...)
return print(string.format(...))
end
And call it like this:
Log("String: %s Number: %d", 'hello' , 42)
Also, it might be better to make the format specifier argument more explicit, and use io.write instead of print to get more control over printing:
function Log(fmt, ...)
return io.write(string.format(fmt, ...))
end

What is the alternative for switch statement in Lua language?

I have this piece of code in C++ and i want to know how can i write some codes that replace switch statement in Lua because i face many problems and i need to use this statement.
int choice;
do// loop
{
cout<<"\n >>> The General Menu <<< \n";
cout << endl;
cout<< " press (1) to Add "<<endl;
cout<< " press (2) to Save "<<endl;
cout<< " press (3) to Quit " << endl;
cout<< endl;
cout<< "Enter your choice please (1/2/3): ";
cin>>choice;
switch(choice)
{
case 1:
add();
break;
case 2:
save();
break;
default:
cout<<" The program has been terminated "<<endl;
cout<<" Thank you! \n";
}
} while (choice != 3);
}
The statement has been used inside a do..while loop.
In general, if you want a switch statement in Lua, what you ought to be doing is building a table. For your simple case of choice that could be 1, 2, or fail, a simple if statement with a few conditions is sufficient. For more complex cases, a table of functions should be employed:
local c_tbl =
{
[1] = add,
[2] = save,
}
local func = c_tbl[choice]
if(func) then
func()
else
print " The program has been terminated."
print " Thank you!";
end
You can use lexical scoping to allow the functions in the table to be able to access local variables, just as if the code was written inline.
Try this one (click here to run the script in a Lua compiler), Hope the code is self-explanatory ;-) and
resembles the same pseudo code format..!!
print("enter your choice : ")
mychoice = io.read()
switch = function (choice)
-- accepts both number as well as string
choice = choice and tonumber(choice) or choice -- returns a number if the choic is a number or string.
-- Define your cases
case =
{
[1] = function ( ) -- case 1 :
print("your choice is Number 1 ") -- code block
end, -- break statement
add = function ( ) -- case 'add' :
print("your choice is string add ") -- code block
end, -- break statement
['+'] = function ( ) -- case '+' :
print("your choice is char + ") -- code block
end, -- break statement
default = function ( ) -- default case
print(" your choice is din't match any of those specified cases")
end, -- u cant exclude end hear :-P
}
-- execution section
if case[choice] then
case[choice]()
else
case["default"]()
end
end
-- Now you can use it as a regular function. Tadaaa..!!
switch(mychoice)
Lua:
if choice == 1
then add()
elseif choice == 2
then save()
else print "The program has been terminated\nThank you!"
end
one more version of switcher (without initializing table as variable):
local case=2;
local result=({[1]="case1", [2]="case2", 3="case3"})[case];
print (result); --> case2
While simply creating a table indexed by cases with functions as elements is most probably the fastest approach, there is this solution I've made which IMO has better code readability:
function switch(element)
local Table = {
["Value"] = element,
["DefaultFunction"] = nil,
["Functions"] = {}
}
Table.case = function(testElement, callback)
Table.Functions[testElement] = callback
return Table
end
Table.default = function(callback)
Table.DefaultFunction = callback
return Table
end
Table.process = function()
local Case = Table.Functions[Table.Value]
if Case then
Case()
elseif Table.DefaultFunction then
Table.DefaultFunction()
end
end
return Table
end
Example Use:
switch(Player:GetName())
.case("Kate", function() print("This player's name rhymes with Fate")end)
.case("Tod", function() print("This player's name rhymes with Cod") end)
.default(function() print("This player's name is not Kate or Tod") end)
.process()
I encountered this issue with functions that would take different parameters - something which the other answers don't handle well.
I solved that with anonymous functions.
-- call the relevant execution based on its opcode
local instructions = {
[01] = function () self:perform_add(table.unpack(valargs)) end,
[02] = function () self:perform_multiply(table.unpack(valargs)) end,
[03] = function () self:perform_store_input(outputargs[1]) end,
[04] = function () self:perform_output(valargs[1]) end,
[05] = function () self:perform_jnz(table.unpack(valargs)) end,
[06] = function () self:perform_jz(table.unpack(valargs)) end,
[07] = function () self:perform_less_than(table.unpack(valargs)) end,
[08] = function () self:perform_equals(table.unpack(valargs)) end,
[99] = function () self:perform_exit() end,
}
local instr = instructions[opcode]
if (instr) then
instr()
else
print("No instruction for opcode " .. opcode)
end
The actions I want to take in my different switch cases are all defined as anonymous functions in a table. The keys used (e.g. 08 here) are the values our variable to switch on might assume (opcode here). The default case of the switch statement happens in my else clause. There is no requirement for a break equivalent - but if you want to have one case continue with the next you would have to call it explicitly.
Reply to comment asking for clarification:
You're right that this example is not complete. You can find my usage here when I did adventofcode 2019 day 7. I can try answer your questions but I never touched lua before, and never after. valargs is a table of arguments because different functions here take different numbers of arguments. But that is not necessarily relevant to the question. Basically, I'm just calling functions here.
In my example, self exists because I defined the functions on a local (and did some weird changes as outlined here). The relevant code parts:
-- a "class"
local IntComputer = {}
function IntComputer:perform_exit()
self.program_ended = true
end
function IntComputer:perform_add(a, b, target)
print(" " .. a .. " + " .. b .. " => " .. target)
self:set_value(target, a+b)
end
If you want switch as a function that is callable, you could use something funny with the callback feature:
(The example below is a switch statement based on the variable type, but you could make the table index into whatever you want to test it for. Just change the return statement of the switch function to not test for type(case))
(This is essentially a lazy table lookup much like Python's dictionary feature but each element is a function)
#!/usr/bin/lua
-- Callback switch statement:
local function switch(a, case)
-- Local variable instead of function(a) on every case:
local value = a
-- Cases list:
local switchcase = {}
-- Cases:
switchcase["string"] = function()
return (tostring(value) .. " is a string")
end
switchcase["number"] = function()
return tostring(value .. " is a number")
end
switchcase["boolean"] = function()
return (tostring(avalue) .. " is a boolean")
end
return switchcase[type(case)](a)
end
local value = 5
print(switch(value,value)) --> 5 is a number
local value = "test"
print(switch(value,value)) --> test is a string
local value = true
print(switch(value,value)) --> true is a boolean
I don't know the performance of this code compared to the two answers given above, but using local variables ought to make it quick enough for repeated use. If you make your switch function in the global scope it could become a standard function for your project to be used.
Here's another fun method using loadstring() and a table lookup.
switch = function(cases,args)
if (cases[args] == nil) then return args else return assert(loadstring ('return ' .. cases[args]))() end
end
local case = 2
local result = switch({
[1] = "2^" .. case,
[2] = string.format("2^%i",case),
[3] = tostring(2^case)
},
case
)
print(result) --> 4
This method is somewhat dangerous to use since loadstring() is similar to Python's eval() function.
I found it ugly to write "function(x)" on every case in the examples provided by the Lua wiki. This is a neat way.
The "default" case is the "return args" part of the function.
I use this code :
while true do local tmpswitch1 = exp ; --[[ switch <exp> do ]]
if tmpswitch1 == exp1 then --[[ case <exp1> : ]]
-- do something
break
end ;if tmpswitch1 == exp2 then --[[ case <exp2> : ]]
-- do something
break
end ; --[[ default : ]]
-- do something
break ; end --[[ switch tmpswitch1 ]]
function case(i,d) return function(t) return t[i] or d end end
x='two'
r=case(x) {
one=1,
two=2,
three=3,
}
case(r,function() print "default" end) {
[1]=function() print "one" end,
[2]=function() print "two" end,
[3]=function() print "three" end,
}()

Unable to figure out lua table inheritence

Hope someone can make sense of what I'm attempting to figure out, Just don't seem to understand Lua enough to achieve this.
--[[
tbl.a.test("moo") returns "Table A moo appears"
tbl.b.test("moo") returns "moo appears"
]]
tbl = {
a = { ID = "Table A" },
b = {
test = function(...) print(... .. " appears") end,
},
}
tbl.a__index = function(self, ...) tbl.b[self](tbl.a.ID .. ...) end
What I'm attempting to do is I could create several tables a, c, d, e and not have to copy test to each one. When tbl.a.test, tbl.c.test, tbl.d.test is used, It'll retrieve the tbl.a.ID var, then call tbl.b.test(ID, "moo")
So far all I'm finding out is it's not able to find .test on anything other than tbl.b
** EDIT **
Thank's to support so far the code is now;
tbl = {
a = { ID = "Table A " },
b = { test = function(...) local id, rest = ... print(id .. ": " .. rest) end },
}
setmetatable(tbl.a, {__index=function(self, k, ...) local rest = ... return tbl.b[k](tbl.a.ID, rest) end})
However, the ... is not being progressed for some odd reason :|
You're missing a period between tbl.a and __index.
__index needs to be on a's metatable, not the table itself.
You don't return anything from your __index function
self in the __index function is the table being indexed, not the key (which is the second argument)
This should work:
setmetatable(tbl.a, {__index=function(self, k) return tbl.b[k](tbl.a.ID) end})
--------------------------------------------------------------------------------
-- [Sub]Class creation
--------------------------------------------------------------------------------
function newclass(new_obj,old_obj)
old_obj = old_obj or {} --use passed-in object (if any)
new_obj = new_obj or {}
assert(type(new_obj) == 'table','New Object/Class is not a table')
assert(type(old_obj) == 'table','Old Object/Class is not a table')
old_obj.__index = old_obj --store __index in parent object (optimization)
return setmetatable(new_obj,old_obj) --create 'new_obj' inheriting 'old_obj'
end
--------------------------------------------------------------------------------
prototype = {
test = function(self,s) print('Table ' .. self.id .. ' ' .. s .. ' appears') end
}
tbl = {}
tbl.a = newclass({id = 'A'},prototype)
tbl.b = newclass({id = 'B'},prototype)
tbl.a:test('moo')
tbl.b:test('moo')
The distinction between class and object in Lua is only theoretical. In practice they are implemented exactly the same way.
Anytime you need to do inheritance, you can use my general-purpose newclass() function to either create a new class/object, or inherit from an existing one.
Any common code & data you would like to have passed on should go into the 'prototype' table (whatever you'd like to call it for each case).
Also, you seem to forget to use the method calling syntax (that uses a colon instead of a dot) when calling methods. Without it, the self parameter is not automatically recognized.

Print a table in pairs

I'm trying to use in pairs inside a function but it doesn't work, it just prints the first row (key). This is my code:
set = {1,2,3,4};
unset = {5,6,7,8};
function listen(ftype)
if (ftype == [[~]]) then
for num,str in pairs(set) do
return str;
end
end
if (ftype == [[$]]) then
for num,str in pairs(unset) do
return str;
end
end
end
print(listen([[~]])..[[ =:= ]]..listen([[$]]));
If I do something like this..
for num,str in pairs(unset) do
print(str);
end
It works like a charm. That’s exactly what I want but inside a function.
You can build your own iterator:
function double_pair(t1, t2)
local i = 0
return function() i = i + 1
return t1[i] and t1[i] .. " =:= " .. t2[i]
end
end
Then you can use it like this:
for str in double_pair(set, unset) do
print(str)
end
Output:
1 =:= 5
2 =:= 6
3 =:= 7
4 =:= 8
Note that you don't need semicolons to end your statement, unless the statements are in one line and you want to make them clear. And [[ =:= ]] is usually used to build long multi-line strings, normally we choose to use double quote " =:= " or single quote ' =:= '.
A function can't return multiple times. Putting an unconditional return inside a loop is nonsensical - it will never get to the second iteration of the loop.
You are essentially trying to return multiple values from the function. Lua supports that; you could, for instance, just return 1,2,3,4. For an unknown number of return values, you can build them up in a table and call unpack on it, like so:
function listen(ftype)
local result = {}
local num, str
if (ftype == [[~]]) then
for num,str in pairs(set) do
table.insert(result, str)
end
elseif (ftype == [[$]]) then
for num,str in pairs(unset) do
table.insert(result, str)
end
end
return unpack(result)
end
But since your results are already in a couple tables, it would be silly to reconstruct them that way. You can just unpack the originals:
function listen(ftype)
if (ftype == [[~]]) then
return unpack(set)
elseif (ftype == [[$]]) then
return unpack(unset)
end
end
Great. But when you put the function call into an expression like your print statement, it will only return the first value, which puts you back where you started.
So to print out your pairs, you can't avoid having to either:
1) do some iteration outside the function
or
2) do the actual printing inside the function
The cleanest solution is probably a custom iterator, as suggested by #YuHao.

Resources