Skip to content

Commit

Permalink
Improve omni completion, add plugin/ script for autocmds
Browse files Browse the repository at this point in the history
  • Loading branch information
xolox committed Jun 18, 2011
1 parent 14d9067 commit 63641c8
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 36 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -14,6 +14,8 @@ The [Lua][lua] file type plug-in for [Vim][vim] makes it easier to work with Lua

* The ['omnifunc'][ofu] option is set to allow dynamic completion of the variables defined in all modules installed on the system using Control-X Control-O, however it needs to be explicitly enabled by setting the `lua_complete_omni` option because this functionality may have undesired side effects! When you invoke omni completion after typing `require '` or `require('` you get completion of module names

![Screenshot of omni completion](http://peterodding.com/code/vim/lua-ftplugin/screenshots/omni-completion.png)

* Several [text-objects][tob] are defined so you can jump between blocks and functions

* A pretty nifty hack of the [matchit plug-in][mit] is included: When the cursor is on a `function` or `return` keyword the `%` mapping cycles between the relevant keywords (`function`, `return`, `end`), this also works for branching statements (`if`, `elseif`, `else`, `end`) and looping statements (`for`, `while`, `repeat`, `until`, `end`)
Expand Down
3 changes: 3 additions & 0 deletions TODO.md
@@ -1,5 +1,8 @@
# To-do list for the Lua file type plug-in for Vim

* `BufReadCmd` automatic command that converts `*.luac` files to byte code listings :-)
* Make the globals checking smarter so it can be enabled by default without being too much of a nuisance?

## Smarter completion

Make completion smarter by supporting function arguments:
Expand Down
60 changes: 35 additions & 25 deletions autoload/xolox/lua.vim
Expand Up @@ -3,7 +3,6 @@
" Last Change: June 18, 2011
" URL: http://peterodding.com/code/vim/lua-ftplugin

let s:version = '0.6.14'
let s:miscdir = expand('<sfile>:p:h:h:h') . '/misc/lua-ftplugin'
let s:omnicomplete_script = s:miscdir . '/omnicomplete.lua'
let s:globals_script = s:miscdir . '/globals.lua'
Expand All @@ -23,9 +22,9 @@ function! xolox#lua#includeexpr(fname) " {{{1
let module = substitute(a:fname, '\.', '/', 'g')
for template in xolox#lua#getsearchpath('$LUA_PATH', 'package.path')
let expanded = substitute(template, '?', module, 'g')
call xolox#misc#msg#debug("lua.vim %s: Expanded %s -> %s", s:version, template, expanded)
call xolox#misc#msg#debug("lua.vim %s: Expanded %s -> %s", g:lua_ftplugin_version, template, expanded)
if filereadable(expanded)
call xolox#misc#msg#debug("lua.vim %s: Matched existing file %s", s:version, expanded)
call xolox#misc#msg#debug("lua.vim %s: Matched existing file %s", g:lua_ftplugin_version, expanded)
return expanded
endif
endfor
Expand All @@ -41,33 +40,35 @@ function! xolox#lua#getsearchpath(envvar, luavar) " {{{1
redir => path
execute 'silent lua print(' . a:luavar . ')'
redir END
call xolox#misc#msg#debug("lua.vim %s: Got %s from Lua Interface for Vim", s:version, a:luavar)
call xolox#misc#msg#debug("lua.vim %s: Got %s from Lua Interface for Vim", g:lua_ftplugin_version, a:luavar)
catch
redir END
endtry
endif
if empty(path)
let path = eval(a:envvar)
if !empty(path)
call xolox#misc#msg#debug("lua.vim %s: Got %s from %s", s:version, a:luavar, a:envvar)
call xolox#misc#msg#debug("lua.vim %s: Got %s from %s", g:lua_ftplugin_version, a:luavar, a:envvar)
else
let path = system('lua -e "io.write(' . a:luavar . ')"')
if v:shell_error
call xolox#misc#msg#warn("lua.vim %s: Failed to get %s from external Lua interpreter: %s", s:version, a:luavar, path)
call xolox#misc#msg#warn("lua.vim %s: Failed to get %s from external Lua interpreter: %s", g:lua_ftplugin_version, a:luavar, path)
else
call xolox#misc#msg#debug("lua.vim %s: Got %s from external Lua interpreter", s:version, a:luavar)
call xolox#misc#msg#debug("lua.vim %s: Got %s from external Lua interpreter", g:lua_ftplugin_version, a:luavar)
endif
endif
endif
return split(xolox#misc#str#trim(path), ';')
endfunction

function! xolox#lua#autocheck() " {{{1
if xolox#lua#getopt('lua_check_syntax', 1)
call xolox#lua#checksyntax()
endif
if xolox#lua#getopt('lua_check_globals', 0) && empty(getqflist())
call xolox#lua#checkglobals(0)
if &filetype == 'lua'
if xolox#lua#getopt('lua_check_syntax', 1)
call xolox#lua#checksyntax()
endif
if xolox#lua#getopt('lua_check_globals', 0) && empty(getqflist())
call xolox#lua#checkglobals(0)
endif
endif
endfunction

Expand All @@ -80,7 +81,7 @@ function! xolox#lua#checksyntax() " {{{1
let message .= " doesn't seem to be available! I'm disabling"
let message .= " automatic syntax checking for Lua scripts."
let g:lua_check_syntax = 0
call xolox#misc#msg#warn(message, s:version)
call xolox#misc#msg#warn(message, g:lua_ftplugin_version)
else
let mp_save = &makeprg
let efm_save = &errorformat
Expand Down Expand Up @@ -114,7 +115,7 @@ function! s:highlighterrors()
let pattern = '^\%%%il.*\n\?'
for entry in getqflist()
call matchadd(hlgroup, '\%' . min([entry.lnum, line('$')]) . 'l')
call xolox#misc#msg#warn("lua.vim %s: Syntax error on line %i: %s", s:version, entry.lnum, entry.text)
call xolox#misc#msg#warn("lua.vim %s: Syntax error on line %i: %s", g:lua_ftplugin_version, entry.lnum, entry.text)
endfor
endfunction

Expand Down Expand Up @@ -155,7 +156,7 @@ function! s:lookupmethod(cword, prefix, pattern)
let method = matchstr(a:cword, a:pattern)
if method != ''
let identifier = a:prefix . method
call xolox#misc#msg#debug("lua.vim %s: Translating '%s' -> '%s'", s:version, a:cword, identifier)
call xolox#misc#msg#debug("lua.vim %s: Translating '%s' -> '%s'", g:lua_ftplugin_version, a:cword, identifier)
call s:lookuptopic(identifier)
endif
endfunction
Expand Down Expand Up @@ -334,7 +335,7 @@ function! xolox#lua#omnifunc(init, base) " {{{1
if a:init
return s:get_completion_prefix()
elseif !xolox#lua#getopt('lua_complete_omni', 0)
throw printf("lua.vim %s: omni completion needs to be explicitly enabled, see the readme!", s:version)
throw printf("lua.vim %s: omni completion needs to be explicitly enabled, see the readme!", g:lua_ftplugin_version)
endif
if !exists('s:omnifunc_modules')
let s:omnifunc_modules = xolox#lua#getomnimodules()
Expand Down Expand Up @@ -366,7 +367,7 @@ function! xolox#lua#getomnimodules() " {{{1
let modules = keys(modulemap)
call sort(modules)
let msg = "lua.vim %s: Collected %i module names for omni completion in %s"
call xolox#misc#timer#stop(msg, s:version, len(modules), starttime)
call xolox#misc#timer#stop(msg, g:lua_ftplugin_version, len(modules), starttime)
return modules
endfunction

Expand All @@ -376,32 +377,32 @@ function! s:expandsearchpath(searchpath, modules)
let components = split(template, '?')
if len(components) != 2
let msg = "lua.vim %s: Failed to parse search path entry: %s"
call xolox#misc#msg#debug(msg, s:version, template)
call xolox#misc#msg#debug(msg, g:lua_ftplugin_version, template)
continue
endif
let [prefix, suffix] = components
" XXX Never recursively search current working directory because
" it might be arbitrarily deep, e.g. when working directory is /
if prefix =~ '^.[\\/]$'
let msg = "lua.vim %s: Refusing to expand dangerous search path entry: %s"
call xolox#misc#msg#debug(msg, s:version, template)
call xolox#misc#msg#debug(msg, g:lua_ftplugin_version, template)
continue
endif
let pattern = substitute(template, '?', '**/*', 'g')
call xolox#misc#msg#debug("lua.vim %s: Transformed %s -> %s", s:version, template, pattern)
call xolox#misc#msg#debug("lua.vim %s: Transformed %s -> %s", g:lua_ftplugin_version, template, pattern)
let msg = "lua.vim %s: Failed to convert pathname to module name, %s doesn't match! (%s: '%s', pathname: '%s')"
for pathname in split(glob(pattern), "\n")
if pathname[0 : len(prefix)-1] != prefix
" Validate prefix of resulting pathname.
call xolox#misc#msg#warn(msg, s:version, 'prefix', 'prefix', prefix, pathname)
call xolox#misc#msg#warn(msg, g:lua_ftplugin_version, 'prefix', 'prefix', prefix, pathname)
elseif pathname[-len(suffix) : -1] != suffix
" Validate suffix of resulting pathname.
call xolox#misc#msg#warn(msg, s:version, 'suffix', 'suffix', suffix, pathname)
call xolox#misc#msg#warn(msg, g:lua_ftplugin_version, 'suffix', 'suffix', suffix, pathname)
elseif pathname !~ 'test'
let relative = pathname[len(prefix) : -len(suffix)-1]
let modulename = substitute(relative, '[\\/]\+', '.', 'g')
let a:modules[modulename] = 1
call xolox#misc#msg#debug("lua.vim %s: Transformed '%s' -> '%s'", s:version, pathname, modulename)
call xolox#misc#msg#debug("lua.vim %s: Transformed '%s' -> '%s'", g:lua_ftplugin_version, pathname, modulename)
endif
endfor
endfor
Expand All @@ -413,7 +414,7 @@ function! xolox#lua#getomnivariables(modules) " {{{1
let variables = eval('[' . substitute(output, '\_s\+', ',', 'g') . ']')
call sort(variables, 1)
let msg = "lua.vim %s: Collected %i variables for omni completion in %s"
call xolox#misc#timer#stop(msg, s:version, len(variables), starttime)
call xolox#misc#timer#stop(msg, g:lua_ftplugin_version, len(variables), starttime)
return variables
endfunction

Expand Down Expand Up @@ -442,6 +443,15 @@ function! xolox#lua#completedynamic(type) " {{{1
return a:type
endfunction

function! xolox#lua#tweakoptions() " {{{1
if &filetype == 'lua'
let s:completeopt_save = &cot
set completeopt+=longest
elseif exists('s:completeopt_save')
let &completeopt = s:completeopt_save
endif
endfunction

function! xolox#lua#dofile(pathname, arguments) " {{{1
if has('lua')
" Use the Lua Interface for Vim.
Expand All @@ -454,7 +464,7 @@ function! xolox#lua#dofile(pathname, arguments) " {{{1
let output = xolox#misc#str#trim(system(join(['lua', a:pathname] + a:arguments)))
if v:shell_error
let msg = "lua.vim %s: Failed to retrieve omni completion candidates (output: '%s')"
call xolox#misc#msg#warn(msg, s:version, output)
call xolox#misc#msg#warn(msg, g:lua_ftplugin_version, output)
endif
endif
return xolox#misc#str#trim(output)
Expand Down
195 changes: 195 additions & 0 deletions doc/lua-ftplugin.txt
@@ -0,0 +1,195 @@
*lua-ftplugin.txt* Lua file type plug-in for the Vim text editor

The Lua [1] file type plug-in for Vim makes it easier to work with Lua source
code in Vim by providing the following features:

- The |'includeexpr'| option is set so that the |gf| (go to file) mapping knows
how to resolve Lua module names using package.path [2]

- The |'include'| option is set so that Vim follows dofile() [3], loadfile() [4]
and require() [5] calls when looking for identifiers in included files
(this works together with the |'includeexpr'| option)

- An automatic command is installed that runs 'luac -p' when you save your Lua
scripts. If 'luac' reports any errors they are shown in the quick-fix list
and Vim jumps to the line of the first error. If 'luac -p' doesn't report
any errors a check for undefined global variables is performed by parsing
the output of 'luac -p -l'

- '<F1>' on a Lua function or 'method' call will try to open the relevant
documentation in the Lua Reference for Vim [6]

- The |'completefunc'| option is set to allow completion of Lua 5.1 keywords,
global variables and library members using Control-X Control-U

- The |'omnifunc'| option is set to allow dynamic completion of the variables
defined in all modules installed on the system using Control-X Control-O,
however it needs to be explicitly enabled by setting the
|lua_complete_omni| option because this functionality may have undesired
side effects! When you invoke omni completion after typing 'require '' or
'require('' you get completion of module names

Screenshot of omni completion, see reference [7]

- Several |text-objects| are defined so you can jump between blocks and
functions

- A pretty nifty hack of the matchit plug-in (see |matchit-install|) is
included: When the cursor is on a 'function' or 'return' keyword the '%'
mapping cycles between the relevant keywords ('function', 'return', 'end'),
this also works for branching statements ('if', 'elseif', 'else', 'end')
and looping statements ('for', 'while', 'repeat', 'until', 'end')

===============================================================================
*lua-ftplugin-installation*
Installation ~

Unzip the most recent ZIP archive [8] file inside your Vim profile directory
(usually this is '~/.vim' on UNIX and '%USERPROFILE%\vimfiles' on Windows),
restart Vim and execute the command ':helptags ~/.vim/doc' (use ':helptags
~\vimfiles\doc' instead on Windows). Now try it out: Edit a Lua script and try
any of the features documented above.

===============================================================================
*lua-ftplugin-options*
Options ~

The Lua file type plug-in handles options as follows: First it looks at buffer
local variables, then it looks at global variables and if neither exists a
default is chosen. This means you can change how the plug-in works for
individual buffers. For example to change the location of the Lua compiler
used to check the syntax:
>
" This sets the default value for all buffers.
:let g:lua_compiler_name = '/usr/local/bin/luac'
" This is how you change the value for one buffer.
:let b:lua_compiler_name = '/usr/local/bin/lualint'
-------------------------------------------------------------------------------
The *lua_path* option

This option contains the value of 'package.path' as a string. You shouldn't
need to change this because the plug-in is aware of $LUA_PATH [2] and if that
isn't set the plug-in will run a Lua interpreter to get the value of
package.path [2].

-------------------------------------------------------------------------------
The *lua_check_syntax* option

When you write a Lua script to disk the plug-in automatically runs the Lua
compiler to check for syntax errors. To disable this behavior you can set this
option to false (0):
>
let g:lua_check_syntax = 0
You can manually check the syntax using the ':CheckSyntax' command.

-------------------------------------------------------------------------------
The *lua_check_globals* option

When you write a Lua script to disk the plug-in automatically runs the Lua
compiler to check for undefined global variables. To disable this behavior you
can set this option to false (0):
>
let g:lua_check_globals = 0
You can manually check the globals using the ':CheckGlobals' command.

-------------------------------------------------------------------------------
The *lua_compiler_name* option

The name or path of the Lua compiler used to check for syntax errors (defaults
to 'luac'). You can set this option to run the Lua compiler from a
non-standard location or to run a dedicated syntax checker like lualint [9].

-------------------------------------------------------------------------------
The *lua_compiler_args* option

The argument(s) required by the compiler or syntax checker (defaults to '-p').

-------------------------------------------------------------------------------
The *lua_error_format* option

If you use a dedicated syntax checker you may need to change this option to
reflect the format of the messages printed by the syntax checker.

-------------------------------------------------------------------------------
The *lua_complete_keywords* option

To disable completion of keywords you can set this option to false (0).

-------------------------------------------------------------------------------
The *lua_complete_globals* option

To disable completion of global functions you can set this option to false
(0).

-------------------------------------------------------------------------------
The *lua_complete_library* option

To disable completion of library functions you can set this option to false
(0).

-------------------------------------------------------------------------------
The *lua_complete_dynamic* option

When you type a dot after a word the Lua file type plug-in will automatically
start completion. To disable this behavior you can set this option to false
(0).

-------------------------------------------------------------------------------
The *lua_complete_omni* option

This option is disabled by default for two reasons:

- The omni completion support works by enumerating and loading all installed
modules. If module loading has side effects this can have unintended
consequences!

- Because all modules installed on the system are loaded, collecting the
completion candidates can be slow. After the first run the completion
candidates are cached so this will only bother you once (until you restart
Vim).

If you want to use the omni completion despite the warnings above, execute the
following command:
>
:let g:lua_complete_omni = 1
Now when you type Control-X Control-O Vim will hang for a moment, after which
you should be presented with an enormous list of completion candidates :-)

===============================================================================
*lua-ftplugin-contact*
Contact ~

If you have questions, bug reports, suggestions, etc. the author can be
contacted at peter@peterodding.com. The latest version is available at
http://peterodding.com/code/vim/lua-ftplugin and http://github.com/xolox/vim-lua-ftplugin.
If you like this plug-in please vote for it on Vim Online [10].

===============================================================================
*lua-ftplugin-license*
License ~

This software is licensed under the MIT license [11]. Copyright 2011 Peter
Odding <peter@peterodding.com>.

===============================================================================
*lua-ftplugin-references*
References ~

[1] http://www.lua.org/
[2] http://www.lua.org/manual/5.1/manual.html#pdf-package.path
[3] http://www.lua.org/manual/5.1/manual.html#pdf-dofile
[4] http://www.lua.org/manual/5.1/manual.html#pdf-loadfile
[5] http://www.lua.org/manual/5.1/manual.html#pdf-require
[6] http://www.vim.org/scripts/script.php?script_id=1291
[7] http://peterodding.com/code/vim/lua-ftplugin/screenshots/omni-completion.png
[8] http://peterodding.com/code/vim/downloads/lua-ftplugin.zip
[9] http://lua-users.org/wiki/LuaLint
[10] http://www.vim.org/scripts/script.php?script_id=3625
[11] http://en.wikipedia.org/wiki/MIT_License

vim: ft=help

0 comments on commit 63641c8

Please sign in to comment.