Skip to content

Laravel LSP for Zed editor.

License

Notifications You must be signed in to change notification settings

GeneaLabs/zed-laravel

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

70 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Laravel for Zed

Laravel for Zed

Cmd+Click your way through Laravel projects

Build Status Latest Release Downloads GitHub Stars

Laravel Zed Extension Rust MIT License

A community extension β€” not affiliated with Laravel LLC


πŸ“¦ Install

Search "Laravel" in Zed Extensions and click Install.

From source: Clone the repo, run cargo build --release in laravel-lsp/, then use "zed: install dev extension".

βš™οΈ Configuration

The extension works out of the box with zero configuration. It automatically discovers your Laravel project structure, including view paths, component namespaces, route files, and service providers.

Optional settings can be added to your Zed settings.json:

{
  "lsp": {
    "laravel-lsp": {
      "settings": {
        "autoCompleteDebounce": 200,
        "blade": {
          "directiveSpacing": false
        }
      }
    }
  }
}
Setting Default Description
autoCompleteDebounce 200 Delay (ms) before autocomplete updates after typing. Lower values (50-100ms) give faster feedback. Higher values (300-500ms) reduce CPU usage.
blade.directiveSpacing false Add space between directive name and parentheses. false: @if($condition) / true: @if ($condition)

πŸ—„οΈ Database autocomplete (exists:, unique: rules, Eloquent properties) requires a working database connection. Configure in your .env:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=myapp
DB_USERNAME=root
DB_PASSWORD=secret

Supports MySQL, PostgreSQL, SQLite, and SQL Server.

✨ Features

πŸ”— Go-to-Definition

Navigate your Laravel codebase by Cmd+Clicking (or Cmd+D) on any recognized pattern. The extension understands Laravel's conventions and jumps directly to the source file, whether it's a view, component, route, config key, or translation.

class UserController extends Controller
{
    public function show(User $user)
    {
        return view('users.profile', compact('user'));
        //          ^^^^^^^^^^^^^^^ β†’ resources/views/users/profile.blade.php
    }
}
@extends('layouts.app')
{{--      ^^^^^^^^^^^ β†’ resources/views/layouts/app.blade.php --}}

<x-button type="submit">Save</x-button>
{{-- ^^^^ β†’ resources/views/components/button.blade.php --}}

<livewire:user-settings :user="$user" />
{{--       ^^^^^^^^^^^^^ β†’ app/Livewire/UserSettings.php --}}
$url = route('users.show', $user);
//           ^^^^^^^^^^^^ β†’ routes/web.php

$name = config('app.name');
//             ^^^^^^^^^^ β†’ config/app.php

$message = __('auth.failed');
//            ^^^^^^^^^^^^ β†’ lang/en/auth.php

Supported patterns: view() View::make() @extends @include @component <x-*> </x-*> <livewire:*> </livewire:*> @livewire() route() to_route() config() Config::get() env() __() trans() @lang ->middleware() app() resolve() asset() @vite app_path() base_path() storage_path() resource_path() public_path() Feature::active() Feature::inactive() Feature::value() @feature

πŸ’‘ Autocomplete

Get intelligent suggestions as you type. The extension provides context-aware completions for views, Blade components, validation rules, Eloquent casts, database schemas, config keys, routes, middleware, translations, environment variables, Eloquent models, and Blade variables.

$request->validate([
    'email' => 'required|email|exists:',
    //                               ^ πŸ—„οΈ database tables appear here

    'email' => 'required|email|exists:users,',
    //                                     ^ πŸ—„οΈ column names appear here

    'name' => 'required|',
    //                  ^ πŸ“‹ 90+ validation rules appear here
]);

$name = config('app.');
//                  ^ βš™οΈ config keys with resolved values

return view('users.');
//                 ^ πŸ“„ view names from resources/views

$url = route('users.');
//                  ^ πŸ”— named routes from routes/*.php

Route::middleware('');
//                ^ πŸ›‘οΈ middleware aliases from bootstrap/app.php

$message = __('auth.');
//                  ^ 🌐 translation keys with values

🎭 Eloquent Cast Types

Get autocomplete for Eloquent cast types in $casts property or casts() method:

protected $casts = [
    'is_admin' => '',
    //            ^ 🎭 cast types appear here
];

protected function casts(): array
{
    return [
        'email_verified_at' => 'datetime',
        'settings' => '',
        //            ^ string, integer, boolean, datetime, array,
        //              encrypted, hashed, collection, object...
    ];
}

Cast completions include:

  • Primitives: string, integer, float, boolean, array, object, collection
  • Dates: datetime, date, timestamp, immutable_date, immutable_datetime
  • Security: encrypted, encrypted:array, encrypted:collection, hashed
  • Numbers: decimal: (with precision parameter)
  • Custom casts from app/Casts/ and installed packages

πŸ—οΈ Eloquent Model Properties

Type $user-> to get completions for model properties, including database columns, casts, accessors, and relationships:

$user->
//    ^ name (string)        ← database column
//    ^ email (string)       ← database column
//    ^ email_verified_at (Carbon)  ← cast to datetime
//    ^ is_admin (bool)      ← cast to boolean
//    ^ full_name (string)   ← accessor
//    ^ posts (Collection)   ← hasMany relationship

Works with type-hinted variables, PHPDoc annotations, and static chains like User::find(1)->.

πŸ“ Blade Variables

Type $ in Blade files to see all available variables passed to the view:

{{ $
{{-- ^ user (User)     ← from controller
     ^ posts (Collection) ← from controller
     ^ title (string)  ← from @props --}}

Variables are resolved from:

  • view('name', compact('user', 'posts'))
  • view('name', ['user' => $user])
  • view('name')->with('user', $user)
  • view('name')->with(['user' => $user])
  • @props(['title' => string]) in Blade components
  • Livewire component public properties

πŸ”„ Loop Variables (Scope-Aware)

Variables from loop directives are available only inside the loop block:

@foreach($users as $user)
    {{ $user->name }}   {{-- βœ… $user available here --}}
    {{ $loop->index }}  {{-- βœ… $loop available in all loops --}}
@endforeach
{{ $user }}  {{-- ❌ $user NOT available outside loop --}}

Supported loop directives:

  • @foreach($items as $item) / @foreach($items as $key => $value)
  • @forelse($items as $item)
  • @for($i = 0; $i < 10; $i++)
  • @while($condition)

Nested loops work correctlyβ€”inner loop variables are scoped to their block.

🎰 Slot Variables (Components)

In component files (resources/views/components/*.blade.php), slot variables are detected from usage:

{{-- components/card.blade.php --}}
<div class="card">
    <header>{{ $header }}</header>   {{-- $header autocomplete available --}}
    <div>{{ $slot }}</div>           {{-- $slot always available --}}
    <footer>{{ $footer }}</footer>   {{-- $footer autocomplete available --}}
</div>

Component files automatically get:

  • $slot β€” default slot content
  • $attributes β€” component attribute bag
  • $component β€” component instance
  • Named slots detected from {{ $name }} usage

🚩 Laravel Pennant Feature Flags

Get autocomplete for Laravel Pennant feature flags in PHP and Blade:

Feature::active('');
//               ^ 🚩 feature names from app/Features/

Feature::for($user)->active('');
//                          ^ 🚩 same completions for scoped checks

Feature::allAreActive(['']);
//                     ^ 🚩 works in array methods too
@feature('')
{{--     ^ 🚩 feature names appear here --}}

Features are discovered from app/Features/*.php class files. Both string keys ('new-api') and class references (NewApi::class) are supported.

❌ Diagnostics

See problems in real-time as you type. The extension validates your Laravel code against your actual project structure, highlighting missing views, undefined components, invalid validation rules, and other issues before you run your application.

Missing files are reported as errors to catch issues early:

return view('users.dashboard');
//          ^^^^^^^^^^^^^^^^^ ❌ View not found: resources/views/users/dashboard.blade.php

Route::middleware('admin-only')->group(...);
//                ^^^^^^^^^^^^ ⚠️ Middleware not found

$request->validate([
    'email' => 'required|emal|unique:users',
    //                   ^^^^ ❌ Unknown validation rule: 'emal'
]);

Feature::active('undefined-feature');
//               ^^^^^^^^^^^^^^^^^^ ❌ Feature not found: app/Features/UndefinedFeature.php
<x-dashboard-widget />
{{-- ^^^^^^^^^^^^^^^^ ❌ Component not found --}}

<livewire:admin-panel />
{{--       ^^^^^^^^^^^ ❌ Livewire component not found --}}

@extends('layouts.missing')
{{--      ^^^^^^^^^^^^^^^^ ❌ View not found --}}

@feature('undefined-feature')
{{--      ^^^^^^^^^^^^^^^^^^ ❌ Feature not found --}}

⚑ Quick Actions

Fix problems with a single click. When you see a warning, press Cmd+. to open quick actions. The extension offers to create missing files with the correct Laravel structureβ€”views, components, middleware, translations, and more.

return view('users.dashboard');
//          ^^^^^^^^^^^^^^^^^ ⚠️ View not found
//                            ⚑ Create view: users.dashboard

Route::middleware('admin-only')->group(...);
//                ^^^^^^^^^^^^ ⚠️ Middleware not found
//                             ⚑ Create middleware: admin-only
<x-dashboard-widget />
{{-- ⚠️ Component not found
     ⚑ Create component (anonymous)
     ⚑ Create component with class --}}

<livewire:admin-panel />
{{-- ⚠️ Livewire component not found
     ⚑ Create Livewire component --}}

Available quick actions:

  • πŸ“„ Create missing views
  • 🧩 Create Blade components (anonymous or with class)
  • ⚑ Create Livewire components
  • πŸ›‘οΈ Create middleware
  • 🚩 Create Laravel Pennant feature classes
  • 🌐 Add translations to existing files
  • πŸ” Add environment variables to .env

🎨 Blade Language Support

Full Blade template language support with syntax highlighting, smart completions, and editor integration.

Syntax Highlighting

Blade directives, PHP blocks, echo statements, and comments are all properly highlighted with distinct colors.

Directive Autocomplete

Type @ to see all 100+ Blade directives with descriptions:

@fo
  ↳ @foreach  Loop through collection
  ↳ @for      For loop
  ↳ @forelse  Loop with empty fallback

Block directives automatically include their closing tags:

@if($condition)
    β–ˆ
@endif

Smart Bracket Expansion

Type { and select from snippet completions:

{
  ↳ {{ ... }}      Echo (escaped)
  ↳ {!! ... !!}    Echo (unescaped)
  ↳ {{-- ... --}}  Blade comment

Cursor is automatically positioned between the brackets for immediate typing.

Closing Tag Navigation

Cmd+Click works on both opening AND closing tags:

<x-button>Submit</x-button>
{{-- ^^^^^^           ^^^^^^ Both navigate to component --}}

<livewire:counter></livewire:counter>
{{--      ^^^^^^^            ^^^^^^^ Both navigate to Livewire class --}}

🚧 Planned Features

  • πŸ“– Hover documentation with resolved values
  • 🎨 Inertia.js support (Inertia::render('Page'))
  • πŸ“ Folio page routing
  • ⚑ Volt component support

🀝 Contributing

Project Structure

zed-laravel/
β”œβ”€β”€ src/lib.rs           # Zed extension (binary download/management)
β”œβ”€β”€ extension.toml       # Extension manifest
β”œβ”€β”€ languages/           # Blade & PHP language definitions
β”‚   β”œβ”€β”€ blade/           # Syntax highlighting, injections, indents
β”‚   └── php_only/        # PHP syntax support
β”œβ”€β”€ laravel-lsp/         # Laravel Language Server (the actual LSP)
β”‚   β”œβ”€β”€ src/main.rs      # LSP server implementation
β”‚   β”œβ”€β”€ src/queries.rs   # Tree-sitter pattern extraction
β”‚   └── tests/           # Integration tests
└── test-project/        # Laravel fixture for testing

Local Development

  1. Clone and build the LSP:

    git clone https://github.com/GeneaLabs/zed-laravel.git
    cd zed-laravel/laravel-lsp
    cargo build --release
  2. Configure Zed to use your local build:

    Add to your Zed settings.json:

    {
      "lsp": {
        "laravel-lsp": {
          "binary": {
            "path": "/path/to/zed-laravel/laravel-lsp/target/release/laravel-lsp"
          }
        }
      }
    }
  3. Install the extension for language support:

    In Zed: Cmd+Shift+P β†’ "zed: install dev extension" β†’ select the zed-laravel directory.

  4. After making changes:

    cd laravel-lsp && cargo build --release

    Then in Zed: Cmd+Shift+P β†’ "zed: reload extensions"

Running Tests

cd laravel-lsp

# Run all tests
cargo test

# Run with output
cargo test -- --nocapture

# Run specific test
cargo test test_view_resolution

Code Style

# Format code
cargo fmt

# Run linter
cargo clippy

MIT Β· GeneaLabs