From ecd37776ca80af11e1a78858e4fd9cb693907ce4 Mon Sep 17 00:00:00 2001 From: naquad Date: Sat, 22 Jun 2013 13:16:25 +0300 Subject: [PATCH] initial import --- README.md | 39 +++++++++ autoload/lgs/artisan.vim | 151 +++++++++++++++++++++++++++++++++ autoload/lgs/cmd.vim | 137 ++++++++++++++++++++++++++++++ autoload/lgs/cmpl.vim | 175 +++++++++++++++++++++++++++++++++++++++ autoload/lgs/cmplf.vim | 127 ++++++++++++++++++++++++++++ autoload/lgs/opts.vim | 102 +++++++++++++++++++++++ autoload/lgs/utils.vim | 55 ++++++++++++ doc/lgs.txt | 169 +++++++++++++++++++++++++++++++++++++ misc/get_envs.php | 15 ++++ misc/get_models.php | 18 ++++ plugin/lgs.vim | 168 +++++++++++++++++++++++++++++++++++++ 11 files changed, 1156 insertions(+) create mode 100644 README.md create mode 100644 autoload/lgs/artisan.vim create mode 100644 autoload/lgs/cmd.vim create mode 100644 autoload/lgs/cmpl.vim create mode 100644 autoload/lgs/cmplf.vim create mode 100644 autoload/lgs/opts.vim create mode 100644 autoload/lgs/utils.vim create mode 100644 doc/lgs.txt create mode 100644 misc/get_envs.php create mode 100644 misc/get_models.php create mode 100644 plugin/lgs.vim diff --git a/README.md b/README.md new file mode 100644 index 0000000..d4fa0fb --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# LGS.vim + +Adds integration with [Laravel 4 Generator bundle](https://github.com/JeffreyWay/Laravel-4-Generators) through LG command allowing user to generate various entities w/o leaving VIM. Convinient navigation through generated/updated files included. This is a very early release, please file bugs if you find any. + +## Installation + +If you're using Pathogen.vim, Vundle or similar software consult their documentation. In case if you're using plain VIM just throw contents of this repository to `~/.vim` directory (or ~/_vim Win32 users). + +## Usage + +LGS.vim provides one single command for invoking generators: `LG` + +Examples: + +``` +" Generates NewModel +LG model NewModel + +" Generates controller Controller +LG controller MyController + +" Will insert form for model Model into current buffer at cursor position +LG form NewModel + +" You can also specify options for generators + +LG form --method=create --html=ul ModelName + +" Syntax with = is supported too +LG controller --path=/path/to/template ControllerName +``` + +See documentation for more details. + +## Known issues + +First of all current discovery of models is very basic and fragile. It is a piece of code executed in `artisan ticker` console. You can find it in `misc/get_models.php`. + +Environment discovery is very dumb: mock `Application` class, load `bootstrap/start.php` catch `detectEnvironment`. In case if `detectEnvironment` is called after some work with `Application` instance it'll just crash and you won't get any completion. diff --git a/autoload/lgs/artisan.vim b/autoload/lgs/artisan.vim new file mode 100644 index 0000000..5680781 --- /dev/null +++ b/autoload/lgs/artisan.vim @@ -0,0 +1,151 @@ +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +"" LGS +"" +"" VIM interface for Laravel 4 generator bundle +"" (https://github.com/JeffreyWay/Laravel-4-Generators) +"" +"" Copyright 2013 Naquad. +"" +"" This program is free software; you can redistribute it and/or modify +"" it under the terms of the GNU General Public License as published by +"" the Free Software Foundation; either version 3 of the License, or +"" (at your option) any later version. +"" +"" This program is distributed in the hope that it will be useful, +"" but WITHOUT ANY WARRANTY; without even the implied warranty of +"" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +"" GNU General Public License +"" for more details. +"" +"" +"" Pack of Artisan-related functions. Running, parsing output +"" of generators etc... +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +if &cp || exists('g:lgs_artisan_autoload_done') + finish +endif + +" Look through directories from current up to root +" for file artisan . +function! lgs#artisan#FindArtisan(start) + let path = fnamemodify(a:start, ':p') + + if !isdirectory(path) + let path = fnamemodify(path, ':h') + endif + + let min_len = has('win32') + 1 + + while strlen(path) > min_len + let artisan = path . '/artisan' + + if filereadable(artisan) + return artisan + endif + + let path = fnamemodify(path, ':h') + endwhile + + return '' +endfunction + +" Checks if buffer has associated artisan file path. +" Adds association if not. +function! lgs#artisan#SetArtisanPathIfNeed() + if !exists('b:artisan') + let b:artisan = lgs#artisan#FindArtisan(expand('%')) + return b:artisan != '' + elseif b:artisan != '' + return filereadable(b:artisan) + else + return 0 + endif +endfunction + +" Output of generators shows +" app/database/migrations/Create_Xyz_table.php +" while really it name is +" app/database/migrations/time_stamp_create_xyz_table.php +" +" To handle this case whenever path with /database/migrations/ substring +" is encountered it is transformed for glob() function and first globbed +" file is. +function! s:MigrationPattern(path) + return substitute(a:path, '\(/database/migrations/\)\(.\+\)', '\=submatch(1) . "*_" . tolower(submatch(2))', '') +endfunction + +" Runs artisan analyzing its output. +" Lots of mess here, definitely requires refactoring. Once. +function! lgs#artisan#RunArtisan(cmd, parse) + let result = system(a:cmd . ' 2>&1') + + if v:shell_error " exception raised + let error = matchlist(result, '\[.*Exception\]\_s\+\([^\n]\+\)') + + if empty(error) " can we figure out what is the error? + return [0, insert(lgs#utils#StringList(result), 'UNKNOWN ERROR', 0)] + else " if yes then show pretty error + return [0, [substitute(error[1], '^\_s\+\|\_\s\+$', '', 'g')] + endif + elseif a:parse + let files = [1, []] + + let base = fnamemodify(b:artisan, ':p:h') . '/' + let bl = strlen(base) - 1 + + " Here we're looking a file name in string. + " It is indeed possible to use known prefixes ("Created ", "Updated ", ...) + " but I didn't wan't to have hardcoded strings. + for line in lgs#utils#StringList(result) + let i = 0 + let added = 0 + + while 1 + let path = line[i :] + if path[: bl] == base " make path relative if possible + let path = path[bl + 1 : ] + endif + + if match(path, '/database/migrations/') != -1 " is that migration? + let t = glob(s:MigrationPattern(path), 0, 1) " glob it first + if !empty(t) " file found, use it + let path = t[0] + endif + endif + + if filereadable(path) + let added = 1 + call add(files[1], {'filename': path, 'nr': len(files[1]), 'text': tolower(line[: i - 1])}) + break + endif + + let i = match(line, '\s', i + 1) + 1 + if i == 0 + break + endif + endwhile + + if !added + call add(files[1], {'nr': len(files[1]), 'text': line}) + endif + endfor + + return files + else + return [1, lgs#utils#StringList(result)] + endif +endfunction + +" Builds command line for invoking artisan +function! lgs#artisan#MakeArtisanCommand(...) + let cmd = '' + + if !executable(b:artisan) + let cmd = shellescape(g:lg_php) . ' ' + endif + + return shellescape(b:artisan) . ' ' . join(map(copy(a:000), 'shellescape(v:val)'), ' ') +endfunction + +let g:lgs_artisan_autoload_done = 1 diff --git a/autoload/lgs/cmd.vim b/autoload/lgs/cmd.vim new file mode 100644 index 0000000..c27ca35 --- /dev/null +++ b/autoload/lgs/cmd.vim @@ -0,0 +1,137 @@ +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +"" LGS +"" +"" VIM interface for Laravel 4 generator bundle +"" (https://github.com/JeffreyWay/Laravel-4-Generators) +"" +"" Copyright 2013 Naquad. +"" +"" This program is free software; you can redistribute it and/or modify +"" it under the terms of the GNU General Public License as published by +"" the Free Software Foundation; either version 3 of the License, or +"" (at your option) any later version. +"" +"" This program is distributed in the hope that it will be useful, +"" but WITHOUT ANY WARRANTY; without even the implied warranty of +"" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +"" GNU General Public License +"" for more details. +"" +"" +"" HARDCORE! This is implemnetation of parser for command line arguments. +"" Problem is that to provide reliable completion for commands +"" one needs to know all arguments. I didn't find anything in VIM +"" so had to write one. +"" +"" Not sure this is a the best possible implementation, but it works. +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +if &cp || exists('g:lgs_cmd_autoload_done') + finish +endif + +" Double quoted escape characters +let s:escaped = { + \ 't': "\t", + \ 'n': "\n", + \ 'r': "\r", + \ 'e': "\e", + \ 'b': "\b", + \ '"': "\"", + \ '\': "\\" + \ } + +" Used in substitution +function! lgs#cmd#UnquoteString(m) + if empty(a:m) + return '' + endif + + return get(s:escaped, a:m, a:m) +endfunction + +" Here come the dragons. +" This is function implements finite state machine for parsing command line +" AND unquoting functionality. +" I think it is possible to make some more generic implementation of +" completion so this won't be needed, but same time I wanted more +" general solution that could be reused later. +function! lgs#cmd#Str2ParamList(str) + let len = strlen(a:str) + let slider = max([0, match(a:str, '\S')]) + let start = slider + let stack = [] + + let params = [] + + while slider < len + let c = a:str[slider] + + if c == ' ' && empty(stack) && slider > start + call add(params, [start, slider - 1, a:str[start : slider - 1]]) + + let slider = match(a:str, '\S', slider) - 1 + if slider < 0 + break + endif + + let start = slider + 1 + elseif c == '\' + if empty(stack) || stack[-1] == '"' + let slider = slider + 1 + endif + elseif c == '"' || c == "'" + if empty(stack) + call add(stack, c) + elseif stack[-1] == c + call remove(stack, -1) + + call add(params, [start, slider, a:str[start : slider]]) + let start = max([slider + 1, match(a:str, '\S', slider + 1)]) + endif + endif + + let slider = slider + 1 + endwhile + + if start != slider && slider > 0 + call add(params, [start, len, a:str[start :]]) + endif + + for param in params + let qtype = param[2][0] + + if qtype == "'" + let param[2] = + \ substitute(substitute(param[2], "^'\\|'$", '', 'g'), "''", "'", 'g') + elseif qtype == '"' + let end = matchstr(param[2], '\\*"$') + + if !empty(end) && strlen(end) % 2 == 1 + let param[2] = param[2][:-2] + endif + + let param[2] = substitute(param[2][1:], '\\\(.\?\)', '\=lgs#cmd#UnquoteString(submatch(1))', 'g') + endif + endfor + + return params +endfunction + +" Takes result of lgs#cmd#Str2ParamList and position in line +" returns number of argument that position is in. +function! lgs#cmd#Pos2ArgNo(params, pos) + let cnt = 0 + + for param in a:params + if param[1] >= a:pos + return cnt + endif + + let cnt = cnt + 1 + endfor + + return cnt +endfunction + +let g:lgs_cmd_autoload_done = 1 diff --git a/autoload/lgs/cmpl.vim b/autoload/lgs/cmpl.vim new file mode 100644 index 0000000..b87536f --- /dev/null +++ b/autoload/lgs/cmpl.vim @@ -0,0 +1,175 @@ +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +"" LGS +"" +"" VIM interface for Laravel 4 generator bundle +"" (https://github.com/JeffreyWay/Laravel-4-Generators) +"" +"" Copyright 2013 Naquad. +"" +"" This program is free software; you can redistribute it and/or modify +"" it under the terms of the GNU General Public License as published by +"" the Free Software Foundation; either version 3 of the License, or +"" (at your option) any later version. +"" +"" This program is distributed in the hope that it will be useful, +"" but WITHOUT ANY WARRANTY; without even the implied warranty of +"" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +"" GNU General Public License +"" for more details. +"" +"" +"" Completion for LG command. Works with g:lgs_generators variable +"" to figure out what to complete and how to complete. +"" Also respects argument positions. +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +if &cp || exists('g:lgs_cmpl_autoload_done') + finish +endif + +" Figures out what to do with completion source. +" Runs functions, uses lists and returns nothing +" if is a number. +function! s:CallCompleter(what, lead, prefix) + let t = type(a:what) + + if t == 0 + return [] + elseif t == 3 + return lgs#utils#StartsWith(copy(a:what), a:lead, a:prefix) + elseif t == 1 + return lgs#utils#StartsWith([a:what], a:lead, a:prefix) + elseif t == 2 + return lgs#utils#StartsWith(a:what(a:lead), a:lead, a:prefix) + elseif + echoerr 'Dont know how to complete with ' . t + endif + + return [] +endfunction + +" Completion for argument at position. +" If argument number is not described in generation configuration +" then tries to use '*' instead. +function! s:CallArgument(generator, argno, lead) + if !has_key(g:lgs_generators[a:generator], 'arguments') + return [] + elseif has_key(g:lgs_generators[a:generator].arguments, a:argno) + return s:CallCompleter(g:lgs_generators[a:generator].arguments[a:argno], a:lead, '') + elseif has_key(g:lgs_generators[a:generator].arguments, '*') + return s:CallCompleter(g:lgs_generators[a:generator]['arguments']['*'], a:lead, '') + endif + + return [] +endfunction + +" Completes options and their values. +function! s:CallOption(generator, option_name, value, prefix, param) + if !has_key(g:lgs_generators[a:generator], 'options') + return [] + endif + + if type(a:value) == 0 && a:value == 0 + let lst = keys(g:lgs_generators[a:generator].options) + + if a:param + let lst = map(lst, 'v:val . "="') + endif + + return lgs#utils#StartsWith(lst, a:option_name, '--') + elseif has_key(g:lgs_generators[a:generator].options, a:option_name) + return s:CallCompleter(g:lgs_generators[a:generator].options[a:option_name], a:value, a:prefix) + endif + + return [] +endfunction + +" Determines are we completing option or argument? +" Pretty slopy, but I didn't see another way. +function! s:IsOption(params, argno, lead) + let last_option = '' + + for p in a:params[2 : a:argno - 1] + if p[2] == '--' + return 0 + endif + + if p[2][0] == '-' && stridx(p[2], '=') == -1 + let last_option = lgs#opts#OptionName(p[2]) + else + let last_option = '' + endif + endfor + + if last_option == '' + if a:lead[0] == '-' + let val = stridx(a:lead, '=') + + if val == -1 + return [lgs#opts#OptionName(a:lead), 0, '', len(a:params) > a:argno] + else + return [lgs#opts#OptionName(a:lead[: val - 1]), a:lead[val + 1 :], a:lead[: val], 0] + endif + else + return 1 + endif + else + return [lgs#opts#OptionName(last_option), a:lead, '', 0] + endif +endfunction + +" Tells position of completed argument excluding options. +function! s:ValueNo(params, argno) + let cnt = 0 + let skip = 0 + + for p in a:params[0 : a:argno - 1] + if p[2][0] == '-' && !skip + if p[2] == '--' + let skip = 1 + elseif stridx(p[2], '=') == -1 + let cnt = cnt - 1 + endif + else + let cnt = cnt + 1 + endif + endfor + + return max([0, cnt - 1]) +endfunction + +" Completion function itself +function! lgs#cmpl#Completion(lead, line, pos) + if !lgs#artisan#SetArtisanPathIfNeed() + return [] + endif + + let params = lgs#cmd#Str2ParamList(a:line) + let argno = lgs#cmd#Pos2ArgNo(params, a:pos) + + if argno == 0 + return [] + elseif argno == 1 + return lgs#utils#StartsWith(keys(g:lgs_generators), a:lead, '') + endif + + let generator = params[1][2] + + if has_key(g:lgs_generators, generator) + let option = s:IsOption(params, argno, a:lead) + let ot = type(option) + + if empty(a:lead) && ot == 0 + let ret = option == 1 ? s:CallOption(generator, '', 0, '', 1) : [] + return extend(ret, s:CallArgument(generator, s:ValueNo(params, argno), a:lead)) + elseif ot == 0 || empty(option) + return s:CallArgument(generator, s:ValueNo(params, argno), a:lead) + else + return s:CallOption(generator, option[0], option[1], option[2], option[3]) + endif + endif + + return [] +endfunction + +let g:lgs_cmpl_autoload_done = 1 diff --git a/autoload/lgs/cmplf.vim b/autoload/lgs/cmplf.vim new file mode 100644 index 0000000..a2bed9b --- /dev/null +++ b/autoload/lgs/cmplf.vim @@ -0,0 +1,127 @@ +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +"" LGS +"" +"" VIM interface for Laravel 4 generator bundle +"" (https://github.com/JeffreyWay/Laravel-4-Generators) +"" +"" Copyright 2013 Naquad. +"" +"" This program is free software; you can redistribute it and/or modify +"" it under the terms of the GNU General Public License as published by +"" the Free Software Foundation; either version 3 of the License, or +"" (at your option) any later version. +"" +"" This program is distributed in the hope that it will be useful, +"" but WITHOUT ANY WARRANTY; without even the implied warranty of +"" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +"" GNU General Public License +"" for more details. +"" +"" +"" Completion sources. +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +if &cp || exists('g:lgs_cmplf_autoload_done') + finish +endif + +" Auxiliary functions to get output from external commands +" and split them. Fails silently. +function! s:QuietCommand(cmd) + if has('win32') + return a:cmd . ' 2>NUL' + else + return a:cmd . ' 2>/dev/null' + endif +endfunction + +function! s:ReadOptionsFromOutput(cmd, input) + let output = system(s:QuietCommand(a:cmd), a:input) + + if v:shell_error + return [] + else + return filter(lgs#utils#StringList(output), '!empty(v:val)') + endif +endfunction + +let s:environments_listing = expand(':p:h') . '/../../misc/get_envs.php' + +" I didn't find anything better than just faking out Application +" class and printing given environments in detectEvnrioment(). +" Script is very basic and should be rewritten. +function! lgs#cmplf#Environments(lead) + let cmd = [ + \ g:lg_php, + \ s:environments_listing, + \ fnamemodify(b:artisan, ':p:h') . '/bootstrap/start.php' + \ ] + + return s:ReadOptionsFromOutput(join(map(cmd, 'shellescape(v:val)'), ' '), ' ') +endfunction + +let s:model_listing = join(readfile(expand(':p:h') . '/../../misc/get_models.php'), '') + +" Models script is ran in artisan tinker console. +" Not sure thats the best solution. +function! lgs#cmplf#Models(lead) + return s:ReadOptionsFromOutput(lgs#artisan#MakeArtisanCommand('tinker', '-q'), s:model_listing) +endfunction + +let s:field_types = [ + \ 'increments', + \ 'bigIncrements', + \ 'index', + \ 'foreign', + \ 'string', + \ 'text', + \ 'integer', + \ 'integer', + \ 'bigInteger', + \ 'mediumInteger', + \ 'tinyInteger', + \ 'smallInteger', + \ 'unsignedInteger', + \ 'unsignedBigInteger', + \ 'float', + \ 'decimal', + \ 'boolean', + \ 'enum', + \ 'date', + \ 'dateTime', + \ 'time', + \ 'timestamp', + \ 'binary', + \ ] + +" Just filter the list of possible types +function! lgs#cmplf#FieldTypeCompletion(lead) + let idx = strridx(a:lead, ':') + + if idx != -1 + return lgs#utils#StartsWith(copy(s:field_types), a:lead[idx + 1 : ], a:lead[: idx]) + endif + + return [] +endfunction + +" Auxiliary option to choose directory if its the only option +function! s:EnterDir(lst) + if len(a:lst) == 1 && isdirectory(a:lst[0]) + return [a:lst[0] . '/'] + endif + + return a:lst +endfunction + +" File path completion +function! lgs#cmplf#FileCompletion(lead) + return s:EnterDir(glob(escape(a:lead, '*?[]\') . '*', 0, 1)) +endfunction + +" Directory path completion +function! lgs#cmplf#DirCompletion(lead) + return s:EnterDir(filter(glob(escape(a:lead, '*?[]\') . '*', 0, 1), 'isdirectory(v:val)')) +endfunction + +let g:lgs_cmplf_autoload_done = 1 diff --git a/autoload/lgs/opts.vim b/autoload/lgs/opts.vim new file mode 100644 index 0000000..3087308 --- /dev/null +++ b/autoload/lgs/opts.vim @@ -0,0 +1,102 @@ +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +"" LGS +"" +"" VIM interface for Laravel 4 generator bundle +"" (https://github.com/JeffreyWay/Laravel-4-Generators) +"" +"" Copyright 2013 Naquad. +"" +"" This program is free software; you can redistribute it and/or modify +"" it under the terms of the GNU General Public License as published by +"" the Free Software Foundation; either version 3 of the License, or +"" (at your option) any later version. +"" +"" This program is distributed in the hope that it will be useful, +"" but WITHOUT ANY WARRANTY; without even the implied warranty of +"" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +"" GNU General Public License +"" for more details. +"" +"" +"" Option parse (a-la getopts w/o short and flag options). +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +if &cp || exists('g:lgs_opts_autoload_done') + finish +endif + +" Remove leading -- +function! lgs#opts#OptionName(opt) + return substitute(a:opt, '^--\?', '', '') +endfunction + +" Walk through argument list and make dictionary with arguments and +" options with their values. +function! lgs#opts#ParseOptions(params) + let arguments = [] + let options = {} + let discard_next = 0 + + let i = 0 + let pl = len(a:params) + + while i < pl + if a:params[i][0] == '-' && !discard_next + if a:params[i] == '--' + let discard_next = 1 + continue + endif + + let option = lgs#opts#OptionName(a:params[i]) + let argpos = stridx(option, '=') + + if argpos == -1 + if i == pl - 1 + return {'success': 0, 'error': '--' . option . ' requires argument!'} + else + let options[option] = a:params[i + 1] + let i = i + 1 + endif + else + let options[option[: argpos - 1]] = option[argpos + 1 :] + endif + else + call add(arguments, a:params[i]) + endif + + let i = i + 1 + endwhile + + return {'success': 1, 'options': options, 'arguments': arguments} +endfunction + +" Checks that given options comply to generator description. +function! lgs#opts#ValidateOptions(name, generator, params) + if (!has_key(a:generator, 'options') || + \ empty(a:generator.options)) && !empty(a:params.options) + call lgs#utils#Warn('Generator ' . name . ' takes no options!') + return 0 + endif + + for o in keys(a:params.options) + if !has_key(a:generator.options, o) + call lgs#utils#Warn('Generator ' . name . ' has no option ' . o) + return 0 + endif + endfor + + return 1 +endfunction + +" Takes dictionary and makes shellescape()d string with options +function! lgs#opts#SerializeOptions(opts) + let result = [] + + for [k, v] in items(a:opts) + call add(result, shellescape('--' . k) . '=' . shellescape(v)) + endfor + + return join(result, ' ') +endfunction + +let g:lgs_opts_autoload_done = 1 diff --git a/autoload/lgs/utils.vim b/autoload/lgs/utils.vim new file mode 100644 index 0000000..6489b6c --- /dev/null +++ b/autoload/lgs/utils.vim @@ -0,0 +1,55 @@ +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +"" LGS +"" +"" VIM interface for Laravel 4 generator bundle +"" (https://github.com/JeffreyWay/Laravel-4-Generators) +"" +"" Copyright 2013 Naquad. +"" +"" This program is free software; you can redistribute it and/or modify +"" it under the terms of the GNU General Public License as published by +"" the Free Software Foundation; either version 3 of the License, or +"" (at your option) any later version. +"" +"" This program is distributed in the hope that it will be useful, +"" but WITHOUT ANY WARRANTY; without even the implied warranty of +"" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +"" GNU General Public License +"" for more details. +"" +"" +"" Some utility functions. +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +if &cp || exists('g:lgs_utils_autoload_done') + finish +endif + +function! lgs#utils#Warn(...) + echoerr join(map(copy(a:000), 'string(v:val)'), ' ') +endfunction + +" Sorts and filters list with completion options +function! lgs#utils#StartsWith(lst, start, prefix) + let sl = strlen(a:start) - 1 + call sort(a:lst) + + if sl < 0 + let ret = a:lst + else + let ret = filter(a:lst, 'v:val[:sl] ==? a:start') + end + + if !empty(a:prefix) + let ret = map(ret, 'a:prefix . v:val') + endif + + return ret +endfunction + +" Splits string into individual lines removing empty ones. +function! lgs#utils#StringList(str) + return filter(split(a:str, '\r\?\n'), '!empty(v:val)') +endfunction + +let g:lgs_utils_autoload_done = 1 diff --git a/doc/lgs.txt b/doc/lgs.txt new file mode 100644 index 0000000..e57ddfe --- /dev/null +++ b/doc/lgs.txt @@ -0,0 +1,169 @@ +*lgs.txt* Laravel 4 Generator bundle interface. v0.1 +*LGS* *LG* *'lgs'* *'lg'* +=============================================================================== +CONTENTS *lgs-contents* + + 1. Intro........................................|lgs-intro| + 2. Options......................................|lgs-options| + 3. Subcommands..................................|lgs-commands| + 4. Known issues.................................|lgs-issues| + +=============================================================================== +INTRO *lgs-intro* + +Adds integration with Laravel 4 Generator bundle +https://github.com/JeffreyWay/Laravel-4-Generators through LG command allowing +user to generate various entities w/o leaving VIM. Convinient navigation +through generated/updated files included. This is a very early release, please +file bugs if you find any at https://github.com/naquad/lgs.vim . + +=============================================================================== +OPTIONS *lg-options* *lgs-options* + + + *'g:lg_php'* +The only variable currently available is *g:lg_php* which indicates which +PHP interpreter to use. By default it is just 'php'. + +=============================================================================== +SUBCOMMANDS *lg-commands* *lgs-commands* + *LG* +Overview:~ + + |LG_form|.......................................|lg-form| + |LG_model|......................................|lg-model| + |LG_migration|..................................|lg-migration| + |LG_resource|...................................|lg-resource| + |LG_scaffold|...................................|lg-scaffold| + |LG_seed|.......................................|lg-seed| + |LG_test|.......................................|lg-test| + |LG_view|.......................................|lg-view| + +------------------------------------------------------------------------------- +Detailed description:~ + +Each subcommand is mapped to 'artisan' generator: > + LG form -> artisan generate:form + LG model -> artisan generate:model + LG migration -> artisan generate:migration + LG resource -> artisan generate:resource + LG scaffold -> artisan generate:scaffold + LG seed -> artisan generate:seed + LG test -> artisan generate:test + LG view -> artisan generate:view +< + +All options that can be passed to commands are optional. +Options are passed as is to 'artisan'. + +Every command opens QuickFix list of files that were update or created. +In case if 'artisan' failed to create/update file that'll be indicated +in QuickFix. + +In detail: + + *LG_form* +:LG form [options] + Generates form and inserts it into current buffer at cursor position. + is required. + + Possible options: + --html={ul,li,ol} + --env= + --metod={create|edit} + + *LG_model* +:LG model [options] + Generates model. New model is required. + + Possible options: + --env= + --path= + --template= + + *LG_migration* +:LG migration [options] [optional fields specification] + Generates new migration. Extra arguments after name will be treated + as fields specification and will be passed to 'artisan' through + |--fields| option. + + is required. + + Possible options: + --env= + --path= + + *LG_resource* +:LG resource [options] [optional fields specification] + Generates new resource with given name. Extra arguments after name will + be treated as fields specification and will be passed to 'artisan' through + |--fields| option. + + is required. + + Possible options: + --env= + --path= + + *LG_seed* +:LG seed [options] + Generates seed file for given model. + + is required. + + Possible options: + --env= + --path= + --template= + + *LG_test* +:LG test [options] + Generates PHPUnit test. + + is required. + + Possible options: + --env= + --path= + --template= + + *LG_view* +:LG view [options] + Generates basic view. + + is required. + + Possible options: + --env= + --path= + --template= + + *LG_controller* +:LG controller [options] + Creates basic controller. + + is required. + + Possible options: + --env= + --path= + --template= + +=============================================================================== +KNOWN ISSUES *lg-issues* + +First of all current discovery of models is very basic and fragile. It is a +piece of code executed in `artisan ticker` console. You can find it +in `misc/get_models.php`. + +Environment discovery is very dumb: mock `Application` class, load +`bootstrap/start.php` catch `detectEnvironment`. In case if `detectEnvironment` is +called after some work with `Application` instance it'll just crash and you +won't get any completion. + +=============================================================================== +CREDITS *lg-credits* + +Developed by Naquad . Distributed under Vim's |license|. + +Project's homepage & GIT repository: https://github.com/naquad/lgs.vim diff --git a/misc/get_envs.php b/misc/get_envs.php new file mode 100644 index 0000000..eddd6ca --- /dev/null +++ b/misc/get_envs.php @@ -0,0 +1,15 @@ + +"" for more details. +"" +"" Main file defining command and generators configuration. +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +if &cp || exists('g:loaded_lgs') + finish +endif + +" PHP is needed to get completions for models and environments +" + run artisan if we're on windows or artisan file itself is just +" not executable +if !exists('g:lg_php') + let g:lg_php = 'php' +endif + +" some default options +let s:default_options = { + \ 'env' : function('lgs#cmplf#Environments'), + \ 'path' : function('lgs#cmplf#DirCompletion'), + \ } + +" more defaults +let s:default_with_template = copy(s:default_options) +let s:default_with_template['template'] = function('lgs#cmplf#FileCompletion') + +" Field type completer. First argument is name of generated +" entity so we don't complete it. +" Everything is a field type. Used by migration, scaffold and resource. +" +" 1: 0 here means don't complete 1st argument in any way +let s:field_type_completion_after_name = { + \ 1: 0, + \ '*': function('lgs#cmplf#FieldTypeCompletion'), + \ } + +" command line specification for each generator +let g:lgs_generators = { + \ 'form': { + \ 'options': { + \ 'method': ['create', 'edit'], + \ 'html': ['ul', 'ol', 'li'], + \ 'env': function('lgs#cmplf#Environments') + \ }, + \ 'arguments': { + \ 1: function('lgs#cmplf#Models') + \ }, + \ 'requires': 'model name', + \ }, + \ 'model': { + \ 'options': s:default_with_template, + \ }, + \ 'migration': { + \ 'options': s:default_options, + \ 'arguments': s:field_type_completion_after_name, + \ 'fields': 1, + \ }, + \ 'resource': { + \ 'options': s:default_options, + \ 'arguments': s:field_type_completion_after_name, + \ 'fields': 1, + \ }, + \ 'scaffold': { + \ 'options': s:default_options, + \ 'arguments': s:field_type_completion_after_name, + \ 'fields': 1, + \ }, + \ 'seed': { + \ 'options': s:default_with_template, + \ 'arguments': { + \ 1: function('lgs#cmplf#Models') + \ } + \ }, + \ 'test': { + \ 'options': s:default_with_template, + \ }, + \ 'view': { + \ 'options': s:default_with_template, + \ }, + \ 'controller': { + \ 'options': s:default_with_template + \ }, + \ } + +function! s:LG(...) + if !lgs#artisan#SetArtisanPathIfNeed() + call lgs#artisan#Warn('Not a Laravel 4 file') + return + endif + + let params = lgs#opts#ParseOptions(a:000) + + if params.success == 0 + call lgs#utils#Warn(params.error) + return + endif + + if empty(params.arguments) + call lgs#utils#Warn('Generator name required') + return + endif + + let generator = remove(params.arguments, 0) + + if !has_key(g:lgs_generators, generator) + call lgs#utils#Warn('Unknown generator ' . generator) + return + endif + + if !lgs#opts#ValidateOptions(generator, g:lgs_generators[generator], params) + return + endif + + if get(g:lgs_generators[generator], 'fields', 0) && len(params.arguments) > 1 + let params.options['fields'] = join(params.arguments[1:], ', ') + let params.arguments = params.arguments[:0] + endif + + let cmd = lgs#artisan#MakeArtisanCommand('generate:' . generator, '-n', '--no-ansi') + + let capitalized = substitute(generator, '^.', '\U&\E', '') + let requires = get(g:lgs_generators[generator], 'requires', generator) + if empty(params.arguments) + call lgs#utils#Warn(capitalized . ' requires ' . requires . ' name') + elseif len(params.arguments) > 1 + call lgs#utils#Warn(capitalized . ' takes only ' . requires . ' name') + else + let cmd .= ' ' . lgs#opts#SerializeOptions(params.options) . ' ' . + \ shellescape(params.arguments[0]) + + let files = lgs#artisan#RunArtisan(cmd, generator != 'form') + + if files[0] == 0 + call lgs#utils#Warn(join(files[1], "\n")) + return + endif + + if generator == 'form' + call append('.', files[1]) + else + call setqflist(files[1], 'a') + let artisan = b:artisan + copen + exec 'cd ' . fnameescape(fnamemodify(artisan, ':p:h')) + endif + endif +endfunction + +command -nargs=+ -complete=customlist,lgs#cmpl#Completion LG :call s:LG() + +let g:loaded_lgs = 1