Skip to content

Commit

Permalink
Merge pull request #4 from nuIIpointerexception/windows-qol
Browse files Browse the repository at this point in the history
Windows & QOL
  • Loading branch information
Stef16Robbe authored Nov 21, 2024
2 parents 5f4d25d + b9b2143 commit 7a8c6a8
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 72 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "harper_zed"
version = "0.0.3"
version = "0.0.4"
edition = "2021"
publish = false

Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ Zed extension for the

![Harper running inside zed](./images/zed_demo.png)

## Project status
## Supported platforms

Tested on macOS and Linux
| Platform | X86_64 | ARM64 |
|---|---|---|
| Linux |||
| MacOS |||
| Windows |||

## Install

Expand All @@ -17,7 +21,7 @@ Tested on macOS and Linux

![Harper in the Zed Extension Gallery](./images/extension_in_gallery.png)

## Acknowledgements
## Acknowledgments

- [elijah-potter](https://github.com/elijah-potter) for creating Harper
- [WeetHet](https://github.com/WeetHet) for their
Expand Down
90 changes: 79 additions & 11 deletions extension.toml
Original file line number Diff line number Diff line change
@@ -1,29 +1,97 @@
id = "harper"
name = "harper"
version = "0.0.3"
version = "0.0.4"
schema_version = 1
authors = ["Stef Robbe <[email protected]>"]
description = "Harper LS support"
repository = "https://github.com/stef16robbe/harper_zed"

[language_servers.harper-ls]
name = "Harper LS"
# Not all of these are supported by Harper yet, see: https://github.com/Stef16Robbe/harper_zed/pull/4#issuecomment-2469318095
# See nvim config for supported langs: https://github.com/neovim/nvim-lspconfig/blob/master/lua/lspconfig/configs/harper_ls.lua
languages = [
"Markdown",
"Rust",
"Typescript",
# "typescriptreact", not supported yet in Zed
"Javascript",
# General Purpose
"Python",
"Go",
"C",
"Cpp",
"Java",
"Rust",
"Zig",
"Dart",
"Elixir",
"Erlang",
"Gleam",
"Groovy",
"Haskell",
"Julia",
"Kotlin",
"Nim",
"OCaml",
"PHP",
"PureScript",
"R",
"Racket",
"Roc",
"Ruby",
"Swift",
"CSharp",
"TOML",
"Scala",
"Scheme",
"Uiua",

# Scripting
"Lua",
# "gitcommit", not supported yet in Zed
"Java",
"Luau",
"Bash",
"Deno",
"Fish",
"GDScript",
"Rego",

# Web
"HTML",
"CSS",
"Javascript",
"Typescript",
"Svelte",
"Astro",
"Vue",
"Elm",

# Markup
"Markdown",
"AsciiDoc",
"ReStructuredText",
"XML",
"Typst",
"LaTeX",

# Data serialization
"TOML",
"JSON",
"Jsonnet",
"YAML",

# Compiled
"Swift",
"CSharp",

# Configuration / Automation
"Ansible",
"Docker",
"Makefile",
"Terraform",
"Yarn",

# Other
"Biome",
"Clojure",
"Emmet",
"GLSL",
"Prisma",
"Proto",

# Not supported yet
# "typescriptreact",
# "gitcommit",
]
143 changes: 86 additions & 57 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,129 +1,158 @@
use std::fs;
use std::path::PathBuf;
use zed_extension_api::{self as zed, Result};

struct HarperExtension {
cached_binary_path: Option<String>,
binary_cache: Option<PathBuf>,
}

#[derive(Clone)]
struct HarperBinary {
path: String,
environment: Option<Vec<(String, String)>>,
path: PathBuf,
env: Option<Vec<(String, String)>>,
}

impl HarperExtension {
fn language_server_binary(
fn new() -> Self {
Self { binary_cache: None }
}

fn get_binary(
&mut self,
language_server_id: &zed::LanguageServerId,
worktree: &zed::Worktree,
) -> Result<HarperBinary> {
if let Some(path) = worktree.which("harper-ls") {
let env = worktree.shell_env();
return Ok(HarperBinary {
path,
environment: Some(env),
path: PathBuf::from(path),
env: Some(worktree.shell_env()),
});
}

if let Some(path) = &self.cached_binary_path {
if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
if let Some(path) = &self.binary_cache {
if path.exists() {
return Ok(HarperBinary {
path: path.clone(),
environment: None,
env: None,
});
}
}

self.install_binary(language_server_id)
}

fn install_binary(
&mut self,
language_server_id: &zed::LanguageServerId,
) -> Result<HarperBinary> {
zed::set_language_server_installation_status(
language_server_id,
&zed::LanguageServerInstallationStatus::CheckingForUpdate,
);

let release = zed::latest_github_release(
"elijah-potter/harper",
zed::GithubReleaseOptions {
require_assets: true,
pre_release: false,
},
)?;
)
.map_err(|e| format!("Failed to fetch latest release: {}", e))?;

let (platform, arch) = zed::current_platform();
let asset_name = format!(
"harper-ls-{arch}-{os}.{archive_type}",
arch = match arch {
zed::Architecture::Aarch64 => "aarch64",
zed::Architecture::X8664 => "x86_64",
zed::Architecture::X86 => return Err("Does not support x86".to_string()),
},
os = match platform {
zed::Os::Mac => "apple-darwin",
zed::Os::Linux => "unknown-linux-gnu",
zed::Os::Windows => "pc-windows",
},
archive_type = match platform {
zed::Os::Windows => "zip",
_ => "tar.gz",
}
);

let gh_asset = release
let arch_name = match arch {
zed::Architecture::Aarch64 => "aarch64",
zed::Architecture::X8664 => "x86_64",
zed::Architecture::X86 => return Err("x86 architecture is not supported".into()),
};

let (os_str, file_ext) = match platform {
zed::Os::Mac => ("apple-darwin", "tar.gz"),
zed::Os::Linux => ("unknown-linux-gnu", "tar.gz"),
zed::Os::Windows => ("pc-windows-msvc", "zip"),
};

let asset_name = format!("harper-ls-{arch_name}-{os_str}.{file_ext}");
let asset = release
.assets
.iter()
.find(|asset| asset.name == asset_name)
.ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
.find(|a| a.name == asset_name)
.ok_or_else(|| format!("No compatible Harper binary found for {arch_name}-{os_str}"))?;

let version_dir = format!("harper-ls-{}", release.version);
let binary_path = format!("{version_dir}/harper-ls");
let mut binary_path = PathBuf::from(&version_dir).join("harper-ls");

if !fs::metadata(&binary_path).map_or(false, |stat| stat.is_file()) {
if platform == zed::Os::Windows {
binary_path.set_extension("exe");
}

if !binary_path.exists() {
zed::set_language_server_installation_status(
language_server_id,
&zed::LanguageServerInstallationStatus::Downloading,
);

zed::download_file(
&gh_asset.download_url,
&version_dir,
zed::DownloadedFileType::GzipTar,
)
.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();
let download_result = (|| -> Result<()> {
zed::download_file(
&asset.download_url,
&version_dir,
if platform == zed::Os::Windows {
zed::DownloadedFileType::Zip
} else {
zed::DownloadedFileType::GzipTar
},
)
.map_err(|e| format!("Failed to download Harper binary: {}", e))?;

zed::make_file_executable(binary_path.to_str().ok_or("Invalid binary path")?)
.map_err(|e| format!("Failed to make binary executable: {}", e))?;

Ok(())
})();

if let Err(e) = download_result {
fs::remove_dir_all(&version_dir).ok();
return Err(e);
}

if let Ok(entries) = fs::read_dir(".") {
for entry in entries.flatten() {
if let Ok(name) = entry.file_name().into_string() {
if name != version_dir {
fs::remove_dir_all(entry.path()).ok();
}
}
}
}
}

self.cached_binary_path = Some(binary_path.clone());
self.binary_cache = Some(binary_path.clone());
Ok(HarperBinary {
path: binary_path,
environment: None,
env: None,
})
}
}

impl zed::Extension for HarperExtension {
fn new() -> Self {
Self {
cached_binary_path: None,
}
Self::new()
}

fn language_server_command(
&mut self,
language_server_id: &zed::LanguageServerId,
worktree: &zed::Worktree,
) -> Result<zed::Command> {
let harper_binary = self.language_server_binary(language_server_id, worktree)?;
let binary = self.get_binary(language_server_id, worktree)?;
Ok(zed::Command {
command: harper_binary.path,
command: binary
.path
.to_str()
.ok_or("Failed to convert binary path to string")?
.to_string(),
args: vec!["--stdio".to_string()],
env: harper_binary.environment.unwrap_or_default(),
env: binary.env.unwrap_or_default(),
})
}
}
Expand Down

0 comments on commit 7a8c6a8

Please sign in to comment.