Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Tooltips to show variable details like state/type/signature/value.
The plug-in now shows tooltips for the current split window in graphical
Vim, when you hover over variables with the mouse cursor. When you don't
understand/can't explain the dynamic highlighting being performed by
luainspect.vim then just hover over the variable in question and you'll
get a textual description of whatever is causing the problem. When the
value of the variable is known you'll get a preview of that as well (in
the case of table the first 20 keys are shown and for functions the file
and line number where they were defined are shown).
  • Loading branch information
xolox committed Aug 11, 2010
1 parent bb99531 commit dc640de
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 12 deletions.
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -2,12 +2,14 @@

The Vim plug-in `luainspect.vim` uses the [LuaInspect](http://lua-users.org/wiki/LuaInspect) tool to (automatically) perform semantic highlighting of variables in Lua source code. It was inspired by [lua2-mode](http://www.enyo.de/fw/software/lua-emacs/lua2-mode.html) (for [Emacs](http://www.gnu.org/software/emacs/)) and the [SciTE](http://www.scintilla.org/SciTE.html) plug-in included with LuaInspect. In addition to the semantic highlighting the following features are currently supported:

* If the text cursor is on a variable while the highlighting is refreshed then all occurrences of the variable will be marked in the style of [Vim's cursorline option](http://vimdoc.sourceforge.net/htmldoc/options.html#%27cursorline%27).

* Press `<F2>` with the text cursor on a variable and the plug-in will prompt you to rename the variable.

* Press `gd` (in normal mode) with the text cursor on a variable and you'll jump to its declaration / first occurrence.

* When you hover over a variable with the mouse cursor in graphical Vim, information about the variable is displayed in a tooltip.

* If the text cursor is on a variable while the highlighting is refreshed then all occurrences of the variable will be marked in the style of [Vim's cursorline option](http://vimdoc.sourceforge.net/htmldoc/options.html#%27cursorline%27).

* When a syntax error is found (during highlighting or using the rename functionality) the lines where the error is reported will be marked like a spelling error.

![Screenshot of semantic highlighting](http://peterodding.com/code/vim/luainspect/screenshot.png)
Expand Down
2 changes: 1 addition & 1 deletion TODO.md
Expand Up @@ -2,6 +2,6 @@

* Right now the highlighting styles used by `luainspect.vim` are the same as those used by the SciTE plug-in and they don't work well on dark backgrounds. As soon as I get around to picking some alternate colors I'll include those in the plug-in.

* Bindings for other features of LuaInspect such as showing tooltips for variables, omni completion for in scope variables (including display of library function signatures), etc. This might be a lot of work but could prove to be really useful in making Lua easy to use in Vim.
* Bindings for other features of LuaInspect such as omni completion for in scope variables (including display of library function signatures).

* Document the g:lua_inspect_path option.
21 changes: 18 additions & 3 deletions luainspect.vim
Expand Up @@ -2,7 +2,7 @@
" Author: Peter Odding <peter@peterodding.com>
" Last Change: August 11, 2010
" URL: http://peterodding.com/code/vim/lua-inspect/
" Version: 0.3.2
" Version: 0.3.3
" License: MIT

" Support for automatic update using the GLVS plug-in.
Expand Down Expand Up @@ -80,14 +80,25 @@ function! s:init_lua_buffer()
inoremap <buffer> <silent> <F2> <C-o>:call <Sid>run_lua_inspect('rename', 0, 1)<CR>
nnoremap <buffer> <silent> <F2> :call <Sid>run_lua_inspect('rename', 0, 1)<CR>
nnoremap <buffer> <silent> gd :call <Sid>run_lua_inspect('goto', 0, 1)<CR>
setlocal ballooneval balloonexpr=LuaInspectToolTip()
endif
endfunction

function! LuaInspectToolTip() " {{{2
let text = s:run_lua_inspect('tooltip', 0, 1)
return type(text) == type('') ? text : ''
endfunction

function! s:run_lua_inspect(action, toggle, enabled) " {{{2
if !a:toggle || s:set_plugin_enabled(a:enabled)
let lines = getline(1, "$")
call insert(lines, col('.'))
call insert(lines, line('.'))
if a:action == 'tooltip'
call insert(lines, v:beval_col)
call insert(lines, v:beval_lnum)
else
call insert(lines, col('.'))
call insert(lines, line('.'))
endif
call insert(lines, a:action)
call s:parse_text(join(lines, "\n"), s:prepare_search_path())
if !empty(b:luainspect_output)
Expand All @@ -114,6 +125,10 @@ function! s:run_lua_inspect(action, toggle, enabled) " {{{2
call setpos('.', [0, linenum, colnum, 0])
call xolox#message("") " Clear any previous message to avoid confusion.
endif
elseif response == 'tooltip'
if len(b:luainspect_output) > 1
return join(b:luainspect_output[1:-1], "\n")
endif
elseif response == 'rename'
if len(b:luainspect_output) == 1
call xolox#warning("No variable under cursor!")
Expand Down
137 changes: 131 additions & 6 deletions luainspect4vim.lua
Expand Up @@ -9,9 +9,12 @@
--]]
local MAX_PREVIEW_KEYS = 20
local LI = require 'luainspect.init'
local LA = require 'luainspect.ast'
local actions, myprint, getcurvar = {}
local LS = require 'luainspect.signatures'
local actions, myprint, getcurvar, knownvarorfield = {}
if type(vim) == 'table' and vim.eval then
-- The Lua interface for Vim redefines print() so it prints inside Vim.
Expand All @@ -35,6 +38,12 @@ function getcurvar(tokenlist, line, column)
end
end
function knownvarorfield(token)
local a = token.ast
local v = a.seevalue or a
return a.definedglobal or v.valueknown and v.value ~= nil
end
function actions.highlight(tokenlist, line, column)
local curvar = getcurvar(tokenlist, line, column)
for i, token in ipairs(tokenlist) do
Expand All @@ -60,11 +69,7 @@ function actions.highlight(tokenlist, line, column)
kind = 'luaInspectLocal'
end
elseif token.ast.isfield then
if token.ast.definedglobal or token.ast.seevalue.valueknown and token.ast.seevalue.value ~= nil then
kind = 'luaInspectFieldDefined'
else
kind = 'luaInspectFieldUndefined'
end
kind = knownvarorfield(token) and 'luaInspectFieldDefined' or 'luaInspectFieldUndefined'
end
if kind then
local l1, c1 = unpack(token.ast.lineinfo.first, 1, 2)
Expand All @@ -74,6 +79,126 @@ function actions.highlight(tokenlist, line, column)
end
end
function actions.tooltip(tokenlist, line, column)
local text = {}
local token = getcurvar(tokenlist, line, column)
if not token then return end
local ast = token.ast
if not ast then return end
-- Describe the variable type and status.
if ast.localdefinition then
if not ast.localdefinition.isused then text[#text+1] = "unused" end
if ast.localdefinition.isset then text[#text+1] = "mutable" end
if ast.localmasking then text[#text+1] = "masking" end
if ast.localmasked then text[#text+1] = "masked" end
if ast.localdefinition.functionlevel < ast.functionlevel then
text[#text+1] = "upvalue"
elseif ast.localdefinition.isparam then
text[#text+1] = "function parameter"
else
text[#text+1] = "local variable"
end
elseif ast.tag == 'Id' then
text[#text+1] = knownvarorfield(token) and "known" or "unknown"
text[#text+1] = "global variable"
elseif ast.isfield then
text[#text+1] = knownvarorfield(token) and "known" or "unknown"
text[#text+1] = "table field"
else
return
end
-- TODO Bug in luainspect's static analysis? :gsub() below is marked as an
-- unknown table field even though table.concat() returns a string?!
text = table.concat(text, ' ')
myprint("This is " .. (text:find '^[aeiou]' and 'an' or 'a') .. ' ' .. text .. '.')
-- Display signatures for standard library functions.
local name = ast.resolvedname
local signature = name and LS.global_signatures[name]
if not signature then
local value = (ast.seevalue or ast).value
for name, sig in pairs(LS.global_signatures) do
if value == loadstring('return ' .. name)() then
signature = sig
end
end
end
if signature then
-- luainspect/signatures.lua contains special bullet characters in the
-- latin1 character encoding (according to Vim) which Vim doesn't like
-- in tooltips (I guess because it expects UTF-8).
signature = signature:gsub('\183', '.')
if not signature:find '%w %b()$' then
myprint 'Its description is:'
myprint(' ' .. signature)
else
myprint 'Its signature is as follows:'
myprint(' ' .. signature)
end
end
-- Try to represent the value as a string.
local value = (ast.seevalue or ast).value
if type(value) == 'table' then
-- Print at most MAX_PREVIEW_KEYS of the table's keys.
local keys = {}
for k, v in pairs(value) do
if type(k) == 'string' then
keys[#keys+1] = k
elseif type(k) == 'number' then
keys[#keys+1] = '[' .. k .. ']'
else
keys[#keys+1] = tostring(k)
end
end
table.sort(keys)
if #keys > MAX_PREVIEW_KEYS then
myprint('Its value is a table with ' .. #keys .. ' fields including:')
for i, k in ipairs(keys) do
myprint(' - ' .. k)
if i == MAX_PREVIEW_KEYS then break end
end
elseif #keys >= 1 then
myprint("Its value is a table with the following field" .. (#keys > 1 and "s" or '') .. ":")
for i, k in ipairs(keys) do myprint(' - ' .. k) end
else
myprint 'Its value is a table.'
end
elseif type(value) == 'string' then
-- Print string value.
if value ~= '' then
myprint("Its value is the string " .. string.format('%q', value) .. ".")
else
myprint "Its value is a string."
end
elseif type(value) == 'function' then
-- Print function details.
local text = { "Its value is a" }
local info = debug.getinfo(value)
text[#text+1] = info.what
text[#text+1] = "function"
-- Try to find out where the function was defined.
local source = (info.source or ''):match '^@(.+)$'
if source and not source:find '[\\/]+luainspect[\\/]+.-%.lua$' then
source = source:gsub('^/home/[^/]+/', '~/')
text[#text+1] = "defined in"
text[#text+1] = source
if info.linedefined then
text[#text+1] = "on line"
text[#text+1] = info.linedefined
end
end
myprint(table.concat(text, ' ') .. '.')
elseif type(value) == 'userdata' then
myprint("Its value is a " .. type(value) .. '.')
elseif value ~= nil then
myprint("Its value is the " .. type(value) .. ' ' .. tostring(value) .. '.')
end
--[[ TODO Print warning notes attached to function calls?
local vast = ast.seevalue or ast
local note = vast.parent and (vast.parent.tag == 'Call' or vast.parent.tag == 'Invoke') and vast.parent.note
if note then myprint("WARNING: " .. note) end
--]]
end
function actions.goto(tokenlist, line, column)
-- FIXME This only jumps to declaration of local / 1st occurrence of global.
local curvar = getcurvar(tokenlist, line, column)
Expand Down

0 comments on commit dc640de

Please sign in to comment.