Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

<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
@xolox authored
Showing with 152 additions and 55 deletions.
  1. +8 −2 README.md
  2. +1 −1  TODO.md
  3. +93 −26 luainspect.vim
  4. +50 −26 luainspect4vim.lua
View
10 README.md
@@ -1,6 +1,12 @@
# Semantic highlighting for Lua in Vim
-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.
+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).
+
+ * If you press `<F2>` with the text cursor on a variable then the plug-in will prompt you to rename the variable.
+
+ * 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)
@@ -30,7 +36,7 @@ You don't need to use this command unless you've disabled automatic highlighting
* <span style="background: #D3D3D3">luaInspectSelectedVariable</span>
* <span style="border-bottom: 1px dotted red">luaInspectSyntaxError</span>
-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).
+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).
### The `g:loaded_luainspect` option
View
2  TODO.md
@@ -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 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.
+ * 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.
* Document the g:lua_inspect_path option.
View
119 luainspect.vim
@@ -1,8 +1,8 @@
" Vim plug-in
" Author: Peter Odding <peter@peterodding.com>
-" Last Change: August 10, 2010
+" Last Change: August 11, 2010
" URL: http://peterodding.com/code/vim/lua-inspect/
-" Version: 0.2.4
+" Version: 0.3
" License: MIT
" Support for automatic update using the GLVS plug-in.
@@ -55,13 +55,13 @@ let s:groups['SyntaxError'] = 'SpellBad'
" (Automatic) command definitions. {{{1
-command! -bar -bang LuaInspect call s:run_lua_inspect(<q-bang> != '!')
+command! -bar -bang LuaInspect call s:run_lua_inspect('highlight', <q-bang> != '!')
augroup PluginLuaInspect
" Clear existing automatic commands.
autocmd!
" Disable easytags.vim because it doesn't play nice with luainspect.vim!
- autocmd BufReadPost * if s:check_plugin_enabled() | let b:easytags_nohl = 1 | endif
+ autocmd BufNewFile,BufReadPost,BufWritePost * call s:init_lua_buffer()
" Define the configured automatic commands.
for s:event in split(g:lua_inspect_events, ',')
execute 'autocmd' s:event '* if s:check_plugin_enabled() | LuaInspect | endif'
@@ -74,15 +74,44 @@ function! s:check_plugin_enabled()
return &ft == 'lua' && !&diff && !exists('b:luainspect_disabled')
endfunction
-function! s:run_lua_inspect(enabled) " {{{2
- if s:set_plugin_enabled(a:enabled)
+function! s:init_lua_buffer()
+ if s:check_plugin_enabled()
+ let b:easytags_nohl = 1
+ inoremap <buffer> <silent> <F2> <C-o>:call <Sid>run_lua_inspect('rename', 1)<CR>
+ nnoremap <buffer> <silent> <F2> :call <Sid>run_lua_inspect('rename', 1)<CR>
+ endif
+endfunction
+
+function! s:run_lua_inspect(action, enable) " {{{2
+ if s:set_plugin_enabled(a:enable)
let lines = getline(1, "$")
call insert(lines, col('.'))
call insert(lines, line('.'))
+ call insert(lines, a:action)
call s:parse_text(join(lines, "\n"), s:prepare_search_path())
- call s:define_default_styles()
- call s:clear_previous_matches()
- call s:highlight_variables()
+ if !empty(b:luainspect_output)
+ let response = b:luainspect_output[0]
+ if response == 'syntax_error' && len(b:luainspect_output) >= 4
+ let linenum = b:luainspect_output[1] + 0
+ let colnum = b:luainspect_output[2] + 0
+ let linenum2 = b:luainspect_output[3] + 0
+ " TODO Can we do something useful with this?!
+ " let message = b:luainspect_output[4]
+ let error_cmd = 'syntax match luaInspectSyntaxError /\%%>%il\%%<%il.*/ containedin=ALLBUT,lua*Comment*'
+ execute printf(error_cmd, linenum - 1, (linenum2 ? linenum2 : line('$')) + 1)
+ return
+ elseif response == 'highlight'
+ call s:define_default_styles()
+ call s:clear_previous_matches()
+ call s:highlight_variables()
+ elseif response == 'rename'
+ if len(b:luainspect_output) == 1
+ call xolox#warning("No variable under cursor!")
+ else
+ call s:rename_variable()
+ endif
+ endif
+ endif
endif
endfunction
@@ -102,7 +131,7 @@ function! s:prepare_search_path() " {{{2
let code = ''
if !(has('lua') && g:lua_inspect_internal && exists('s:changed_path'))
let template = 'package.path = ''%s/?.lua;'' .. package.path'
- let code = printf(template, escape(expand(g:lua_inspect_path), '"\'))
+ let code = printf(template, escape(expand(g:lua_inspect_path), '"\'''))
if has('lua') && g:lua_inspect_internal
execute 'lua' code
let s:changed_path = 1
@@ -122,7 +151,7 @@ function! s:parse_text(input, search_path) " {{{2
" Ignore missing shell.vim plug-in.
let b:luainspect_output = split(system(command, a:input), "\n")
if v:shell_error
- let msg = "Failed to execute lua-inspect as external process! %s"
+ let msg = "Failed to execute luainspect as external process! %s"
throw printf(msg, strtrans(join(b:luainspect_output, "\n")))
endif
endtry
@@ -172,24 +201,62 @@ function! s:clear_previous_matches() " {{{2
endfunction
function! s:highlight_variables() " {{{2
- if len(b:luainspect_output) == 1
- let line = b:luainspect_output[0]
- let errinfo = matchlist(line, '^\(\d\+\)\s*\(\d*\)$')
- if len(errinfo) >= 3
- let error_cmd = 'syntax match luaInspectSyntaxError /\%%>%il\%%<%il.*/ containedin=ALLBUT,lua*Comment*'
- execute printf(error_cmd, errinfo[1] - 1, (errinfo[2] != '' ? errinfo[2] : line('$')) + 1)
- return
+ for line in b:luainspect_output[1:-1]
+ if s:check_output(line, '^\w\+\(\s\+\d\+\)\{3}$')
+ let [hlgroup, linenum, firstcol, lastcol] = split(line)
+ let pattern = s:highlight_position(linenum + 0, firstcol - 1, lastcol + 2)
+ execute 'syntax match' hlgroup '/' . pattern . '/'
endif
- endif
- for line in b:luainspect_output
- if match(line, '^\w\+\(\s\+\d\+\)\{3}$') == -1
- call xolox#warning("Invalid output from luainspect4vim.lua: %s", strtrans(line))
- return
+ endfor
+endfunction
+
+function! s:rename_variable() " {{{2
+ " Highlight occurrences of variable before rename.
+ let highlights = []
+ for line in b:luainspect_output[1:-1]
+ if s:check_output(line, '^\d\+\(\s\+\d\+\)\{2}$')
+ let [linenum, firstcol, lastcol] = split(line)
+ let pattern = s:highlight_position(linenum + 0, firstcol - 1, lastcol + 2)
+ call add(highlights, matchadd('IncSearch', pattern))
endif
- let [type, lnum, start, end] = split(line)
- let syntax_cmd = 'syntax match %s /\%%%il\%%>%ic\<\w\+\>\%%<%ic/'
- execute printf(syntax_cmd, type, lnum, start - 1, end + 2)
endfor
+ redraw
+ " Prompt for new name.
+ let oldname = expand('<cword>')
+ let prompt = "Please enter the new name for %s: "
+ let newname = input(printf(prompt, oldname), oldname)
+ " Clear highlighting of occurrences.
+ call map(highlights, 'matchdelete(v:val)')
+ " Perform rename?
+ if newname != '' && newname != oldname
+ let num_renamed = 0
+ for fields in reverse(b:luainspect_output[1:-1])
+ let [linenum, firstcol, lastcol] = split(fields)
+ let linenum += 0
+ let firstcol -= 2
+ let lastcol += 0
+ let line = getline(linenum)
+ let prefix = firstcol > 0 ? line[0 : firstcol] : ''
+ let suffix = lastcol < len(line) ? line[lastcol : -1] : ''
+ call setline(linenum, prefix . newname . suffix)
+ let num_renamed += 1
+ endfor
+ let msg = "Renamed %i occurrences of %s to %s"
+ call xolox#message(msg, num_renamed, oldname, newname)
+ endif
+endfunction
+
+function! s:check_output(line, pattern) " {{{2
+ if match(a:line, a:pattern) >= 0
+ return 1
+ else
+ call xolox#warning("Invalid output from luainspect4vim.lua: '%s'", strtrans(a:line))
+ return 0
+ endif
+endfunction
+
+function! s:highlight_position(linenum, firstcol, lastcol) " {{{2
+ return printf('\%%%il\%%>%ic\<\w\+\>\%%<%ic', a:linenum, a:firstcol, a:lastcol)
endfunction
" }}}1
View
76 luainspect4vim.lua
@@ -3,13 +3,16 @@
This module is part of the luainspect.vim plug-in for the Vim text editor.
Author: Peter Odding <peter@peterodding.com>
- Last Change: August 10, 2010
+ Last Change: August 11, 2010
URL: http://peterodding.com/code/vim/lua-inspect/
License: MIT
--]]
-local myprint
+local LI = require 'luainspect.init'
+local LA = require 'luainspect.ast'
+local myprint, getcurvar, highlight, rename
+
if type(vim) == 'table' and vim.eval then
-- The Lua interface for Vim redefines print() so it prints inside Vim.
myprint = print
@@ -20,7 +23,7 @@ else
function myprint(text) io.write(text, '\n') end
end
-local function getcurvar(tokenlist, line, column)
+function getcurvar(tokenlist, line, column)
for i, token in ipairs(tokenlist) do
if token.ast.lineinfo then
local l1, c1 = unpack(token.ast.lineinfo.first, 1, 2)
@@ -32,26 +35,8 @@ local function getcurvar(tokenlist, line, column)
end
end
-return function(src)
- local LI = require 'luainspect.init'
- local LA = require 'luainspect.ast'
- local line, column, src = src:match '^(%d+)\n(%d+)\n(.*)$'
- line = tonumber(line)
- column = tonumber(column)
- src = LA.remove_shebang(src)
- local f, err, linenum, colnum, linenum2 = LA.loadstring(src)
- if not f then
- if not linenum2 then
- myprint(linenum)
- else
- myprint(linenum2 .. ' ' .. linenum)
- end
- return
- end
- local ast; ast, err, linenum, colnum, linenum2 = LA.ast_from_string(src, "noname.lua")
- if not ast then return end
- local tokenlist = LA.ast_to_tokenlist(ast, src)
- LI.inspect(ast, tokenlist)
+function highlight(tokenlist, line, column)
+ myprint 'highlight'
local curvar = getcurvar(tokenlist, line, column)
for i, token in ipairs(tokenlist) do
local kind
@@ -85,13 +70,52 @@ return function(src)
if kind then
local l1, c1 = unpack(token.ast.lineinfo.first, 1, 2)
local l2, c2 = unpack(token.ast.lineinfo.last, 1, 2)
- if l1 == l2 then
- myprint(kind .. ' ' .. l1 .. ' ' .. c1 .. ' ' .. c2)
- end
+ if l1 == l2 then myprint(kind .. ' ' .. l1 .. ' ' .. c1 .. ' ' .. c2) end
end
end
end
+function rename(tokenlist, line, column)
+ myprint 'rename'
+ local curvar = getcurvar(tokenlist, line, column)
+ for i, token in ipairs(tokenlist) do
+ if curvar and curvar.ast.id == token.ast.id then
+ local l1, c1 = unpack(token.ast.lineinfo.first, 1, 2)
+ local l2, c2 = unpack(token.ast.lineinfo.last, 1, 2)
+ if l1 == l2 then myprint(l1 .. ' ' .. c1 .. ' ' .. c2) end
+ end
+ end
+end
+
+return function(src)
+ local action, line, column, src = src:match '^(%S+)\n(%d+)\n(%d+)\n(.*)$'
+ line = tonumber(line)
+ column = tonumber(column)
+ src = LA.remove_shebang(src)
+ -- Quickly parse the source code using loadstring() to check for syntax errors.
+ local f, err, linenum, colnum, linenum2 = LA.loadstring(src)
+ if not f then
+ myprint 'syntax_error'
+ myprint(linenum)
+ myprint(colnum)
+ myprint(linenum2 or 0)
+ myprint(err or '')
+ return
+ end
+ -- Now parse the source code using metalua to build an abstract syntax tree.
+ local ast; ast, err, linenum, colnum, linenum2 = LA.ast_from_string(src, "noname.lua")
+ if not ast then return end
+ -- Create a list of tokens from the AST and decorate it using luainspect.
+ local tokenlist = LA.ast_to_tokenlist(ast, src)
+ LI.inspect(ast, tokenlist)
+ -- Branch on the requested action.
+ if action == 'highlight' then
+ highlight(tokenlist, line, column)
+ elseif action == 'rename' then
+ rename(tokenlist, line, column)
+ end
+end
+
-- Enable type checking of ast.* expressions.
--! require 'luainspect.typecheck' (context)
Please sign in to comment.
Something went wrong with that request. Please try again.