lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Dimitris Papavasiliou wrote:
I've written this code which, given a lua command line in a string (s) and a cursor position (i) finds all candidate table keys to complete the command at the position. It completes table indexing expressions of the form 'table.keyprefix'.
[snip]
Anyway since this is my first attempt at string processing lua code (or any non-trivial lua code for that matter) I'm sure there are better ways to do some things. I'm not (particularly) interested in speed, more in better code. I'm especially looking for better ways to:
a) find the substring I need to parse (the first while loop).
b) if possible when breaking sub into path and prefix use a pattern that will return "" for path and the whole string sub for prefix if the string sub contains no dots. Then I can get rid of the if statement afterwards and use something like table = assert(loadstring("return " .. path))() or nil instead.

Hey, this is an interesting idea, scanning actual Lua table to search the list of active members of tables.

Here is your original code, with some comments added (stream comments):

-- the command and cursor position
s = "print(string."
i = string.len(s)

-- scan the string backwards starting at the cursor to find the substring
-- we're interested in (sub)
--[[ Why are you including '\' in the accepted chars? ]]
while string.find(string.sub(s, i, i), "[%w_\.]") do
--[[ Little optimisation on this kind of things: put found chars in an array, and table.concat them ]]
   sub = string.sub(s, i, i) .. (sub or "")
   i = i - 1
end


--[[ It seems OK here, but you should avoid to use 'table'
as variable name, as it is the name of a library table...
No need of real variables for returned indexes,
tradition is to use _ as dummy variable. ]]
local i, j, prefix, path, table

-- break a string of the form "foo.bar.pre" into "foo.bar" (path) and
-- "pre" (prefix).
i, j, path, prefix = string.find(sub, "([%w_%.]+)%.([%w_]*)")

-- if there were no dots in the string then the table from wich to complete
-- is _G and the prefix is the whole string
if(path == nil) then
   table = _G
   prefix = sub
else
   -- else get the table represented by path
   table = assert(loadstring("return " .. path))()
end

-- now look up all keys in table starting with (but different from) prefix and print them
for k in pairs(table) do
--[[ Perhaps a bit faster, not going the regex route (and avoid two concats)...
len = string.len(prefix) -- Before the loop
   if string.sub(k, 1, len) == prefix then
]]
   if string.find(k, "^" .. prefix .. ".+") then
       print(k)
   end
end

Now, my version, where I tried to do some optimisations:

-- the command and cursor position
s = "print(d.string.g"
i = string.len(s)

-- scan the string backwards starting at the cursor to find the substring
-- we're interested in (sub)
while string.find(string.sub(s, i, i), "[%w_\.]") do
   i = i - 1
end
sub = string.sub(s, i)

local prefix, path, scantable, len

-- break a string of the form "foo.bar.pre" into "foo.bar" (path) and
-- "pre" (prefix).
_, _, path, prefix = string.find(sub, "([%w_.]-)%.([%w_]*)")
--~ print("-> " .. (path or 'nil') .. '|' .. (prefix or 'nil'))

-- if there were no dots in the string then the table from wich to complete
-- is _G and the prefix is the whole string
if path == nil then
   scantable = _G
   prefix = sub
else
   -- else get the table represented by path
   scantable = assert(loadstring("return " .. path))() or {}
end

-- now look up all keys in table starting with (but different from) prefix and print them
len = string.len(prefix)
for k in pairs(table) do
   if string.len(k) > len and -- Instantaneous and avoids a number of string extractions
			string.sub(k, 1, len) == prefix then
       print(k)
   end
end

Sorry, I didn't found yet a way to improve the path/prefix regex, but frankly I find the above test probably clearer than a complex regex and some tricks.

---

Others answers:

Scott wrote:
One thing your code does not consider that can be very helpfull is the use
of the ':' seperator in addition to '.'  I wrote something very similar for
the console of our game and I don't remember there being any trick to
getting ':' working.  Just check for ':' anywhere you would also look for
'.'

I don't agree, at least in the scope of this snippet.
When you have myFileHandle:o you cannot know what kind of object is myFileHandle, so you cannot provide efficient autocompletion (ie. only the set of methods applicable to this object).
Or if you can, I would be interested to know how.

---

Sam wrote:
I tried your piece of code, and I found that your autocompletion forgets an element when you specify the begining of the string. This element is the string itself.

No, and the comment on the last loop makes this clear: there is no point in listing the string itself, as there is nothing more to autocomplete...

---

Note: SciTE provides a nice autocompletion support of various language, including Lua. Autocompletion isn't based on real language, but rather on a list of functions and parameters.

HTH.

--
--=#=--=#=--=#=--=#=--=#=--=#=--=#=--=#=--=#=--
Philippe Lhoste (Paris -- France)
Professional programmer and amateur artist
http://jove.prohosting.com/~philho/ (outdated)
http://philho.multimania.com (in French, for files to download)
--=#=--=#=--=#=--=#=--=#=--=#=--=#=--=#=--=#=--