Skip to content
Merged
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
5 changes: 5 additions & 0 deletions extension.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ language_ids = { PHP = "php" }
name = "Phpactor"
language = "PHP"

[language_servers.phpantom]
name = "PHPantom"
language = "PHP"
language_ids = { PHP = "php" }

[debug_adapters.Xdebug]

[grammars.php]
Expand Down
2 changes: 2 additions & 0 deletions src/language_servers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod intelephense;
mod phpactor;
mod phpantom;
mod phptools;

pub use intelephense::*;
pub use phpactor::*;
pub use phpantom::*;
pub use phptools::*;
141 changes: 141 additions & 0 deletions src/language_servers/phpantom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use std::fs;

use zed_extension_api::settings::LspSettings;
use zed_extension_api::{self as zed, DownloadedFileType, LanguageServerId, Result};

const REPO: &str = "PHPantom-dev/phpantom_lsp";
const BINARY_NAME: &str = "phpantom_lsp";

pub struct Phpantom {
cached_binary_path: Option<String>,
}

impl Phpantom {
pub const LANGUAGE_SERVER_ID: &'static str = "phpantom";

pub fn new() -> Self {
Self {
cached_binary_path: None,
}
}

pub fn language_server_command(
&mut self,
language_server_id: &LanguageServerId,
worktree: &zed::Worktree,
) -> Result<zed::Command> {
// Allow users to point at their own build via
// `lsp.phpantom.binary.{path,arguments}` in the settings.
if let Some(binary) = LspSettings::for_worktree("phpantom", worktree)
.ok()
.and_then(|settings| settings.binary)
{
if let Some(path) = binary.path {
return Ok(zed::Command {
command: path,
args: binary.arguments.unwrap_or_default(),
env: Default::default(),
});
}
}

Ok(zed::Command {
command: self.language_server_binary_path(language_server_id, worktree)?,
args: vec![],
env: Default::default(),
})
}

fn language_server_binary_path(
&mut self,
language_server_id: &LanguageServerId,
worktree: &zed::Worktree,
) -> Result<String> {
if let Some(path) = worktree.which(BINARY_NAME) {
return Ok(path);
}

if let Some(path) = &self.cached_binary_path {
if fs::metadata(path).is_ok_and(|stat| stat.is_file()) {
return Ok(path.clone());
}
}

zed::set_language_server_installation_status(
language_server_id,
&zed::LanguageServerInstallationStatus::CheckingForUpdate,
);
let release = zed::latest_github_release(
REPO,
zed::GithubReleaseOptions {
require_assets: true,
pre_release: false,
},
)?;

let (platform, arch) = zed::current_platform();

let (os_str, file_type) = match platform {
zed::Os::Mac => ("apple-darwin", DownloadedFileType::GzipTar),
zed::Os::Linux => ("unknown-linux-gnu", DownloadedFileType::GzipTar),
zed::Os::Windows => ("pc-windows-msvc", DownloadedFileType::Zip),
};

let arch_str = match arch {
zed::Architecture::Aarch64 => "aarch64",
zed::Architecture::X8664 => "x86_64",
_ => return Err(format!("unsupported architecture: {arch:?}")),
};

let asset_name = format!(
"{BINARY_NAME}-{arch_str}-{os_str}.{extension}",
extension = match file_type {
DownloadedFileType::GzipTar => "tar.gz",
DownloadedFileType::Zip => "zip",
_ => "",
}
);
let asset = release
.assets
.iter()
.find(|asset| asset.name == asset_name)
.ok_or_else(|| {
format!(
"no release asset found matching {asset_name:?} — you may need to build \
{BINARY_NAME} from source for your platform"
)
})?;

let version_dir = format!("{BINARY_NAME}-{}", release.version);
fs::create_dir_all(&version_dir).map_err(|e| format!("failed to create directory: {e}"))?;

let binary_path = match platform {
zed::Os::Windows => format!("{version_dir}/{BINARY_NAME}.exe"),
_ => format!("{version_dir}/{BINARY_NAME}"),
};

if !fs::metadata(&binary_path).is_ok_and(|stat| stat.is_file()) {
zed::set_language_server_installation_status(
language_server_id,
&zed::LanguageServerInstallationStatus::Downloading,
);

zed::download_file(&asset.download_url, &version_dir, file_type)
.map_err(|e| format!("failed to download file: {e}"))?;

zed::make_file_executable(&binary_path)?;

let entries =
fs::read_dir(".").map_err(|e| format!("failed to list working directory: {e}"))?;
for entry in entries {
let entry = entry.map_err(|e| format!("failed to load directory entry: {e}"))?;
if entry.file_name().to_str() != Some(&version_dir) {
fs::remove_dir_all(entry.path()).ok();
}
}
}

self.cached_binary_path = Some(binary_path.clone());
Ok(binary_path)
}
}
8 changes: 7 additions & 1 deletion src/php.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ use zed_extension_api::{
};

use crate::{
language_servers::{Intelephense, PhpTools, Phpactor},
language_servers::{Intelephense, PhpTools, Phpactor, Phpantom},
xdebug::XDebug,
};

struct PhpExtension {
phptools: Option<PhpTools>,
intelephense: Option<Intelephense>,
phpactor: Option<Phpactor>,
phpantom: Option<Phpantom>,
xdebug: XDebug,
}

Expand All @@ -26,6 +27,7 @@ impl zed::Extension for PhpExtension {
phptools: None,
intelephense: None,
phpactor: None,
phpantom: None,
xdebug: XDebug::new(),
}
}
Expand Down Expand Up @@ -85,6 +87,10 @@ impl zed::Extension for PhpExtension {
})
}
}
Phpantom::LANGUAGE_SERVER_ID => {
let phpantom = self.phpantom.get_or_insert_with(Phpantom::new);
phpantom.language_server_command(language_server_id, worktree)
}
language_server_id => Err(format!("unknown language server: {language_server_id}")),
}
}
Expand Down
Loading