From 527e1b76a06c04baf2b9b597f1fbd5e0ef02fca4 Mon Sep 17 00:00:00 2001 From: Peter Odding Date: Thu, 12 Aug 2010 22:43:00 +0200 Subject: [PATCH] Split plug-in into command definitions / autoload functions --- autoload.vim | 253 +++++++++++++++++++++++++++++++++++++++++++++ luainspect.vim | 274 ++----------------------------------------------- 2 files changed, 261 insertions(+), 266 deletions(-) create mode 100644 autoload.vim diff --git a/autoload.vim b/autoload.vim new file mode 100644 index 0000000..4ec1512 --- /dev/null +++ b/autoload.vim @@ -0,0 +1,253 @@ +" Vim script. +" Author: Peter Odding +" Last Change: August 12, 2010 +" URL: http://peterodding.com/code/vim/lua-inspect/ +" License: MIT + +let s:script = expand(':p:~') + +function! luainspect#auto_enable() " {{{1 + if !&diff && !exists('b:luainspect_disabled') + " Disable easytags.vim because it doesn't play nice with luainspect.vim! + let b:easytags_nohl = 1 + " Define buffer local mappings for rename / goto definition features. + inoremap :call luainspect#make_request('rename') + nnoremap :call luainspect#make_request('rename') + nnoremap gd :call luainspect#make_request('goto') + " Enable balloon evaluation / dynamic tool tips. + setlocal ballooneval balloonexpr=LuaInspectToolTip() + " Install automatic commands to update the highlighting. + for event in split(g:lua_inspect_events, ',') + execute 'autocmd!' event ' LuaInspect' + endfor + endif +endfunction + +function! luainspect#highlight_cmd(disable) " {{{1 + if a:disable + call s:clear_previous_matches() + unlet! b:luainspect_input b:luainspect_output + let b:luainspect_disabled = 1 + else + unlet! b:luainspect_disabled + let starttime = xolox#timer#start() + call luainspect#make_request('highlight') + let bufname = expand('%:p:~') + let msg = "%s: Highlighted %s in %s." + call xolox#timer#stop(msg, s:script, bufname, starttime) + endif +endfunction + +function! luainspect#make_request(action) " {{{1 + if a:action == 'tooltip' + let lines = getbufline(v:beval_bufnr, 1, "$") + call insert(lines, v:beval_col) + call insert(lines, v:beval_lnum) + else + let lines = getline(1, "$") + 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) + let response = b:luainspect_output[0] + if response == 'syntax_error' && len(b:luainspect_output) >= 4 + " Never perform syntax error highlighting in non-Lua buffers! + let linenum = b:luainspect_output[1] + 0 + let colnum = b:luainspect_output[2] + 0 + let linenum2 = b:luainspect_output[3] + 0 + let b:luainspect_syntax_error = b:luainspect_output[4] + if a:action != 'tooltip' || v:beval_bufnr == bufnr('%') + let error_cmd = 'syntax match luaInspectSyntaxError /\%%>%il\%%<%il.*/ containedin=ALLBUT,lua*Comment*' + execute printf(error_cmd, linenum - 1, (linenum2 ? linenum2 : line('$')) + 1) + endif + " But always let the user know that a syntax error exists. + let bufname = fnamemodify(bufname(a:action != 'tooltip' ? '%' : v:beval_bufnr), ':p:~') + call xolox#warning("Syntax error around line %i in %s: %s", linenum, bufname, b:luainspect_syntax_error) + return + endif + unlet! b:luainspect_syntax_error + if response == 'highlight' + call s:define_default_styles() + call s:clear_previous_matches() + call s:highlight_variables() + elseif response == 'goto' + if len(b:luainspect_output) < 3 + call xolox#warning("No variable under cursor!") + else + let linenum = b:luainspect_output[1] + 0 + let colnum = b:luainspect_output[2] + 0 + 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!") + else + call s:rename_variable() + endif + endif + endif +endfunction + +function! s:prepare_search_path() " {{{1 + 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), '"\''')) + if has('lua') && g:lua_inspect_internal + execute 'lua' code + let s:changed_path = 1 + endif + endif + return code +endfunction + +function! s:parse_text(input, search_path) " {{{1 + if !(exists('b:luainspect_input') && b:luainspect_input == a:input) + if !(has('lua') && g:lua_inspect_internal) + let template = 'lua -e "%s; require ''luainspect4vim'' (io.read ''*a'')"' + let command = printf(template, a:search_path) + try + let b:luainspect_output = xolox#shell#execute(command, 1, a:input) + catch /^Vim\%((\a\+)\)\=:E117/ + " 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 luainspect as external process! %s" + throw printf(msg, strtrans(join(b:luainspect_output, "\n"))) + endif + endtry + else + redir => output + silent lua << EOF + if io == nil then + -- The Lua interface for Vim previously didn't include io.*! + io = { type = function() end } + end + require 'luainspect4vim' (vim.eval 'a:input') +EOF + redir END + let b:luainspect_output = split(output, "\n") + endif + " Remember the text that was just parsed. + let b:luainspect_input = a:input + endif +endfunction + +function! s:define_default_styles() " {{{1 + " Always define the default highlighting styles + " (copied from /luainspect/scite.lua for consistency). + for [group, styles] in items(s:groups) + let group = 'luaInspect' . group + if type(styles) == type('') + let defgroup = styles + else + let defgroup = 'luaInspectDefault' . group + let style = &bg == 'light' ? styles[0] : styles[1] + execute 'highlight' defgroup style + endif + " Don't link the actual highlighting styles to the defaults if the user + " has already defined or linked the highlighting group. This enables color + " schemes and vimrc scripts to override the styles (see :help :hi-default). + execute 'highlight def link' group defgroup + unlet styles " to avoid E706. + endfor +endfunction + +function! s:clear_previous_matches() " {{{1 + " Clear existing highlighting. + for group in keys(s:groups) + let group = 'luaInspect' . group + if hlexists(group) + execute 'syntax clear' group + endif + endfor +endfunction + +function! s:highlight_variables() " {{{1 + call clearmatches() + for line in b:luainspect_output[1:-1] + if s:check_output(line, '^\w\+\(\s\+\d\+\)\{3}$') + let [group, linenum, firstcol, lastcol] = split(line) + let pattern = s:highlight_position(linenum + 0, firstcol - 1, lastcol + 2) + if group == 'luaInspectWrongArgCount' + call matchadd(group, pattern) + elseif group == 'luaInspectSelectedVariable' + call matchadd(group, pattern, 20) + else + execute 'syntax match' group '/' . pattern . '/' + endif + endif + endfor +endfunction + +function! s:rename_variable() " {{{1 + " 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 + endfor + redraw + " Prompt for new name. + let oldname = expand('') + 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) " {{{1 + 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) " {{{1 + return printf('\%%%il\%%>%ic\<\w\+\>\%%<%ic', a:linenum, a:firstcol, a:lastcol) +endfunction + +" Highlighting groups and their default light/dark styles. {{{1 + +let s:groups = {} +let s:groups['GlobalDefined'] = ['guifg=#600000', 'guifg=#ffc080'] +let s:groups['GlobalUndefined'] = 'ErrorMsg' +let s:groups['LocalUnused'] = ['guifg=#ffffff guibg=#000080', 'guifg=#ffffff guibg=#000080'] +let s:groups['LocalMutated'] = ['gui=italic guifg=#000080', 'gui=italic guifg=#c0c0ff'] +let s:groups['UpValue'] = ['guifg=#0000ff', 'guifg=#e8e8ff'] +let s:groups['Param'] = ['guifg=#000040', 'guifg=#8080ff'] +let s:groups['Local'] = ['guifg=#000040', 'guifg=#c0c0ff'] +let s:groups['FieldDefined'] = ['guifg=#600000', 'guifg=#ffc080'] +let s:groups['FieldUndefined'] = ['guifg=#c00000', 'guifg=#ff0000'] +let s:groups['SelectedVariable'] = 'CursorLine' +let s:groups['SyntaxError'] = 'SpellBad' +let s:groups['WrongArgCount'] = 'SpellLocal' diff --git a/luainspect.vim b/luainspect.vim index dba76d8..8a764c8 100644 --- a/luainspect.vim +++ b/luainspect.vim @@ -13,10 +13,6 @@ if &cp || exists('g:loaded_luainspect') finish endif -let s:script = expand(':p:~') - -" Configuration defaults. {{{1 - if !exists('g:lua_inspect_events') " Change this to enable semantic highlighting on your preferred events. let g:lua_inspect_events = 'CursorHold,CursorHoldI,BufWritePost' @@ -41,278 +37,24 @@ if !exists('g:lua_inspect_internal') let g:lua_inspect_internal = has('lua') endif -" The highlight groups and default styles/links defined by this plug-in. -let s:groups = {} -let s:groups['GlobalDefined'] = ['guifg=#600000', 'guifg=#ffc080'] -let s:groups['GlobalUndefined'] = 'ErrorMsg' -let s:groups['LocalUnused'] = ['guifg=#ffffff guibg=#000080', 'guifg=#ffffff guibg=#000080'] -let s:groups['LocalMutated'] = ['gui=italic guifg=#000080', 'gui=italic guifg=#c0c0ff'] -let s:groups['UpValue'] = ['guifg=#0000ff', 'guifg=#e8e8ff'] -let s:groups['Param'] = ['guifg=#000040', 'guifg=#8080ff'] -let s:groups['Local'] = ['guifg=#000040', 'guifg=#c0c0ff'] -let s:groups['FieldDefined'] = ['guifg=#600000', 'guifg=#ffc080'] -let s:groups['FieldUndefined'] = ['guifg=#c00000', 'guifg=#ff0000'] -let s:groups['SelectedVariable'] = 'CursorLine' -let s:groups['SyntaxError'] = 'SpellBad' -let s:groups['WrongArgCount'] = 'SpellLocal' - -" (Automatic) command definitions. {{{1 - -command! -bar -bang LuaInspect call s:luainspect_command( == '!') +" This command updates highlighting when automatic highlighting is disabled. +command! -bar -bang LuaInspect call luainspect#highlight_cmd( == '!') +" Automatically enable the plug-in in Lua buffers. augroup PluginLuaInspect - " Clear existing automatic commands. - autocmd! - " Automatically enable the plug-in on these events. - autocmd BufNewFile,BufReadPost,BufWritePost * call s:auto_enable_plugin() - " Define the configured automatic commands for highlighting. - for s:event in split(g:lua_inspect_events, ',') - execute 'autocmd' s:event '* if s:check_plugin_valid() | LuaInspect | endif' - endfor - unlet s:event + autocmd! FileType lua call luainspect#auto_enable() augroup END -" Script local functions. {{{1 - -function! s:check_plugin_valid() " {{{2 - return &ft == 'lua' && !&diff && !exists('b:luainspect_disabled') -endfunction - -function! s:luainspect_command(disable) " {{{2 - if a:disable - call s:clear_previous_matches() - unlet! b:luainspect_input b:luainspect_output - let b:luainspect_disabled = 1 - else - unlet! b:luainspect_disabled - let starttime = xolox#timer#start() - call s:run_lua_inspect('highlight') - let bufname = expand('%:p:~') - let msg = "%s: Highlighted %s in %s." - call xolox#timer#stop(msg, s:script, bufname, starttime) - endif -endfunction - -function! s:auto_enable_plugin() " {{{2 - if s:check_plugin_valid() - " Disable easytags.vim because it doesn't play nice with luainspect.vim! - let b:easytags_nohl = 1 - inoremap :call run_lua_inspect('rename') - nnoremap :call run_lua_inspect('rename') - nnoremap gd :call run_lua_inspect('goto') - setlocal ballooneval balloonexpr=LuaInspectToolTip() - endif -endfunction - -function! LuaInspectToolTip() " {{{2 - let text = s:run_lua_inspect('tooltip') +" The &balloonexpr option requires a global function. +function! LuaInspectToolTip() + let result = luainspect#make_request('tooltip') if exists('b:luainspect_syntax_error') return b:luainspect_syntax_error else - return type(text) == type('') ? text : '' - endif -endfunction - -function! s:run_lua_inspect(action) " {{{2 - if a:action == 'tooltip' - let lines = getbufline(v:beval_bufnr, 1, "$") - call insert(lines, v:beval_col) - call insert(lines, v:beval_lnum) - else - let lines = getline(1, "$") - 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) - let response = b:luainspect_output[0] - if response == 'syntax_error' && len(b:luainspect_output) >= 4 - " Never perform syntax error highlighting in non-Lua buffers! - let linenum = b:luainspect_output[1] + 0 - let colnum = b:luainspect_output[2] + 0 - let linenum2 = b:luainspect_output[3] + 0 - let b:luainspect_syntax_error = b:luainspect_output[4] - if a:action != 'tooltip' || v:beval_bufnr == bufnr('%') - let error_cmd = 'syntax match luaInspectSyntaxError /\%%>%il\%%<%il.*/ containedin=ALLBUT,lua*Comment*' - execute printf(error_cmd, linenum - 1, (linenum2 ? linenum2 : line('$')) + 1) - endif - " But always let the user know that a syntax error exists. - let bufname = fnamemodify(bufname(a:action != 'tooltip' ? '%' : v:beval_bufnr), ':p:~') - call xolox#warning("Syntax error around line %i in %s: %s", linenum, bufname, b:luainspect_syntax_error) - return - endif - unlet! b:luainspect_syntax_error - if response == 'highlight' - call s:define_default_styles() - call s:clear_previous_matches() - call s:highlight_variables() - elseif response == 'goto' - if len(b:luainspect_output) < 3 - call xolox#warning("No variable under cursor!") - else - let linenum = b:luainspect_output[1] + 0 - let colnum = b:luainspect_output[2] + 0 - 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!") - else - call s:rename_variable() - endif - endif + return type(result) == type('') ? result : '' endif endfunction -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), '"\''')) - if has('lua') && g:lua_inspect_internal - execute 'lua' code - let s:changed_path = 1 - endif - endif - return code -endfunction - -function! s:parse_text(input, search_path) " {{{2 - if !(exists('b:luainspect_input') && b:luainspect_input == a:input) - if !(has('lua') && g:lua_inspect_internal) - let template = 'lua -e "%s; require ''luainspect4vim'' (io.read ''*a'')"' - let command = printf(template, a:search_path) - try - let b:luainspect_output = xolox#shell#execute(command, 1, a:input) - catch /^Vim\%((\a\+)\)\=:E117/ - " 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 luainspect as external process! %s" - throw printf(msg, strtrans(join(b:luainspect_output, "\n"))) - endif - endtry - else - redir => output - silent lua << EOF - if io == nil then - -- The Lua interface for Vim previously didn't include io.*! - io = { type = function() end } - end - require 'luainspect4vim' (vim.eval 'a:input') -EOF - redir END - let b:luainspect_output = split(output, "\n") - endif - " Remember the text that was just parsed. - let b:luainspect_input = a:input - endif -endfunction - -function! s:define_default_styles() " {{{2 - " Always define the default highlighting styles - " (copied from /luainspect/scite.lua for consistency). - for [group, styles] in items(s:groups) - let group = 'luaInspect' . group - if type(styles) == type('') - let defgroup = styles - else - let defgroup = 'luaInspectDefault' . group - let style = &bg == 'light' ? styles[0] : styles[1] - execute 'highlight' defgroup style - endif - " Don't link the actual highlighting styles to the defaults if the user - " has already defined or linked the highlighting group. This enables color - " schemes and vimrc scripts to override the styles (see :help :hi-default). - execute 'highlight def link' group defgroup - unlet styles " to avoid E706. - endfor -endfunction - -function! s:clear_previous_matches() " {{{2 - " Clear existing highlighting. - for group in keys(s:groups) - let group = 'luaInspect' . group - if hlexists(group) - execute 'syntax clear' group - endif - endfor -endfunction - -function! s:highlight_variables() " {{{2 - call clearmatches() - for line in b:luainspect_output[1:-1] - if s:check_output(line, '^\w\+\(\s\+\d\+\)\{3}$') - let [group, linenum, firstcol, lastcol] = split(line) - let pattern = s:highlight_position(linenum + 0, firstcol - 1, lastcol + 2) - if group == 'luaInspectWrongArgCount' - call matchadd(group, pattern) - elseif group == 'luaInspectSelectedVariable' - call matchadd(group, pattern, 20) - else - execute 'syntax match' group '/' . pattern . '/' - endif - endif - 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 - endfor - redraw - " Prompt for new name. - let oldname = expand('') - 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 - " Make sure the plug-in is only loaded once. let g:loaded_luainspect = 1