feat(languages): start Laravel LSP next to Intelephense in Laravel projects (#2117)

Laravel ships a first-party language server (laravel/lsp, the one behind
the official VS Code/Zed/Sublime extensions) that answers hover,
definition and completion inside config('…'), route('…') and view('…')
calls — places Intelephense returns null.

Register it in languages/php.toml after Intelephense and the phpactor
fallback (Intelephense keeps hover, rename and symbols for PHP code),
gated by require-root-marker on artisan so it only starts in Laravel
projects — the same pattern angular-language-server uses for
angular.json.

Add a ToolSpec so Settings › Language Servers can install it: GitHub
releases carry plain per-platform binaries (server-v*-<arch>-<os>[.exe]),
which Method::Release + ArchiveKind::Raw already handle; the versioned
asset name lands in bin/ via the usual link/wrapper step. laravel-lsp
runs the project's PHP, so needs stays empty (PHP detection is the
project interpreter's job).

Closes #2100
This commit is contained in:
arookieofc authored and GitHub committed 2026-11-08 17:36:30 +08:00
1 parent 75cb45ed94
commit f6e942dd00
4 files changed
+80

No files matched your search

+21
View File
@@ -692,6 +692,27 @@ mod tests {
}
}
#[test]
fn laravel_lsp_serves_php_files_only_below_artisan() {
let reg = reg();
let cfg = reg.get(&LanguageId::new("php")).unwrap();
let names: Vec<&str> = cfg.language_servers.iter().map(|s| s.name.as_str()).collect();
// After the fallback: hover, rename and symbols stay with Intelephense.
assert_eq!(names, ["intelephense", "phpactor", "laravel-lsp"]);
let lsp = cfg.language_servers.iter().find(|s| s.name == "laravel-lsp").unwrap();
assert_eq!(lsp.command.as_str(), "laravel-lsp");
assert!(lsp.require_root_marker && !lsp.fallback);
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
for dir in ["shop", "lib"] {
std::fs::create_dir_all(root.join(dir)).unwrap();
}
std::fs::write(root.join("shop/artisan"), "").unwrap();
let served = |file: &str| crate::marked_root(Some(root), &root.join(file), &lsp.root_markers);
assert_eq!(served("shop/app/Models/Order.php"), Some(root.join("shop")));
assert_eq!(served("lib/src/Parser.php"), None);
}
#[test]
fn ruff_serves_python_files_only_below_its_config() {
let reg = reg();
+19
View File
@@ -151,6 +151,25 @@ mod tests {
assert_eq!(select_asset(&names, rules, WIN), Some(names[4]));
}
#[test]
fn laravel_lsp_assets() {
let names = [
"server-v0.0.32-arm64-darwin",
"server-v0.0.32-x64-darwin",
"server-v0.0.32-arm64-linux",
"server-v0.0.32-x64-linux",
"server-v0.0.32-x64-win32.exe",
];
let rules = crate::registry::find("laravel-lsp").unwrap().asset_rules();
assert_eq!(select_asset(&names, rules, MAC_ARM), Some(names[0]));
assert_eq!(select_asset(&names, rules, MAC_X64), Some(names[1]));
assert_eq!(select_asset(&names, rules, LINUX_ARM), Some(names[2]));
assert_eq!(select_asset(&names, rules, LINUX_X64), Some(names[3]));
assert_eq!(select_asset(&names, rules, WIN), Some(names[4]));
// The composer-installed helper is not a release asset.
assert_eq!(select_asset(&["laravel-lsp"], rules, LINUX_X64), None);
}
#[test]
fn kmp_lsp_assets_include_the_native_jar_indexer() {
let names = [
+29
View File
@@ -396,6 +396,35 @@ pub static TOOLS: &[ToolSpec] = &[
homepage: "https://intelephense.com",
needs: &[],
},
ToolSpec {
name: "laravel-lsp",
display: "Laravel LSP",
kind: LanguageServer,
commands: &["laravel-lsp"],
// Plain binaries per platform (`server-v*-<arch>-<os>[.exe]`).
method: Method::Release {
source: ReleaseSource::GitHub { repo: "laravel/lsp" },
assets: &[
rule(Mac, Arm64, "server-v*-arm64-darwin"),
rule(Mac, X64, "server-v*-x64-darwin"),
rule(Linux, Arm64, "server-v*-arm64-linux"),
rule(Linux, X64, "server-v*-x64-linux"),
rule(Windows, X64, "server-v*-x64-win32.exe"),
],
locate: &[
"laravel-lsp",
"server-v*-arm64-darwin",
"server-v*-x64-darwin",
"server-v*-arm64-linux",
"server-v*-x64-linux",
"server-v*-x64-win32",
],
},
fallback: None,
version: ver("laravel-lsp"),
homepage: "https://github.com/laravel/lsp",
needs: &[],
},
ToolSpec {
name: "gopls",
display: "gopls",
+11
View File
@@ -26,6 +26,17 @@ args = ["language-server"]
root-markers = ["composer.json", ".phpactor.json"]
install-hint = "Install phpactor (https://phpactor.readthedocs.io/en/master/usage/standalone.html)"
# Laravel projects only: hover, definition and completion inside
# config('…'), route('…') and view('…'). Last: Intelephense keeps PHP hover,
# rename and symbols. https://github.com/laravel/lsp
[[language-servers]]
name = "laravel-lsp"
command = "laravel-lsp"
args = ["--stdio"]
root-markers = ["artisan"]
require-root-marker = true
install-hint = "composer global require laravel/lsp"
[debugger]
adapter = "php-debug"
command = "php-debug-adapter"