Skip to content

Commit 4f8224d

Browse files
committed
Provide :Homestead command
Interact with the current project on a Homestead VM from the host machine over SSH. See :help :Homestead for usage.
1 parent 1c5fd02 commit 4f8224d

File tree

6 files changed

+265
-3
lines changed

6 files changed

+265
-3
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Vim support for [Laravel/Lumen][laravel] projects.
1818
* Navigation commands such as `:Econtroller`, `:Eroutes`, `:Etest` and [many more][wiki-navigation].
1919
* Enhanced `gf` command works on class names, template names, config and translation keys.
2020
* Complete view/route names in insert mode.
21+
* Interact with a Homestead guest VM from the host machine using `:Homestead`.
2122
* Use `:Console` to fire up a REPL (`artisan tinker`).
2223
* Use `:Start` to serve the app locally (`artisan serve`).
2324

autoload/laravel.vim

+53-1
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,20 @@ function! s:app_views_path(...) dict abort
149149
return join([self._root, 'resources/views'] + a:000, '/')
150150
endfunction
151151

152+
""
153+
" Get path to source root on the Homestead VM, optionally with [path] appended.
154+
function! s:app_homestead_path(...) dict abort
155+
if self.cache.needs('homestead_root')
156+
call self.cache.set('homestead_root', laravel#homestead#root(self._root))
157+
endif
158+
159+
let root = self.cache.get('homestead_root')
160+
161+
return empty(root) ? '' : join([root] + a:000, '/')
162+
endfunction
163+
152164
call s:add_methods('app', ['glob', 'has_dir', 'has_file', 'has_path'])
153-
call s:add_methods('app', ['path', 'src_path', 'config_path', 'migrations_path', 'find_migration', 'expand_migration', 'views_path'])
165+
call s:add_methods('app', ['path', 'src_path', 'config_path', 'migrations_path', 'find_migration', 'expand_migration', 'views_path', 'homestead_path'])
154166

155167
""
156168
" Detect app's namespace
@@ -565,6 +577,46 @@ function! laravel#buffer_commands() abort
565577
" Invoke Artisan with [arguments] (with intelligent completion).
566578
command! -buffer -bang -bar -nargs=* -complete=customlist,laravel#artisan#complete
567579
\ Artisan execute laravel#artisan#exec(<q-bang>, <f-args>)
580+
581+
""
582+
" @command Homestead {cmd}
583+
" Invoke shell {cmd} on the Homestead VM over SSH.
584+
"
585+
" Several strategies for executing the ssh command in order:
586+
"
587+
" * Dispatch's |:Start| command
588+
" * The built-in |:terminal|
589+
" * At Vim's command line via |:!|
590+
"
591+
" The {cmd} is executed with the working directory being the project's
592+
" directory on the VM. The project's directory is detected from your
593+
" Homestead.json configuration file, using the "folders" mappings to do the
594+
" translation from host path to guest path: >
595+
" "folders": [
596+
" {
597+
" "map": "~/code",
598+
" "to": "/home/vagrant/code"
599+
" }
600+
" ],
601+
" <
602+
"
603+
" The plug-in will look for the Homestead.json file in the directory
604+
" specified in @setting(laravel_homestead_dir) or in ~/Homestead.
605+
"
606+
" Note: Only Homestead.json is taken into account, and not Homestead.yaml,
607+
" since Vim cannot parse YAML. If you prefer to use the Homestead.yaml file,
608+
" it's sufficient to set only the "folders" array in Homestead.json.
609+
"
610+
" @command Homestead
611+
" Start an interactive SSH session on the Homestead VM.
612+
"
613+
" @command Homestead! [arguments]
614+
" Invoke Vagrant with [arguments] in the context of the Homestead directory
615+
" on the host machine. For example, to start the VM: >
616+
" :Homestead! up
617+
" <
618+
command! -buffer -bang -bar -nargs=* -complete=shellcmd
619+
\ Homestead execute laravel#homestead#exec(<q-bang>, <f-args>)
568620
endfunction
569621

570622
""

autoload/laravel/homestead.vim

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
" autoload/laravel/homestead.vim - Laravel Homestead support for Vim
2+
" Maintainer: Noah Frederick
3+
4+
""
5+
" The directory where Homestead is installed.
6+
let s:dir = get(g:, 'laravel_homestead_dir', '~/Homestead')
7+
let s:yaml = s:dir . '/Homestead.yaml'
8+
let s:json = s:dir . '/Homestead.json'
9+
10+
""
11+
" Get Dict from JSON {expr}.
12+
function! s:json_decode(expr) abort
13+
try
14+
if exists('*json_decode')
15+
let expr = type(a:expr) == type([]) ? join(a:expr, "\n") : a:expr
16+
return json_decode(expr)
17+
else
18+
return projectionist#json_parse(a:expr)
19+
endif
20+
catch /^Vim\%((\a\+)\)\=:E474/
21+
call laravel#error('Homestead.json cannot be parsed')
22+
catch /^invalid JSON/
23+
call laravel#error('Homestead.json cannot be parsed')
24+
catch /^Vim\%((\a\+)\)\=:E117/
25+
call laravel#error('projectionist is not available')
26+
endtry
27+
return {}
28+
endfunction
29+
30+
""
31+
" Get path to current project on the Homestead VM.
32+
function! laravel#homestead#root(app_root) abort
33+
if !filereadable(s:json)
34+
call laravel#error('Homestead.json cannot be read: '
35+
\ . s:json . ' (set g:laravel_homestead_dir)')
36+
return ''
37+
endif
38+
39+
let config = s:json_decode(readfile(s:json))
40+
41+
for folder in get(config, 'folders', [])
42+
let source = expand(folder.map)
43+
44+
if a:app_root . '/' =~# '^' . source . '/'
45+
return substitute(a:app_root, '^' . source, folder.to, '')
46+
endif
47+
endfor
48+
49+
return ''
50+
endfunction
51+
52+
""
53+
" Change working directory to {dir}, respecting current window's local dir
54+
" state. Returns old working directory to be restored later by a second
55+
" invocation of the function.
56+
function! s:cd(dir) abort
57+
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
58+
let cwd = getcwd()
59+
execute cd fnameescape(a:dir)
60+
return cwd
61+
endfunction
62+
63+
""
64+
" Build SSH shell command from command-line arguments.
65+
function! s:ssh(args) abort
66+
if empty(a:args)
67+
return 'vagrant ssh'
68+
endif
69+
70+
let root = laravel#app().homestead_path()
71+
72+
if empty(root)
73+
call laravel#error('Homestead site not configured for '
74+
\ . laravel#app().path())
75+
return ''
76+
endif
77+
78+
let args = insert(a:args, 'cd ' . fnamemodify(root, ':S') . ' &&')
79+
return 'vagrant ssh -- ' . shellescape(join(args))
80+
endfunction
81+
82+
""
83+
" Build Vagrant shell command from command-line arguments.
84+
function! s:vagrant(args) abort
85+
let args = empty(a:args) ? ['status'] : a:args
86+
return 'vagrant ' . join(args)
87+
endfunction
88+
89+
""
90+
" The :Homestead command.
91+
function! laravel#homestead#exec(...) abort
92+
let args = copy(a:000)
93+
let vagrant = remove(args, 0)
94+
95+
if !isdirectory(s:dir)
96+
return laravel#error('Homestead directory does not exist: '
97+
\ . s:dir . ' (set g:laravel_homestead_dir)')
98+
endif
99+
100+
let cmdline = vagrant ==# '!' ? s:vagrant(args) : s:ssh(args)
101+
102+
if empty(cmdline)
103+
" There is no path configured for the VM.
104+
return ''
105+
endif
106+
107+
if exists(':Start')
108+
execute 'Start -title=homestead -wait=always -dir='.fnameescape(s:dir) cmdline
109+
elseif exists(':terminal')
110+
tab split
111+
execute 'lcd' fnameescape(s:dir)
112+
execute 'terminal' cmdline
113+
else
114+
let cwd = s:cd(s:dir)
115+
execute '!' . cmdline
116+
call s:cd(cwd)
117+
endif
118+
119+
return ''
120+
endfunction
121+
122+
""
123+
" @private
124+
" Hack for testing script-local functions.
125+
function! laravel#homestead#sid()
126+
nnoremap <SID> <SID>
127+
return maparg('<SID>', 'n')
128+
endfunction
129+
130+
" vim: fdm=marker:sw=2:sts=2:et

doc/laravel.txt

+54-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ Noah Frederick *Laravel.vim* *laravel*
44
==============================================================================
55
CONTENTS *laravel-contents*
66
1. Introduction..............................................|laravel-intro|
7-
2. Commands...............................................|laravel-commands|
8-
3. About.....................................................|laravel-about|
7+
2. Configuration............................................|laravel-config|
8+
3. Commands...............................................|laravel-commands|
9+
4. About.....................................................|laravel-about|
910

1011
==============================================================================
1112
INTRODUCTION *laravel-intro*
@@ -15,16 +16,67 @@ Some features include:
1516

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

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

24+
==============================================================================
25+
CONFIGURATION *laravel-config*
26+
27+
*g:laravel_homestead_dir*
28+
The directory where Homestead is installed. Default:
29+
>
30+
'~/Homestead'
31+
<
32+
2233
==============================================================================
2334
COMMANDS *laravel-commands*
2435

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

39+
:Homestead {cmd} *:Homestead*
40+
Invoke shell {cmd} on the Homestead VM over SSH.
41+
42+
Several strategies for executing the ssh command in order:
43+
44+
* Dispatch's |:Start| command
45+
* The built-in |:terminal|
46+
* At Vim's command line via |:!|
47+
48+
The {cmd} is executed with the working directory being the project's
49+
directory on the VM. The project's directory is detected from your
50+
Homestead.json configuration file, using the "folders" mappings to do the
51+
translation from host path to guest path:
52+
>
53+
"folders": [
54+
{
55+
"map": "~/code",
56+
"to": "/home/vagrant/code"
57+
}
58+
],
59+
<
60+
61+
The plug-in will look for the Homestead.json file in the directory specified
62+
in |g:laravel_homestead_dir| or in ~/Homestead.
63+
64+
Note: Only Homestead.json is taken into account, and not Homestead.yaml,
65+
since Vim cannot parse YAML. If you prefer to use the Homestead.yaml file,
66+
it's sufficient to set only the "folders" array in Homestead.json.
67+
68+
69+
:Homestead
70+
Start an interactive SSH session on the Homestead VM.
71+
72+
73+
:Homestead! [arguments]
74+
Invoke Vagrant with [arguments] in the context of the Homestead directory on
75+
the host machine. For example, to start the VM:
76+
>
77+
:Homestead! up
78+
<
79+
2880
==============================================================================
2981
ABOUT *laravel-about*
3082

plugin/laravel.vim

+7
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,17 @@
99
"
1010
" * The |:Artisan| command wraps artisan with intelligent completion.
1111
" * Includes projections for projectionist.vim.
12+
" * Use |:Homestead| to send commands over SSH to your development VM.
1213
" * Use |:Console| to fire up a REPL (artisan tinker).
1314
"
1415
" This plug-in is only available if 'compatible' is not set.
1516

17+
""
18+
" @setting g:laravel_homestead_dir
19+
" The directory where Homestead is installed. Default: >
20+
" '~/Homestead'
21+
" <
22+
1623
""
1724
" @section About, about
1825
" @plugin(stylized) is distributed under the same terms as Vim itself (see

test/homestead.vader

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Before (in a laravel buffer):
2+
let g:laravel_homestead_dir = expand('test/fixtures')
3+
tabedit test/fixtures/laravel-8/.env
4+
5+
After (clean up buffer):
6+
bdelete
7+
8+
Execute (Detect Homestead app root):
9+
AssertEqual laravel#homestead#root(expand('~/code/project-a')), '/home/vagrant/code/project-a'
10+
11+
Execute (Invalid Homestead app root):
12+
AssertEqual laravel#homestead#root(expand('~/invalid/project-a')), ''
13+
14+
Execute (Access Homestead app root via app object):
15+
" Fake the app root.
16+
let b:app = deepcopy(laravel#app())
17+
let b:app._root = expand('~/code/project-a')
18+
19+
AssertEqual b:app.homestead_path(), '/home/vagrant/code/project-a'
20+
AssertEqual b:app.homestead_path('public'), '/home/vagrant/code/project-a/public'

0 commit comments

Comments
 (0)