Skip to content

Provide :Homestead command #7

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Vim support for [Laravel/Lumen][laravel] projects.
* Navigation commands such as `:Econtroller`, `:Eroutes`, `:Etest` and [many more][wiki-navigation].
* Enhanced `gf` command works on class names, template names, config and translation keys.
* Complete view/route names in insert mode.
* Interact with a Homestead guest VM from the host machine using `:Homestead`.
* Use `:Console` to fire up a REPL (`artisan tinker`).
* Use `:Start` to serve the app locally (`artisan serve`).

Expand Down
54 changes: 53 additions & 1 deletion autoload/laravel.vim
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,20 @@ function! s:app_views_path(...) dict abort
return join([self._root, 'resources/views'] + a:000, '/')
endfunction

""
" Get path to source root on the Homestead VM, optionally with [path] appended.
function! s:app_homestead_path(...) dict abort
if self.cache.needs('homestead_root')
call self.cache.set('homestead_root', laravel#homestead#root(self._root))
endif

let root = self.cache.get('homestead_root')

return empty(root) ? '' : join([root] + a:000, '/')
endfunction

call s:add_methods('app', ['glob', 'has_dir', 'has_file', 'has_path'])
call s:add_methods('app', ['path', 'src_path', 'config_path', 'migrations_path', 'find_migration', 'expand_migration', 'views_path'])
call s:add_methods('app', ['path', 'src_path', 'config_path', 'migrations_path', 'find_migration', 'expand_migration', 'views_path', 'homestead_path'])

""
" Detect app's namespace
Expand Down Expand Up @@ -565,6 +577,46 @@ function! laravel#buffer_commands() abort
" Invoke Artisan with [arguments] (with intelligent completion).
command! -buffer -bang -bar -nargs=* -complete=customlist,laravel#artisan#complete
\ Artisan execute laravel#artisan#exec(<q-bang>, <f-args>)

""
" @command Homestead {cmd}
" Invoke shell {cmd} on the Homestead VM over SSH.
"
" Several strategies for executing the ssh command in order:
"
" * Dispatch's |:Start| command
" * The built-in |:terminal|
" * At Vim's command line via |:!|
"
" The {cmd} is executed with the working directory being the project's
" directory on the VM. The project's directory is detected from your
" Homestead.json configuration file, using the "folders" mappings to do the
" translation from host path to guest path: >
" "folders": [
" {
" "map": "~/code",
" "to": "/home/vagrant/code"
" }
" ],
" <
"
" The plug-in will look for the Homestead.json file in the directory
" specified in @setting(laravel_homestead_dir) or in ~/Homestead.
"
" Note: Only Homestead.json is taken into account, and not Homestead.yaml,
" since Vim cannot parse YAML. If you prefer to use the Homestead.yaml file,
" it's sufficient to set only the "folders" array in Homestead.json.
"
" @command Homestead
" Start an interactive SSH session on the Homestead VM.
"
" @command Homestead! [arguments]
" Invoke Vagrant with [arguments] in the context of the Homestead directory
" on the host machine. For example, to start the VM: >
" :Homestead! up
" <
command! -buffer -bang -bar -nargs=* -complete=shellcmd
\ Homestead execute laravel#homestead#exec(<q-bang>, <f-args>)
endfunction

""
Expand Down
130 changes: 130 additions & 0 deletions autoload/laravel/homestead.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
" autoload/laravel/homestead.vim - Laravel Homestead support for Vim
" Maintainer: Noah Frederick

""
" The directory where Homestead is installed.
let s:dir = get(g:, 'laravel_homestead_dir', '~/Homestead')
let s:yaml = s:dir . '/Homestead.yaml'
let s:json = s:dir . '/Homestead.json'

""
" Get Dict from JSON {expr}.
function! s:json_decode(expr) abort
try
if exists('*json_decode')
let expr = type(a:expr) == type([]) ? join(a:expr, "\n") : a:expr
return json_decode(expr)
else
return projectionist#json_parse(a:expr)
endif
catch /^Vim\%((\a\+)\)\=:E474/
call laravel#error('Homestead.json cannot be parsed')
catch /^invalid JSON/
call laravel#error('Homestead.json cannot be parsed')
catch /^Vim\%((\a\+)\)\=:E117/
call laravel#error('projectionist is not available')
endtry
return {}
endfunction

""
" Get path to current project on the Homestead VM.
function! laravel#homestead#root(app_root) abort
if !filereadable(s:json)
call laravel#error('Homestead.json cannot be read: '
\ . s:json . ' (set g:laravel_homestead_dir)')
return ''
endif

let config = s:json_decode(readfile(s:json))

for folder in get(config, 'folders', [])
let source = expand(folder.map)

if a:app_root . '/' =~# '^' . source . '/'
return substitute(a:app_root, '^' . source, folder.to, '')
endif
endfor

return ''
endfunction

""
" Change working directory to {dir}, respecting current window's local dir
" state. Returns old working directory to be restored later by a second
" invocation of the function.
function! s:cd(dir) abort
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let cwd = getcwd()
execute cd fnameescape(a:dir)
return cwd
endfunction

""
" Build SSH shell command from command-line arguments.
function! s:ssh(args) abort
if empty(a:args)
return 'vagrant ssh'
endif

let root = laravel#app().homestead_path()

if empty(root)
call laravel#error('Homestead site not configured for '
\ . laravel#app().path())
return ''
endif

let args = insert(a:args, 'cd ' . fnamemodify(root, ':S') . ' &&')
return 'vagrant ssh -- ' . shellescape(join(args))
endfunction

""
" Build Vagrant shell command from command-line arguments.
function! s:vagrant(args) abort
let args = empty(a:args) ? ['status'] : a:args
return 'vagrant ' . join(args)
endfunction

""
" The :Homestead command.
function! laravel#homestead#exec(...) abort
let args = copy(a:000)
let vagrant = remove(args, 0)

if !isdirectory(s:dir)
return laravel#error('Homestead directory does not exist: '
\ . s:dir . ' (set g:laravel_homestead_dir)')
endif

let cmdline = vagrant ==# '!' ? s:vagrant(args) : s:ssh(args)

if empty(cmdline)
" There is no path configured for the VM.
return ''
endif

if exists(':Start')
execute 'Start -title=homestead -wait=always -dir='.fnameescape(s:dir) cmdline
elseif exists(':terminal')
tab split
execute 'lcd' fnameescape(s:dir)
execute 'terminal' cmdline
else
let cwd = s:cd(s:dir)
execute '!' . cmdline
call s:cd(cwd)
endif

return ''
endfunction

""
" @private
" Hack for testing script-local functions.
function! laravel#homestead#sid()
nnoremap <SID> <SID>
return maparg('<SID>', 'n')
endfunction

" vim: fdm=marker:sw=2:sts=2:et
56 changes: 54 additions & 2 deletions doc/laravel.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ Noah Frederick *Laravel.vim* *laravel*
==============================================================================
CONTENTS *laravel-contents*
1. Introduction..............................................|laravel-intro|
2. Commands...............................................|laravel-commands|
3. About.....................................................|laravel-about|
2. Configuration............................................|laravel-config|
3. Commands...............................................|laravel-commands|
4. About.....................................................|laravel-about|

==============================================================================
INTRODUCTION *laravel-intro*
Expand All @@ -15,16 +16,67 @@ Some features include:

* The |:Artisan| command wraps artisan with intelligent completion.
* Includes projections for projectionist.vim.
* Use |:Homestead| to send commands over SSH to your development VM.
* Use |:Console| to fire up a REPL (artisan tinker).

This plug-in is only available if 'compatible' is not set.

==============================================================================
CONFIGURATION *laravel-config*

*g:laravel_homestead_dir*
The directory where Homestead is installed. Default:
>
'~/Homestead'
<

==============================================================================
COMMANDS *laravel-commands*

:Artisan[!] [arguments] *:Artisan*
Invoke Artisan with [arguments] (with intelligent completion).

:Homestead {cmd} *:Homestead*
Invoke shell {cmd} on the Homestead VM over SSH.

Several strategies for executing the ssh command in order:

* Dispatch's |:Start| command
* The built-in |:terminal|
* At Vim's command line via |:!|

The {cmd} is executed with the working directory being the project's
directory on the VM. The project's directory is detected from your
Homestead.json configuration file, using the "folders" mappings to do the
translation from host path to guest path:
>
"folders": [
{
"map": "~/code",
"to": "/home/vagrant/code"
}
],
<

The plug-in will look for the Homestead.json file in the directory specified
in |g:laravel_homestead_dir| or in ~/Homestead.

Note: Only Homestead.json is taken into account, and not Homestead.yaml,
since Vim cannot parse YAML. If you prefer to use the Homestead.yaml file,
it's sufficient to set only the "folders" array in Homestead.json.


:Homestead
Start an interactive SSH session on the Homestead VM.


:Homestead! [arguments]
Invoke Vagrant with [arguments] in the context of the Homestead directory on
the host machine. For example, to start the VM:
>
:Homestead! up
<

==============================================================================
ABOUT *laravel-about*

Expand Down
7 changes: 7 additions & 0 deletions plugin/laravel.vim
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@
"
" * The |:Artisan| command wraps artisan with intelligent completion.
" * Includes projections for projectionist.vim.
" * Use |:Homestead| to send commands over SSH to your development VM.
" * Use |:Console| to fire up a REPL (artisan tinker).
"
" This plug-in is only available if 'compatible' is not set.

""
" @setting g:laravel_homestead_dir
" The directory where Homestead is installed. Default: >
" '~/Homestead'
" <

""
" @section About, about
" @plugin(stylized) is distributed under the same terms as Vim itself (see
Expand Down
20 changes: 20 additions & 0 deletions test/homestead.vader
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Before (in a laravel buffer):
let g:laravel_homestead_dir = expand('test/fixtures')
tabedit test/fixtures/laravel-8/.env

After (clean up buffer):
bdelete

Execute (Detect Homestead app root):
AssertEqual laravel#homestead#root('/home/local/code/project'), '/home/vagrant/code/project'

Execute (Invalid Homestead app root):
AssertEqual laravel#homestead#root('/home/local/invalid/project'), ''

Execute (Access Homestead app root via app object):
" Fake the app root.
let b:app = deepcopy(laravel#app())
let b:app._root = '/home/local/code/project'

AssertEqual b:app.homestead_path(), '/home/vagrant/code/project'
AssertEqual b:app.homestead_path('public'), '/home/vagrant/code/project/public'