├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── README.md ├── Rakefile ├── addon-info.json ├── autoload ├── laravel.vim └── laravel │ ├── artisan.vim │ ├── completion.vim │ ├── goto.vim │ └── projectionist.vim ├── doc └── laravel.txt ├── plugin └── laravel.vim └── test ├── artisan.vader ├── feature_detection.vader ├── fixtures ├── .gitkeep └── init.vim ├── initialization.vader ├── navigation.vader └── not_in_project.vader /.gitignore: -------------------------------------------------------------------------------- 1 | # Packaged plug-in dir 2 | pkg/ 3 | 4 | # Fixtures installed by CI 5 | /test/fixtures/* 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 7.4 4 | 5 | before_script: | 6 | git clone https://github.com/junegunn/vader.vim.git 7 | git clone https://github.com/tpope/vim-projectionist.git 8 | git clone https://github.com/noahfrederick/vim-composer.git 9 | composer create-project laravel/laravel:^8.0 test/fixtures/laravel-8 10 | script: | 11 | vim -Nu test/fixtures/init.vim -c 'Vader! test/*' > /dev/null 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Testing 4 | 5 | Tests are written for [vader.vim][vader]. 6 | The test suite can be run via the rake task: 7 | 8 | bundle install 9 | bundle exec rake test 10 | 11 | Or by specifying the tests to run on the command line: 12 | 13 | vim -c "Vader! test/*" 14 | 15 | ## Documentation 16 | 17 | The documentation in `doc/` is generated from the plug-in source code via 18 | [vimdoc][vimdoc]. Do not edit `doc/.txt` directly. Refer to the 19 | existing inline documentation as a guide for documenting new code. 20 | 21 | The help doc can be rebuilt by running: 22 | 23 | bundle exec rake doc 24 | 25 | [vader]: https://github.com/junegunn/vader.vim 26 | [vimdoc]: https://github.com/google/vimdoc 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![Laravel.vim](https://noahfrederick.com/pi/vim-laravel-888by140-40a40a.png) 2 | 3 | Vim support for [Laravel/Lumen][laravel] projects. 4 | 5 | [![Release][release]](https://github.com/noahfrederick/vim-laravel/releases) 6 | [![Build Status][buildimg]](https://travis-ci.org/noahfrederick/vim-laravel) 7 | 8 | > :warning: This is a prerelease version, which may introduce breaking changes. 9 | 10 | [laravel]: https://laravel.com/ 11 | [release]: https://img.shields.io/github/v/release/noahfrederick/vim-laravel.svg 12 | [buildimg]: https://img.shields.io/travis/noahfrederick/vim-laravel/master.svg 13 | 14 | ## Features 15 | 16 | * The `:Artisan` command wraps `!php artisan` with intelligent completion. 17 | * Automatically load new files generated by `:Artisan make:*` commands into a buffer. 18 | * Navigation commands such as `:Econtroller`, `:Eroutes`, `:Etest` and [many more][wiki-navigation]. 19 | * Enhanced `gf` command works on class names, template names, config and translation keys. 20 | * Complete view/route names in insert mode. 21 | * Use `:Console` to fire up a REPL (`artisan tinker`). 22 | * Use `:Start` to serve the app locally (`artisan serve`). 23 | 24 | [wiki-navigation]: https://github.com/noahfrederick/vim-laravel/wiki/Navigation-Commands 25 | 26 | ## Installation 27 | 28 | Laravel.vim depends on a few other plug-ins for optional features, including 29 | [composer.vim][vim-composer], [dispatch.vim][dispatch], and 30 | [projectionist.vim][projectionist]: 31 | 32 | Plug 'tpope/vim-dispatch' "| Optional 33 | Plug 'tpope/vim-projectionist' "| 34 | Plug 'noahfrederick/vim-composer' "| 35 | Plug 'noahfrederick/vim-laravel' 36 | 37 | See the [full list of requirements and integrations][wiki-requirements]. 38 | 39 | [wiki-requirements]: https://github.com/noahfrederick/vim-laravel/wiki/Requirements 40 | 41 | ## Credits and License 42 | 43 | Thanks to Tim Pope for [rails.vim][rails] on which Laravel.vim is modeled. 44 | 45 | Copyright © Noah Frederick. Distributed under the same terms as Vim itself. 46 | See `:help license`. 47 | 48 | [vim-composer]: https://github.com/noahfrederick/vim-composer 49 | [projectionist]: https://github.com/tpope/vim-projectionist 50 | [dispatch]: https://github.com/tpope/vim-dispatch 51 | [rails]: https://github.com/tpope/vim-rails 52 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | 3 | require 'rake/packagetask' 4 | require 'json' 5 | 6 | plugin = JSON.load(File.new('addon-info.json')) 7 | 8 | desc 'Run tests with vader' 9 | task :test do 10 | sh 'vim -c "Vader! test/*"' 11 | end 12 | 13 | desc 'Rebuild the documentation with vimdoc' 14 | task :doc do 15 | sh 'vimdoc ./' 16 | end 17 | 18 | Rake::PackageTask.new(plugin['name']) do |p| 19 | p.version = plugin['version'] 20 | p.need_zip = true 21 | p.package_files.include(['plugin/*.vim', 'autoload/*.vim', 'doc/*.txt']) 22 | end 23 | -------------------------------------------------------------------------------- /addon-info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel", 3 | "version": "0.5.0", 4 | "author": "Noah Frederick", 5 | "description": "Vim support for Laravel/Lumen projects", 6 | "homepage": "https://github.com/noahfrederick/vim-laravel" 7 | } 8 | -------------------------------------------------------------------------------- /autoload/laravel.vim: -------------------------------------------------------------------------------- 1 | " autoload/laravel.vim - Laravel autoloads 2 | " Maintainer: Noah Frederick 3 | 4 | "" 5 | " @private 6 | " Throw error with {msg}. 7 | function! laravel#throw(msg) abort 8 | let v:errmsg = 'laravel: ' . a:msg 9 | throw v:errmsg 10 | endfunction 11 | 12 | "" 13 | " @private 14 | " Print warning {msg}. 15 | function! laravel#warn(msg) abort 16 | echohl WarningMsg | echomsg a:msg | echohl None 17 | endfunction 18 | 19 | "" 20 | " @private 21 | " Print error {msg}. 22 | function! laravel#error(msg) abort 23 | echohl ErrorMsg | echomsg a:msg | echohl None 24 | endfunction 25 | 26 | "" 27 | " Get Funcref from script-local function {name}. 28 | function! s:function(name) abort 29 | let func_name = split(expand(''), '\.\.')[-1] 30 | return function(substitute(a:name, '^s:', matchstr(func_name, '\d\+_'), '')) 31 | endfunction 32 | 33 | "" 34 | " Add {method_names} to prototype {namespace} Dict. Follows the same pattern 35 | " as rake.vim. 36 | function! s:add_methods(namespace, method_names) abort 37 | for name in a:method_names 38 | let s:{a:namespace}_prototype[name] = s:function('s:' . a:namespace . '_' . name) 39 | endfor 40 | endfunction 41 | 42 | let s:app_prototype = {} 43 | let s:apps = {} 44 | 45 | "" 46 | " @private 47 | " Get the app object belonging to the current app root, or that 48 | " of [root]. Initializes the app if not initialized. 49 | function! laravel#app(...) abort 50 | let root = get(a:000, 0, exists('b:laravel_root') && b:laravel_root !=# '' ? b:laravel_root : '') 51 | 52 | if empty(root) 53 | return {} 54 | endif 55 | 56 | if !has_key(s:apps, root) 57 | let s:apps[root] = deepcopy(s:app_prototype) 58 | let s:apps[root]._root = root 59 | endif 60 | 61 | return get(s:apps, root, {}) 62 | endfunction 63 | 64 | "" 65 | " Get the app object belonging to the current app root, or that 66 | " of [root]. Throws an error if not in a Laravel app. 67 | function! s:app(...) abort 68 | let app = call('laravel#app', a:000) 69 | 70 | if !empty(app) 71 | return app 72 | endif 73 | 74 | call laravel#throw('not a Laravel app: ' . expand('%:p')) 75 | endfunction 76 | 77 | "" 78 | " Get absolute path to app root, optionally with [path] appended. 79 | function! s:app_path(...) dict abort 80 | return join([self._root] + a:000, '/') 81 | endfunction 82 | 83 | "" 84 | " glob() in context of app root. 85 | function! s:app_glob(pat) dict abort 86 | return glob(self.path(a:pat), 1, 1) 87 | endfunction 88 | 89 | "" 90 | " Check whether path exists in project. 91 | function! s:app_has_path(path) dict abort 92 | let path = a:path[0] ==# '/' ? a:path : self.path(a:path) 93 | 94 | if stridx(path, '*') != -1 95 | return !empty(glob(path, 1, 1)) 96 | endif 97 | 98 | return getftime(path) != -1 99 | endfunction 100 | 101 | "" 102 | " Check whether directory exists in project. 103 | function! s:app_has_dir(dir) dict abort 104 | let path = a:dir[0] ==# '/' ? a:dir : self.path(a:dir) 105 | return isdirectory(path) 106 | endfunction 107 | 108 | "" 109 | " Check whether file is readable in project. 110 | function! s:app_has_file(file) dict abort 111 | let path = a:file[0] ==# '/' ? a:file : self.path(a:file) 112 | return filereadable(path) 113 | endfunction 114 | 115 | "" 116 | " Get absolute path to source directory, optionally with [path] appended. 117 | function! s:app_src_path(...) dict abort 118 | return join([self._root, 'app'] + a:000, '/') 119 | endfunction 120 | 121 | "" 122 | " Get absolute path to config directory, optionally with [path] appended. 123 | function! s:app_config_path(...) dict abort 124 | return join([self._root, 'config'] + a:000, '/') 125 | endfunction 126 | 127 | "" 128 | " Get absolute path to migrations directory, optionally with [path] appended. 129 | function! s:app_migrations_path(...) dict abort 130 | return join([self._root, 'database/migrations'] + a:000, '/') 131 | endfunction 132 | 133 | "" 134 | " Translate migration name to path. In case multiple migrations share the 135 | " same name, return the most recent. 136 | function! s:app_find_migration(slug) dict abort 137 | let candidates = glob(self.migrations_path('*_'.a:slug.'.php'), 1, 1) 138 | return (empty(candidates) ? '' : remove(candidates, -1)) 139 | endfunction 140 | 141 | "" 142 | " Expand migration slug to full name with timestamp. 143 | function! s:app_expand_migration(slug) dict abort 144 | return fnamemodify(self.find_migration(a:slug), ':t:r') 145 | endfunction 146 | 147 | "" 148 | " Get absolute path to views directory, optionally with [path] appended. 149 | function! s:app_views_path(...) dict abort 150 | return join([self._root, 'resources/views'] + a:000, '/') 151 | endfunction 152 | 153 | call s:add_methods('app', ['glob', 'has_dir', 'has_file', 'has_path']) 154 | call s:add_methods('app', ['path', 'src_path', 'config_path', 'migrations_path', 'find_migration', 'expand_migration', 'views_path']) 155 | 156 | "" 157 | " Detect app's namespace 158 | function! s:app_namespaces() abort dict 159 | let namespaces = {} 160 | 161 | try 162 | let composer = composer#project() 163 | 164 | for [namespace, path] in items(composer.query('autoload.psr-4', {})) 165 | let namespaces[path] = namespace[0:-2] 166 | endfor 167 | 168 | for [namespace, path] in items(composer.query('autoload-dev.psr-4', {})) 169 | let namespaces[path] = namespace[0:-2] 170 | endfor 171 | catch /^Vim\%((\a\+)\)\=:E117/ 172 | " Fail gracefully when composer.vim isn't available. 173 | let namespaces = { 174 | \ 'app/': 'App', 175 | \ 'database/factories/': 'Database\Factories', 176 | \ 'database/seeders/': 'Database\Seeders', 177 | \ 'tests/': 'Tests', 178 | \ } 179 | endtry 180 | 181 | return namespaces 182 | endfunction 183 | 184 | "" 185 | " Get app's namespace or namespace for file at [path] 186 | function! s:app_namespace(...) abort dict 187 | if self.cache.needs('namespaces') 188 | call self.cache.set('namespaces', self.namespaces()) 189 | endif 190 | 191 | let namespaces = self.cache.get('namespaces') 192 | 193 | if a:0 == 0 194 | return namespaces['app/'] 195 | endif 196 | 197 | let path = substitute(fnamemodify(a:1, ':p'), '\V\^' . self.path() . '/', '', '') 198 | 199 | for [prefix, namespace] in items(namespaces) 200 | if path =~# '\V\^' . prefix 201 | let path = substitute(path, '\V\^' . prefix, '', '') 202 | let ns = [namespace] + split(path, '/')[0:-2] 203 | 204 | return join(ns, '\') 205 | endif 206 | endfor 207 | 208 | return '' 209 | endfunction 210 | 211 | "" 212 | " Detect existence of {feature} in the current app. 213 | function! s:app_has(feature) abort dict 214 | if a:feature =~# '\v^(laravel|lumen)' 215 | return s:has_framework(a:feature) 216 | endif 217 | 218 | return s:has_feature_by_path(self, a:feature) 219 | endfunction 220 | 221 | function! s:has_framework(feature) 222 | let parts = split(a:feature, '\s\+') 223 | let name = remove(parts, 0) 224 | 225 | if len(parts) > 1 226 | let comparator = get(parts, 0) 227 | let ver = get(parts, 1) 228 | else 229 | let comparator = '==' 230 | let ver = get(parts, 0, '') 231 | end 232 | 233 | if name == 'laravel' 234 | let package = 'laravel/framework' 235 | elseif name == 'lumen' 236 | let package = 'laravel/lumen-framework' 237 | else 238 | return 0 239 | endif 240 | 241 | try 242 | return !!composer#project().is_installed(package, comparator, ver) 243 | catch /^Vim\%((\a\+)\)\=:E117/ 244 | return 0 245 | endtry 246 | endfunction 247 | 248 | function! s:has_feature_by_path(app, feature) 249 | let map = { 250 | \ 'artisan': 'artisan', 251 | \ 'blade': 'resources/views/**/*.blade.php', 252 | \ 'commands': 'app/Commands/', 253 | \ 'dotenv': '.env|.env.example', 254 | \ 'gulp': 'gulpfile.js', 255 | \ 'handlers': 'app/Handlers/', 256 | \ 'jobs': 'app/Jobs/', 257 | \ 'listeners': 'app/Listeners/', 258 | \ 'mail': 'app/Mail/', 259 | \ 'models': 'app/Models/', 260 | \ 'namespaced-tests': 'tests/Feature/|tests/Unit/', 261 | \ 'notifications': 'app/Notifications/', 262 | \ 'policies': 'app/Policies/', 263 | \ 'scopes': 'app/Scopes/', 264 | \ 'traits': 'app/Traits/', 265 | \ } 266 | 267 | let path = get(map, a:feature, a:feature.'/') 268 | 269 | return !empty(filter(split(path, '|'), 'a:app.has_path(v:val)')) 270 | endfunction 271 | 272 | call s:add_methods('app', ['namespaces', 'namespace', 'has']) 273 | 274 | "" 275 | " Read first [n] lines of file at {path}. 276 | " Adapted from rails.vim. 277 | function! s:readfile(path, ...) 278 | let nr = bufnr('^' . a:path . '$') 279 | 280 | if nr < 0 && exists('+shellslash') && ! &shellslash 281 | let nr = bufnr('^' . substitute(a:path, '/', '\\', 'g') . '$') 282 | endif 283 | 284 | if bufloaded(nr) 285 | return getbufline(nr, 1, a:0 ? a:1 : '$') 286 | elseif !filereadable(a:path) 287 | return [] 288 | elseif a:0 289 | return readfile(a:path, '', a:1) 290 | else 291 | return readfile(a:path) 292 | endif 293 | endfunction 294 | 295 | "" 296 | " Get dict of registered facades in the form 297 | " 298 | " { 'App': 'Illuminate\Support\Facades\App', ... } 299 | function! s:app_facades() abort dict 300 | if self.cache.needs('facades') 301 | let facades = {} 302 | 303 | if ! self.has_file(self.config_path('app.php')) 304 | return facades 305 | endif 306 | 307 | let lines = s:readfile(self.config_path('app.php')) 308 | 309 | let start_pat = "^\\s\\+'aliases'\\s\\+=>\\s\\+[" 310 | let end_pat = '^\s\+],' 311 | let item_pat = "^\\s\\+'\\(\\w\\+\\)'\\s\\+=>\\s\\+\\([A-Za-z_\\\\]\\+\\)::class," 312 | let start_seen = 0 313 | 314 | for line in lines 315 | if start_seen && line =~# end_pat 316 | break 317 | elseif line =~# start_pat 318 | let start_seen = 1 319 | elseif start_seen && line =~# item_pat 320 | let [class, ns] = matchlist(line, item_pat)[1:2] 321 | let facades[class] = ns 322 | else 323 | continue 324 | endif 325 | endfor 326 | 327 | call self.cache.set('facades', facades) 328 | endif 329 | 330 | return self.cache.get('facades') 331 | endfunction 332 | 333 | "" 334 | " Get dict of app's named routes: 335 | " 336 | " { 'route.name': 'GET|POST route/url', ... } 337 | function! s:app_routes() abort dict 338 | if self.cache.needs('routes') 339 | let lines = laravel#artisan#capture(laravel#app(), 'route:list') 340 | call filter(lines, 'v:val =~# ''^| ''') 341 | " Remove header line 342 | call remove(lines, 0) 343 | call map(lines, 'substitute(v:val, ''^|\(.*\)|$'', ''\1'', '''')') 344 | call map(lines, 'split(v:val, '' | '', 1)') 345 | " Remove unnamed routes 346 | call filter(lines, 'v:val[3] !~# ''^\s*$''') 347 | 348 | let routes = {} 349 | 350 | for line in lines 351 | let name = substitute(line[3], '\s\+$', '', '') 352 | let method = substitute(line[1], '\s\+$', '', '') 353 | let uri = substitute(line[2], '\s\+$', '', '') 354 | let routes[name] = method . ' ' . uri 355 | endfor 356 | 357 | call self.cache.set('routes', routes) 358 | endif 359 | 360 | return self.cache.get('routes') 361 | endfunction 362 | 363 | "" 364 | " Get dict of app's templates: 365 | " 366 | " { 'layouts.app': 'layouts/app.blade.php', ... } 367 | function! s:app_templates() abort dict 368 | if self.cache.needs('templates') 369 | let files = self.glob('resources/views/**/*.php') 370 | call map(files, 'substitute(v:val, ''' . self.views_path() . '/'', "", "")') 371 | 372 | let slugs = map(copy(files), 'fnamemodify(v:val, ":r:r")') 373 | call map(slugs, 'substitute(v:val, "/", ".", "g")') 374 | 375 | let templates = {} 376 | let index = 0 377 | 378 | while index < len(files) 379 | let templates[slugs[index]] = files[index] 380 | let index = index + 1 381 | endwhile 382 | 383 | call self.cache.set('templates', templates) 384 | endif 385 | 386 | return self.cache.get('templates') 387 | endfunction 388 | 389 | "" 390 | " Get app's configured locale. 391 | " 392 | " 'en' 393 | function! s:app_locale() abort dict 394 | if self.cache.needs('locale') 395 | let locale = 'en' 396 | 397 | if ! self.has_file(self.config_path('app.php')) 398 | return locale 399 | endif 400 | 401 | let lines = s:readfile(self.config_path('app.php')) 402 | let match = matchstr(join(lines, "\n"), "'locale'\\s*=>\\s*'\\zs\\w\\+\\ze'") 403 | 404 | if ! empty(match) 405 | let locale = match 406 | endif 407 | 408 | call self.cache.set('locale', locale) 409 | endif 410 | 411 | return self.cache.get('locale') 412 | endfunction 413 | 414 | call s:add_methods('app', ['facades', 'routes', 'templates', 'locale']) 415 | 416 | "" 417 | " Get artisan command line, optionally including [args]. 418 | " 419 | " echo laravel#app().makeprg() 420 | " => 'php artisan' 421 | " 422 | " echo laravel#app().makeprg('route:list') 423 | " => 'php artisan route:list' 424 | " 425 | " Arguments may be passed as individual function parameters or as a list. 426 | " 427 | " echo laravel#app().makeprg(['route:list', '-vvv']) 428 | " echo laravel#app().makeprg('route:list', '-vvv') 429 | " => 'php artisan route:list -vvv' 430 | function! s:app_makeprg(...) abort dict 431 | if a:0 == 1 && type(a:1) == type([]) 432 | let args = a:1 433 | elseif a:0 > 0 434 | let args = copy(a:000) 435 | else 436 | let args = [] 437 | endif 438 | 439 | let makeprg = get(g:, 'laravel_artisan', ['php', 'artisan']) 440 | 441 | if type(makeprg) != type([]) 442 | let makeprg = [makeprg] 443 | endif 444 | 445 | return join(makeprg + args) 446 | endfunction 447 | 448 | "" 449 | " Get list of available artisan commands. 450 | function! s:app_artisan_commands() abort dict 451 | return laravel#artisan#commands(self) 452 | endfunction 453 | 454 | call s:add_methods('app', ['makeprg', 'artisan_commands']) 455 | 456 | let s:cache_prototype = {'_dict': {}} 457 | 458 | function! s:cache_clear(...) dict abort 459 | if a:0 == 0 460 | let self._dict = {} 461 | elseif has_key(self, '_dict') && has_key(self._dict, a:1) 462 | unlet! self._dict[a:1] 463 | endif 464 | endfunction 465 | 466 | function! laravel#cache_clear(...) abort 467 | if exists('b:laravel_root') 468 | return call(laravel#app().cache.clear, a:000, laravel#app().cache) 469 | endif 470 | endfunction 471 | 472 | function! s:cache_get(...) dict abort 473 | if a:0 == 0 474 | return self._dict 475 | else 476 | return self._dict[a:1] 477 | endif 478 | endfunction 479 | 480 | function! s:cache_set(key, value) dict abort 481 | let self._dict[a:key] = a:value 482 | endfunction 483 | 484 | function! s:cache_has(key) dict abort 485 | return has_key(self._dict, a:key) 486 | endfunction 487 | 488 | function! s:cache_needs(key) dict abort 489 | return !has_key(self._dict, a:key) 490 | endfunction 491 | 492 | call s:add_methods('cache', ['clear', 'get', 'set', 'has', 'needs']) 493 | 494 | let s:app_prototype.cache = s:cache_prototype 495 | 496 | augroup laravel_cache 497 | autocmd! 498 | autocmd BufWritePost routes.php,routes/*.php call laravel#cache_clear('routes') 499 | augroup END 500 | 501 | "" 502 | " Get namespace of buffer's file 503 | function! s:buffer_namespace() abort dict 504 | let path = self.path 505 | let ns = s:app.namespace() 506 | 507 | if empty(path) 508 | return ns 509 | endif 510 | 511 | let path = fnamemodify(path, ':p:h') 512 | let path = substitute(path, '\V\^' . s:app.src_path(), '') 513 | return join([ns] + split(path, '/'), '\') 514 | endfunction 515 | 516 | function! s:buffer_type() abort dict 517 | endfunction 518 | 519 | function! s:buffer_is_type(types) abort dict 520 | let types = type(a:types) == type([]) ? a:types : [a:types] 521 | return (index(types, self.type()) != -1) 522 | endfunction 523 | 524 | "" 525 | " @private 526 | " Apply extensions to the PHP syntax definitions 527 | function! laravel#buffer_syntax() abort 528 | endfunction 529 | 530 | "" 531 | " @private 532 | " Set up Laravel buffers 533 | function! laravel#buffer_setup() abort 534 | call laravel#buffer_commands() 535 | call laravel#buffer_mappings() 536 | 537 | silent doautocmd User Laravel 538 | endfunction 539 | 540 | "" 541 | " @private 542 | " Set up log buffers 543 | function! laravel#log_buffer_setup() abort 544 | setlocal readonly 545 | setlocal foldexpr=laravel#log_foldexpr(v:lnum) 546 | setlocal foldmethod=expr 547 | endfunction 548 | 549 | "" 550 | " @private 551 | " Fold log files 552 | function! laravel#log_foldexpr(lnum) abort 553 | let line = getline(a:lnum) 554 | 555 | if line =~# '^\[stacktrace\]$' 556 | return 'a1' 557 | elseif line =~# ' {main}$' && getline(a:lnum + 1) !~# '^"}\s*$' 558 | return 's1' 559 | elseif line =~# '^"}\s*$' && getline(a:lnum - 1) =~# ' {main}$' 560 | return 's1' 561 | endif 562 | 563 | return '=' 564 | endfunction 565 | 566 | "" 567 | " @private 568 | " Set up commands for Laravel buffers 569 | function! laravel#buffer_commands() abort 570 | "" 571 | " @command Artisan[!] [arguments] 572 | " Invoke Artisan with [arguments] (with intelligent completion). 573 | command! -buffer -bang -bar -nargs=* -complete=customlist,laravel#artisan#complete 574 | \ Artisan execute laravel#artisan#exec(, ) 575 | endfunction 576 | 577 | "" 578 | " @private 579 | " Set up mappings for Laravel buffers 580 | function! laravel#buffer_mappings() abort 581 | if &filetype =~# 'php' 582 | if hasmapto('(laravel-goto-php)') | return 1 | endif 583 | nmap gf (laravel-goto-php) 584 | elseif &filetype =~# 'blade' 585 | if hasmapto('(laravel-goto-blade)') | return 1 | endif 586 | nmap gf (laravel-goto-blade) 587 | else 588 | return 2 589 | endif 590 | endfunction 591 | 592 | " vim: fdm=marker:sw=2:sts=2:et 593 | -------------------------------------------------------------------------------- /autoload/laravel/artisan.vim: -------------------------------------------------------------------------------- 1 | " autoload/laravel/artisan.vim - Laravel Artisan support for Vim 2 | " Maintainer: Noah Frederick 3 | 4 | "" 5 | " Change working directory to {dir}, respecting current window's local dir 6 | " state. Returns old working directory to be restored later by a second 7 | " invocation of the function. 8 | function! s:cd(dir) abort 9 | let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' 10 | let cwd = getcwd() 11 | execute cd fnameescape(a:dir) 12 | return cwd 13 | endfunction 14 | 15 | "" 16 | " Implement the one-argument version of uniq() for older Vims. 17 | function! s:uniq(list) abort 18 | if exists('*uniq') 19 | return uniq(a:list) 20 | endif 21 | 22 | let i = 0 23 | let last = '' 24 | 25 | while i < len(a:list) 26 | let str = string(a:list[i]) 27 | if str ==# last && i > 0 28 | call remove(a:list, i) 29 | else 30 | let last = str 31 | let i += 1 32 | endif 33 | endwhile 34 | 35 | return a:list 36 | endfunction 37 | 38 | "" 39 | " Get Dict from JSON {expr}. 40 | function! s:json_decode(expr) abort 41 | try 42 | if exists('*json_decode') 43 | let expr = type(a:expr) == type([]) ? join(a:expr, "\n") : a:expr 44 | return json_decode(expr) 45 | else 46 | return projectionist#json_parse(a:expr) 47 | endif 48 | catch /^Vim\%((\a\+)\)\=:E474/ 49 | call laravel#throw('JSON cannot be parsed') 50 | catch /^invalid JSON/ 51 | call laravel#throw('JSON cannot be parsed') 52 | catch /^Vim\%((\a\+)\)\=:E117/ 53 | call laravel#throw('projectionist is not available') 54 | endtry 55 | return {} 56 | endfunction 57 | 58 | "" 59 | " Get output from Artisan with {args} in project's root directory. 60 | function! laravel#artisan#capture(app, args) abort 61 | try 62 | let cwd = s:cd(a:app.path()) 63 | let result = systemlist(a:app.makeprg(a:args)) 64 | finally 65 | call s:cd(cwd) 66 | endtry 67 | 68 | return result 69 | endfunction 70 | 71 | "" 72 | " Get Dict from artisan list --format=json. 73 | function! s:artisan_json(app) abort 74 | if a:app.cache.needs('artisan_json') 75 | let lines = laravel#artisan#capture(a:app, ['list', '--format=json']) 76 | 77 | let json = {} 78 | 79 | if v:shell_error == 0 80 | try 81 | let json = s:json_decode(lines) 82 | endtry 83 | endif 84 | 85 | call a:app.cache.set('artisan_json', json) 86 | endif 87 | 88 | return a:app.cache.get('artisan_json') 89 | endfunction 90 | 91 | "" 92 | " Get Dict of artisan subcommands and their descriptions. 93 | function! laravel#artisan#commands(app) abort 94 | if a:app.cache.needs('artisan_commands') 95 | let definitions = get(s:artisan_json(a:app), 'commands', []) 96 | let commands = {} 97 | 98 | if !empty(definitions) 99 | for command in definitions 100 | let commands[command.name] = command.description 101 | endfor 102 | endif 103 | 104 | call a:app.cache.set('artisan_commands', commands) 105 | endif 106 | 107 | return deepcopy(a:app.cache.get('artisan_commands')) 108 | endfunction 109 | 110 | "" 111 | " Get Dict of artisan subcommands and their options. 112 | function! laravel#artisan#command_options(app) abort 113 | if a:app.cache.needs('artisan_command_options') 114 | let definitions = get(s:artisan_json(a:app), 'commands', []) 115 | let commands = {} 116 | 117 | if !empty(definitions) 118 | for command in definitions 119 | let options = [] 120 | 121 | for [name, option] in items(get(command.definition, 'options', {})) 122 | if option.accept_value 123 | call add(options, option.name . '=') 124 | else 125 | call add(options, option.name) 126 | endif 127 | 128 | if !empty(option.shortcut) 129 | call add(options, option.shortcut) 130 | endif 131 | endfor 132 | 133 | let commands[command.name] = options 134 | endfor 135 | endif 136 | 137 | call a:app.cache.set('artisan_command_options', commands) 138 | endif 139 | 140 | return deepcopy(a:app.cache.get('artisan_command_options')) 141 | endfunction 142 | 143 | function! s:artisan_commands() abort 144 | return laravel#artisan#command_options(laravel#app()) 145 | endfunction 146 | 147 | "" 148 | " Get list of artisan subcommand flags. 149 | function! s:artisan_flags(subcommand) abort 150 | return get(s:artisan_commands(), a:subcommand, s:artisan_global_flags) 151 | endfunction 152 | 153 | let s:artisan_global_flags = [ 154 | \ '--help', 155 | \ '-h', 156 | \ '--quiet', 157 | \ '-q', 158 | \ '--verbose', 159 | \ '-v', 160 | \ '-vv', 161 | \ '-vvv', 162 | \ '--version', 163 | \ '-V', 164 | \ '--ansi', 165 | \ '--no-ansi', 166 | \ '--no-interaction', 167 | \ '-n', 168 | \ '--env=', 169 | \ ] 170 | 171 | "" 172 | " The :Artisan command. 173 | function! laravel#artisan#exec(...) abort 174 | let args = copy(a:000) 175 | let bang = remove(args, 0) 176 | 177 | let cwd = s:cd(laravel#app().path()) 178 | execute '!' . laravel#app().makeprg(args) 179 | call s:cd(cwd) 180 | 181 | call s:artisan_doautocmd(args, bang, v:shell_error) 182 | return '' 183 | endfunction 184 | 185 | "" 186 | " Execute Artisan autocommand with event object. 187 | function! s:artisan_doautocmd(args, bang, status) abort 188 | let g:artisan = { 189 | \ 'args': a:args, 190 | \ 'bang': a:bang, 191 | \ 'status': a:status, 192 | \ } 193 | let g:artisan.flags = filter(copy(a:args), 'v:val =~# "^-"') 194 | let g:artisan.rest = filter(copy(a:args), 'v:val !~# "^-"') 195 | if empty(g:artisan.rest) 196 | let parts = [''] 197 | else 198 | let parts = split(remove(g:artisan.rest, 0), ':') 199 | endif 200 | let g:artisan.namespace = len(parts) > 1 ? parts[0] : '' 201 | let g:artisan.name = len(parts) > 1 ? parts[1] : parts[0] 202 | 203 | silent doautocmd User Artisan 204 | endfunction 205 | 206 | augroup laravel_artisan 207 | autocmd! 208 | if exists('g:loaded_projectionist') 209 | autocmd User Artisan nested 210 | \ if g:artisan.status == 0 && g:artisan.namespace ==# 'make' | 211 | \ execute s:artisan_edit(g:artisan) | 212 | \ endif 213 | endif 214 | augroup END 215 | 216 | function! s:snakecase(str) 217 | let str = substitute(a:str, '\(\u\+\)\(\u\l\)', '\1_\2', 'g') 218 | let str = substitute(str, '\(\l\|\d\)\(\u\)', '\1_\2', 'g') 219 | return tolower(str) 220 | endfunction 221 | 222 | "" 223 | " Edit the file generated by an Artisan command. 224 | function! s:artisan_edit(command) abort 225 | if a:command.name ==# 'auth' 226 | return '' 227 | elseif a:command.name ==# 'console' 228 | let type = 'command' 229 | elseif a:command.name ==# 'model' && laravel#app().has('laravel < 8') 230 | let type = 'lib' 231 | else 232 | let type = a:command.name 233 | endif 234 | 235 | if !exists(':E' . type) 236 | call laravel#warn('Cannot edit generated ' . type . ' file') 237 | return '' 238 | endif 239 | 240 | let classname = remove(a:command.rest, 0) 241 | let namespace = '' 242 | 243 | if a:command.name ==# 'migration' 244 | let classname = laravel#app().expand_migration(s:snakecase(classname)) 245 | elseif a:command.name ==# 'test' && laravel#app().has('namespaced-tests') 246 | let namespace = index(a:command.args, '--unit') != -1 ? 'Unit/' : 'Feature/' 247 | endif 248 | 249 | return 'E' . type . a:command.bang . ' ' . namespace . classname 250 | endfunction 251 | 252 | "" 253 | " @private 254 | " Completion for the :Artisan command. 255 | function! laravel#artisan#complete(A, L, P) abort 256 | let commands = keys(s:artisan_commands()) 257 | 258 | silent! call remove(commands, index(commands, 'help')) 259 | let subcommand = matchstr(a:L, '\<\(' . join(commands, '\|') . '\)\>') 260 | 261 | if empty(subcommand) 262 | let candidates = commands + ['help'] + s:artisan_flags('_') 263 | else 264 | let candidates = s:artisan_flags(subcommand) 265 | endif 266 | 267 | return s:filter_completions(candidates, a:A) 268 | endfunction 269 | 270 | "" 271 | " Sort and filter completion {candidates} based on the current argument {A}. 272 | " Adapted from bundler.vim. 273 | function! s:filter_completions(candidates, A) abort 274 | let candidates = copy(a:candidates) 275 | if len(candidates) == 0 276 | return [] 277 | endif 278 | call sort(candidates) 279 | call s:uniq(candidates) 280 | 281 | let commands = filter(copy(candidates), "v:val[0] !=# '-'") 282 | let flags = filter(copy(candidates), "v:val[0] ==# '-'") 283 | 284 | let candidates = commands + flags 285 | 286 | let filtered = filter(copy(candidates), 'v:val[0:strlen(a:A)-1] ==# a:A') 287 | if !empty(filtered) | return filtered | endif 288 | 289 | let regex = substitute(a:A, '[^/:]', '[&].*', 'g') 290 | let filtered = filter(copy(candidates), 'v:val =~# "^".regex') 291 | if !empty(filtered) | return filtered | endif 292 | 293 | let filtered = filter(copy(candidates), '"/".v:val =~# "[/:]".regex') 294 | if !empty(filtered) | return filtered | endif 295 | 296 | let regex = substitute(a:A, '.', '[&].*', 'g') 297 | let filtered = filter(copy(candidates),'"/".v:val =~# regex') 298 | return filtered 299 | endfunction 300 | 301 | "" 302 | " @private 303 | " Hack for testing script-local functions. 304 | function! laravel#artisan#sid() 305 | nnoremap 306 | return maparg('', 'n') 307 | endfunction 308 | 309 | " vim: fdm=marker:sw=2:sts=2:et 310 | -------------------------------------------------------------------------------- /autoload/laravel/completion.vim: -------------------------------------------------------------------------------- 1 | " autoload/laravel/completion.vim - Completion sources 2 | " Maintainer: Noah Frederick 3 | 4 | function! laravel#completion#cm_routes(opt, ctx) abort 5 | if exists('b:laravel_root') 6 | call s:cm_complete(laravel#app().routes(), a:opt, a:ctx) 7 | endif 8 | endfunction 9 | 10 | function! laravel#completion#cm_views(opt, ctx) abort 11 | if exists('b:laravel_root') 12 | call s:cm_complete(laravel#app().templates(), a:opt, a:ctx) 13 | endif 14 | endfunction 15 | 16 | function! s:cm_complete(candidates, opt, ctx) abort 17 | let matches = map(keys(a:candidates), '{"word": v:val, "info": a:candidates[v:val]}') 18 | call cm#complete(a:opt, a:ctx, a:ctx['startcol'], matches) 19 | endfunction 20 | 21 | function! laravel#completion#ncm2_routes(ctx) abort 22 | if exists('b:laravel_root') 23 | call s:ncm2_complete(laravel#app().routes(), a:ctx) 24 | endif 25 | endfunction 26 | 27 | function! laravel#completion#ncm2_views(ctx) abort 28 | if exists('b:laravel_root') 29 | call s:ncm2_complete(laravel#app().templates(), a:ctx) 30 | endif 31 | endfunction 32 | 33 | function! s:ncm2_complete(candidates, ctx) abort 34 | let matches = map(keys(a:candidates), '{"word": v:val, "info": a:candidates[v:val]}') 35 | call ncm2#complete(a:ctx, a:ctx['startccol'], matches) 36 | endfunction 37 | 38 | " vim: fdm=marker:sw=2:sts=2:et 39 | -------------------------------------------------------------------------------- /autoload/laravel/goto.vim: -------------------------------------------------------------------------------- 1 | " autoload/laravel/goto.vim - Go to thing under cursor 2 | " Maintainer: Noah Frederick 3 | 4 | "" 5 | " @private 6 | " Go to thing under cursor in PHP buffer 7 | function! laravel#goto#filetype_php() abort 8 | let path = laravel#goto#view() 9 | if !empty(path) | return 'Eview '.path | endif 10 | 11 | let path = laravel#goto#config() 12 | if !empty(path) | return 'Econfig '.path | endif 13 | 14 | let path = laravel#goto#language() 15 | if !empty(path) | return 'Elanguage '.laravel#app().locale().'/'.path | endif 16 | 17 | try 18 | let cmd = composer#autoload#find() 19 | if !empty(cmd) | return cmd | endif 20 | catch /^Vim\%((\a\+)\)\=:E117/ 21 | endtry 22 | 23 | return 'normal! gf' 24 | endfunction 25 | 26 | "" 27 | " @private 28 | " Go to thing under cursor in Blade buffer 29 | function! laravel#goto#filetype_blade() abort 30 | let path = laravel#goto#template() 31 | if !empty(path) | return 'Eview '.path | endif 32 | 33 | let path = laravel#goto#config() 34 | if !empty(path) | return 'Econfig '.path | endif 35 | 36 | let path = laravel#goto#language() 37 | if !empty(path) | return 'Elanguage '.laravel#app().locale().'/'.path | endif 38 | 39 | try 40 | let cmd = composer#autoload#find() 41 | if !empty(cmd) | return cmd | endif 42 | catch /^Vim\%((\a\+)\)\=:E117/ 43 | endtry 44 | 45 | return 'normal! gf' 46 | endfunction 47 | 48 | "" 49 | " Find name in context 50 | function! s:find_name(pattern) abort 51 | " Search position of thing on current line 52 | let [lnum, col] = searchpos(a:pattern, 'cnb', line('.')) 53 | let cursor = col('.') 54 | 55 | if col == 0 56 | return '' 57 | endif 58 | 59 | " Capture the name 60 | let buf = getline('.')[col - 1:] 61 | let name = substitute(buf, a:pattern, '\1', '') 62 | return s:path(name) 63 | endfunction 64 | 65 | "" 66 | " Convert name with dot notation to file path 67 | function! s:path(name) abort 68 | return substitute(a:name, '\.', '/', 'g') 69 | endfunction 70 | 71 | "" 72 | " @private 73 | " Capture config name at cursor 74 | function! laravel#goto#config() abort 75 | return s:find_name('\ 104 | return maparg('', 'n') 105 | endfunction 106 | 107 | " vim: fdm=marker:sw=2:sts=2:et 108 | -------------------------------------------------------------------------------- /autoload/laravel/projectionist.vim: -------------------------------------------------------------------------------- 1 | " autoload/laravel/projectionist.vim - Projections for Laravel projects 2 | " Maintainer: Noah Frederick 3 | 4 | if !exists('g:projectionist_transformations') 5 | let g:projectionist_transformations = {} 6 | endif 7 | 8 | function! g:projectionist_transformations.namespace(input, o) abort 9 | return laravel#app().namespace(expand('%')) 10 | endfunction 11 | 12 | function! laravel#projectionist#append() abort 13 | let templates = { 14 | \ 'generic': [ 15 | \ ':p")) && empty(&filetype) | 64 | \ call laravel#buffer_setup() | 65 | \ endif 66 | autocmd VimEnter * 67 | \ if empty(expand("")) && s:laravel_detect(getcwd()) | 68 | \ call laravel#buffer_setup() | 69 | \ endif 70 | autocmd FileType * if s:laravel_detect() | call laravel#buffer_setup() | endif 71 | autocmd BufNewFile,BufReadPost */storage/logs/*.log if s:laravel_detect() | call laravel#log_buffer_setup() | endif 72 | 73 | " File-type-specific setup 74 | autocmd Syntax php 75 | \ if s:laravel_detect() | call laravel#buffer_syntax() | endif 76 | augroup END 77 | 78 | nnoremap (laravel-goto-php) :execute laravel#goto#filetype_php() 79 | nnoremap (laravel-goto-blade) :execute laravel#goto#filetype_blade() 80 | 81 | " }}} 82 | " Projections {{{ 83 | 84 | " Ensure that projectionist gets loaded first 85 | if !exists('g:loaded_projectionist') 86 | runtime! plugin/projectionist.vim 87 | endif 88 | 89 | augroup laravel_projections 90 | autocmd! 91 | autocmd User ProjectionistDetect 92 | \ if s:laravel_detect(get(g:, 'projectionist_file', '')) | 93 | \ call laravel#projectionist#append() | 94 | \ endif 95 | augroup END 96 | 97 | " }}} 98 | " Completion {{{ 99 | 100 | augroup laravel_completion 101 | autocmd! 102 | " Set up nvim-completion-manager sources 103 | " :help NCM-source-examples 104 | autocmd User CmSetup call cm#register_source({'name' : 'Laravel Route', 105 | \ 'abbreviation': 'Route', 106 | \ 'priority': 9, 107 | \ 'scoping': 1, 108 | \ 'scopes': ['php', 'blade'], 109 | \ 'word_pattern': '[A-Za-z0-9_.:-]+', 110 | \ 'cm_refresh_patterns': ['\broute\([''"]'], 111 | \ 'cm_refresh': 'laravel#completion#cm_routes', 112 | \ }) 113 | autocmd User CmSetup call cm#register_source({'name' : 'Laravel View', 114 | \ 'abbreviation': 'View', 115 | \ 'priority': 9, 116 | \ 'scoping': 1, 117 | \ 'scopes': ['php', 'blade'], 118 | \ 'word_pattern': '[A-Za-z0-9_.:-]+', 119 | \ 'cm_refresh_patterns': ['\bview\([''"]', '@(component|extends|include)\([''"]'], 120 | \ 'cm_refresh': 'laravel#completion#cm_views', 121 | \ }) 122 | 123 | " Set up ncm2 sources 124 | " :help ncm2#register_source-example 125 | autocmd User Ncm2Plugin call ncm2#register_source({ 126 | \ 'name': 'Laravel Route', 127 | \ 'priority': 9, 128 | \ 'subscope_enable': 1, 129 | \ 'scope': ['php', 'blade'], 130 | \ 'mark': 'Route', 131 | \ 'word_pattern': '[A-Za-z0-9_.:-]+', 132 | \ 'complete_length': -1, 133 | \ 'complete_pattern': ['\broute\([''"]'], 134 | \ 'on_complete': 'laravel#completion#ncm2_routes', 135 | \ }) 136 | autocmd User Ncm2Plugin call ncm2#register_source({ 137 | \ 'name': 'Laravel View', 138 | \ 'priority': 9, 139 | \ 'subscope_enable': 1, 140 | \ 'scope': ['php', 'blade'], 141 | \ 'mark': 'View', 142 | \ 'word_pattern': '[A-Za-z0-9_.:-]+', 143 | \ 'complete_length': -1, 144 | \ 'complete_pattern': ['\bview\([''"]', '@(component|extends|include)\([''"]'], 145 | \ 'on_complete': 'laravel#completion#ncm2_views', 146 | \ }) 147 | augroup END 148 | 149 | " }}} 150 | " vim: fdm=marker:sw=2:sts=2:et 151 | -------------------------------------------------------------------------------- /test/artisan.vader: -------------------------------------------------------------------------------- 1 | Before (in a laravel buffer): 2 | tabedit test/fixtures/laravel-8/.env 3 | 4 | After (remove generated file): 5 | call delete(expand('%')) 6 | bdelete 7 | 8 | Execute (Create a new custom Eloquent cast class): 9 | silent Artisan make:cast Example 10 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Casts/Example.php' 11 | 12 | Execute (Create a new channel class): 13 | silent Artisan make:channel Example 14 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Broadcasting/Example.php' 15 | 16 | Execute (Create a new Artisan command): 17 | silent Artisan make:command Example 18 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Console/Commands/Example.php' 19 | 20 | Execute (Create a new view component class): 21 | silent Artisan make:component Example 22 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/View/Components/Example.php' 23 | 24 | Execute (Create a new controller class): 25 | silent Artisan make:controller Example 26 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Http/Controllers/Example.php' 27 | 28 | Execute (Create a new event class): 29 | silent Artisan make:event Example 30 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Events/Example.php' 31 | 32 | Execute (Create a new custom exception class): 33 | silent Artisan make:exception Example 34 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Exceptions/Example.php' 35 | 36 | Execute (Create a new model factory): 37 | silent Artisan make:factory Example 38 | AssertEqual expand('%'), 'test/fixtures/laravel-8/database/factories/ExampleFactory.php' 39 | 40 | Execute (Create a new job class): 41 | silent Artisan make:job Example 42 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Jobs/Example.php' 43 | 44 | Execute (Create a new event listener class): 45 | silent Artisan make:listener Example 46 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Listeners/Example.php' 47 | 48 | Execute (Create a new email class): 49 | silent Artisan make:mail Example 50 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Mail/Example.php' 51 | 52 | Execute (Create a new middleware class): 53 | silent Artisan make:middleware Example 54 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Http/Middleware/Example.php' 55 | 56 | Execute (Create a new migration file): 57 | silent Artisan make:migration Example 58 | Assert expand('%') =~# 'test/fixtures/laravel-8/database/migrations/.*_example.php' 59 | 60 | Execute (Create a new Eloquent model class): 61 | silent Artisan make:model Example 62 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Models/Example.php' 63 | 64 | Execute (Create a new notification class): 65 | silent Artisan make:notification Example 66 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Notifications/Example.php' 67 | 68 | Execute (Create a new observer class): 69 | silent Artisan make:observer Example 70 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Observers/Example.php' 71 | 72 | Execute (Create a new policy class): 73 | silent Artisan make:policy Example 74 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Policies/Example.php' 75 | 76 | Execute (Create a new service provider class): 77 | silent Artisan make:provider Example 78 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Providers/Example.php' 79 | 80 | Execute (Create a new form request class): 81 | silent Artisan make:request Example 82 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Http/Requests/Example.php' 83 | 84 | Execute (Create a new resource): 85 | silent Artisan make:resource Example 86 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Http/Resources/Example.php' 87 | 88 | Execute (Create a new validation rule): 89 | silent Artisan make:rule Example 90 | AssertEqual expand('%'), 'test/fixtures/laravel-8/app/Rules/Example.php' 91 | 92 | Execute (Create a new seeder class): 93 | silent Artisan make:seeder Example 94 | AssertEqual expand('%'), 'test/fixtures/laravel-8/database/seeders/Example.php' 95 | 96 | Execute (Create a new test class): 97 | silent Artisan make:test Example 98 | AssertEqual expand('%'), 'test/fixtures/laravel-8/tests/Feature/Example.php' 99 | 100 | Execute (Create a new feature test class): 101 | silent Artisan make:test Example 102 | AssertEqual expand('%'), 'test/fixtures/laravel-8/tests/Feature/Example.php' 103 | 104 | Execute (Create a new unit test class): 105 | silent Artisan make:test --unit Example 106 | AssertEqual expand('%'), 'test/fixtures/laravel-8/tests/Unit/Example.php' 107 | -------------------------------------------------------------------------------- /test/feature_detection.vader: -------------------------------------------------------------------------------- 1 | Before (in a laravel 8 buffer): 2 | edit test/fixtures/laravel-8/.env 3 | 4 | After (clean up empty dirs): 5 | silent !find . -type d -empty -delete 6 | 7 | Execute (has framework): 8 | AssertEqual laravel#app().has('lumen'), 0 9 | AssertEqual laravel#app().has('laravel'), 1 10 | 11 | Execute (has version): 12 | AssertEqual laravel#app().has('laravel 7'), 0 13 | AssertEqual laravel#app().has('laravel 8'), 1 14 | AssertEqual laravel#app().has('laravel == 7'), 0 15 | AssertEqual laravel#app().has('laravel == 8'), 1 16 | 17 | Execute (has version range): 18 | AssertEqual laravel#app().has('laravel < 7'), 0 19 | AssertEqual laravel#app().has('laravel > 7'), 1 20 | 21 | Execute (has feature artisan): 22 | AssertEqual laravel#app().has('artisan'), 1 23 | 24 | Execute (has feature blade): 25 | AssertEqual laravel#app().has('blade'), 1 26 | 27 | Execute (has feature commands): 28 | AssertEqual laravel#app().has('commands'), 0 29 | 30 | Execute (has feature dotenv): 31 | AssertEqual laravel#app().has('dotenv'), 1 32 | 33 | Execute (has feature gulp): 34 | AssertEqual laravel#app().has('gulp'), 0 35 | 36 | Execute (has feature handlers): 37 | AssertEqual laravel#app().has('handlers'), 0 38 | 39 | Execute (has feature models): 40 | AssertEqual laravel#app().has('models'), 1 41 | 42 | Execute (has feature namespaced-tests): 43 | AssertEqual laravel#app().has('namespaced-tests'), 1 44 | 45 | Execute (has feature scopes): 46 | AssertEqual laravel#app().has('scopes'), 0 47 | 48 | Execute (has feature traits): 49 | AssertEqual laravel#app().has('traits'), 0 50 | 51 | Execute (has arbitrary path): 52 | AssertEqual laravel#app().has('app/DoesNotExist'), 0 53 | AssertEqual laravel#app().has('app/Http'), 1 54 | AssertEqual laravel#app().has('app/Http|app/DoesNotExist'), 1 55 | -------------------------------------------------------------------------------- /test/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noahfrederick/vim-laravel/e526f394901625f5c170de753539273cc8f51c6e/test/fixtures/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/init.vim: -------------------------------------------------------------------------------- 1 | filetype off 2 | set rtp+=vader.vim 3 | set rtp+=vim-projectionist 4 | set rtp+=vim-composer 5 | set rtp+=. 6 | filetype plugin indent on 7 | syntax enable 8 | -------------------------------------------------------------------------------- /test/initialization.vader: -------------------------------------------------------------------------------- 1 | Execute (sets guard variable): 2 | AssertEqual g:loaded_laravel, 1 3 | 4 | Execute (Laravel root is detected): 5 | edit test/fixtures/laravel-8/.env 6 | AssertEqual b:laravel_root, fnamemodify('test/fixtures/laravel-8', ':p:h') 7 | -------------------------------------------------------------------------------- /test/navigation.vader: -------------------------------------------------------------------------------- 1 | Before (in a laravel buffer): 2 | edit test/fixtures/laravel-8/.env 3 | execute 'cd' b:laravel_root 4 | 5 | Execute (edit arbitrary file): 6 | Elib Foo/Bar 7 | AssertEqual expand('%'), 'app/Foo/Bar.php' 8 | set expandtab shiftwidth=4 9 | 10 | Expect (class template): 11 |