Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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...
commit dc640de6799ba3da5f70073828da91ffaf0b596a 1 parent bb99531
Peter Odding authored August 11, 2010
6  README.md
Source Rendered
@@ -2,12 +2,14 @@
2 2
 
3 3
 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:
4 4
 
5  
- * 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).
6  
-
7 5
  * Press `<F2>` with the text cursor on a variable and the plug-in will prompt you to rename the variable.
8 6
 
9 7
  * Press `gd` (in normal mode) with the text cursor on a variable and you'll jump to its declaration / first occurrence.
10 8
 
  9
+ * When you hover over a variable with the mouse cursor in graphical Vim, information about the variable is displayed in a tooltip.
  10
+
  11
+ * 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).
  12
+
11 13
  * 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.
12 14
 
13 15
 ![Screenshot of semantic highlighting](http://peterodding.com/code/vim/luainspect/screenshot.png)
2  TODO.md
Source Rendered
@@ -2,6 +2,6 @@
2 2
 
3 3
  * 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.
4 4
 
5  
- * 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.
  5
+ * Bindings for other features of LuaInspect such as omni completion for in scope variables (including display of library function signatures).
6 6
 
7 7
  * Document the g:lua_inspect_path option.
21  luainspect.vim
@@ -2,7 +2,7 @@
2 2
 " Author: Peter Odding <peter@peterodding.com>
3 3
 " Last Change: August 11, 2010
4 4
 " URL: http://peterodding.com/code/vim/lua-inspect/
5  
-" Version: 0.3.2
  5
+" Version: 0.3.3
6 6
 " License: MIT
7 7
 
8 8
 " Support for automatic update using the GLVS plug-in.
@@ -80,14 +80,25 @@ function! s:init_lua_buffer()
80 80
     inoremap <buffer> <silent> <F2> <C-o>:call <Sid>run_lua_inspect('rename', 0, 1)<CR>
81 81
     nnoremap <buffer> <silent> <F2> :call <Sid>run_lua_inspect('rename', 0, 1)<CR>
82 82
     nnoremap <buffer> <silent> gd :call <Sid>run_lua_inspect('goto', 0, 1)<CR>
  83
+    setlocal ballooneval balloonexpr=LuaInspectToolTip()
83 84
   endif
84 85
 endfunction
85 86
 
  87
+function! LuaInspectToolTip() " {{{2
  88
+  let text = s:run_lua_inspect('tooltip', 0, 1)
  89
+  return type(text) == type('') ? text : ''
  90
+endfunction
  91
+
86 92
 function! s:run_lua_inspect(action, toggle, enabled) " {{{2
87 93
   if !a:toggle || s:set_plugin_enabled(a:enabled)
88 94
     let lines = getline(1, "$")
89  
-    call insert(lines, col('.'))
90  
-    call insert(lines, line('.'))
  95
+    if a:action == 'tooltip'
  96
+      call insert(lines, v:beval_col)
  97
+      call insert(lines, v:beval_lnum)
  98
+    else
  99
+      call insert(lines, col('.'))
  100
+      call insert(lines, line('.'))
  101
+    endif
91 102
     call insert(lines, a:action)
92 103
     call s:parse_text(join(lines, "\n"), s:prepare_search_path())
93 104
     if !empty(b:luainspect_output)
@@ -114,6 +125,10 @@ function! s:run_lua_inspect(action, toggle, enabled) " {{{2
114 125
           call setpos('.', [0, linenum, colnum, 0])
115 126
           call xolox#message("") " Clear any previous message to avoid confusion.
116 127
         endif
  128
+      elseif response == 'tooltip'
  129
+        if len(b:luainspect_output) > 1
  130
+          return join(b:luainspect_output[1:-1], "\n")
  131
+        endif
117 132
       elseif response == 'rename'
118 133
         if len(b:luainspect_output) == 1
119 134
           call xolox#warning("No variable under cursor!")
137  luainspect4vim.lua
@@ -9,9 +9,12 @@
9 9
 
10 10
 --]]
11 11
 
  12
+local MAX_PREVIEW_KEYS = 20
  13
+
12 14
 local LI = require 'luainspect.init'
13 15
 local LA = require 'luainspect.ast'
14  
-local actions, myprint, getcurvar = {}
  16
+local LS = require 'luainspect.signatures'
  17
+local actions, myprint, getcurvar, knownvarorfield = {}
15 18
 
16 19
 if type(vim) == 'table' and vim.eval then
17 20
   -- The Lua interface for Vim redefines print() so it prints inside Vim.
@@ -35,6 +38,12 @@ function getcurvar(tokenlist, line, column)
35 38
   end
36 39
 end
37 40
 
  41
+function knownvarorfield(token)
  42
+  local a = token.ast
  43
+  local v = a.seevalue or a
  44
+  return a.definedglobal or v.valueknown and v.value ~= nil
  45
+end
  46
+
38 47
 function actions.highlight(tokenlist, line, column)
39 48
   local curvar = getcurvar(tokenlist, line, column)
40 49
   for i, token in ipairs(tokenlist) do
@@ -60,11 +69,7 @@ function actions.highlight(tokenlist, line, column)
60 69
         kind = 'luaInspectLocal'
61 70
       end
62 71
     elseif token.ast.isfield then
63  
-      if token.ast.definedglobal or token.ast.seevalue.valueknown and token.ast.seevalue.value ~= nil then
64  
-        kind = 'luaInspectFieldDefined'
65  
-      else
66  
-        kind = 'luaInspectFieldUndefined'
67  
-      end
  72
+      kind = knownvarorfield(token) and 'luaInspectFieldDefined' or 'luaInspectFieldUndefined'
68 73
     end
69 74
     if kind then
70 75
       local l1, c1 = unpack(token.ast.lineinfo.first, 1, 2)
@@ -74,6 +79,126 @@ function actions.highlight(tokenlist, line, column)
74 79
   end
75 80
 end
76 81
 
  82
+function actions.tooltip(tokenlist, line, column)
  83
+  local text = {}
  84
+  local token = getcurvar(tokenlist, line, column)
  85
+  if not token then return end
  86
+  local ast = token.ast
  87
+  if not ast then return end
  88
+  -- Describe the variable type and status.
  89
+  if ast.localdefinition then
  90
+    if not ast.localdefinition.isused then text[#text+1] = "unused" end
  91
+    if ast.localdefinition.isset then text[#text+1] = "mutable" end
  92
+    if ast.localmasking then text[#text+1] = "masking" end
  93
+    if ast.localmasked then text[#text+1] = "masked" end
  94
+    if ast.localdefinition.functionlevel < ast.functionlevel then
  95
+      text[#text+1] = "upvalue"
  96
+    elseif ast.localdefinition.isparam then
  97
+      text[#text+1]  = "function parameter"
  98
+    else
  99
+      text[#text+1] = "local variable"
  100
+    end
  101
+  elseif ast.tag == 'Id' then
  102
+    text[#text+1] = knownvarorfield(token) and "known" or "unknown"
  103
+    text[#text+1] = "global variable"
  104
+  elseif ast.isfield then
  105
+    text[#text+1] = knownvarorfield(token) and "known" or "unknown"
  106
+    text[#text+1] = "table field"
  107
+  else
  108
+    return
  109
+  end
  110
+  -- TODO Bug in luainspect's static analysis? :gsub() below is marked as an
  111
+  -- unknown table field even though table.concat() returns a string?!
  112
+  text = table.concat(text, ' ')
  113
+  myprint("This is " .. (text:find '^[aeiou]' and 'an' or 'a') .. ' ' .. text .. '.')
  114
+  -- Display signatures for standard library functions.
  115
+  local name = ast.resolvedname
  116
+  local signature = name and LS.global_signatures[name]
  117
+  if not signature then
  118
+    local value = (ast.seevalue or ast).value
  119
+    for name, sig in pairs(LS.global_signatures) do
  120
+      if value == loadstring('return ' .. name)() then
  121
+        signature = sig
  122
+      end
  123
+    end
  124
+  end
  125
+  if signature then
  126
+    -- luainspect/signatures.lua contains special bullet characters in the
  127
+    -- latin1 character encoding (according to Vim) which Vim doesn't like
  128
+    -- in tooltips (I guess because it expects UTF-8).
  129
+    signature = signature:gsub('\183', '.')
  130
+    if not signature:find '%w %b()$' then
  131
+      myprint 'Its description is:'
  132
+      myprint('    ' .. signature)
  133
+    else
  134
+      myprint 'Its signature is as follows:'
  135
+      myprint('    ' .. signature)
  136
+    end
  137
+  end
  138
+  -- Try to represent the value as a string.
  139
+  local value = (ast.seevalue or ast).value
  140
+  if type(value) == 'table' then
  141
+    -- Print at most MAX_PREVIEW_KEYS of the table's keys.
  142
+    local keys = {}
  143
+    for k, v in pairs(value) do
  144
+      if type(k) == 'string' then
  145
+        keys[#keys+1] = k
  146
+      elseif type(k) == 'number' then
  147
+        keys[#keys+1] = '[' .. k .. ']'
  148
+      else
  149
+        keys[#keys+1] = tostring(k)
  150
+      end
  151
+    end
  152
+    table.sort(keys)
  153
+    if #keys > MAX_PREVIEW_KEYS then
  154
+      myprint('Its value is a table with ' .. #keys .. ' fields including:')
  155
+      for i, k in ipairs(keys) do
  156
+        myprint(' - ' .. k)
  157
+        if i == MAX_PREVIEW_KEYS then break end
  158
+      end
  159
+    elseif #keys >= 1 then
  160
+      myprint("Its value is a table with the following field" .. (#keys > 1 and "s" or '') .. ":")
  161
+      for i, k in ipairs(keys) do myprint(' - ' .. k) end
  162
+    else
  163
+      myprint 'Its value is a table.'
  164
+    end
  165
+  elseif type(value) == 'string' then
  166
+    -- Print string value.
  167
+    if value ~= '' then
  168
+      myprint("Its value is the string " .. string.format('%q', value) .. ".")
  169
+    else
  170
+      myprint "Its value is a string."
  171
+    end
  172
+  elseif type(value) == 'function' then
  173
+    -- Print function details.
  174
+    local text = { "Its value is a" }
  175
+    local info = debug.getinfo(value)
  176
+    text[#text+1] = info.what
  177
+    text[#text+1] = "function"
  178
+    -- Try to find out where the function was defined.
  179
+    local source = (info.source or ''):match '^@(.+)$'
  180
+    if source and not source:find '[\\/]+luainspect[\\/]+.-%.lua$' then
  181
+      source = source:gsub('^/home/[^/]+/', '~/')
  182
+      text[#text+1] = "defined in"
  183
+      text[#text+1] = source
  184
+      if info.linedefined then
  185
+        text[#text+1] = "on line"
  186
+        text[#text+1] = info.linedefined
  187
+      end
  188
+    end
  189
+    myprint(table.concat(text, ' ') .. '.')
  190
+  elseif type(value) == 'userdata' then
  191
+    myprint("Its value is a " .. type(value) .. '.')
  192
+  elseif value ~= nil then
  193
+    myprint("Its value is the " .. type(value) .. ' ' .. tostring(value) .. '.')
  194
+  end
  195
+  --[[ TODO Print warning notes attached to function calls?
  196
+  local vast = ast.seevalue or ast
  197
+  local note = vast.parent and (vast.parent.tag == 'Call' or vast.parent.tag == 'Invoke') and vast.parent.note
  198
+  if note then myprint("WARNING: " .. note) end
  199
+  --]]
  200
+end
  201
+
77 202
 function actions.goto(tokenlist, line, column)
78 203
   -- FIXME This only jumps to declaration of local / 1st occurrence of global.
79 204
   local curvar = getcurvar(tokenlist, line, column)

0 notes on commit dc640de

Please sign in to comment.
Something went wrong with that request. Please try again.