Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

<F2> mapping to rename all occurrences of variable

 * The plug-in now defines a buffer local <F2> mapping inside Lua
   buffers which renames all occurrences of the variable under the
   text cursor.

 * I've structured the communication between Vim and Lua so that more
   modes/actions can be easily added and so that when, for example, a
   syntax error is found while trying to perform the rename action, the
   plug-in will recognize and highlight the syntax error instead of
   doing nothing (because the syntax error makes the AST unavailable).

 * Just for reference: The Python script I use to package the plug-in
   now includes the luainspect README/COPYRIGHT & metalua LICENSE files
  • Loading branch information...
commit 0c52221a952874807e50d668b0c89511378bdb10 1 parent 4c2c0bf
Peter Odding authored August 11, 2010
10  README.md
Source Rendered
... ...
@@ -1,6 +1,12 @@
1 1
 # Semantic highlighting for Lua in Vim
2 2
 
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.
  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
+
  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
+ * If you press `<F2>` with the text cursor on a variable then the plug-in will prompt you to rename the variable.
  8
+
  9
+ * 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.
4 10
 
5 11
 ![Screenshot of semantic highlighting](http://peterodding.com/code/vim/luainspect/screenshot.png)
6 12
 
@@ -30,7 +36,7 @@ You don't need to use this command unless you've disabled automatic highlighting
30 36
  * <span style="background: #D3D3D3">luaInspectSelectedVariable</span>
31 37
  * <span style="border-bottom: 1px dotted red">luaInspectSyntaxError</span>
32 38
 
33  
-When a syntax error is found no highlighting can be performed but the lines where the error is reported will be marked like a spelling error. If you don't like one or more of the default styles the Vim documentation [describes how to change them](http://vimdoc.sourceforge.net/htmldoc/syntax.html#:hi-default). If you want to disable the semantic highlighting in a specific Vim buffer execute `:LuaInspect!` in that buffer. When you want to reenable the highlighting execute `:LuaInspect` again, but now without the [bang](http://vimdoc.sourceforge.net/htmldoc/map.html#:command-bang).
  39
+If you don't like one or more of the default styles the Vim documentation [describes how to change them](http://vimdoc.sourceforge.net/htmldoc/syntax.html#:hi-default). If you want to disable the semantic highlighting in a specific Vim buffer execute `:LuaInspect!` in that buffer. When you want to reenable the highlighting execute `:LuaInspect` again, but now without the [bang](http://vimdoc.sourceforge.net/htmldoc/map.html#:command-bang).
34 40
 
35 41
 ### The `g:loaded_luainspect` option
36 42
 
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 like renaming variables on command and showing tooltips for identifiers. 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 showing tooltips for variables, goto definition (`gd` mapping) 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.
6 6
 
7 7
  * Document the g:lua_inspect_path option.
119  luainspect.vim
... ...
@@ -1,8 +1,8 @@
1 1
 " Vim plug-in
2 2
 " Author: Peter Odding <peter@peterodding.com>
3  
-" Last Change: August 10, 2010
  3
+" Last Change: August 11, 2010
4 4
 " URL: http://peterodding.com/code/vim/lua-inspect/
5  
-" Version: 0.2.4
  5
+" Version: 0.3
6 6
 " License: MIT
7 7
 
8 8
 " Support for automatic update using the GLVS plug-in.
@@ -55,13 +55,13 @@ let s:groups['SyntaxError'] = 'SpellBad'
55 55
 
56 56
 " (Automatic) command definitions. {{{1
57 57
 
58  
-command! -bar -bang LuaInspect call s:run_lua_inspect(<q-bang> != '!')
  58
+command! -bar -bang LuaInspect call s:run_lua_inspect('highlight', <q-bang> != '!')
59 59
 
60 60
 augroup PluginLuaInspect
61 61
   " Clear existing automatic commands.
62 62
   autocmd!
63 63
   " Disable easytags.vim because it doesn't play nice with luainspect.vim!
64  
-  autocmd BufReadPost * if s:check_plugin_enabled() | let b:easytags_nohl = 1 | endif
  64
+  autocmd BufNewFile,BufReadPost,BufWritePost * call s:init_lua_buffer()
65 65
   " Define the configured automatic commands.
66 66
   for s:event in split(g:lua_inspect_events, ',')
67 67
     execute 'autocmd' s:event '* if s:check_plugin_enabled() | LuaInspect | endif'
@@ -74,15 +74,44 @@ function! s:check_plugin_enabled()
74 74
   return &ft == 'lua' && !&diff && !exists('b:luainspect_disabled')
75 75
 endfunction
76 76
 
77  
-function! s:run_lua_inspect(enabled) " {{{2
78  
-  if s:set_plugin_enabled(a:enabled)
  77
+function! s:init_lua_buffer()
  78
+  if s:check_plugin_enabled()
  79
+    let b:easytags_nohl = 1
  80
+    inoremap <buffer> <silent> <F2> <C-o>:call <Sid>run_lua_inspect('rename', 1)<CR>
  81
+    nnoremap <buffer> <silent> <F2> :call <Sid>run_lua_inspect('rename', 1)<CR>
  82
+  endif
  83
+endfunction
  84
+
  85
+function! s:run_lua_inspect(action, enable) " {{{2
  86
+  if s:set_plugin_enabled(a:enable)
79 87
     let lines = getline(1, "$")
80 88
     call insert(lines, col('.'))
81 89
     call insert(lines, line('.'))
  90
+    call insert(lines, a:action)
82 91
     call s:parse_text(join(lines, "\n"), s:prepare_search_path())
83  
-    call s:define_default_styles()
84  
-    call s:clear_previous_matches()
85  
-    call s:highlight_variables()
  92
+    if !empty(b:luainspect_output)
  93
+      let response = b:luainspect_output[0]
  94
+      if response == 'syntax_error' && len(b:luainspect_output) >= 4
  95
+        let linenum = b:luainspect_output[1] + 0
  96
+        let colnum = b:luainspect_output[2] + 0
  97
+        let linenum2 = b:luainspect_output[3] + 0
  98
+        " TODO Can we do something useful with this?!
  99
+        " let message = b:luainspect_output[4]
  100
+        let error_cmd = 'syntax match luaInspectSyntaxError /\%%>%il\%%<%il.*/ containedin=ALLBUT,lua*Comment*'
  101
+        execute printf(error_cmd, linenum - 1, (linenum2 ? linenum2 : line('$')) + 1)
  102
+        return
  103
+      elseif response == 'highlight'
  104
+        call s:define_default_styles()
  105
+        call s:clear_previous_matches()
  106
+        call s:highlight_variables()
  107
+      elseif response == 'rename'
  108
+        if len(b:luainspect_output) == 1
  109
+          call xolox#warning("No variable under cursor!")
  110
+        else
  111
+          call s:rename_variable()
  112
+        endif
  113
+      endif
  114
+    endif
86 115
   endif
87 116
 endfunction
88 117
 
@@ -102,7 +131,7 @@ function! s:prepare_search_path() " {{{2
102 131
   let code = ''
103 132
   if !(has('lua') && g:lua_inspect_internal && exists('s:changed_path'))
104 133
     let template = 'package.path = ''%s/?.lua;'' .. package.path'
105  
-    let code = printf(template, escape(expand(g:lua_inspect_path), '"\'))
  134
+    let code = printf(template, escape(expand(g:lua_inspect_path), '"\'''))
106 135
     if has('lua') && g:lua_inspect_internal
107 136
       execute 'lua' code
108 137
       let s:changed_path = 1
@@ -122,7 +151,7 @@ function! s:parse_text(input, search_path) " {{{2
122 151
         " Ignore missing shell.vim plug-in.
123 152
         let b:luainspect_output = split(system(command, a:input), "\n")
124 153
         if v:shell_error
125  
-          let msg = "Failed to execute lua-inspect as external process! %s"
  154
+          let msg = "Failed to execute luainspect as external process! %s"
126 155
           throw printf(msg, strtrans(join(b:luainspect_output, "\n")))
127 156
         endif
128 157
       endtry
@@ -172,24 +201,62 @@ function! s:clear_previous_matches() " {{{2
172 201
 endfunction
173 202
 
174 203
 function! s:highlight_variables() " {{{2
175  
-  if len(b:luainspect_output) == 1
176  
-    let line = b:luainspect_output[0]
177  
-    let errinfo = matchlist(line, '^\(\d\+\)\s*\(\d*\)$')
178  
-    if len(errinfo) >= 3
179  
-      let error_cmd = 'syntax match luaInspectSyntaxError /\%%>%il\%%<%il.*/ containedin=ALLBUT,lua*Comment*'
180  
-      execute printf(error_cmd, errinfo[1] - 1, (errinfo[2] != '' ? errinfo[2] : line('$')) + 1)
181  
-      return
  204
+  for line in b:luainspect_output[1:-1]
  205
+    if s:check_output(line, '^\w\+\(\s\+\d\+\)\{3}$')
  206
+      let [hlgroup, linenum, firstcol, lastcol] = split(line)
  207
+      let pattern = s:highlight_position(linenum + 0, firstcol - 1, lastcol + 2)
  208
+      execute 'syntax match' hlgroup '/' . pattern . '/'
182 209
     endif
183  
-  endif
184  
-  for line in b:luainspect_output
185  
-    if match(line, '^\w\+\(\s\+\d\+\)\{3}$') == -1
186  
-      call xolox#warning("Invalid output from luainspect4vim.lua: %s", strtrans(line))
187  
-      return
  210
+  endfor
  211
+endfunction
  212
+
  213
+function! s:rename_variable() " {{{2
  214
+  " Highlight occurrences of variable before rename.
  215
+  let highlights = []
  216
+  for line in b:luainspect_output[1:-1]
  217
+    if s:check_output(line, '^\d\+\(\s\+\d\+\)\{2}$')
  218
+      let [linenum, firstcol, lastcol] = split(line)
  219
+      let pattern = s:highlight_position(linenum + 0, firstcol - 1, lastcol + 2)
  220
+      call add(highlights, matchadd('IncSearch', pattern))
188 221
     endif
189  
-    let [type, lnum, start, end] = split(line)
190  
-    let syntax_cmd = 'syntax match %s /\%%%il\%%>%ic\<\w\+\>\%%<%ic/'
191  
-    execute printf(syntax_cmd, type, lnum, start - 1, end + 2)
192 222
   endfor
  223
+  redraw
  224
+  " Prompt for new name.
  225
+  let oldname = expand('<cword>')
  226
+  let prompt = "Please enter the new name for %s: "
  227
+  let newname = input(printf(prompt, oldname), oldname)
  228
+  " Clear highlighting of occurrences.
  229
+  call map(highlights, 'matchdelete(v:val)')
  230
+  " Perform rename?
  231
+  if newname != '' && newname != oldname
  232
+    let num_renamed = 0
  233
+    for fields in reverse(b:luainspect_output[1:-1])
  234
+      let [linenum, firstcol, lastcol] = split(fields)
  235
+      let linenum += 0
  236
+      let firstcol -= 2
  237
+      let lastcol += 0
  238
+      let line = getline(linenum)
  239
+      let prefix = firstcol > 0 ? line[0 : firstcol] : ''
  240
+      let suffix = lastcol < len(line) ? line[lastcol : -1] : ''
  241
+      call setline(linenum, prefix . newname . suffix)
  242
+      let num_renamed += 1
  243
+    endfor
  244
+    let msg = "Renamed %i occurrences of %s to %s"
  245
+    call xolox#message(msg, num_renamed, oldname, newname)
  246
+  endif
  247
+endfunction
  248
+
  249
+function! s:check_output(line, pattern) " {{{2
  250
+  if match(a:line, a:pattern) >= 0
  251
+    return 1
  252
+  else
  253
+    call xolox#warning("Invalid output from luainspect4vim.lua: '%s'", strtrans(a:line))
  254
+    return 0
  255
+  endif
  256
+endfunction
  257
+
  258
+function! s:highlight_position(linenum, firstcol, lastcol) " {{{2
  259
+  return printf('\%%%il\%%>%ic\<\w\+\>\%%<%ic', a:linenum, a:firstcol, a:lastcol)
193 260
 endfunction
194 261
 
195 262
 " }}}1
76  luainspect4vim.lua
@@ -3,13 +3,16 @@
3 3
  This module is part of the luainspect.vim plug-in for the Vim text editor.
4 4
 
5 5
  Author: Peter Odding <peter@peterodding.com>
6  
- Last Change: August 10, 2010
  6
+ Last Change: August 11, 2010
7 7
  URL: http://peterodding.com/code/vim/lua-inspect/
8 8
  License: MIT
9 9
 
10 10
 --]]
11 11
 
12  
-local myprint
  12
+local LI = require 'luainspect.init'
  13
+local LA = require 'luainspect.ast'
  14
+local myprint, getcurvar, highlight, rename
  15
+
13 16
 if type(vim) == 'table' and vim.eval then
14 17
   -- The Lua interface for Vim redefines print() so it prints inside Vim.
15 18
   myprint = print
@@ -20,7 +23,7 @@ else
20 23
   function myprint(text) io.write(text, '\n') end
21 24
 end
22 25
 
23  
-local function getcurvar(tokenlist, line, column)
  26
+function getcurvar(tokenlist, line, column)
24 27
   for i, token in ipairs(tokenlist) do
25 28
     if token.ast.lineinfo then
26 29
       local l1, c1 = unpack(token.ast.lineinfo.first, 1, 2)
@@ -32,26 +35,8 @@ local function getcurvar(tokenlist, line, column)
32 35
   end
33 36
 end
34 37
 
35  
-return function(src)
36  
-  local LI = require 'luainspect.init'
37  
-  local LA = require 'luainspect.ast'
38  
-  local line, column, src = src:match '^(%d+)\n(%d+)\n(.*)$'
39  
-  line = tonumber(line)
40  
-  column = tonumber(column)
41  
-  src = LA.remove_shebang(src)
42  
-  local f, err, linenum, colnum, linenum2 = LA.loadstring(src)
43  
-  if not f then
44  
-    if not linenum2 then
45  
-      myprint(linenum)
46  
-    else
47  
-      myprint(linenum2 .. ' ' .. linenum)
48  
-    end
49  
-    return
50  
-  end
51  
-  local ast; ast, err, linenum, colnum, linenum2 = LA.ast_from_string(src, "noname.lua")
52  
-  if not ast then return end
53  
-  local tokenlist = LA.ast_to_tokenlist(ast, src)
54  
-  LI.inspect(ast, tokenlist)
  38
+function highlight(tokenlist, line, column)
  39
+  myprint 'highlight'
55 40
   local curvar = getcurvar(tokenlist, line, column)
56 41
   for i, token in ipairs(tokenlist) do
57 42
     local kind
@@ -85,13 +70,52 @@ return function(src)
85 70
     if kind then
86 71
       local l1, c1 = unpack(token.ast.lineinfo.first, 1, 2)
87 72
       local l2, c2 = unpack(token.ast.lineinfo.last, 1, 2)
88  
-      if l1 == l2 then
89  
-        myprint(kind .. ' ' .. l1 .. ' ' .. c1 .. ' ' .. c2)
90  
-      end
  73
+      if l1 == l2 then myprint(kind .. ' ' .. l1 .. ' ' .. c1 .. ' ' .. c2) end
91 74
     end
92 75
   end
93 76
 end
94 77
 
  78
+function rename(tokenlist, line, column)
  79
+  myprint 'rename'
  80
+  local curvar = getcurvar(tokenlist, line, column)
  81
+  for i, token in ipairs(tokenlist) do
  82
+    if curvar and curvar.ast.id == token.ast.id then
  83
+      local l1, c1 = unpack(token.ast.lineinfo.first, 1, 2)
  84
+      local l2, c2 = unpack(token.ast.lineinfo.last, 1, 2)
  85
+      if l1 == l2 then myprint(l1 .. ' ' .. c1 .. ' ' .. c2) end
  86
+    end
  87
+  end
  88
+end
  89
+
  90
+return function(src)
  91
+  local action, line, column, src = src:match '^(%S+)\n(%d+)\n(%d+)\n(.*)$'
  92
+  line = tonumber(line)
  93
+  column = tonumber(column)
  94
+  src = LA.remove_shebang(src)
  95
+  -- Quickly parse the source code using loadstring() to check for syntax errors.
  96
+  local f, err, linenum, colnum, linenum2 = LA.loadstring(src)
  97
+  if not f then
  98
+    myprint 'syntax_error'
  99
+    myprint(linenum)
  100
+    myprint(colnum)
  101
+    myprint(linenum2 or 0)
  102
+    myprint(err or '')
  103
+    return
  104
+  end
  105
+  -- Now parse the source code using metalua to build an abstract syntax tree.
  106
+  local ast; ast, err, linenum, colnum, linenum2 = LA.ast_from_string(src, "noname.lua")
  107
+  if not ast then return end
  108
+  -- Create a list of tokens from the AST and decorate it using luainspect.
  109
+  local tokenlist = LA.ast_to_tokenlist(ast, src)
  110
+  LI.inspect(ast, tokenlist)
  111
+  -- Branch on the requested action.
  112
+  if action == 'highlight' then
  113
+    highlight(tokenlist, line, column)
  114
+  elseif action == 'rename' then
  115
+    rename(tokenlist, line, column)
  116
+  end
  117
+end
  118
+
95 119
 -- Enable type checking of ast.* expressions.
96 120
 --! require 'luainspect.typecheck' (context)
97 121
 

0 notes on commit 0c52221

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