mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-16 00:47:39 +03:00
Rename zed2 -> zed
Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
b594e59134
commit
7986ee18cd
2
.github/workflows/release_nightly.yml
vendored
2
.github/workflows/release_nightly.yml
vendored
@ -92,7 +92,7 @@ jobs:
|
||||
run: script/generate-licenses
|
||||
|
||||
- name: Create app bundle
|
||||
run: script/bundle -2
|
||||
run: script/bundle
|
||||
|
||||
- name: Upload Zed Nightly
|
||||
run: script/upload-nightly
|
||||
|
1271
Cargo.lock
generated
1271
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -129,7 +129,6 @@ members = [
|
||||
"crates/welcome2",
|
||||
"crates/xtask",
|
||||
"crates/zed",
|
||||
"crates/zed2",
|
||||
"crates/zed-actions",
|
||||
"crates/zed_actions2"
|
||||
]
|
||||
|
@ -1,5 +1,4 @@
|
||||
[package]
|
||||
authors = ["Nathan Sobo <nathansobo@gmail.com>"]
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
@ -12,71 +11,69 @@ path = "src/zed.rs"
|
||||
doctest = false
|
||||
|
||||
[[bin]]
|
||||
name = "Zed"
|
||||
name = "zed"
|
||||
path = "src/main.rs"
|
||||
|
||||
[[example]]
|
||||
name = "semantic_index_eval"
|
||||
|
||||
[dependencies]
|
||||
audio = { path = "../audio" }
|
||||
activity_indicator = { path = "../activity_indicator" }
|
||||
auto_update = { path = "../auto_update" }
|
||||
breadcrumbs = { path = "../breadcrumbs" }
|
||||
call = { path = "../call" }
|
||||
channel = { path = "../channel" }
|
||||
ai = { package = "ai2", path = "../ai2"}
|
||||
audio = { package = "audio2", path = "../audio2" }
|
||||
activity_indicator = { package = "activity_indicator2", path = "../activity_indicator2"}
|
||||
auto_update = { package = "auto_update2", path = "../auto_update2" }
|
||||
breadcrumbs = { package = "breadcrumbs2", path = "../breadcrumbs2" }
|
||||
call = { package = "call2", path = "../call2" }
|
||||
channel = { package = "channel2", path = "../channel2" }
|
||||
cli = { path = "../cli" }
|
||||
collab_ui = { path = "../collab_ui" }
|
||||
collab_ui = { package = "collab_ui2", path = "../collab_ui2" }
|
||||
collections = { path = "../collections" }
|
||||
command_palette = { path = "../command_palette" }
|
||||
component_test = { path = "../component_test" }
|
||||
context_menu = { path = "../context_menu" }
|
||||
client = { path = "../client" }
|
||||
clock = { path = "../clock" }
|
||||
copilot = { path = "../copilot" }
|
||||
copilot_button = { path = "../copilot_button" }
|
||||
diagnostics = { path = "../diagnostics" }
|
||||
db = { path = "../db" }
|
||||
editor = { path = "../editor" }
|
||||
feedback = { path = "../feedback" }
|
||||
file_finder = { path = "../file_finder" }
|
||||
search = { path = "../search" }
|
||||
fs = { path = "../fs" }
|
||||
command_palette = { package="command_palette2", path = "../command_palette2" }
|
||||
# component_test = { path = "../component_test" }
|
||||
client = { package = "client2", path = "../client2" }
|
||||
# clock = { path = "../clock" }
|
||||
copilot = { package = "copilot2", path = "../copilot2" }
|
||||
copilot_button = { package = "copilot_button2", path = "../copilot_button2" }
|
||||
diagnostics = { package = "diagnostics2", path = "../diagnostics2" }
|
||||
db = { package = "db2", path = "../db2" }
|
||||
editor = { package="editor2", path = "../editor2" }
|
||||
feedback = { package="feedback2", path = "../feedback2" }
|
||||
file_finder = { package="file_finder2", path = "../file_finder2" }
|
||||
search = { package = "search2", path = "../search2" }
|
||||
fs = { package = "fs2", path = "../fs2" }
|
||||
fsevent = { path = "../fsevent" }
|
||||
fuzzy = { path = "../fuzzy" }
|
||||
go_to_line = { path = "../go_to_line" }
|
||||
gpui = { path = "../gpui" }
|
||||
install_cli = { path = "../install_cli" }
|
||||
journal = { path = "../journal" }
|
||||
language = { path = "../language" }
|
||||
language_selector = { path = "../language_selector" }
|
||||
lsp = { path = "../lsp" }
|
||||
language_tools = { path = "../language_tools" }
|
||||
go_to_line = { package = "go_to_line2", path = "../go_to_line2" }
|
||||
gpui = { package = "gpui2", path = "../gpui2" }
|
||||
install_cli = { package = "install_cli2", path = "../install_cli2" }
|
||||
journal = { package = "journal2", path = "../journal2" }
|
||||
language = { package = "language2", path = "../language2" }
|
||||
language_selector = { package = "language_selector2", path = "../language_selector2" }
|
||||
lsp = { package = "lsp2", path = "../lsp2" }
|
||||
menu = { package = "menu2", path = "../menu2" }
|
||||
language_tools = { package = "language_tools2", path = "../language_tools2" }
|
||||
node_runtime = { path = "../node_runtime" }
|
||||
notifications = { path = "../notifications" }
|
||||
assistant = { path = "../assistant" }
|
||||
outline = { path = "../outline" }
|
||||
plugin_runtime = { path = "../plugin_runtime",optional = true }
|
||||
project = { path = "../project" }
|
||||
project_panel = { path = "../project_panel" }
|
||||
project_symbols = { path = "../project_symbols" }
|
||||
quick_action_bar = { path = "../quick_action_bar" }
|
||||
recent_projects = { path = "../recent_projects" }
|
||||
rpc = { path = "../rpc" }
|
||||
settings = { path = "../settings" }
|
||||
feature_flags = { path = "../feature_flags" }
|
||||
notifications = { package = "notifications2", path = "../notifications2" }
|
||||
assistant = { package = "assistant2", path = "../assistant2" }
|
||||
outline = { package = "outline2", path = "../outline2" }
|
||||
# plugin_runtime = { path = "../plugin_runtime",optional = true }
|
||||
project = { package = "project2", path = "../project2" }
|
||||
project_panel = { package = "project_panel2", path = "../project_panel2" }
|
||||
project_symbols = { package = "project_symbols2", path = "../project_symbols2" }
|
||||
quick_action_bar = { package = "quick_action_bar2", path = "../quick_action_bar2" }
|
||||
recent_projects = { package = "recent_projects2", path = "../recent_projects2" }
|
||||
rope = { package = "rope2", path = "../rope2"}
|
||||
rpc = { package = "rpc2", path = "../rpc2" }
|
||||
settings = { package = "settings2", path = "../settings2" }
|
||||
feature_flags = { package = "feature_flags2", path = "../feature_flags2" }
|
||||
sum_tree = { path = "../sum_tree" }
|
||||
shellexpand = "2.1.0"
|
||||
text = { path = "../text" }
|
||||
terminal_view = { path = "../terminal_view" }
|
||||
theme = { path = "../theme" }
|
||||
theme_selector = { path = "../theme_selector" }
|
||||
text = { package = "text2", path = "../text2" }
|
||||
terminal_view = { package = "terminal_view2", path = "../terminal_view2" }
|
||||
theme = { package = "theme2", path = "../theme2" }
|
||||
theme_selector = { package = "theme_selector2", path = "../theme_selector2" }
|
||||
util = { path = "../util" }
|
||||
semantic_index = { path = "../semantic_index" }
|
||||
vim = { path = "../vim" }
|
||||
workspace = { path = "../workspace" }
|
||||
welcome = { path = "../welcome" }
|
||||
zed-actions = {path = "../zed-actions"}
|
||||
semantic_index = { package = "semantic_index2", path = "../semantic_index2" }
|
||||
vim = { package = "vim2", path = "../vim2" }
|
||||
workspace = { package = "workspace2", path = "../workspace2" }
|
||||
welcome = { package = "welcome2", path = "../welcome2" }
|
||||
zed_actions = {package = "zed_actions2", path = "../zed_actions2"}
|
||||
anyhow.workspace = true
|
||||
async-compression.workspace = true
|
||||
async-tar = "0.4.2"
|
||||
@ -147,20 +144,19 @@ urlencoding = "2.1.2"
|
||||
uuid.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
ai = { path = "../ai" }
|
||||
call = { path = "../call", features = ["test-support"] }
|
||||
client = { path = "../client", features = ["test-support"] }
|
||||
editor = { path = "../editor", features = ["test-support"] }
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
language = { path = "../language", features = ["test-support"] }
|
||||
lsp = { path = "../lsp", features = ["test-support"] }
|
||||
project = { path = "../project", features = ["test-support"] }
|
||||
rpc = { path = "../rpc", features = ["test-support"] }
|
||||
settings = { path = "../settings", features = ["test-support"] }
|
||||
text = { path = "../text", features = ["test-support"] }
|
||||
util = { path = "../util", features = ["test-support"] }
|
||||
workspace = { path = "../workspace", features = ["test-support"] }
|
||||
|
||||
call = { package = "call2", path = "../call2", features = ["test-support"] }
|
||||
# client = { path = "../client", features = ["test-support"] }
|
||||
# editor = { path = "../editor", features = ["test-support"] }
|
||||
# gpui = { path = "../gpui", features = ["test-support"] }
|
||||
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
||||
language = { package = "language2", path = "../language2", features = ["test-support"] }
|
||||
# lsp = { path = "../lsp", features = ["test-support"] }
|
||||
project = { package = "project2", path = "../project2", features = ["test-support"] }
|
||||
# rpc = { path = "../rpc", features = ["test-support"] }
|
||||
# settings = { path = "../settings", features = ["test-support"] }
|
||||
text = { package = "text2", path = "../text2", features = ["test-support"] }
|
||||
# util = { path = "../util", features = ["test-support"] }
|
||||
# workspace = { path = "../workspace", features = ["test-support"] }
|
||||
unindent.workspace = true
|
||||
|
||||
[package.metadata.bundle-dev]
|
||||
@ -171,6 +167,14 @@ osx_minimum_system_version = "10.15.7"
|
||||
osx_info_plist_exts = ["resources/info/*"]
|
||||
osx_url_schemes = ["zed-dev"]
|
||||
|
||||
[package.metadata.bundle-nightly]
|
||||
icon = ["resources/app-icon-nightly@2x.png", "resources/app-icon-nightly.png"]
|
||||
identifier = "dev.zed.Zed-Nightly"
|
||||
name = "Zed Nightly"
|
||||
osx_minimum_system_version = "10.15.7"
|
||||
osx_info_plist_exts = ["resources/info/*"]
|
||||
osx_url_schemes = ["zed-nightly"]
|
||||
|
||||
[package.metadata.bundle-preview]
|
||||
icon = ["resources/app-icon-preview@2x.png", "resources/app-icon-preview.png"]
|
||||
identifier = "dev.zed.Zed-Preview"
|
||||
|
@ -3,10 +3,7 @@ use std::process::Command;
|
||||
fn main() {
|
||||
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7");
|
||||
|
||||
if let Ok(value) = std::env::var("ZED_PREVIEW_CHANNEL") {
|
||||
println!("cargo:rustc-env=ZED_PREVIEW_CHANNEL={value}");
|
||||
}
|
||||
|
||||
println!("cargo:rerun-if-env-changed=ZED_BUNDLE");
|
||||
if std::env::var("ZED_BUNDLE").ok().as_deref() == Some("true") {
|
||||
// Find WebRTC.framework in the Frameworks folder when running as part of an application bundle.
|
||||
println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks");
|
||||
@ -24,31 +21,24 @@ fn main() {
|
||||
// Register exported Objective-C selectors, protocols, etc
|
||||
println!("cargo:rustc-link-arg=-Wl,-ObjC");
|
||||
|
||||
// Install dependencies for theme-generation
|
||||
let output = Command::new("npm")
|
||||
.current_dir("../../styles")
|
||||
.args(["install", "--no-save"])
|
||||
.output()
|
||||
.expect("failed to run npm");
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"failed to install theme dependencies {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
// Populate git sha environment variable if git is available
|
||||
println!("cargo:rerun-if-changed=.git/logs/HEAD");
|
||||
if let Ok(output) = Command::new("git").args(["rev-parse", "HEAD"]).output() {
|
||||
if output.status.success() {
|
||||
let git_sha = String::from_utf8_lossy(&output.stdout);
|
||||
let git_sha = git_sha.trim();
|
||||
|
||||
// Regenerate themes
|
||||
let output = Command::new("npm")
|
||||
.current_dir("../../styles")
|
||||
.args(["run", "build"])
|
||||
.output()
|
||||
.expect("failed to run npm");
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"build script failed {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
println!("cargo:rustc-env=ZED_COMMIT_SHA={git_sha}");
|
||||
|
||||
println!("cargo:rerun-if-changed=../../styles/src");
|
||||
if let Ok(build_profile) = std::env::var("PROFILE") {
|
||||
if build_profile == "release" {
|
||||
// This is currently the best way to make `cargo build ...`'s build script
|
||||
// to print something to stdout without extra verbosity.
|
||||
println!(
|
||||
"cargo:warning=Info: using '{git_sha}' hash for ZED_COMMIT_SHA env var"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,533 +0,0 @@
|
||||
use ai::providers::open_ai::OpenAIEmbeddingProvider;
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{self, UserStore};
|
||||
use gpui::{AsyncAppContext, ModelHandle, Task};
|
||||
use language::LanguageRegistry;
|
||||
use node_runtime::RealNodeRuntime;
|
||||
use project::{Project, RealFs};
|
||||
use semantic_index::semantic_index_settings::SemanticIndexSettings;
|
||||
use semantic_index::{SearchResult, SemanticIndex};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{default_settings, SettingsStore};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{cmp, env, fs};
|
||||
use util::channel::{RELEASE_CHANNEL, RELEASE_CHANNEL_NAME};
|
||||
use util::http::{self};
|
||||
use util::paths::EMBEDDINGS_DIR;
|
||||
use zed::languages;
|
||||
|
||||
#[derive(Deserialize, Clone, Serialize)]
|
||||
struct EvaluationQuery {
|
||||
query: String,
|
||||
matches: Vec<String>,
|
||||
}
|
||||
|
||||
impl EvaluationQuery {
|
||||
fn match_pairs(&self) -> Vec<(PathBuf, u32)> {
|
||||
let mut pairs = Vec::new();
|
||||
for match_identifier in self.matches.iter() {
|
||||
let mut match_parts = match_identifier.split(":");
|
||||
|
||||
if let Some(file_path) = match_parts.next() {
|
||||
if let Some(row_number) = match_parts.next() {
|
||||
pairs.push((PathBuf::from(file_path), row_number.parse::<u32>().unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
pairs
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct RepoEval {
|
||||
repo: String,
|
||||
commit: String,
|
||||
assertions: Vec<EvaluationQuery>,
|
||||
}
|
||||
|
||||
const TMP_REPO_PATH: &str = "eval_repos";
|
||||
|
||||
fn parse_eval() -> anyhow::Result<Vec<RepoEval>> {
|
||||
let eval_folder = env::current_dir()?
|
||||
.as_path()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("zed/crates/semantic_index/eval");
|
||||
|
||||
let mut repo_evals: Vec<RepoEval> = Vec::new();
|
||||
for entry in fs::read_dir(eval_folder)? {
|
||||
let file_path = entry.unwrap().path();
|
||||
if let Some(extension) = file_path.extension() {
|
||||
if extension == "json" {
|
||||
if let Ok(file) = fs::read_to_string(file_path) {
|
||||
let repo_eval = serde_json::from_str(file.as_str());
|
||||
|
||||
match repo_eval {
|
||||
Ok(repo_eval) => {
|
||||
repo_evals.push(repo_eval);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Err: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(repo_evals)
|
||||
}
|
||||
|
||||
fn clone_repo(repo_eval: RepoEval) -> anyhow::Result<(String, PathBuf)> {
|
||||
let repo_name = Path::new(repo_eval.repo.as_str())
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
.replace(".git", "");
|
||||
|
||||
let clone_path = fs::canonicalize(env::current_dir()?)?
|
||||
.parent()
|
||||
.ok_or(anyhow!("path canonicalization failed"))?
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join(TMP_REPO_PATH);
|
||||
|
||||
// Delete Clone Path if already exists
|
||||
let _ = fs::remove_dir_all(&clone_path);
|
||||
let _ = fs::create_dir(&clone_path);
|
||||
|
||||
let _ = Command::new("git")
|
||||
.args(["clone", repo_eval.repo.as_str()])
|
||||
.current_dir(clone_path.clone())
|
||||
.output()?;
|
||||
// Update clone path to be new directory housing the repo.
|
||||
let clone_path = clone_path.join(repo_name.clone());
|
||||
let _ = Command::new("git")
|
||||
.args(["checkout", repo_eval.commit.as_str()])
|
||||
.current_dir(clone_path.clone())
|
||||
.output()?;
|
||||
|
||||
Ok((repo_name, clone_path))
|
||||
}
|
||||
|
||||
fn dcg(hits: Vec<usize>) -> f32 {
|
||||
let mut result = 0.0;
|
||||
for (idx, hit) in hits.iter().enumerate() {
|
||||
result += *hit as f32 / (2.0 + idx as f32).log2();
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn get_hits(
|
||||
eval_query: EvaluationQuery,
|
||||
search_results: Vec<SearchResult>,
|
||||
k: usize,
|
||||
cx: &AsyncAppContext,
|
||||
) -> (Vec<usize>, Vec<usize>) {
|
||||
let ideal = vec![1; cmp::min(eval_query.matches.len(), k)];
|
||||
|
||||
let mut hits = Vec::new();
|
||||
for result in search_results {
|
||||
let (path, start_row, end_row) = result.buffer.read_with(cx, |buffer, _cx| {
|
||||
let path = buffer.file().unwrap().path().to_path_buf();
|
||||
let start_row = buffer.offset_to_point(result.range.start.offset).row;
|
||||
let end_row = buffer.offset_to_point(result.range.end.offset).row;
|
||||
(path, start_row, end_row)
|
||||
});
|
||||
|
||||
let match_pairs = eval_query.match_pairs();
|
||||
let mut found = 0;
|
||||
for (match_path, match_row) in match_pairs {
|
||||
if match_path == path {
|
||||
if match_row >= start_row && match_row <= end_row {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hits.push(found);
|
||||
}
|
||||
|
||||
// For now, we are calculating ideal_hits a bit different, as technically
|
||||
// with overlapping ranges, one match can result in more than result.
|
||||
let mut ideal_hits = hits.clone();
|
||||
ideal_hits.retain(|x| x == &1);
|
||||
|
||||
let ideal = if ideal.len() > ideal_hits.len() {
|
||||
ideal
|
||||
} else {
|
||||
ideal_hits
|
||||
};
|
||||
|
||||
// Fill ideal to 10 length
|
||||
let mut filled_ideal = [0; 10];
|
||||
for (idx, i) in ideal.to_vec().into_iter().enumerate() {
|
||||
filled_ideal[idx] = i;
|
||||
}
|
||||
|
||||
(filled_ideal.to_vec(), hits)
|
||||
}
|
||||
|
||||
fn evaluate_ndcg(hits: Vec<usize>, ideal: Vec<usize>) -> Vec<f32> {
|
||||
// NDCG or Normalized Discounted Cumulative Gain, is determined by comparing the relevance of
|
||||
// items returned by the search engine relative to the hypothetical ideal.
|
||||
// Relevance is represented as a series of booleans, in which each search result returned
|
||||
// is identified as being inside the test set of matches (1) or not (0).
|
||||
|
||||
// For example, if result 1, 3 and 5 match the 3 relevant results provided
|
||||
// actual dcg is calculated against a vector of [1, 0, 1, 0, 1]
|
||||
// whereas ideal dcg is calculated against a vector of [1, 1, 1, 0, 0]
|
||||
// as this ideal vector assumes the 3 relevant results provided were returned first
|
||||
// normalized dcg is then calculated as actual dcg / ideal dcg.
|
||||
|
||||
// NDCG ranges from 0 to 1, which higher values indicating better performance
|
||||
// Commonly NDCG is expressed as NDCG@k, in which k represents the metric calculated
|
||||
// including only the top k values returned.
|
||||
// The @k metrics can help you identify, at what point does the relevant results start to fall off.
|
||||
// Ie. a NDCG@1 of 0.9 and a NDCG@3 of 0.5 may indicate that the first result returned in usually
|
||||
// very high quality, whereas rank results quickly drop off after the first result.
|
||||
|
||||
let mut ndcg = Vec::new();
|
||||
for idx in 1..(hits.len() + 1) {
|
||||
let hits_at_k = hits[0..idx].to_vec();
|
||||
let ideal_at_k = ideal[0..idx].to_vec();
|
||||
|
||||
let at_k = dcg(hits_at_k.clone()) / dcg(ideal_at_k.clone());
|
||||
|
||||
ndcg.push(at_k);
|
||||
}
|
||||
|
||||
ndcg
|
||||
}
|
||||
|
||||
fn evaluate_map(hits: Vec<usize>) -> Vec<f32> {
|
||||
let mut map_at_k = Vec::new();
|
||||
|
||||
let non_zero = hits.iter().sum::<usize>() as f32;
|
||||
if non_zero == 0.0 {
|
||||
return vec![0.0; hits.len()];
|
||||
}
|
||||
|
||||
let mut rolling_non_zero = 0.0;
|
||||
let mut rolling_map = 0.0;
|
||||
for (idx, h) in hits.into_iter().enumerate() {
|
||||
rolling_non_zero += h as f32;
|
||||
if h == 1 {
|
||||
rolling_map += rolling_non_zero / (idx + 1) as f32;
|
||||
}
|
||||
map_at_k.push(rolling_map / non_zero);
|
||||
}
|
||||
|
||||
map_at_k
|
||||
}
|
||||
|
||||
fn evaluate_mrr(hits: Vec<usize>) -> f32 {
|
||||
for (idx, h) in hits.into_iter().enumerate() {
|
||||
if h == 1 {
|
||||
return 1.0 / (idx + 1) as f32;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
fn init_logger() {
|
||||
env_logger::init();
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct QueryMetrics {
|
||||
query: EvaluationQuery,
|
||||
millis_to_search: Duration,
|
||||
ndcg: Vec<f32>,
|
||||
map: Vec<f32>,
|
||||
mrr: f32,
|
||||
hits: Vec<usize>,
|
||||
precision: Vec<f32>,
|
||||
recall: Vec<f32>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SummaryMetrics {
|
||||
millis_to_search: f32,
|
||||
ndcg: Vec<f32>,
|
||||
map: Vec<f32>,
|
||||
mrr: f32,
|
||||
precision: Vec<f32>,
|
||||
recall: Vec<f32>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct RepoEvaluationMetrics {
|
||||
millis_to_index: Duration,
|
||||
query_metrics: Vec<QueryMetrics>,
|
||||
repo_metrics: Option<SummaryMetrics>,
|
||||
}
|
||||
|
||||
impl RepoEvaluationMetrics {
|
||||
fn new(millis_to_index: Duration) -> Self {
|
||||
RepoEvaluationMetrics {
|
||||
millis_to_index,
|
||||
query_metrics: Vec::new(),
|
||||
repo_metrics: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn save(&self, repo_name: String) -> Result<()> {
|
||||
let results_string = serde_json::to_string(&self)?;
|
||||
fs::write(format!("./{}_evaluation.json", repo_name), results_string)
|
||||
.expect("Unable to write file");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn summarize(&mut self) {
|
||||
let l = self.query_metrics.len() as f32;
|
||||
let millis_to_search: f32 = self
|
||||
.query_metrics
|
||||
.iter()
|
||||
.map(|metrics| metrics.millis_to_search.as_millis())
|
||||
.sum::<u128>() as f32
|
||||
/ l;
|
||||
|
||||
let mut ndcg_sum = vec![0.0; 10];
|
||||
let mut map_sum = vec![0.0; 10];
|
||||
let mut precision_sum = vec![0.0; 10];
|
||||
let mut recall_sum = vec![0.0; 10];
|
||||
let mut mmr_sum = 0.0;
|
||||
|
||||
for query_metric in self.query_metrics.iter() {
|
||||
for (ndcg, query_ndcg) in ndcg_sum.iter_mut().zip(query_metric.ndcg.clone()) {
|
||||
*ndcg += query_ndcg;
|
||||
}
|
||||
|
||||
for (mapp, query_map) in map_sum.iter_mut().zip(query_metric.map.clone()) {
|
||||
*mapp += query_map;
|
||||
}
|
||||
|
||||
for (pre, query_pre) in precision_sum.iter_mut().zip(query_metric.precision.clone()) {
|
||||
*pre += query_pre;
|
||||
}
|
||||
|
||||
for (rec, query_rec) in recall_sum.iter_mut().zip(query_metric.recall.clone()) {
|
||||
*rec += query_rec;
|
||||
}
|
||||
|
||||
mmr_sum += query_metric.mrr;
|
||||
}
|
||||
|
||||
let ndcg = ndcg_sum.iter().map(|val| val / l).collect::<Vec<f32>>();
|
||||
let map = map_sum.iter().map(|val| val / l).collect::<Vec<f32>>();
|
||||
let precision = precision_sum
|
||||
.iter()
|
||||
.map(|val| val / l)
|
||||
.collect::<Vec<f32>>();
|
||||
let recall = recall_sum.iter().map(|val| val / l).collect::<Vec<f32>>();
|
||||
let mrr = mmr_sum / l;
|
||||
|
||||
self.repo_metrics = Some(SummaryMetrics {
|
||||
millis_to_search,
|
||||
ndcg,
|
||||
map,
|
||||
mrr,
|
||||
precision,
|
||||
recall,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_precision(hits: Vec<usize>) -> Vec<f32> {
|
||||
let mut rolling_hit: f32 = 0.0;
|
||||
let mut precision = Vec::new();
|
||||
for (idx, hit) in hits.into_iter().enumerate() {
|
||||
rolling_hit += hit as f32;
|
||||
precision.push(rolling_hit / ((idx as f32) + 1.0));
|
||||
}
|
||||
|
||||
precision
|
||||
}
|
||||
|
||||
fn evaluate_recall(hits: Vec<usize>, ideal: Vec<usize>) -> Vec<f32> {
|
||||
let total_relevant = ideal.iter().sum::<usize>() as f32;
|
||||
let mut recall = Vec::new();
|
||||
let mut rolling_hit: f32 = 0.0;
|
||||
for hit in hits {
|
||||
rolling_hit += hit as f32;
|
||||
recall.push(rolling_hit / total_relevant);
|
||||
}
|
||||
|
||||
recall
|
||||
}
|
||||
|
||||
async fn evaluate_repo(
|
||||
repo_name: String,
|
||||
index: ModelHandle<SemanticIndex>,
|
||||
project: ModelHandle<Project>,
|
||||
query_matches: Vec<EvaluationQuery>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<RepoEvaluationMetrics> {
|
||||
// Index Project
|
||||
let index_t0 = Instant::now();
|
||||
index
|
||||
.update(cx, |index, cx| index.index_project(project.clone(), cx))
|
||||
.await?;
|
||||
let mut repo_metrics = RepoEvaluationMetrics::new(index_t0.elapsed());
|
||||
|
||||
for query in query_matches {
|
||||
// Query each match in order
|
||||
let search_t0 = Instant::now();
|
||||
let search_results = index
|
||||
.update(cx, |index, cx| {
|
||||
index.search_project(project.clone(), query.clone().query, 10, vec![], vec![], cx)
|
||||
})
|
||||
.await?;
|
||||
let millis_to_search = search_t0.elapsed();
|
||||
|
||||
// Get Hits/Ideal
|
||||
let k = 10;
|
||||
let (ideal, hits) = self::get_hits(query.clone(), search_results, k, cx);
|
||||
|
||||
// Evaluate ndcg@k, for k = 1, 3, 5, 10
|
||||
let ndcg = evaluate_ndcg(hits.clone(), ideal.clone());
|
||||
|
||||
// Evaluate map@k, for k = 1, 3, 5, 10
|
||||
let map = evaluate_map(hits.clone());
|
||||
|
||||
// Evaluate mrr
|
||||
let mrr = evaluate_mrr(hits.clone());
|
||||
|
||||
// Evaluate precision
|
||||
let precision = evaluate_precision(hits.clone());
|
||||
|
||||
// Evaluate Recall
|
||||
let recall = evaluate_recall(hits.clone(), ideal);
|
||||
|
||||
let query_metrics = QueryMetrics {
|
||||
query,
|
||||
millis_to_search,
|
||||
ndcg,
|
||||
map,
|
||||
mrr,
|
||||
hits,
|
||||
precision,
|
||||
recall,
|
||||
};
|
||||
|
||||
repo_metrics.query_metrics.push(query_metrics);
|
||||
}
|
||||
|
||||
repo_metrics.summarize();
|
||||
let _ = repo_metrics.save(repo_name);
|
||||
|
||||
anyhow::Ok(repo_metrics)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Launch new repo as a new Zed workspace/project
|
||||
let app = gpui::App::new(()).unwrap();
|
||||
let fs = Arc::new(RealFs);
|
||||
let http = http::client();
|
||||
let http_client = http::client();
|
||||
init_logger();
|
||||
|
||||
app.run(move |cx| {
|
||||
cx.set_global(*RELEASE_CHANNEL);
|
||||
|
||||
let client = client::Client::new(http.clone(), cx);
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client.clone(), cx));
|
||||
|
||||
// Initialize Settings
|
||||
let mut store = SettingsStore::default();
|
||||
store
|
||||
.set_default_settings(default_settings().as_ref(), cx)
|
||||
.unwrap();
|
||||
cx.set_global(store);
|
||||
|
||||
// Initialize Languages
|
||||
let login_shell_env_loaded = Task::ready(());
|
||||
let mut languages = LanguageRegistry::new(login_shell_env_loaded);
|
||||
languages.set_executor(cx.background().clone());
|
||||
let languages = Arc::new(languages);
|
||||
|
||||
let node_runtime = RealNodeRuntime::new(http.clone());
|
||||
languages::init(languages.clone(), node_runtime.clone(), cx);
|
||||
language::init(cx);
|
||||
|
||||
project::Project::init(&client, cx);
|
||||
semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx);
|
||||
|
||||
settings::register::<SemanticIndexSettings>(cx);
|
||||
|
||||
let db_file_path = EMBEDDINGS_DIR
|
||||
.join(Path::new(RELEASE_CHANNEL_NAME.as_str()))
|
||||
.join("embeddings_db");
|
||||
|
||||
let languages = languages.clone();
|
||||
|
||||
let fs = fs.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
let semantic_index = SemanticIndex::new(
|
||||
fs.clone(),
|
||||
db_file_path,
|
||||
Arc::new(OpenAIEmbeddingProvider::new(http_client, cx.background())),
|
||||
languages.clone(),
|
||||
cx.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Ok(repo_evals) = parse_eval() {
|
||||
for repo in repo_evals {
|
||||
let cloned = clone_repo(repo.clone());
|
||||
match cloned {
|
||||
Ok((repo_name, clone_path)) => {
|
||||
println!(
|
||||
"Cloned {:?} @ {:?} into {:?}",
|
||||
repo.repo, repo.commit, &clone_path
|
||||
);
|
||||
|
||||
// Create Project
|
||||
let project = cx.update(|cx| {
|
||||
Project::local(
|
||||
client.clone(),
|
||||
node_runtime::FakeNodeRuntime::new(),
|
||||
user_store.clone(),
|
||||
languages.clone(),
|
||||
fs.clone(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
// Register Worktree
|
||||
let _ = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.find_or_create_local_worktree(clone_path, true, cx)
|
||||
})
|
||||
.await;
|
||||
|
||||
let _ = evaluate_repo(
|
||||
repo_name,
|
||||
semantic_index.clone(),
|
||||
project,
|
||||
repo.assertions,
|
||||
&mut cx,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Error cloning: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use gpui::AssetSource;
|
||||
use anyhow::anyhow;
|
||||
|
||||
use gpui::{AssetSource, Result, SharedString};
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
@ -7,6 +8,7 @@ use rust_embed::RustEmbed;
|
||||
#[include = "fonts/**/*"]
|
||||
#[include = "icons/**/*"]
|
||||
#[include = "themes/**/*"]
|
||||
#[exclude = "themes/src/*"]
|
||||
#[include = "sounds/**/*"]
|
||||
#[include = "*.md"]
|
||||
#[exclude = "*.DS_Store"]
|
||||
@ -19,7 +21,15 @@ impl AssetSource for Assets {
|
||||
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
||||
}
|
||||
|
||||
fn list(&self, path: &str) -> Vec<std::borrow::Cow<'static, str>> {
|
||||
Self::iter().filter(|p| p.starts_with(path)).collect()
|
||||
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
|
||||
Ok(Self::iter()
|
||||
.filter_map(|p| {
|
||||
if p.starts_with(path) {
|
||||
Some(p.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ use gpui::AppContext;
|
||||
pub use language::*;
|
||||
use node_runtime::NodeRuntime;
|
||||
use rust_embed::RustEmbed;
|
||||
use settings::Settings;
|
||||
use std::{borrow::Cow, str, sync::Arc};
|
||||
use util::asset_str;
|
||||
use util::{asset_str, paths::PLUGINS_DIR};
|
||||
|
||||
use self::elixir::ElixirSettings;
|
||||
|
||||
@ -48,7 +49,7 @@ pub fn init(
|
||||
node_runtime: Arc<dyn NodeRuntime>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
settings::register::<elixir::ElixirSettings>(cx);
|
||||
ElixirSettings::register(cx);
|
||||
|
||||
let language = |name, grammar, adapters| {
|
||||
languages.register(name, load_config(name), grammar, adapters, load_queries)
|
||||
@ -74,7 +75,7 @@ pub fn init(
|
||||
],
|
||||
);
|
||||
|
||||
match &settings::get::<ElixirSettings>(cx).lsp {
|
||||
match &ElixirSettings::get(None, cx).lsp {
|
||||
elixir::ElixirLspSetting::ElixirLs => language(
|
||||
"elixir",
|
||||
tree_sitter_elixir::language(),
|
||||
@ -227,6 +228,21 @@ pub fn init(
|
||||
tree_sitter_uiua::language(),
|
||||
vec![Arc::new(uiua::UiuaLanguageServer {})],
|
||||
);
|
||||
|
||||
if let Ok(children) = std::fs::read_dir(&*PLUGINS_DIR) {
|
||||
for child in children {
|
||||
if let Ok(child) = child {
|
||||
let path = child.path();
|
||||
let config_path = path.join("config.toml");
|
||||
if let Ok(config) = std::fs::read(&config_path) {
|
||||
let config: LanguageConfig = toml::from_slice(&config).unwrap();
|
||||
if let Some(grammar_name) = config.grammar_name.clone() {
|
||||
languages.register_wasm(path.into(), grammar_name, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
@ -273,18 +273,19 @@ async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServ
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::TestAppContext;
|
||||
use gpui::{Context, TestAppContext};
|
||||
use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
|
||||
use settings::SettingsStore;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_c_autoindent(cx: &mut TestAppContext) {
|
||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
// cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
let test_settings = SettingsStore::test(cx);
|
||||
cx.set_global(test_settings);
|
||||
language::init(cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(2);
|
||||
});
|
||||
@ -292,8 +293,9 @@ mod tests {
|
||||
});
|
||||
let language = crate::languages::language("c", tree_sitter_c::language(), None).await;
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let mut buffer = Buffer::new(0, cx.model_id() as u64, "").with_language(language, cx);
|
||||
cx.new_model(|cx| {
|
||||
let mut buffer =
|
||||
Buffer::new(0, cx.entity_id().as_u64(), "").with_language(language, cx);
|
||||
|
||||
// empty function
|
||||
buffer.edit([(0..0, "int main() {}")], None, cx);
|
||||
|
@ -6,7 +6,7 @@ pub use language::*;
|
||||
use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind};
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use settings::Setting;
|
||||
use settings::Settings;
|
||||
use smol::fs::{self, File};
|
||||
use std::{
|
||||
any::Any,
|
||||
@ -46,7 +46,7 @@ pub struct ElixirSettingsContent {
|
||||
lsp: Option<ElixirLspSetting>,
|
||||
}
|
||||
|
||||
impl Setting for ElixirSettings {
|
||||
impl Settings for ElixirSettings {
|
||||
const KEY: Option<&'static str> = Some("elixir");
|
||||
|
||||
type FileContent = ElixirSettingsContent;
|
||||
@ -54,7 +54,7 @@ impl Setting for ElixirSettings {
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &gpui::AppContext,
|
||||
_: &mut gpui::AppContext,
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
@ -85,7 +85,7 @@ impl LspAdapter for ElixirLspAdapter {
|
||||
const NOTIFICATION_MESSAGE: &str = "Could not run the elixir language server, `elixir-ls`, because `elixir` was not found.";
|
||||
|
||||
let delegate = delegate.clone();
|
||||
Some(cx.spawn(|mut cx| async move {
|
||||
Some(cx.spawn(|cx| async move {
|
||||
let elixir_output = smol::process::Command::new("elixir")
|
||||
.args(["--version"])
|
||||
.output()
|
||||
@ -97,7 +97,7 @@ impl LspAdapter for ElixirLspAdapter {
|
||||
{
|
||||
cx.update(|cx| {
|
||||
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
|
||||
})
|
||||
})?
|
||||
}
|
||||
return Err(anyhow!("cannot run elixir-ls"));
|
||||
}
|
||||
|
@ -18,10 +18,10 @@
|
||||
target: (identifier) @name)
|
||||
operator: "when")
|
||||
])
|
||||
(#any-match? @name "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
|
||||
(#match? @name "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
|
||||
)
|
||||
|
||||
(call
|
||||
target: (identifier) @name
|
||||
(arguments (alias) @name)
|
||||
(#any-match? @name "^(defmodule|defprotocol)$")) @item
|
||||
(#match? @name "^(defmodule|defprotocol)$")) @item
|
||||
|
@ -67,7 +67,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
"Could not install the Go language server `gopls`, because `go` was not found.";
|
||||
|
||||
let delegate = delegate.clone();
|
||||
Some(cx.spawn(|mut cx| async move {
|
||||
Some(cx.spawn(|cx| async move {
|
||||
let install_output = process::Command::new("go").args(["version"]).output().await;
|
||||
if install_output.is_err() {
|
||||
if DID_SHOW_NOTIFICATION
|
||||
@ -76,7 +76,7 @@ impl super::LspAdapter for GoLspAdapter {
|
||||
{
|
||||
cx.update(|cx| {
|
||||
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
|
||||
})
|
||||
})?
|
||||
}
|
||||
return Err(anyhow!("cannot install gopls"));
|
||||
}
|
||||
@ -372,7 +372,7 @@ fn adjust_runs(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::languages::language;
|
||||
use gpui::color::Color;
|
||||
use gpui::Hsla;
|
||||
use theme::SyntaxTheme;
|
||||
|
||||
#[gpui::test]
|
||||
@ -384,12 +384,12 @@ mod tests {
|
||||
)
|
||||
.await;
|
||||
|
||||
let theme = SyntaxTheme::new(vec![
|
||||
("type".into(), Color::green().into()),
|
||||
("keyword".into(), Color::blue().into()),
|
||||
("function".into(), Color::red().into()),
|
||||
("number".into(), Color::yellow().into()),
|
||||
("property".into(), Color::white().into()),
|
||||
let theme = SyntaxTheme::new_test([
|
||||
("type", Hsla::default()),
|
||||
("keyword", Hsla::default()),
|
||||
("function", Hsla::default()),
|
||||
("number", Hsla::default()),
|
||||
("property", Hsla::default()),
|
||||
]);
|
||||
language.set_theme(&theme);
|
||||
|
||||
|
@ -108,7 +108,7 @@ impl LspAdapter for JsonLspAdapter {
|
||||
_workspace_root: &Path,
|
||||
cx: &mut AppContext,
|
||||
) -> BoxFuture<'static, serde_json::Value> {
|
||||
let action_names = cx.all_action_names().collect::<Vec<_>>();
|
||||
let action_names = cx.all_action_names();
|
||||
let staff_mode = cx.is_staff();
|
||||
let language_names = &self.languages.language_names();
|
||||
let settings_schema = cx.global::<SettingsStore>().json_schema(
|
||||
|
@ -3,8 +3,8 @@ use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use futures::lock::Mutex;
|
||||
use gpui::executor::Background;
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use language2::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp2::LanguageServerBinary;
|
||||
use plugin_runtime::{Plugin, PluginBinary, PluginBuilder, WasiFn};
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use util::ResultExt;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use language::{CodeLabel, Language, LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use std::{any::Any, path::PathBuf};
|
||||
|
||||
pub struct NuLanguageServer;
|
||||
|
||||
@ -52,30 +52,4 @@ impl LspAdapter for NuLanguageServer {
|
||||
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||
None
|
||||
}
|
||||
|
||||
async fn label_for_completion(
|
||||
&self,
|
||||
completion: &lsp::CompletionItem,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
return Some(CodeLabel {
|
||||
runs: language
|
||||
.highlight_text(&completion.label.clone().into(), 0..completion.label.len()),
|
||||
text: completion.label.clone(),
|
||||
filter_range: 0..completion.label.len(),
|
||||
});
|
||||
}
|
||||
|
||||
async fn label_for_symbol(
|
||||
&self,
|
||||
name: &str,
|
||||
_: lsp::SymbolKind,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
Some(CodeLabel {
|
||||
runs: language.highlight_text(&name.into(), 0..name.len()),
|
||||
text: name.to_string(),
|
||||
filter_range: 0..name.len(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -177,28 +177,30 @@ async fn get_cached_server_binary(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::{ModelContext, TestAppContext};
|
||||
use gpui::{Context, ModelContext, TestAppContext};
|
||||
use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
|
||||
use settings::SettingsStore;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_python_autoindent(cx: &mut TestAppContext) {
|
||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
// cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
let language =
|
||||
crate::languages::language("python", tree_sitter_python::language(), None).await;
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
let test_settings = SettingsStore::test(cx);
|
||||
cx.set_global(test_settings);
|
||||
language::init(cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let mut buffer = Buffer::new(0, cx.model_id() as u64, "").with_language(language, cx);
|
||||
cx.new_model(|cx| {
|
||||
let mut buffer =
|
||||
Buffer::new(0, cx.entity_id().as_u64(), "").with_language(language, cx);
|
||||
let append = |buffer: &mut Buffer, text: &str, cx: &mut ModelContext<Buffer>| {
|
||||
let ix = buffer.len();
|
||||
buffer.edit([(ix..ix, text)], Some(AutoindentMode::EachLine), cx);
|
||||
|
@ -294,7 +294,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::languages::language;
|
||||
use gpui::{color::Color, TestAppContext};
|
||||
use gpui::{Context, Hsla, TestAppContext};
|
||||
use language::language_settings::AllLanguageSettings;
|
||||
use settings::SettingsStore;
|
||||
use theme::SyntaxTheme;
|
||||
@ -349,11 +349,11 @@ mod tests {
|
||||
)
|
||||
.await;
|
||||
let grammar = language.grammar().unwrap();
|
||||
let theme = SyntaxTheme::new(vec![
|
||||
("type".into(), Color::green().into()),
|
||||
("keyword".into(), Color::blue().into()),
|
||||
("function".into(), Color::red().into()),
|
||||
("property".into(), Color::white().into()),
|
||||
let theme = SyntaxTheme::new_test([
|
||||
("type", Hsla::default()),
|
||||
("keyword", Hsla::default()),
|
||||
("function", Hsla::default()),
|
||||
("property", Hsla::default()),
|
||||
]);
|
||||
|
||||
language.set_theme(&theme);
|
||||
@ -456,11 +456,11 @@ mod tests {
|
||||
)
|
||||
.await;
|
||||
let grammar = language.grammar().unwrap();
|
||||
let theme = SyntaxTheme::new(vec![
|
||||
("type".into(), Color::green().into()),
|
||||
("keyword".into(), Color::blue().into()),
|
||||
("function".into(), Color::red().into()),
|
||||
("property".into(), Color::white().into()),
|
||||
let theme = SyntaxTheme::new_test([
|
||||
("type", Hsla::default()),
|
||||
("keyword", Hsla::default()),
|
||||
("function", Hsla::default()),
|
||||
("property", Hsla::default()),
|
||||
]);
|
||||
|
||||
language.set_theme(&theme);
|
||||
@ -494,11 +494,12 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_rust_autoindent(cx: &mut TestAppContext) {
|
||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
// cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
let test_settings = SettingsStore::test(cx);
|
||||
cx.set_global(test_settings);
|
||||
language::init(cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(2);
|
||||
});
|
||||
@ -507,8 +508,9 @@ mod tests {
|
||||
|
||||
let language = crate::languages::language("rust", tree_sitter_rust::language(), None).await;
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let mut buffer = Buffer::new(0, cx.model_id() as u64, "").with_language(language, cx);
|
||||
cx.new_model(|cx| {
|
||||
let mut buffer =
|
||||
Buffer::new(0, cx.entity_id().as_u64(), "").with_language(language, cx);
|
||||
|
||||
// indent between braces
|
||||
buffer.set_text("fn a() {}", cx);
|
||||
|
@ -351,7 +351,7 @@ async fn get_cached_eslint_server_binary(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::TestAppContext;
|
||||
use gpui::{Context, TestAppContext};
|
||||
use unindent::Unindent;
|
||||
|
||||
#[gpui::test]
|
||||
@ -378,10 +378,10 @@ mod tests {
|
||||
"#
|
||||
.unindent();
|
||||
|
||||
let buffer = cx.add_model(|cx| {
|
||||
language::Buffer::new(0, cx.model_id() as u64, text).with_language(language, cx)
|
||||
let buffer = cx.new_model(|cx| {
|
||||
language::Buffer::new(0, cx.entity_id().as_u64(), text).with_language(language, cx)
|
||||
});
|
||||
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None).unwrap());
|
||||
let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None).unwrap());
|
||||
assert_eq!(
|
||||
outline
|
||||
.items
|
||||
|
@ -3,7 +3,7 @@ path_suffixes = ["ua"]
|
||||
line_comment = "# "
|
||||
autoclose_before = ")]}\""
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = false },
|
||||
{ start = "{", end = "}", close = true, newline = false},
|
||||
{ start = "[", end = "]", close = true, newline = false },
|
||||
{ start = "(", end = ")", close = true, newline = false },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||
|
@ -1,59 +1,62 @@
|
||||
// Allow binary to be called Zed for a nice application menu when running executable directly
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use backtrace::Backtrace;
|
||||
use chrono::Utc;
|
||||
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
|
||||
use client::{
|
||||
self, Client, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN,
|
||||
};
|
||||
use client::{Client, UserStore};
|
||||
use collab_ui::channel_view::ChannelView;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use editor::Editor;
|
||||
use fs::RealFs;
|
||||
use futures::StreamExt;
|
||||
use gpui::{Action, App, AppContext, AssetSource, AsyncAppContext, Task};
|
||||
use isahc::{config::Configurable, Request};
|
||||
use gpui::{App, AppContext, AsyncAppContext, Context, SemanticVersion, Task};
|
||||
use isahc::{prelude::Configurable, Request};
|
||||
use language::LanguageRegistry;
|
||||
use log::LevelFilter;
|
||||
|
||||
use node_runtime::RealNodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
use project::Fs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{default_settings, handle_settings_file_changes, watch_config_file, SettingsStore};
|
||||
use settings::{
|
||||
default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore,
|
||||
};
|
||||
use simplelog::ConfigBuilder;
|
||||
use smol::process::Command;
|
||||
use std::{
|
||||
env,
|
||||
ffi::OsStr,
|
||||
fs::OpenOptions,
|
||||
io::{IsTerminal, Write as _},
|
||||
io::{IsTerminal, Write},
|
||||
panic,
|
||||
path::Path,
|
||||
path::{Path, PathBuf},
|
||||
sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Arc, Weak,
|
||||
},
|
||||
thread,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use util::{
|
||||
channel::{parse_zed_link, ReleaseChannel},
|
||||
async_maybe,
|
||||
channel::{parse_zed_link, AppCommitSha, ReleaseChannel, RELEASE_CHANNEL},
|
||||
http::{self, HttpClient},
|
||||
paths, ResultExt,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
use welcome::{show_welcome_experience, FIRST_OPEN};
|
||||
|
||||
use fs::RealFs;
|
||||
use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt};
|
||||
use welcome::{show_welcome_view, FIRST_OPEN};
|
||||
use workspace::{AppState, WorkspaceStore};
|
||||
use zed::{
|
||||
assets::Assets,
|
||||
build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus,
|
||||
only_instance::{ensure_only_instance, IsOnlyInstance},
|
||||
open_listener::{handle_cli_connection, OpenListener, OpenRequest},
|
||||
app_menus, build_window_options, ensure_only_instance, handle_cli_connection,
|
||||
handle_keymap_file_changes, initialize_workspace, languages, Assets, IsOnlyInstance,
|
||||
OpenListener, OpenRequest,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
menu::init();
|
||||
zed_actions::init();
|
||||
|
||||
let http = http::client();
|
||||
init_paths();
|
||||
init_logger();
|
||||
@ -63,48 +66,61 @@ fn main() {
|
||||
}
|
||||
|
||||
log::info!("========== starting zed ==========");
|
||||
let mut app = gpui::App::new(Assets).unwrap();
|
||||
let app = App::production(Arc::new(Assets));
|
||||
|
||||
let (installation_id, existing_installation_id_found) =
|
||||
app.background().block(installation_id()).ok().unzip();
|
||||
let (installation_id, existing_installation_id_found) = app
|
||||
.background_executor()
|
||||
.block(installation_id())
|
||||
.ok()
|
||||
.unzip();
|
||||
let session_id = Uuid::new_v4().to_string();
|
||||
init_panic_hook(&app, installation_id.clone(), session_id.clone());
|
||||
|
||||
load_embedded_fonts(&app);
|
||||
|
||||
let fs = Arc::new(RealFs);
|
||||
let user_settings_file_rx =
|
||||
watch_config_file(app.background(), fs.clone(), paths::SETTINGS.clone());
|
||||
let user_keymap_file_rx =
|
||||
watch_config_file(app.background(), fs.clone(), paths::KEYMAP.clone());
|
||||
let user_settings_file_rx = watch_config_file(
|
||||
&app.background_executor(),
|
||||
fs.clone(),
|
||||
paths::SETTINGS.clone(),
|
||||
);
|
||||
let user_keymap_file_rx = watch_config_file(
|
||||
&app.background_executor(),
|
||||
fs.clone(),
|
||||
paths::KEYMAP.clone(),
|
||||
);
|
||||
|
||||
let login_shell_env_loaded = if stdout_is_a_pty() {
|
||||
Task::ready(())
|
||||
} else {
|
||||
app.background().spawn(async {
|
||||
app.background_executor().spawn(async {
|
||||
load_login_shell_environment().await.log_err();
|
||||
})
|
||||
};
|
||||
|
||||
let (listener, mut open_rx) = OpenListener::new();
|
||||
let listener = Arc::new(listener);
|
||||
let callback_listener = listener.clone();
|
||||
app.on_open_urls(move |urls, _| callback_listener.open_urls(urls))
|
||||
.on_reopen(move |cx| {
|
||||
if cx.has_global::<Weak<AppState>>() {
|
||||
if let Some(app_state) = cx.global::<Weak<AppState>>().upgrade() {
|
||||
workspace::open_new(&app_state, cx, |workspace, cx| {
|
||||
Editor::new_file(workspace, &Default::default(), cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
let open_listener = listener.clone();
|
||||
app.on_open_urls(move |urls, _| open_listener.open_urls(&urls));
|
||||
app.on_reopen(move |cx| {
|
||||
if cx.has_global::<Weak<AppState>>() {
|
||||
if let Some(app_state) = cx.global::<Weak<AppState>>().upgrade() {
|
||||
workspace::open_new(&app_state, cx, |workspace, cx| {
|
||||
Editor::new_file(workspace, &Default::default(), cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.run(move |cx| {
|
||||
cx.set_global(*RELEASE_CHANNEL);
|
||||
if let Some(build_sha) = option_env!("ZED_COMMIT_SHA") {
|
||||
cx.set_global(AppCommitSha(build_sha.into()))
|
||||
}
|
||||
|
||||
cx.set_global(listener.clone());
|
||||
|
||||
load_embedded_fonts(cx);
|
||||
|
||||
let mut store = SettingsStore::default();
|
||||
store
|
||||
.set_default_settings(default_settings().as_ref(), cx)
|
||||
@ -116,35 +132,25 @@ fn main() {
|
||||
let client = client::Client::new(http.clone(), cx);
|
||||
let mut languages = LanguageRegistry::new(login_shell_env_loaded);
|
||||
let copilot_language_server_id = languages.next_language_server_id();
|
||||
languages.set_executor(cx.background().clone());
|
||||
languages.set_executor(cx.background_executor().clone());
|
||||
languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone());
|
||||
let languages = Arc::new(languages);
|
||||
let node_runtime = RealNodeRuntime::new(http.clone());
|
||||
|
||||
language::init(cx);
|
||||
languages::init(languages.clone(), node_runtime.clone(), cx);
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
|
||||
let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx));
|
||||
let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
|
||||
let workspace_store = cx.new_model(|cx| WorkspaceStore::new(client.clone(), cx));
|
||||
|
||||
cx.set_global(client.clone());
|
||||
|
||||
theme::init(Assets, cx);
|
||||
context_menu::init(cx);
|
||||
theme::init(theme::LoadThemes::All, cx);
|
||||
project::Project::init(&client, cx);
|
||||
client::init(&client, cx);
|
||||
command_palette::init(cx);
|
||||
language::init(cx);
|
||||
editor::init(cx);
|
||||
go_to_line::init(cx);
|
||||
file_finder::init(cx);
|
||||
outline::init(cx);
|
||||
project_symbols::init(cx);
|
||||
project_panel::init(Assets, cx);
|
||||
channel::init(&client, user_store.clone(), cx);
|
||||
diagnostics::init(cx);
|
||||
search::init(cx);
|
||||
semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx);
|
||||
vim::init(cx);
|
||||
terminal_view::init(cx);
|
||||
copilot::init(
|
||||
copilot_language_server_id,
|
||||
http.clone(),
|
||||
@ -152,26 +158,25 @@ fn main() {
|
||||
cx,
|
||||
);
|
||||
assistant::init(cx);
|
||||
component_test::init(cx);
|
||||
// component_test::init(cx);
|
||||
|
||||
cx.spawn(|cx| watch_themes(fs.clone(), cx)).detach();
|
||||
cx.spawn(|_| watch_languages(fs.clone(), languages.clone()))
|
||||
.detach();
|
||||
watch_file_types(fs.clone(), cx);
|
||||
|
||||
languages.set_theme(theme::current(cx).clone());
|
||||
cx.observe_global::<SettingsStore, _>({
|
||||
languages.set_theme(cx.theme().clone());
|
||||
cx.observe_global::<SettingsStore>({
|
||||
let languages = languages.clone();
|
||||
move |cx| languages.set_theme(theme::current(cx).clone())
|
||||
move |cx| languages.set_theme(cx.theme().clone())
|
||||
})
|
||||
.detach();
|
||||
|
||||
client.telemetry().start(installation_id, session_id, cx);
|
||||
let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
|
||||
let telemetry_settings = *client::TelemetrySettings::get_global(cx);
|
||||
client.telemetry().report_setting_event(
|
||||
telemetry_settings,
|
||||
"theme",
|
||||
theme::current(cx).meta.name.to_string(),
|
||||
cx.theme().name.to_string(),
|
||||
);
|
||||
let event_operation = match existing_installation_id_found {
|
||||
Some(false) => "first open",
|
||||
@ -182,13 +187,11 @@ fn main() {
|
||||
.report_app_event(telemetry_settings, event_operation, true);
|
||||
|
||||
let app_state = Arc::new(AppState {
|
||||
languages,
|
||||
languages: languages.clone(),
|
||||
client: client.clone(),
|
||||
user_store,
|
||||
fs,
|
||||
user_store: user_store.clone(),
|
||||
fs: fs.clone(),
|
||||
build_window_options,
|
||||
initialize_workspace,
|
||||
background_actions,
|
||||
workspace_store,
|
||||
node_runtime,
|
||||
});
|
||||
@ -200,25 +203,35 @@ fn main() {
|
||||
workspace::init(app_state.clone(), cx);
|
||||
recent_projects::init(cx);
|
||||
|
||||
go_to_line::init(cx);
|
||||
file_finder::init(cx);
|
||||
outline::init(cx);
|
||||
project_symbols::init(cx);
|
||||
project_panel::init(Assets, cx);
|
||||
channel::init(&client, user_store.clone(), cx);
|
||||
search::init(cx);
|
||||
semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx);
|
||||
vim::init(cx);
|
||||
terminal_view::init(cx);
|
||||
|
||||
journal::init(app_state.clone(), cx);
|
||||
language_selector::init(cx);
|
||||
theme_selector::init(cx);
|
||||
activity_indicator::init(cx);
|
||||
language_tools::init(cx);
|
||||
call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
||||
notifications::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
||||
collab_ui::init(&app_state, cx);
|
||||
feedback::init(cx);
|
||||
welcome::init(cx);
|
||||
zed::init(&app_state, cx);
|
||||
|
||||
cx.set_menus(menus::menus());
|
||||
cx.set_menus(app_menus());
|
||||
initialize_workspace(app_state.clone(), cx);
|
||||
|
||||
if stdout_is_a_pty() {
|
||||
cx.platform().activate(true);
|
||||
cx.activate(true);
|
||||
let urls = collect_url_args();
|
||||
if !urls.is_empty() {
|
||||
listener.open_urls(urls)
|
||||
listener.open_urls(&urls)
|
||||
}
|
||||
} else {
|
||||
upload_previous_panics(http.clone(), cx);
|
||||
@ -228,32 +241,51 @@ fn main() {
|
||||
if std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_some()
|
||||
&& !listener.triggered.load(Ordering::Acquire)
|
||||
{
|
||||
listener.open_urls(collect_url_args())
|
||||
listener.open_urls(&collect_url_args())
|
||||
}
|
||||
}
|
||||
|
||||
let mut triggered_authentication = false;
|
||||
|
||||
fn open_paths_and_log_errs(
|
||||
paths: &[PathBuf],
|
||||
app_state: &Arc<AppState>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
let task = workspace::open_paths(&paths, &app_state, None, cx);
|
||||
cx.spawn(|_| async move {
|
||||
if let Some((_window, results)) = task.await.log_err() {
|
||||
for result in results {
|
||||
if let Some(Err(e)) = result {
|
||||
log::error!("Error opening path: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
match open_rx.try_next() {
|
||||
Ok(Some(OpenRequest::Paths { paths })) => {
|
||||
cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
|
||||
.detach();
|
||||
open_paths_and_log_errs(&paths, &app_state, cx)
|
||||
}
|
||||
Ok(Some(OpenRequest::CliConnection { connection })) => {
|
||||
cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
|
||||
let app_state = app_state.clone();
|
||||
cx.spawn(move |cx| handle_cli_connection(connection, app_state, cx))
|
||||
.detach();
|
||||
}
|
||||
Ok(Some(OpenRequest::JoinChannel { channel_id })) => {
|
||||
triggered_authentication = true;
|
||||
let app_state = app_state.clone();
|
||||
let client = client.clone();
|
||||
cx.spawn(|mut cx| async move {
|
||||
cx.spawn(|cx| async move {
|
||||
// ignore errors here, we'll show a generic "not signed in"
|
||||
let _ = authenticate(client, &cx).await;
|
||||
cx.update(|cx| workspace::join_channel(channel_id, app_state, None, cx))
|
||||
.await
|
||||
cx.update(|cx| workspace::join_channel(channel_id, app_state, None, cx))?
|
||||
.await?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx)
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
Ok(Some(OpenRequest::OpenChannelNotes { channel_id })) => {
|
||||
triggered_authentication = true;
|
||||
@ -262,12 +294,16 @@ fn main() {
|
||||
cx.spawn(|mut cx| async move {
|
||||
// ignore errors here, we'll show a generic "not signed in"
|
||||
let _ = authenticate(client, &cx).await;
|
||||
let workspace =
|
||||
let workspace_window =
|
||||
workspace::get_any_active_workspace(app_state, cx.clone()).await?;
|
||||
cx.update(|cx| ChannelView::open(channel_id, workspace, cx))
|
||||
.await
|
||||
let _ = workspace_window
|
||||
.update(&mut cx, |_, cx| {
|
||||
ChannelView::open(channel_id, cx.view().clone(), cx)
|
||||
})?
|
||||
.await?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx)
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
Ok(None) | Err(_) => cx
|
||||
.spawn({
|
||||
@ -277,36 +313,49 @@ fn main() {
|
||||
.detach(),
|
||||
}
|
||||
|
||||
cx.spawn(|mut cx| {
|
||||
let app_state = app_state.clone();
|
||||
async move {
|
||||
while let Some(request) = open_rx.next().await {
|
||||
match request {
|
||||
OpenRequest::Paths { paths } => {
|
||||
cx.update(|cx| {
|
||||
workspace::open_paths(&paths, &app_state.clone(), None, cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
OpenRequest::CliConnection { connection } => {
|
||||
cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
|
||||
.detach();
|
||||
}
|
||||
OpenRequest::JoinChannel { channel_id } => cx
|
||||
.update(|cx| {
|
||||
workspace::join_channel(channel_id, app_state.clone(), None, cx)
|
||||
})
|
||||
.detach(),
|
||||
OpenRequest::OpenChannelNotes { channel_id } => {
|
||||
let app_state = app_state.clone();
|
||||
if let Ok(workspace) =
|
||||
workspace::get_any_active_workspace(app_state, cx.clone()).await
|
||||
{
|
||||
let app_state = app_state.clone();
|
||||
cx.spawn(move |cx| async move {
|
||||
while let Some(request) = open_rx.next().await {
|
||||
match request {
|
||||
OpenRequest::Paths { paths } => {
|
||||
cx.update(|cx| open_paths_and_log_errs(&paths, &app_state, cx))
|
||||
.ok();
|
||||
}
|
||||
OpenRequest::CliConnection { connection } => {
|
||||
let app_state = app_state.clone();
|
||||
cx.spawn(move |cx| {
|
||||
handle_cli_connection(connection, app_state.clone(), cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
OpenRequest::JoinChannel { channel_id } => {
|
||||
let app_state = app_state.clone();
|
||||
cx.update(|mut cx| {
|
||||
cx.spawn(|cx| async move {
|
||||
cx.update(|cx| {
|
||||
ChannelView::open(channel_id, workspace, cx).detach();
|
||||
})
|
||||
}
|
||||
}
|
||||
workspace::join_channel(channel_id, app_state, None, cx)
|
||||
})?
|
||||
.await?;
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(&mut cx);
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
OpenRequest::OpenChannelNotes { channel_id } => {
|
||||
let app_state = app_state.clone();
|
||||
let open_notes_task = cx.spawn(|mut cx| async move {
|
||||
let workspace_window =
|
||||
workspace::get_any_active_workspace(app_state, cx.clone()).await?;
|
||||
let _ = workspace_window
|
||||
.update(&mut cx, |_, cx| {
|
||||
ChannelView::open(channel_id, cx.view().clone(), cx)
|
||||
})?
|
||||
.await?;
|
||||
anyhow::Ok(())
|
||||
});
|
||||
cx.update(|cx| open_notes_task.detach_and_log_err(cx))
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -357,21 +406,26 @@ async fn installation_id() -> Result<(String, bool)> {
|
||||
Ok((installation_id, false))
|
||||
}
|
||||
|
||||
async fn restore_or_create_workspace(app_state: &Arc<AppState>, mut cx: AsyncAppContext) {
|
||||
if let Some(location) = workspace::last_opened_workspace_paths().await {
|
||||
cx.update(|cx| workspace::open_paths(location.paths().as_ref(), app_state, None, cx))
|
||||
.await
|
||||
.log_err();
|
||||
} else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) {
|
||||
cx.update(|cx| show_welcome_experience(app_state, cx));
|
||||
} else {
|
||||
cx.update(|cx| {
|
||||
workspace::open_new(app_state, cx, |workspace, cx| {
|
||||
Editor::new_file(workspace, &Default::default(), cx)
|
||||
})
|
||||
.detach();
|
||||
});
|
||||
}
|
||||
async fn restore_or_create_workspace(app_state: &Arc<AppState>, cx: AsyncAppContext) {
|
||||
async_maybe!({
|
||||
if let Some(location) = workspace::last_opened_workspace_paths().await {
|
||||
cx.update(|cx| workspace::open_paths(location.paths().as_ref(), app_state, None, cx))?
|
||||
.await
|
||||
.log_err();
|
||||
} else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) {
|
||||
cx.update(|cx| show_welcome_view(app_state, cx)).log_err();
|
||||
} else {
|
||||
cx.update(|cx| {
|
||||
workspace::open_new(app_state, cx, |workspace, cx| {
|
||||
Editor::new_file(workspace, &Default::default(), cx)
|
||||
})
|
||||
.detach();
|
||||
})?;
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
|
||||
fn init_paths() {
|
||||
@ -444,7 +498,7 @@ static PANIC_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: String) {
|
||||
let is_pty = stdout_is_a_pty();
|
||||
let platform = app.platform();
|
||||
let app_metadata = app.metadata();
|
||||
|
||||
panic::set_hook(Box::new(move |info| {
|
||||
let prior_panic_count = PANIC_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||
@ -480,8 +534,8 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
let app_version = ZED_APP_VERSION
|
||||
.or_else(|| platform.app_version().ok())
|
||||
let app_version = client::ZED_APP_VERSION
|
||||
.or(app_metadata.app_version)
|
||||
.map_or("dev".to_string(), |v| v.to_string());
|
||||
|
||||
let backtrace = Backtrace::new();
|
||||
@ -508,11 +562,11 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
|
||||
}),
|
||||
app_version: app_version.clone(),
|
||||
release_channel: RELEASE_CHANNEL.display_name().into(),
|
||||
os_name: platform.os_name().into(),
|
||||
os_version: platform
|
||||
.os_version()
|
||||
.ok()
|
||||
.map(|os_version| os_version.to_string()),
|
||||
os_name: app_metadata.os_name.into(),
|
||||
os_version: app_metadata
|
||||
.os_version
|
||||
.as_ref()
|
||||
.map(SemanticVersion::to_string),
|
||||
architecture: env::consts::ARCH.into(),
|
||||
panicked_on: Utc::now().timestamp_millis(),
|
||||
backtrace,
|
||||
@ -545,83 +599,76 @@ fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: Strin
|
||||
}
|
||||
|
||||
fn upload_previous_panics(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
|
||||
let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
|
||||
let telemetry_settings = *client::TelemetrySettings::get_global(cx);
|
||||
|
||||
cx.background()
|
||||
.spawn({
|
||||
async move {
|
||||
let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL);
|
||||
let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?;
|
||||
while let Some(child) = children.next().await {
|
||||
let child = child?;
|
||||
let child_path = child.path();
|
||||
cx.background_executor()
|
||||
.spawn(async move {
|
||||
let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL);
|
||||
let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?;
|
||||
while let Some(child) = children.next().await {
|
||||
let child = child?;
|
||||
let child_path = child.path();
|
||||
|
||||
if child_path.extension() != Some(OsStr::new("panic")) {
|
||||
continue;
|
||||
}
|
||||
let filename = if let Some(filename) = child_path.file_name() {
|
||||
filename.to_string_lossy()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
if child_path.extension() != Some(OsStr::new("panic")) {
|
||||
continue;
|
||||
}
|
||||
let filename = if let Some(filename) = child_path.file_name() {
|
||||
filename.to_string_lossy()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if !filename.starts_with("zed") {
|
||||
continue;
|
||||
}
|
||||
if !filename.starts_with("zed") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if telemetry_settings.diagnostics {
|
||||
let panic_file_content = smol::fs::read_to_string(&child_path)
|
||||
.await
|
||||
.context("error reading panic file")?;
|
||||
if telemetry_settings.diagnostics {
|
||||
let panic_file_content = smol::fs::read_to_string(&child_path)
|
||||
.await
|
||||
.context("error reading panic file")?;
|
||||
|
||||
let panic = serde_json::from_str(&panic_file_content)
|
||||
.ok()
|
||||
.or_else(|| {
|
||||
let panic = serde_json::from_str(&panic_file_content)
|
||||
.ok()
|
||||
.or_else(|| {
|
||||
panic_file_content
|
||||
.lines()
|
||||
.next()
|
||||
.and_then(|line| serde_json::from_str(line).ok())
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
log::error!(
|
||||
"failed to deserialize panic file {:?}",
|
||||
panic_file_content
|
||||
.lines()
|
||||
.next()
|
||||
.and_then(|line| serde_json::from_str(line).ok())
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
log::error!(
|
||||
"failed to deserialize panic file {:?}",
|
||||
panic_file_content
|
||||
);
|
||||
None
|
||||
});
|
||||
);
|
||||
None
|
||||
});
|
||||
|
||||
if let Some(panic) = panic {
|
||||
let body = serde_json::to_string(&PanicRequest {
|
||||
panic,
|
||||
token: ZED_SECRET_CLIENT_TOKEN.into(),
|
||||
})
|
||||
.unwrap();
|
||||
if let Some(panic) = panic {
|
||||
let body = serde_json::to_string(&PanicRequest {
|
||||
panic,
|
||||
token: client::ZED_SECRET_CLIENT_TOKEN.into(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let request = Request::post(&panic_report_url)
|
||||
.redirect_policy(isahc::config::RedirectPolicy::Follow)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(body.into())?;
|
||||
let response =
|
||||
http.send(request).await.context("error sending panic")?;
|
||||
if !response.status().is_success() {
|
||||
log::error!(
|
||||
"Error uploading panic to server: {}",
|
||||
response.status()
|
||||
);
|
||||
}
|
||||
let request = Request::post(&panic_report_url)
|
||||
.redirect_policy(isahc::config::RedirectPolicy::Follow)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(body.into())?;
|
||||
let response = http.send(request).await.context("error sending panic")?;
|
||||
if !response.status().is_success() {
|
||||
log::error!("Error uploading panic to server: {}", response.status());
|
||||
}
|
||||
}
|
||||
|
||||
// We've done what we can, delete the file
|
||||
std::fs::remove_file(child_path)
|
||||
.context("error removing panic")
|
||||
.log_err();
|
||||
}
|
||||
Ok::<_, anyhow::Error>(())
|
||||
|
||||
// We've done what we can, delete the file
|
||||
std::fs::remove_file(child_path)
|
||||
.context("error removing panic")
|
||||
.log_err();
|
||||
}
|
||||
.log_err()
|
||||
Ok::<_, anyhow::Error>(())
|
||||
})
|
||||
.detach();
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
async fn load_login_shell_environment() -> Result<()> {
|
||||
@ -680,58 +727,38 @@ fn collect_url_args() -> Vec<String> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn load_embedded_fonts(app: &App) {
|
||||
let font_paths = Assets.list("fonts");
|
||||
fn load_embedded_fonts(cx: &AppContext) {
|
||||
let asset_source = cx.asset_source();
|
||||
let font_paths = asset_source.list("fonts").unwrap();
|
||||
let embedded_fonts = Mutex::new(Vec::new());
|
||||
smol::block_on(app.background().scoped(|scope| {
|
||||
let executor = cx.background_executor();
|
||||
|
||||
executor.block(executor.scoped(|scope| {
|
||||
for font_path in &font_paths {
|
||||
if !font_path.ends_with(".ttf") {
|
||||
continue;
|
||||
}
|
||||
|
||||
scope.spawn(async {
|
||||
let font_path = &*font_path;
|
||||
let font_bytes = Assets.load(font_path).unwrap().to_vec();
|
||||
let font_bytes = asset_source.load(font_path).unwrap().to_vec();
|
||||
embedded_fonts.lock().push(Arc::from(font_bytes));
|
||||
});
|
||||
}
|
||||
}));
|
||||
app.platform()
|
||||
.fonts()
|
||||
|
||||
cx.text_system()
|
||||
.add_fonts(&embedded_fonts.into_inner())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
async fn watch_themes(fs: Arc<dyn Fs>, mut cx: AsyncAppContext) -> Option<()> {
|
||||
let mut events = fs
|
||||
.watch("styles/src".as_ref(), std::time::Duration::from_millis(100))
|
||||
.await;
|
||||
while (events.next().await).is_some() {
|
||||
let output = Command::new("npm")
|
||||
.current_dir("styles")
|
||||
.args(["run", "build"])
|
||||
.output()
|
||||
.await
|
||||
.log_err()?;
|
||||
if output.status.success() {
|
||||
cx.update(|cx| theme_selector::reload(cx))
|
||||
} else {
|
||||
eprintln!(
|
||||
"build script failed {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
async fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>) -> Option<()> {
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
async fn watch_languages(fs: Arc<dyn Fs>, languages: Arc<LanguageRegistry>) -> Option<()> {
|
||||
let mut events = fs
|
||||
.watch(
|
||||
"crates/zed/src/languages".as_ref(),
|
||||
std::time::Duration::from_millis(100),
|
||||
"crates/zed2/src/languages".as_ref(),
|
||||
Duration::from_millis(100),
|
||||
)
|
||||
.await;
|
||||
while (events.next().await).is_some() {
|
||||
@ -741,12 +768,14 @@ async fn watch_languages(fs: Arc<dyn Fs>, languages: Arc<LanguageRegistry>) -> O
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn watch_file_types(fs: Arc<dyn Fs>, cx: &mut AppContext) {
|
||||
cx.spawn(|mut cx| async move {
|
||||
fn watch_file_types(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
|
||||
use std::time::Duration;
|
||||
|
||||
cx.spawn(|cx| async move {
|
||||
let mut events = fs
|
||||
.watch(
|
||||
"assets/icons/file_icons/file_types.json".as_ref(),
|
||||
std::time::Duration::from_millis(100),
|
||||
Duration::from_millis(100),
|
||||
)
|
||||
.await;
|
||||
while (events.next().await).is_some() {
|
||||
@ -755,29 +784,16 @@ fn watch_file_types(fs: Arc<dyn Fs>, cx: &mut AppContext) {
|
||||
*file_types = project_panel::file_associations::FileAssociations::new(Assets);
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
async fn watch_themes(_fs: Arc<dyn Fs>, _cx: AsyncAppContext) -> Option<()> {
|
||||
async fn watch_languages(_: Arc<dyn fs::Fs>, _: Arc<LanguageRegistry>) -> Option<()> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
async fn watch_languages(_: Arc<dyn Fs>, _: Arc<LanguageRegistry>) -> Option<()> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn watch_file_types(_fs: Arc<dyn Fs>, _cx: &mut AppContext) {}
|
||||
|
||||
pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] {
|
||||
&[
|
||||
("Go to file", &file_finder::Toggle),
|
||||
("Open command palette", &command_palette::Toggle),
|
||||
("Open recent projects", &recent_projects::OpenRecent),
|
||||
("Change your settings", &zed_actions::OpenSettings),
|
||||
]
|
||||
}
|
||||
fn watch_file_types(_fs: Arc<dyn fs::Fs>, _cx: &mut AppContext) {}
|
||||
|
@ -1,174 +0,0 @@
|
||||
use gpui::{Menu, MenuItem, OsAction};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn menus() -> Vec<Menu<'static>> {
|
||||
vec![
|
||||
Menu {
|
||||
name: "Zed",
|
||||
items: vec![
|
||||
MenuItem::action("About Zed…", super::About),
|
||||
MenuItem::action("Check for Updates", auto_update::Check),
|
||||
MenuItem::separator(),
|
||||
MenuItem::submenu(Menu {
|
||||
name: "Preferences",
|
||||
items: vec![
|
||||
MenuItem::action("Open Settings", super::OpenSettings),
|
||||
MenuItem::action("Open Key Bindings", super::OpenKeymap),
|
||||
MenuItem::action("Open Default Settings", super::OpenDefaultSettings),
|
||||
MenuItem::action("Open Default Key Bindings", super::OpenDefaultKeymap),
|
||||
MenuItem::action("Open Local Settings", super::OpenLocalSettings),
|
||||
MenuItem::action("Select Theme", theme_selector::Toggle),
|
||||
],
|
||||
}),
|
||||
MenuItem::action("Install CLI", install_cli::Install),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Hide Zed", super::Hide),
|
||||
MenuItem::action("Hide Others", super::HideOthers),
|
||||
MenuItem::action("Show All", super::ShowAll),
|
||||
MenuItem::action("Quit", super::Quit),
|
||||
],
|
||||
},
|
||||
Menu {
|
||||
name: "File",
|
||||
items: vec![
|
||||
MenuItem::action("New", workspace::NewFile),
|
||||
MenuItem::action("New Window", workspace::NewWindow),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Open…", workspace::Open),
|
||||
MenuItem::action("Open Recent...", recent_projects::OpenRecent),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Add Folder to Project…", workspace::AddFolderToProject),
|
||||
MenuItem::action("Save", workspace::Save { save_intent: None }),
|
||||
MenuItem::action("Save As…", workspace::SaveAs),
|
||||
MenuItem::action("Save All", workspace::SaveAll { save_intent: None }),
|
||||
MenuItem::action(
|
||||
"Close Editor",
|
||||
workspace::CloseActiveItem { save_intent: None },
|
||||
),
|
||||
MenuItem::action("Close Window", workspace::CloseWindow),
|
||||
],
|
||||
},
|
||||
Menu {
|
||||
name: "Edit",
|
||||
items: vec![
|
||||
MenuItem::os_action("Undo", editor::Undo, OsAction::Undo),
|
||||
MenuItem::os_action("Redo", editor::Redo, OsAction::Redo),
|
||||
MenuItem::separator(),
|
||||
MenuItem::os_action("Cut", editor::Cut, OsAction::Cut),
|
||||
MenuItem::os_action("Copy", editor::Copy, OsAction::Copy),
|
||||
MenuItem::os_action("Paste", editor::Paste, OsAction::Paste),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Find", search::buffer_search::Deploy { focus: true }),
|
||||
MenuItem::action("Find In Project", workspace::NewSearch),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Toggle Line Comment", editor::ToggleComments::default()),
|
||||
MenuItem::action("Emoji & Symbols", editor::ShowCharacterPalette),
|
||||
],
|
||||
},
|
||||
Menu {
|
||||
name: "Selection",
|
||||
items: vec![
|
||||
MenuItem::os_action("Select All", editor::SelectAll, OsAction::SelectAll),
|
||||
MenuItem::action("Expand Selection", editor::SelectLargerSyntaxNode),
|
||||
MenuItem::action("Shrink Selection", editor::SelectSmallerSyntaxNode),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Add Cursor Above", editor::AddSelectionAbove),
|
||||
MenuItem::action("Add Cursor Below", editor::AddSelectionBelow),
|
||||
MenuItem::action(
|
||||
"Select Next Occurrence",
|
||||
editor::SelectNext {
|
||||
replace_newest: false,
|
||||
},
|
||||
),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Move Line Up", editor::MoveLineUp),
|
||||
MenuItem::action("Move Line Down", editor::MoveLineDown),
|
||||
MenuItem::action("Duplicate Selection", editor::DuplicateLine),
|
||||
],
|
||||
},
|
||||
Menu {
|
||||
name: "View",
|
||||
items: vec![
|
||||
MenuItem::action("Zoom In", super::IncreaseBufferFontSize),
|
||||
MenuItem::action("Zoom Out", super::DecreaseBufferFontSize),
|
||||
MenuItem::action("Reset Zoom", super::ResetBufferFontSize),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Toggle Left Dock", workspace::ToggleLeftDock),
|
||||
MenuItem::action("Toggle Right Dock", workspace::ToggleRightDock),
|
||||
MenuItem::action("Toggle Bottom Dock", workspace::ToggleBottomDock),
|
||||
MenuItem::action("Close All Docks", workspace::CloseAllDocks),
|
||||
MenuItem::submenu(Menu {
|
||||
name: "Editor Layout",
|
||||
items: vec![
|
||||
MenuItem::action("Split Up", workspace::SplitUp),
|
||||
MenuItem::action("Split Down", workspace::SplitDown),
|
||||
MenuItem::action("Split Left", workspace::SplitLeft),
|
||||
MenuItem::action("Split Right", workspace::SplitRight),
|
||||
],
|
||||
}),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Project Panel", project_panel::ToggleFocus),
|
||||
MenuItem::action("Command Palette", command_palette::Toggle),
|
||||
MenuItem::action("Diagnostics", diagnostics::Deploy),
|
||||
MenuItem::separator(),
|
||||
],
|
||||
},
|
||||
Menu {
|
||||
name: "Go",
|
||||
items: vec![
|
||||
MenuItem::action("Back", workspace::GoBack),
|
||||
MenuItem::action("Forward", workspace::GoForward),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Go to File", file_finder::Toggle),
|
||||
MenuItem::action("Go to Symbol in Project", project_symbols::Toggle),
|
||||
MenuItem::action("Go to Symbol in Editor", outline::Toggle),
|
||||
MenuItem::action("Go to Definition", editor::GoToDefinition),
|
||||
MenuItem::action("Go to Type Definition", editor::GoToTypeDefinition),
|
||||
MenuItem::action("Find All References", editor::FindAllReferences),
|
||||
MenuItem::action("Go to Line/Column", go_to_line::Toggle),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Next Problem", editor::GoToDiagnostic),
|
||||
MenuItem::action("Previous Problem", editor::GoToPrevDiagnostic),
|
||||
],
|
||||
},
|
||||
Menu {
|
||||
name: "Window",
|
||||
items: vec![
|
||||
MenuItem::action("Minimize", super::Minimize),
|
||||
MenuItem::action("Zoom", super::Zoom),
|
||||
MenuItem::separator(),
|
||||
],
|
||||
},
|
||||
Menu {
|
||||
name: "Help",
|
||||
items: vec![
|
||||
MenuItem::action("Command Palette", command_palette::Toggle),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("View Telemetry", crate::OpenTelemetryLog),
|
||||
MenuItem::action("View Dependency Licenses", crate::OpenLicenses),
|
||||
MenuItem::action("Show Welcome", workspace::Welcome),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Give us feedback", feedback::feedback_editor::GiveFeedback),
|
||||
MenuItem::action(
|
||||
"Copy System Specs Into Clipboard",
|
||||
feedback::CopySystemSpecsIntoClipboard,
|
||||
),
|
||||
MenuItem::action("File Bug Report", feedback::FileBugReport),
|
||||
MenuItem::action("Request Feature", feedback::RequestFeature),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action(
|
||||
"Documentation",
|
||||
crate::OpenBrowser {
|
||||
url: "https://zed.dev/docs".into(),
|
||||
},
|
||||
),
|
||||
MenuItem::action(
|
||||
"Zed Twitter",
|
||||
crate::OpenBrowser {
|
||||
url: "https://twitter.com/zeddotdev".into(),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
@ -54,7 +54,7 @@ impl OpenListener {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn open_urls(&self, urls: Vec<String>) {
|
||||
pub fn open_urls(&self, urls: &[String]) {
|
||||
self.triggered.store(true, Ordering::Release);
|
||||
let request = if let Some(server_name) =
|
||||
urls.first().and_then(|url| url.strip_prefix("zed-cli://"))
|
||||
@ -101,7 +101,7 @@ impl OpenListener {
|
||||
None
|
||||
}
|
||||
|
||||
fn handle_file_urls(&self, urls: Vec<String>) -> Option<OpenRequest> {
|
||||
fn handle_file_urls(&self, urls: &[String]) -> Option<OpenRequest> {
|
||||
let paths: Vec<_> = urls
|
||||
.iter()
|
||||
.flat_map(|url| url.strip_prefix("file://"))
|
||||
@ -187,105 +187,109 @@ pub async fn handle_cli_connection(
|
||||
};
|
||||
|
||||
let mut errored = false;
|
||||
match cx
|
||||
.update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
|
||||
.await
|
||||
{
|
||||
Ok((workspace, items)) => {
|
||||
let mut item_release_futures = Vec::new();
|
||||
|
||||
for (item, path) in items.into_iter().zip(&paths) {
|
||||
match item {
|
||||
Some(Ok(item)) => {
|
||||
if let Some(point) = caret_positions.remove(path) {
|
||||
if let Some(active_editor) = item.downcast::<Editor>() {
|
||||
active_editor
|
||||
.downgrade()
|
||||
.update(&mut cx, |editor, cx| {
|
||||
let snapshot =
|
||||
editor.snapshot(cx).display_snapshot;
|
||||
let point = snapshot
|
||||
.buffer_snapshot
|
||||
.clip_point(point, Bias::Left);
|
||||
editor.change_selections(
|
||||
Some(Autoscroll::center()),
|
||||
cx,
|
||||
|s| s.select_ranges([point..point]),
|
||||
);
|
||||
})
|
||||
.log_err();
|
||||
match cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx)) {
|
||||
Ok(task) => match task.await {
|
||||
Ok((workspace, items)) => {
|
||||
let mut item_release_futures = Vec::new();
|
||||
|
||||
for (item, path) in items.into_iter().zip(&paths) {
|
||||
match item {
|
||||
Some(Ok(item)) => {
|
||||
if let Some(point) = caret_positions.remove(path) {
|
||||
if let Some(active_editor) = item.downcast::<Editor>() {
|
||||
workspace
|
||||
.update(&mut cx, |_, cx| {
|
||||
active_editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor
|
||||
.snapshot(cx)
|
||||
.display_snapshot;
|
||||
let point = snapshot
|
||||
.buffer_snapshot
|
||||
.clip_point(point, Bias::Left);
|
||||
editor.change_selections(
|
||||
Some(Autoscroll::center()),
|
||||
cx,
|
||||
|s| s.select_ranges([point..point]),
|
||||
);
|
||||
});
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let released = oneshot::channel();
|
||||
cx.update(|cx| {
|
||||
item.on_release(
|
||||
cx,
|
||||
Box::new(move |_| {
|
||||
let _ = released.0.send(());
|
||||
}),
|
||||
)
|
||||
.detach();
|
||||
});
|
||||
item_release_futures.push(released.1);
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
responses
|
||||
.send(CliResponse::Stderr {
|
||||
message: format!("error opening {:?}: {}", path, err),
|
||||
cx.update(|cx| {
|
||||
let released = oneshot::channel();
|
||||
item.on_release(
|
||||
cx,
|
||||
Box::new(move |_| {
|
||||
let _ = released.0.send(());
|
||||
}),
|
||||
)
|
||||
.detach();
|
||||
item_release_futures.push(released.1);
|
||||
})
|
||||
.log_err();
|
||||
errored = true;
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
responses
|
||||
.send(CliResponse::Stderr {
|
||||
message: format!(
|
||||
"error opening {:?}: {}",
|
||||
path, err
|
||||
),
|
||||
})
|
||||
.log_err();
|
||||
errored = true;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
if wait {
|
||||
let background = cx.background();
|
||||
let wait = async move {
|
||||
if paths.is_empty() {
|
||||
let (done_tx, done_rx) = oneshot::channel();
|
||||
if let Some(workspace) = workspace.upgrade(&cx) {
|
||||
let _subscription = cx.update(|cx| {
|
||||
cx.observe_release(&workspace, move |_, _| {
|
||||
if wait {
|
||||
let background = cx.background_executor().clone();
|
||||
let wait = async move {
|
||||
if paths.is_empty() {
|
||||
let (done_tx, done_rx) = oneshot::channel();
|
||||
let _subscription = workspace.update(&mut cx, |_, cx| {
|
||||
cx.on_release(move |_, _, _| {
|
||||
let _ = done_tx.send(());
|
||||
})
|
||||
});
|
||||
drop(workspace);
|
||||
let _ = done_rx.await;
|
||||
}
|
||||
} else {
|
||||
let _ =
|
||||
futures::future::try_join_all(item_release_futures).await;
|
||||
};
|
||||
}
|
||||
.fuse();
|
||||
futures::pin_mut!(wait);
|
||||
} else {
|
||||
let _ = futures::future::try_join_all(item_release_futures)
|
||||
.await;
|
||||
};
|
||||
}
|
||||
.fuse();
|
||||
futures::pin_mut!(wait);
|
||||
|
||||
loop {
|
||||
// Repeatedly check if CLI is still open to avoid wasting resources
|
||||
// waiting for files or workspaces to close.
|
||||
let mut timer = background.timer(Duration::from_secs(1)).fuse();
|
||||
futures::select_biased! {
|
||||
_ = wait => break,
|
||||
_ = timer => {
|
||||
if responses.send(CliResponse::Ping).is_err() {
|
||||
break;
|
||||
loop {
|
||||
// Repeatedly check if CLI is still open to avoid wasting resources
|
||||
// waiting for files or workspaces to close.
|
||||
let mut timer = background.timer(Duration::from_secs(1)).fuse();
|
||||
futures::select_biased! {
|
||||
_ = wait => break,
|
||||
_ = timer => {
|
||||
if responses.send(CliResponse::Ping).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
errored = true;
|
||||
responses
|
||||
.send(CliResponse::Stderr {
|
||||
message: format!("error opening {:?}: {}", paths, error),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
Err(error) => {
|
||||
errored = true;
|
||||
responses
|
||||
.send(CliResponse::Stderr {
|
||||
message: format!("error opening {:?}: {}", paths, error),
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
},
|
||||
Err(_) => errored = true,
|
||||
}
|
||||
|
||||
responses
|
||||
|
@ -1,7 +0,0 @@
|
||||
#[cfg(test)]
|
||||
#[ctor::ctor]
|
||||
fn init_logger() {
|
||||
if std::env::var("RUST_LOG").is_ok() {
|
||||
env_logger::init();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,192 +0,0 @@
|
||||
[package]
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed2"
|
||||
version = "2.0.0"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
name = "zed2"
|
||||
path = "src/zed2.rs"
|
||||
doctest = false
|
||||
|
||||
[[bin]]
|
||||
name = "zed2"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
ai = { package = "ai2", path = "../ai2"}
|
||||
audio = { package = "audio2", path = "../audio2" }
|
||||
activity_indicator = { package = "activity_indicator2", path = "../activity_indicator2"}
|
||||
auto_update = { package = "auto_update2", path = "../auto_update2" }
|
||||
breadcrumbs = { package = "breadcrumbs2", path = "../breadcrumbs2" }
|
||||
call = { package = "call2", path = "../call2" }
|
||||
channel = { package = "channel2", path = "../channel2" }
|
||||
cli = { path = "../cli" }
|
||||
collab_ui = { package = "collab_ui2", path = "../collab_ui2" }
|
||||
collections = { path = "../collections" }
|
||||
command_palette = { package="command_palette2", path = "../command_palette2" }
|
||||
# component_test = { path = "../component_test" }
|
||||
client = { package = "client2", path = "../client2" }
|
||||
# clock = { path = "../clock" }
|
||||
copilot = { package = "copilot2", path = "../copilot2" }
|
||||
copilot_button = { package = "copilot_button2", path = "../copilot_button2" }
|
||||
diagnostics = { package = "diagnostics2", path = "../diagnostics2" }
|
||||
db = { package = "db2", path = "../db2" }
|
||||
editor = { package="editor2", path = "../editor2" }
|
||||
feedback = { package="feedback2", path = "../feedback2" }
|
||||
file_finder = { package="file_finder2", path = "../file_finder2" }
|
||||
search = { package = "search2", path = "../search2" }
|
||||
fs = { package = "fs2", path = "../fs2" }
|
||||
fsevent = { path = "../fsevent" }
|
||||
go_to_line = { package = "go_to_line2", path = "../go_to_line2" }
|
||||
gpui = { package = "gpui2", path = "../gpui2" }
|
||||
install_cli = { package = "install_cli2", path = "../install_cli2" }
|
||||
journal = { package = "journal2", path = "../journal2" }
|
||||
language = { package = "language2", path = "../language2" }
|
||||
language_selector = { package = "language_selector2", path = "../language_selector2" }
|
||||
lsp = { package = "lsp2", path = "../lsp2" }
|
||||
menu = { package = "menu2", path = "../menu2" }
|
||||
language_tools = { package = "language_tools2", path = "../language_tools2" }
|
||||
node_runtime = { path = "../node_runtime" }
|
||||
notifications = { package = "notifications2", path = "../notifications2" }
|
||||
assistant = { package = "assistant2", path = "../assistant2" }
|
||||
outline = { package = "outline2", path = "../outline2" }
|
||||
# plugin_runtime = { path = "../plugin_runtime",optional = true }
|
||||
project = { package = "project2", path = "../project2" }
|
||||
project_panel = { package = "project_panel2", path = "../project_panel2" }
|
||||
project_symbols = { package = "project_symbols2", path = "../project_symbols2" }
|
||||
quick_action_bar = { package = "quick_action_bar2", path = "../quick_action_bar2" }
|
||||
recent_projects = { package = "recent_projects2", path = "../recent_projects2" }
|
||||
rope = { package = "rope2", path = "../rope2"}
|
||||
rpc = { package = "rpc2", path = "../rpc2" }
|
||||
settings = { package = "settings2", path = "../settings2" }
|
||||
feature_flags = { package = "feature_flags2", path = "../feature_flags2" }
|
||||
sum_tree = { path = "../sum_tree" }
|
||||
shellexpand = "2.1.0"
|
||||
text = { package = "text2", path = "../text2" }
|
||||
terminal_view = { package = "terminal_view2", path = "../terminal_view2" }
|
||||
theme = { package = "theme2", path = "../theme2" }
|
||||
theme_selector = { package = "theme_selector2", path = "../theme_selector2" }
|
||||
util = { path = "../util" }
|
||||
semantic_index = { package = "semantic_index2", path = "../semantic_index2" }
|
||||
vim = { package = "vim2", path = "../vim2" }
|
||||
workspace = { package = "workspace2", path = "../workspace2" }
|
||||
welcome = { package = "welcome2", path = "../welcome2" }
|
||||
zed_actions = {package = "zed_actions2", path = "../zed_actions2"}
|
||||
anyhow.workspace = true
|
||||
async-compression.workspace = true
|
||||
async-tar = "0.4.2"
|
||||
async-recursion = "0.3"
|
||||
async-trait.workspace = true
|
||||
backtrace = "0.3"
|
||||
chrono = "0.4"
|
||||
ctor.workspace = true
|
||||
env_logger.workspace = true
|
||||
futures.workspace = true
|
||||
ignore = "0.4"
|
||||
image = "0.23"
|
||||
indexmap = "1.6.2"
|
||||
isahc.workspace = true
|
||||
lazy_static.workspace = true
|
||||
libc = "0.2"
|
||||
log.workspace = true
|
||||
num_cpus = "1.13.0"
|
||||
parking_lot.workspace = true
|
||||
postage.workspace = true
|
||||
rand.workspace = true
|
||||
regex.workspace = true
|
||||
rsa = "0.4"
|
||||
rust-embed.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
schemars.workspace = true
|
||||
simplelog = "0.9"
|
||||
smallvec.workspace = true
|
||||
smol.workspace = true
|
||||
tempdir.workspace = true
|
||||
thiserror.workspace = true
|
||||
tiny_http = "0.8"
|
||||
toml.workspace = true
|
||||
tree-sitter.workspace = true
|
||||
tree-sitter-bash.workspace = true
|
||||
tree-sitter-c.workspace = true
|
||||
tree-sitter-cpp.workspace = true
|
||||
tree-sitter-css.workspace = true
|
||||
tree-sitter-elixir.workspace = true
|
||||
tree-sitter-elm.workspace = true
|
||||
tree-sitter-embedded-template.workspace = true
|
||||
tree-sitter-glsl.workspace = true
|
||||
tree-sitter-go.workspace = true
|
||||
tree-sitter-heex.workspace = true
|
||||
tree-sitter-json.workspace = true
|
||||
tree-sitter-rust.workspace = true
|
||||
tree-sitter-markdown.workspace = true
|
||||
tree-sitter-python.workspace = true
|
||||
tree-sitter-toml.workspace = true
|
||||
tree-sitter-typescript.workspace = true
|
||||
tree-sitter-ruby.workspace = true
|
||||
tree-sitter-html.workspace = true
|
||||
tree-sitter-php.workspace = true
|
||||
tree-sitter-scheme.workspace = true
|
||||
tree-sitter-svelte.workspace = true
|
||||
tree-sitter-racket.workspace = true
|
||||
tree-sitter-yaml.workspace = true
|
||||
tree-sitter-lua.workspace = true
|
||||
tree-sitter-nix.workspace = true
|
||||
tree-sitter-nu.workspace = true
|
||||
tree-sitter-vue.workspace = true
|
||||
tree-sitter-uiua.workspace = true
|
||||
|
||||
url = "2.2"
|
||||
urlencoding = "2.1.2"
|
||||
uuid.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
call = { package = "call2", path = "../call2", features = ["test-support"] }
|
||||
# client = { path = "../client", features = ["test-support"] }
|
||||
# editor = { path = "../editor", features = ["test-support"] }
|
||||
# gpui = { path = "../gpui", features = ["test-support"] }
|
||||
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
|
||||
language = { package = "language2", path = "../language2", features = ["test-support"] }
|
||||
# lsp = { path = "../lsp", features = ["test-support"] }
|
||||
project = { package = "project2", path = "../project2", features = ["test-support"] }
|
||||
# rpc = { path = "../rpc", features = ["test-support"] }
|
||||
# settings = { path = "../settings", features = ["test-support"] }
|
||||
text = { package = "text2", path = "../text2", features = ["test-support"] }
|
||||
# util = { path = "../util", features = ["test-support"] }
|
||||
# workspace = { path = "../workspace", features = ["test-support"] }
|
||||
unindent.workspace = true
|
||||
|
||||
[package.metadata.bundle-dev]
|
||||
icon = ["resources/app-icon-preview@2x.png", "resources/app-icon-preview.png"]
|
||||
identifier = "dev.zed.Zed-Dev"
|
||||
name = "Zed Dev"
|
||||
osx_minimum_system_version = "10.15.7"
|
||||
osx_info_plist_exts = ["resources/info/*"]
|
||||
osx_url_schemes = ["zed-dev"]
|
||||
|
||||
[package.metadata.bundle-nightly]
|
||||
icon = ["resources/app-icon-nightly@2x.png", "resources/app-icon-nightly.png"]
|
||||
identifier = "dev.zed.Zed-Nightly"
|
||||
name = "Zed Nightly"
|
||||
osx_minimum_system_version = "10.15.7"
|
||||
osx_info_plist_exts = ["resources/info/*"]
|
||||
osx_url_schemes = ["zed-nightly"]
|
||||
|
||||
[package.metadata.bundle-preview]
|
||||
icon = ["resources/app-icon-preview@2x.png", "resources/app-icon-preview.png"]
|
||||
identifier = "dev.zed.Zed-Preview"
|
||||
name = "Zed Preview"
|
||||
osx_minimum_system_version = "10.15.7"
|
||||
osx_info_plist_exts = ["resources/info/*"]
|
||||
osx_url_schemes = ["zed-preview"]
|
||||
|
||||
[package.metadata.bundle-stable]
|
||||
icon = ["resources/app-icon@2x.png", "resources/app-icon.png"]
|
||||
identifier = "dev.zed.Zed"
|
||||
name = "Zed"
|
||||
osx_minimum_system_version = "10.15.7"
|
||||
osx_info_plist_exts = ["resources/info/*"]
|
||||
osx_url_schemes = ["zed"]
|
@ -1,44 +0,0 @@
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7");
|
||||
|
||||
println!("cargo:rerun-if-env-changed=ZED_BUNDLE");
|
||||
if std::env::var("ZED_BUNDLE").ok().as_deref() == Some("true") {
|
||||
// Find WebRTC.framework in the Frameworks folder when running as part of an application bundle.
|
||||
println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks");
|
||||
} else {
|
||||
// Find WebRTC.framework as a sibling of the executable when running outside of an application bundle.
|
||||
println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path");
|
||||
}
|
||||
|
||||
// Weakly link ReplayKit to ensure Zed can be used on macOS 10.15+.
|
||||
println!("cargo:rustc-link-arg=-Wl,-weak_framework,ReplayKit");
|
||||
|
||||
// Seems to be required to enable Swift concurrency
|
||||
println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/lib/swift");
|
||||
|
||||
// Register exported Objective-C selectors, protocols, etc
|
||||
println!("cargo:rustc-link-arg=-Wl,-ObjC");
|
||||
|
||||
// Populate git sha environment variable if git is available
|
||||
println!("cargo:rerun-if-changed=.git/logs/HEAD");
|
||||
if let Ok(output) = Command::new("git").args(["rev-parse", "HEAD"]).output() {
|
||||
if output.status.success() {
|
||||
let git_sha = String::from_utf8_lossy(&output.stdout);
|
||||
let git_sha = git_sha.trim();
|
||||
|
||||
println!("cargo:rustc-env=ZED_COMMIT_SHA={git_sha}");
|
||||
|
||||
if let Ok(build_profile) = std::env::var("PROFILE") {
|
||||
if build_profile == "release" {
|
||||
// This is currently the best way to make `cargo build ...`'s build script
|
||||
// to print something to stdout without extra verbosity.
|
||||
println!(
|
||||
"cargo:warning=Info: using '{git_sha}' hash for ZED_COMMIT_SHA env var"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 187 KiB |
Binary file not shown.
Before Width: | Height: | Size: 539 KiB |
Binary file not shown.
Before Width: | Height: | Size: 187 KiB |
Binary file not shown.
Before Width: | Height: | Size: 663 KiB |
Binary file not shown.
Before Width: | Height: | Size: 164 KiB |
Binary file not shown.
Before Width: | Height: | Size: 442 KiB |
@ -1,62 +0,0 @@
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>Document</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.text</string>
|
||||
<string>public.plain-text</string>
|
||||
<string>public.utf8-plain-text</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>Document</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Zed Text Document</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>****</string>
|
||||
</array>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Default</string>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>Gemfile</string>
|
||||
<string>c</string>
|
||||
<string>c++</string>
|
||||
<string>cc</string>
|
||||
<string>cpp</string>
|
||||
<string>css</string>
|
||||
<string>erb</string>
|
||||
<string>ex</string>
|
||||
<string>exs</string>
|
||||
<string>go</string>
|
||||
<string>h</string>
|
||||
<string>h++</string>
|
||||
<string>hh</string>
|
||||
<string>hpp</string>
|
||||
<string>html</string>
|
||||
<string>js</string>
|
||||
<string>json</string>
|
||||
<string>jsx</string>
|
||||
<string>md</string>
|
||||
<string>py</string>
|
||||
<string>rb</string>
|
||||
<string>rkt</string>
|
||||
<string>rs</string>
|
||||
<string>scm</string>
|
||||
<string>toml</string>
|
||||
<string>ts</string>
|
||||
<string>tsx</string>
|
||||
<string>txt</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
@ -1,24 +0,0 @@
|
||||
<key>NSSystemAdministrationUsageDescription</key>
|
||||
<string>The operation being performed by a program in Zed requires elevated permission.</string>
|
||||
<key>NSAppleEventsUsageDescription</key>
|
||||
<string>An application in Zed wants to use AppleScript.</string>
|
||||
<key>NSBluetoothAlwaysUsageDescription</key>
|
||||
<string>An application in Zed wants to use Bluetooth.</string>
|
||||
<key>NSCalendarsUsageDescription</key>
|
||||
<string>An application in Zed wants to use Calendar data.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>An application in Zed wants to use the camera.</string>
|
||||
<key>NSContactsUsageDescription</key>
|
||||
<string>An application in Zed wants to use your contacts.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>An application in Zed wants to use your location information, even in the background.</string>
|
||||
<key>NSLocationUsageDescription</key>
|
||||
<string>An application in Zed wants to use your location information.</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>An application in Zed wants to use your location information while active.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>An application in Zed wants to use your microphone.</string>
|
||||
<key>NSSpeechRecognitionUsageDescription</key>
|
||||
<string>An application in Zed wants to use speech recognition.</string>
|
||||
<key>NSRemindersUsageDescription</key>
|
||||
<string>An application in Zed wants to use your reminders.</string>
|
@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.automation.apple-events</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.addressbook</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.calendars</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.location</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.photos-library</key>
|
||||
<true/>
|
||||
<!-- <key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/> -->
|
||||
</dict>
|
||||
</plist>
|
@ -1,35 +0,0 @@
|
||||
use anyhow::anyhow;
|
||||
|
||||
use gpui::{AssetSource, Result, SharedString};
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "../../assets"]
|
||||
#[include = "fonts/**/*"]
|
||||
#[include = "icons/**/*"]
|
||||
#[include = "themes/**/*"]
|
||||
#[exclude = "themes/src/*"]
|
||||
#[include = "sounds/**/*"]
|
||||
#[include = "*.md"]
|
||||
#[exclude = "*.DS_Store"]
|
||||
pub struct Assets;
|
||||
|
||||
impl AssetSource for Assets {
|
||||
fn load(&self, path: &str) -> Result<std::borrow::Cow<[u8]>> {
|
||||
Self::get(path)
|
||||
.map(|f| f.data)
|
||||
.ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path))
|
||||
}
|
||||
|
||||
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
|
||||
Ok(Self::iter()
|
||||
.filter_map(|p| {
|
||||
if p.starts_with(path) {
|
||||
Some(p.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
@ -1,299 +0,0 @@
|
||||
use anyhow::Context;
|
||||
use gpui::AppContext;
|
||||
pub use language::*;
|
||||
use node_runtime::NodeRuntime;
|
||||
use rust_embed::RustEmbed;
|
||||
use settings::Settings;
|
||||
use std::{borrow::Cow, str, sync::Arc};
|
||||
use util::{asset_str, paths::PLUGINS_DIR};
|
||||
|
||||
use self::elixir::ElixirSettings;
|
||||
|
||||
mod c;
|
||||
mod css;
|
||||
mod elixir;
|
||||
mod go;
|
||||
mod html;
|
||||
mod json;
|
||||
#[cfg(feature = "plugin_runtime")]
|
||||
mod language_plugin;
|
||||
mod lua;
|
||||
mod nu;
|
||||
mod php;
|
||||
mod python;
|
||||
mod ruby;
|
||||
mod rust;
|
||||
mod svelte;
|
||||
mod tailwind;
|
||||
mod typescript;
|
||||
mod uiua;
|
||||
mod vue;
|
||||
mod yaml;
|
||||
|
||||
// 1. Add tree-sitter-{language} parser to zed crate
|
||||
// 2. Create a language directory in zed/crates/zed/src/languages and add the language to init function below
|
||||
// 3. Add config.toml to the newly created language directory using existing languages as a template
|
||||
// 4. Copy highlights from tree sitter repo for the language into a highlights.scm file.
|
||||
// Note: github highlights take the last match while zed takes the first
|
||||
// 5. Add indents.scm, outline.scm, and brackets.scm to implement indent on newline, outline/breadcrumbs,
|
||||
// and autoclosing brackets respectively
|
||||
// 6. If the language has injections add an injections.scm query file
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "src/languages"]
|
||||
#[exclude = "*.rs"]
|
||||
struct LanguageDir;
|
||||
|
||||
pub fn init(
|
||||
languages: Arc<LanguageRegistry>,
|
||||
node_runtime: Arc<dyn NodeRuntime>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
ElixirSettings::register(cx);
|
||||
|
||||
let language = |name, grammar, adapters| {
|
||||
languages.register(name, load_config(name), grammar, adapters, load_queries)
|
||||
};
|
||||
|
||||
language("bash", tree_sitter_bash::language(), vec![]);
|
||||
language(
|
||||
"c",
|
||||
tree_sitter_c::language(),
|
||||
vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>],
|
||||
);
|
||||
language(
|
||||
"cpp",
|
||||
tree_sitter_cpp::language(),
|
||||
vec![Arc::new(c::CLspAdapter)],
|
||||
);
|
||||
language(
|
||||
"css",
|
||||
tree_sitter_css::language(),
|
||||
vec![
|
||||
Arc::new(css::CssLspAdapter::new(node_runtime.clone())),
|
||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
);
|
||||
|
||||
match &ElixirSettings::get(None, cx).lsp {
|
||||
elixir::ElixirLspSetting::ElixirLs => language(
|
||||
"elixir",
|
||||
tree_sitter_elixir::language(),
|
||||
vec![
|
||||
Arc::new(elixir::ElixirLspAdapter),
|
||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
),
|
||||
elixir::ElixirLspSetting::NextLs => language(
|
||||
"elixir",
|
||||
tree_sitter_elixir::language(),
|
||||
vec![Arc::new(elixir::NextLspAdapter)],
|
||||
),
|
||||
elixir::ElixirLspSetting::Local { path, arguments } => language(
|
||||
"elixir",
|
||||
tree_sitter_elixir::language(),
|
||||
vec![Arc::new(elixir::LocalLspAdapter {
|
||||
path: path.clone(),
|
||||
arguments: arguments.clone(),
|
||||
})],
|
||||
),
|
||||
}
|
||||
|
||||
language(
|
||||
"go",
|
||||
tree_sitter_go::language(),
|
||||
vec![Arc::new(go::GoLspAdapter)],
|
||||
);
|
||||
language(
|
||||
"heex",
|
||||
tree_sitter_heex::language(),
|
||||
vec![
|
||||
Arc::new(elixir::ElixirLspAdapter),
|
||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
);
|
||||
language(
|
||||
"json",
|
||||
tree_sitter_json::language(),
|
||||
vec![Arc::new(json::JsonLspAdapter::new(
|
||||
node_runtime.clone(),
|
||||
languages.clone(),
|
||||
))],
|
||||
);
|
||||
language("markdown", tree_sitter_markdown::language(), vec![]);
|
||||
language(
|
||||
"python",
|
||||
tree_sitter_python::language(),
|
||||
vec![Arc::new(python::PythonLspAdapter::new(
|
||||
node_runtime.clone(),
|
||||
))],
|
||||
);
|
||||
language(
|
||||
"rust",
|
||||
tree_sitter_rust::language(),
|
||||
vec![Arc::new(rust::RustLspAdapter)],
|
||||
);
|
||||
language("toml", tree_sitter_toml::language(), vec![]);
|
||||
language(
|
||||
"tsx",
|
||||
tree_sitter_typescript::language_tsx(),
|
||||
vec![
|
||||
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
||||
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
);
|
||||
language(
|
||||
"typescript",
|
||||
tree_sitter_typescript::language_typescript(),
|
||||
vec![
|
||||
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
||||
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
);
|
||||
language(
|
||||
"javascript",
|
||||
tree_sitter_typescript::language_tsx(),
|
||||
vec![
|
||||
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
||||
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
);
|
||||
language(
|
||||
"html",
|
||||
tree_sitter_html::language(),
|
||||
vec![
|
||||
Arc::new(html::HtmlLspAdapter::new(node_runtime.clone())),
|
||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
);
|
||||
language(
|
||||
"ruby",
|
||||
tree_sitter_ruby::language(),
|
||||
vec![Arc::new(ruby::RubyLanguageServer)],
|
||||
);
|
||||
language(
|
||||
"erb",
|
||||
tree_sitter_embedded_template::language(),
|
||||
vec![
|
||||
Arc::new(ruby::RubyLanguageServer),
|
||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
);
|
||||
language("scheme", tree_sitter_scheme::language(), vec![]);
|
||||
language("racket", tree_sitter_racket::language(), vec![]);
|
||||
language(
|
||||
"lua",
|
||||
tree_sitter_lua::language(),
|
||||
vec![Arc::new(lua::LuaLspAdapter)],
|
||||
);
|
||||
language(
|
||||
"yaml",
|
||||
tree_sitter_yaml::language(),
|
||||
vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))],
|
||||
);
|
||||
language(
|
||||
"svelte",
|
||||
tree_sitter_svelte::language(),
|
||||
vec![
|
||||
Arc::new(svelte::SvelteLspAdapter::new(node_runtime.clone())),
|
||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
);
|
||||
language(
|
||||
"php",
|
||||
tree_sitter_php::language(),
|
||||
vec![
|
||||
Arc::new(php::IntelephenseLspAdapter::new(node_runtime.clone())),
|
||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||
],
|
||||
);
|
||||
|
||||
language("elm", tree_sitter_elm::language(), vec![]);
|
||||
language("glsl", tree_sitter_glsl::language(), vec![]);
|
||||
language("nix", tree_sitter_nix::language(), vec![]);
|
||||
language(
|
||||
"nu",
|
||||
tree_sitter_nu::language(),
|
||||
vec![Arc::new(nu::NuLanguageServer {})],
|
||||
);
|
||||
language(
|
||||
"vue",
|
||||
tree_sitter_vue::language(),
|
||||
vec![Arc::new(vue::VueLspAdapter::new(node_runtime))],
|
||||
);
|
||||
language(
|
||||
"uiua",
|
||||
tree_sitter_uiua::language(),
|
||||
vec![Arc::new(uiua::UiuaLanguageServer {})],
|
||||
);
|
||||
|
||||
if let Ok(children) = std::fs::read_dir(&*PLUGINS_DIR) {
|
||||
for child in children {
|
||||
if let Ok(child) = child {
|
||||
let path = child.path();
|
||||
let config_path = path.join("config.toml");
|
||||
if let Ok(config) = std::fs::read(&config_path) {
|
||||
let config: LanguageConfig = toml::from_slice(&config).unwrap();
|
||||
if let Some(grammar_name) = config.grammar_name.clone() {
|
||||
languages.register_wasm(path.into(), grammar_name, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub async fn language(
|
||||
name: &str,
|
||||
grammar: tree_sitter::Language,
|
||||
lsp_adapter: Option<Arc<dyn LspAdapter>>,
|
||||
) -> Arc<Language> {
|
||||
Arc::new(
|
||||
Language::new(load_config(name), Some(grammar))
|
||||
.with_lsp_adapters(lsp_adapter.into_iter().collect())
|
||||
.await
|
||||
.with_queries(load_queries(name))
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn load_config(name: &str) -> LanguageConfig {
|
||||
toml::from_slice(
|
||||
&LanguageDir::get(&format!("{}/config.toml", name))
|
||||
.unwrap()
|
||||
.data,
|
||||
)
|
||||
.with_context(|| format!("failed to load config.toml for language {name:?}"))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn load_queries(name: &str) -> LanguageQueries {
|
||||
LanguageQueries {
|
||||
highlights: load_query(name, "/highlights"),
|
||||
brackets: load_query(name, "/brackets"),
|
||||
indents: load_query(name, "/indents"),
|
||||
outline: load_query(name, "/outline"),
|
||||
embedding: load_query(name, "/embedding"),
|
||||
injections: load_query(name, "/injections"),
|
||||
overrides: load_query(name, "/overrides"),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_query(name: &str, filename_prefix: &str) -> Option<Cow<'static, str>> {
|
||||
let mut result = None;
|
||||
for path in LanguageDir::iter() {
|
||||
if let Some(remainder) = path.strip_prefix(name) {
|
||||
if remainder.starts_with(filename_prefix) {
|
||||
let contents = asset_str::<LanguageDir>(path.as_ref());
|
||||
match &mut result {
|
||||
None => result = Some(contents),
|
||||
Some(r) => r.to_mut().push_str(contents.as_ref()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
("(" @open ")" @close)
|
||||
("[" @open "]" @close)
|
||||
("{" @open "}" @close)
|
@ -1,9 +0,0 @@
|
||||
name = "Shell Script"
|
||||
path_suffixes = ["sh", "bash", "bashrc", "bash_profile", "bash_aliases", "bash_logout", "profile", "zsh", "zshrc", "zshenv", "zsh_profile", "zsh_aliases", "zsh_histfile", "zlogin", "zprofile"]
|
||||
line_comment = "# "
|
||||
first_line_pattern = "^#!.*\\b(?:ba|z)?sh\\b"
|
||||
brackets = [
|
||||
{ start = "[", end = "]", close = true, newline = false },
|
||||
{ start = "(", end = ")", close = true, newline = false },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
]
|
@ -1,59 +0,0 @@
|
||||
[
|
||||
(string)
|
||||
(raw_string)
|
||||
(heredoc_body)
|
||||
(heredoc_start)
|
||||
(ansi_c_string)
|
||||
] @string
|
||||
|
||||
(command_name) @function
|
||||
|
||||
(variable_name) @property
|
||||
|
||||
[
|
||||
"case"
|
||||
"do"
|
||||
"done"
|
||||
"elif"
|
||||
"else"
|
||||
"esac"
|
||||
"export"
|
||||
"fi"
|
||||
"for"
|
||||
"function"
|
||||
"if"
|
||||
"in"
|
||||
"select"
|
||||
"then"
|
||||
"unset"
|
||||
"until"
|
||||
"while"
|
||||
"local"
|
||||
"declare"
|
||||
] @keyword
|
||||
|
||||
(comment) @comment
|
||||
|
||||
(function_definition name: (word) @function)
|
||||
|
||||
(file_descriptor) @number
|
||||
|
||||
[
|
||||
(command_substitution)
|
||||
(process_substitution)
|
||||
(expansion)
|
||||
]@embedded
|
||||
|
||||
[
|
||||
"$"
|
||||
"&&"
|
||||
">"
|
||||
">>"
|
||||
"<"
|
||||
"|"
|
||||
] @operator
|
||||
|
||||
(
|
||||
(command (_) @constant)
|
||||
(#match? @constant "^-")
|
||||
)
|
@ -1,321 +0,0 @@
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
pub use language::*;
|
||||
use lsp::LanguageServerBinary;
|
||||
use smol::fs::{self, File};
|
||||
use std::{any::Any, path::PathBuf, sync::Arc};
|
||||
use util::{
|
||||
fs::remove_matching,
|
||||
github::{latest_github_release, GitHubLspBinaryVersion},
|
||||
ResultExt,
|
||||
};
|
||||
|
||||
pub struct CLspAdapter;
|
||||
|
||||
#[async_trait]
|
||||
impl super::LspAdapter for CLspAdapter {
|
||||
async fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("clangd".into())
|
||||
}
|
||||
|
||||
fn short_name(&self) -> &'static str {
|
||||
"clangd"
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
let release = latest_github_release("clangd/clangd", false, delegate.http_client()).await?;
|
||||
let asset_name = format!("clangd-mac-{}.zip", release.name);
|
||||
let asset = release
|
||||
.assets
|
||||
.iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
||||
let version = GitHubLspBinaryVersion {
|
||||
name: release.name,
|
||||
url: asset.browser_download_url.clone(),
|
||||
};
|
||||
Ok(Box::new(version) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
let zip_path = container_dir.join(format!("clangd_{}.zip", version.name));
|
||||
let version_dir = container_dir.join(format!("clangd_{}", version.name));
|
||||
let binary_path = version_dir.join("bin/clangd");
|
||||
|
||||
if fs::metadata(&binary_path).await.is_err() {
|
||||
let mut response = delegate
|
||||
.http_client()
|
||||
.get(&version.url, Default::default(), true)
|
||||
.await
|
||||
.context("error downloading release")?;
|
||||
let mut file = File::create(&zip_path).await?;
|
||||
if !response.status().is_success() {
|
||||
Err(anyhow!(
|
||||
"download failed with status {}",
|
||||
response.status().to_string()
|
||||
))?;
|
||||
}
|
||||
futures::io::copy(response.body_mut(), &mut file).await?;
|
||||
|
||||
let unzip_status = smol::process::Command::new("unzip")
|
||||
.current_dir(&container_dir)
|
||||
.arg(&zip_path)
|
||||
.output()
|
||||
.await?
|
||||
.status;
|
||||
if !unzip_status.success() {
|
||||
Err(anyhow!("failed to unzip clangd archive"))?;
|
||||
}
|
||||
|
||||
remove_matching(&container_dir, |entry| entry != version_dir).await;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir)
|
||||
.await
|
||||
.map(|mut binary| {
|
||||
binary.arguments = vec!["--help".into()];
|
||||
binary
|
||||
})
|
||||
}
|
||||
|
||||
async fn label_for_completion(
|
||||
&self,
|
||||
completion: &lsp::CompletionItem,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
let label = completion
|
||||
.label
|
||||
.strip_prefix('•')
|
||||
.unwrap_or(&completion.label)
|
||||
.trim();
|
||||
|
||||
match completion.kind {
|
||||
Some(lsp::CompletionItemKind::FIELD) if completion.detail.is_some() => {
|
||||
let detail = completion.detail.as_ref().unwrap();
|
||||
let text = format!("{} {}", detail, label);
|
||||
let source = Rope::from(format!("struct S {{ {} }}", text).as_str());
|
||||
let runs = language.highlight_text(&source, 11..11 + text.len());
|
||||
return Some(CodeLabel {
|
||||
filter_range: detail.len() + 1..text.len(),
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
}
|
||||
Some(lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE)
|
||||
if completion.detail.is_some() =>
|
||||
{
|
||||
let detail = completion.detail.as_ref().unwrap();
|
||||
let text = format!("{} {}", detail, label);
|
||||
let runs = language.highlight_text(&Rope::from(text.as_str()), 0..text.len());
|
||||
return Some(CodeLabel {
|
||||
filter_range: detail.len() + 1..text.len(),
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
}
|
||||
Some(lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD)
|
||||
if completion.detail.is_some() =>
|
||||
{
|
||||
let detail = completion.detail.as_ref().unwrap();
|
||||
let text = format!("{} {}", detail, label);
|
||||
let runs = language.highlight_text(&Rope::from(text.as_str()), 0..text.len());
|
||||
return Some(CodeLabel {
|
||||
filter_range: detail.len() + 1..text.rfind('(').unwrap_or(text.len()),
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
}
|
||||
Some(kind) => {
|
||||
let highlight_name = match kind {
|
||||
lsp::CompletionItemKind::STRUCT
|
||||
| lsp::CompletionItemKind::INTERFACE
|
||||
| lsp::CompletionItemKind::CLASS
|
||||
| lsp::CompletionItemKind::ENUM => Some("type"),
|
||||
lsp::CompletionItemKind::ENUM_MEMBER => Some("variant"),
|
||||
lsp::CompletionItemKind::KEYWORD => Some("keyword"),
|
||||
lsp::CompletionItemKind::VALUE | lsp::CompletionItemKind::CONSTANT => {
|
||||
Some("constant")
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(highlight_id) = language
|
||||
.grammar()
|
||||
.and_then(|g| g.highlight_id_for_name(highlight_name?))
|
||||
{
|
||||
let mut label = CodeLabel::plain(label.to_string(), None);
|
||||
label.runs.push((
|
||||
0..label.text.rfind('(').unwrap_or(label.text.len()),
|
||||
highlight_id,
|
||||
));
|
||||
return Some(label);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Some(CodeLabel::plain(label.to_string(), None))
|
||||
}
|
||||
|
||||
async fn label_for_symbol(
|
||||
&self,
|
||||
name: &str,
|
||||
kind: lsp::SymbolKind,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
let (text, filter_range, display_range) = match kind {
|
||||
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
|
||||
let text = format!("void {} () {{}}", name);
|
||||
let filter_range = 0..name.len();
|
||||
let display_range = 5..5 + name.len();
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::STRUCT => {
|
||||
let text = format!("struct {} {{}}", name);
|
||||
let filter_range = 7..7 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::ENUM => {
|
||||
let text = format!("enum {} {{}}", name);
|
||||
let filter_range = 5..5 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::INTERFACE | lsp::SymbolKind::CLASS => {
|
||||
let text = format!("class {} {{}}", name);
|
||||
let filter_range = 6..6 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::CONSTANT => {
|
||||
let text = format!("const int {} = 0;", name);
|
||||
let filter_range = 10..10 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::MODULE => {
|
||||
let text = format!("namespace {} {{}}", name);
|
||||
let filter_range = 10..10 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::TYPE_PARAMETER => {
|
||||
let text = format!("typename {} {{}};", name);
|
||||
let filter_range = 9..9 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(CodeLabel {
|
||||
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
|
||||
text: text[display_range].to_string(),
|
||||
filter_range,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||
(|| async move {
|
||||
let mut last_clangd_dir = None;
|
||||
let mut entries = fs::read_dir(&container_dir).await?;
|
||||
while let Some(entry) = entries.next().await {
|
||||
let entry = entry?;
|
||||
if entry.file_type().await?.is_dir() {
|
||||
last_clangd_dir = Some(entry.path());
|
||||
}
|
||||
}
|
||||
let clangd_dir = last_clangd_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
||||
let clangd_bin = clangd_dir.join("bin/clangd");
|
||||
if clangd_bin.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: clangd_bin,
|
||||
arguments: vec![],
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"missing clangd binary in directory {:?}",
|
||||
clangd_dir
|
||||
))
|
||||
}
|
||||
})()
|
||||
.await
|
||||
.log_err()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::{Context, TestAppContext};
|
||||
use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
|
||||
use settings::SettingsStore;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_c_autoindent(cx: &mut TestAppContext) {
|
||||
// cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
cx.update(|cx| {
|
||||
let test_settings = SettingsStore::test(cx);
|
||||
cx.set_global(test_settings);
|
||||
language::init(cx);
|
||||
cx.update_global::<SettingsStore, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
let language = crate::languages::language("c", tree_sitter_c::language(), None).await;
|
||||
|
||||
cx.new_model(|cx| {
|
||||
let mut buffer =
|
||||
Buffer::new(0, cx.entity_id().as_u64(), "").with_language(language, cx);
|
||||
|
||||
// empty function
|
||||
buffer.edit([(0..0, "int main() {}")], None, cx);
|
||||
|
||||
// indent inside braces
|
||||
let ix = buffer.len() - 1;
|
||||
buffer.edit([(ix..ix, "\n\n")], Some(AutoindentMode::EachLine), cx);
|
||||
assert_eq!(buffer.text(), "int main() {\n \n}");
|
||||
|
||||
// indent body of single-statement if statement
|
||||
let ix = buffer.len() - 2;
|
||||
buffer.edit([(ix..ix, "if (a)\nb;")], Some(AutoindentMode::EachLine), cx);
|
||||
assert_eq!(buffer.text(), "int main() {\n if (a)\n b;\n}");
|
||||
|
||||
// indent inside field expression
|
||||
let ix = buffer.len() - 3;
|
||||
buffer.edit([(ix..ix, "\n.c")], Some(AutoindentMode::EachLine), cx);
|
||||
assert_eq!(buffer.text(), "int main() {\n if (a)\n b\n .c;\n}");
|
||||
|
||||
buffer
|
||||
});
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
("[" @open "]" @close)
|
||||
("{" @open "}" @close)
|
||||
("\"" @open "\"" @close)
|
@ -1,12 +0,0 @@
|
||||
name = "C"
|
||||
path_suffixes = ["c"]
|
||||
line_comment = "// "
|
||||
autoclose_before = ";:.,=}])>"
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
]
|
@ -1,43 +0,0 @@
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(declaration
|
||||
declarator: [
|
||||
(function_declarator
|
||||
declarator: (_) @name)
|
||||
(pointer_declarator
|
||||
"*" @name
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name))
|
||||
(pointer_declarator
|
||||
"*" @name
|
||||
declarator: (pointer_declarator
|
||||
"*" @name
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name)))
|
||||
]
|
||||
) @item
|
||||
)
|
||||
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(function_definition
|
||||
declarator: [
|
||||
(function_declarator
|
||||
declarator: (_) @name
|
||||
)
|
||||
(pointer_declarator
|
||||
"*" @name
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
))
|
||||
(pointer_declarator
|
||||
"*" @name
|
||||
declarator: (pointer_declarator
|
||||
"*" @name
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name)))
|
||||
]
|
||||
) @item
|
||||
)
|
@ -1,109 +0,0 @@
|
||||
[
|
||||
"break"
|
||||
"case"
|
||||
"const"
|
||||
"continue"
|
||||
"default"
|
||||
"do"
|
||||
"else"
|
||||
"enum"
|
||||
"extern"
|
||||
"for"
|
||||
"if"
|
||||
"inline"
|
||||
"return"
|
||||
"sizeof"
|
||||
"static"
|
||||
"struct"
|
||||
"switch"
|
||||
"typedef"
|
||||
"union"
|
||||
"volatile"
|
||||
"while"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"#define"
|
||||
"#elif"
|
||||
"#else"
|
||||
"#endif"
|
||||
"#if"
|
||||
"#ifdef"
|
||||
"#ifndef"
|
||||
"#include"
|
||||
(preproc_directive)
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"--"
|
||||
"-"
|
||||
"-="
|
||||
"->"
|
||||
"="
|
||||
"!="
|
||||
"*"
|
||||
"&"
|
||||
"&&"
|
||||
"+"
|
||||
"++"
|
||||
"+="
|
||||
"<"
|
||||
"=="
|
||||
">"
|
||||
"||"
|
||||
] @operator
|
||||
|
||||
[
|
||||
"."
|
||||
";"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"{"
|
||||
"}"
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
(string_literal)
|
||||
(system_lib_string)
|
||||
(char_literal)
|
||||
] @string
|
||||
|
||||
(comment) @comment
|
||||
|
||||
(number_literal) @number
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
(null)
|
||||
] @constant
|
||||
|
||||
(identifier) @variable
|
||||
|
||||
((identifier) @constant
|
||||
(#match? @constant "^_*[A-Z][A-Z\\d_]*$"))
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function)
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function))
|
||||
(function_declarator
|
||||
declarator: (identifier) @function)
|
||||
(preproc_function_def
|
||||
name: (identifier) @function.special)
|
||||
|
||||
(field_identifier) @property
|
||||
(statement_identifier) @label
|
||||
|
||||
[
|
||||
(type_identifier)
|
||||
(primitive_type)
|
||||
(sized_type_specifier)
|
||||
] @type
|
||||
|
@ -1,9 +0,0 @@
|
||||
[
|
||||
(field_expression)
|
||||
(assignment_expression)
|
||||
(if_statement)
|
||||
(for_statement)
|
||||
] @indent
|
||||
|
||||
(_ "{" "}" @end) @indent
|
||||
(_ "(" ")" @end) @indent
|
@ -1,7 +0,0 @@
|
||||
(preproc_def
|
||||
value: (preproc_arg) @content
|
||||
(#set! "language" "c"))
|
||||
|
||||
(preproc_function_def
|
||||
value: (preproc_arg) @content
|
||||
(#set! "language" "c"))
|
@ -1,70 +0,0 @@
|
||||
(preproc_def
|
||||
"#define" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
(preproc_function_def
|
||||
"#define" @context
|
||||
name: (_) @name
|
||||
parameters: (preproc_params
|
||||
"(" @context
|
||||
")" @context)) @item
|
||||
|
||||
(type_definition
|
||||
"typedef" @context
|
||||
declarator: (_) @name) @item
|
||||
|
||||
(declaration
|
||||
(type_qualifier)? @context
|
||||
type: (_)? @context
|
||||
declarator: [
|
||||
(function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context))
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)))
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (pointer_declarator
|
||||
"*" @context
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context))))
|
||||
]
|
||||
) @item
|
||||
|
||||
(function_definition
|
||||
(type_qualifier)? @context
|
||||
type: (_)? @context
|
||||
declarator: [
|
||||
(function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context))
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)))
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (pointer_declarator
|
||||
"*" @context
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context))))
|
||||
]
|
||||
) @item
|
@ -1,2 +0,0 @@
|
||||
(comment) @comment
|
||||
(string_literal) @string
|
@ -1,3 +0,0 @@
|
||||
("[" @open "]" @close)
|
||||
("{" @open "}" @close)
|
||||
("\"" @open "\"" @close)
|
@ -1,12 +0,0 @@
|
||||
name = "C++"
|
||||
path_suffixes = ["cc", "cpp", "h", "hpp", "cxx", "hxx", "inl"]
|
||||
line_comment = "// "
|
||||
autoclose_before = ";:.,=}])>"
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
]
|
@ -1,61 +0,0 @@
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(function_definition
|
||||
(type_qualifier)? @name
|
||||
type: (_)? @name
|
||||
declarator: [
|
||||
(function_declarator
|
||||
declarator: (_) @name)
|
||||
(pointer_declarator
|
||||
"*" @name
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name))
|
||||
(pointer_declarator
|
||||
"*" @name
|
||||
declarator: (pointer_declarator
|
||||
"*" @name
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name)))
|
||||
(reference_declarator
|
||||
["&" "&&"] @name
|
||||
(function_declarator
|
||||
declarator: (_) @name))
|
||||
]
|
||||
(type_qualifier)? @name) @item
|
||||
)
|
||||
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(template_declaration
|
||||
(class_specifier
|
||||
"class" @name
|
||||
name: (_) @name)
|
||||
) @item
|
||||
)
|
||||
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(class_specifier
|
||||
"class" @name
|
||||
name: (_) @name) @item
|
||||
)
|
||||
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(enum_specifier
|
||||
"enum" @name
|
||||
name: (_) @name) @item
|
||||
)
|
||||
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(declaration
|
||||
type: (struct_specifier
|
||||
"struct" @name)
|
||||
declarator: (_) @name) @item
|
||||
)
|
@ -1,158 +0,0 @@
|
||||
(identifier) @variable
|
||||
|
||||
(call_expression
|
||||
function: (qualified_identifier
|
||||
name: (identifier) @function))
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function)
|
||||
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function))
|
||||
|
||||
(preproc_function_def
|
||||
name: (identifier) @function.special)
|
||||
|
||||
(template_function
|
||||
name: (identifier) @function)
|
||||
|
||||
(template_method
|
||||
name: (field_identifier) @function)
|
||||
|
||||
(function_declarator
|
||||
declarator: (identifier) @function)
|
||||
|
||||
(function_declarator
|
||||
declarator: (qualified_identifier
|
||||
name: (identifier) @function))
|
||||
|
||||
(function_declarator
|
||||
declarator: (field_identifier) @function)
|
||||
|
||||
((namespace_identifier) @type
|
||||
(#match? @type "^[A-Z]"))
|
||||
|
||||
(auto) @type
|
||||
(type_identifier) @type
|
||||
|
||||
((identifier) @constant
|
||||
(#match? @constant "^_*[A-Z][A-Z\\d_]*$"))
|
||||
|
||||
(field_identifier) @property
|
||||
(statement_identifier) @label
|
||||
(this) @variable.special
|
||||
|
||||
[
|
||||
"break"
|
||||
"case"
|
||||
"catch"
|
||||
"class"
|
||||
"co_await"
|
||||
"co_return"
|
||||
"co_yield"
|
||||
"const"
|
||||
"constexpr"
|
||||
"continue"
|
||||
"default"
|
||||
"delete"
|
||||
"do"
|
||||
"else"
|
||||
"enum"
|
||||
"explicit"
|
||||
"extern"
|
||||
"final"
|
||||
"for"
|
||||
"friend"
|
||||
"if"
|
||||
"if"
|
||||
"inline"
|
||||
"mutable"
|
||||
"namespace"
|
||||
"new"
|
||||
"noexcept"
|
||||
"override"
|
||||
"private"
|
||||
"protected"
|
||||
"public"
|
||||
"return"
|
||||
"sizeof"
|
||||
"static"
|
||||
"struct"
|
||||
"switch"
|
||||
"template"
|
||||
"throw"
|
||||
"try"
|
||||
"typedef"
|
||||
"typename"
|
||||
"union"
|
||||
"using"
|
||||
"virtual"
|
||||
"volatile"
|
||||
"while"
|
||||
(primitive_type)
|
||||
(type_qualifier)
|
||||
] @keyword
|
||||
|
||||
[
|
||||
"#define"
|
||||
"#elif"
|
||||
"#else"
|
||||
"#endif"
|
||||
"#if"
|
||||
"#ifdef"
|
||||
"#ifndef"
|
||||
"#include"
|
||||
(preproc_directive)
|
||||
] @keyword
|
||||
|
||||
(comment) @comment
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
(null)
|
||||
(nullptr)
|
||||
] @constant
|
||||
|
||||
(number_literal) @number
|
||||
|
||||
[
|
||||
(string_literal)
|
||||
(system_lib_string)
|
||||
(char_literal)
|
||||
(raw_string_literal)
|
||||
] @string
|
||||
|
||||
[
|
||||
"."
|
||||
";"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"{"
|
||||
"}"
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
"--"
|
||||
"-"
|
||||
"-="
|
||||
"->"
|
||||
"="
|
||||
"!="
|
||||
"*"
|
||||
"&"
|
||||
"&&"
|
||||
"+"
|
||||
"++"
|
||||
"+="
|
||||
"<"
|
||||
"=="
|
||||
">"
|
||||
"||"
|
||||
] @operator
|
@ -1,7 +0,0 @@
|
||||
[
|
||||
(field_expression)
|
||||
(assignment_expression)
|
||||
] @indent
|
||||
|
||||
(_ "{" "}" @end) @indent
|
||||
(_ "(" ")" @end) @indent
|
@ -1,7 +0,0 @@
|
||||
(preproc_def
|
||||
value: (preproc_arg) @content
|
||||
(#set! "language" "c++"))
|
||||
|
||||
(preproc_function_def
|
||||
value: (preproc_arg) @content
|
||||
(#set! "language" "c++"))
|
@ -1,149 +0,0 @@
|
||||
(preproc_def
|
||||
"#define" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
(preproc_function_def
|
||||
"#define" @context
|
||||
name: (_) @name
|
||||
parameters: (preproc_params
|
||||
"(" @context
|
||||
")" @context)) @item
|
||||
|
||||
(type_definition
|
||||
"typedef" @context
|
||||
declarator: (_) @name) @item
|
||||
|
||||
(struct_specifier
|
||||
"struct" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
(class_specifier
|
||||
"class" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
(enum_specifier
|
||||
"enum" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
(enumerator
|
||||
name: (_) @name) @item
|
||||
|
||||
(declaration
|
||||
(storage_class_specifier) @context
|
||||
(type_qualifier)? @context
|
||||
type: (_) @context
|
||||
declarator: (init_declarator
|
||||
declarator: (_) @name)) @item
|
||||
|
||||
(function_definition
|
||||
(type_qualifier)? @context
|
||||
type: (_)? @context
|
||||
declarator: [
|
||||
(function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context))
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)))
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (pointer_declarator
|
||||
"*" @context
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context))))
|
||||
(reference_declarator
|
||||
["&" "&&"] @context
|
||||
(function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)))
|
||||
]
|
||||
(type_qualifier)? @context) @item
|
||||
|
||||
(declaration
|
||||
(type_qualifier)? @context
|
||||
type: (_)? @context
|
||||
declarator: [
|
||||
(field_identifier) @name
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (field_identifier) @name)
|
||||
(function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context))
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)))
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (pointer_declarator
|
||||
"*" @context
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context))))
|
||||
(reference_declarator
|
||||
["&" "&&"] @context
|
||||
(function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)))
|
||||
]
|
||||
(type_qualifier)? @context) @item
|
||||
|
||||
(field_declaration
|
||||
(type_qualifier)? @context
|
||||
type: (_) @context
|
||||
declarator: [
|
||||
(field_identifier) @name
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (field_identifier) @name)
|
||||
(function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context))
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)))
|
||||
(pointer_declarator
|
||||
"*" @context
|
||||
declarator: (pointer_declarator
|
||||
"*" @context
|
||||
declarator: (function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context))))
|
||||
(reference_declarator
|
||||
["&" "&&"] @context
|
||||
(function_declarator
|
||||
declarator: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)))
|
||||
]
|
||||
(type_qualifier)? @context) @item
|
@ -1,2 +0,0 @@
|
||||
(comment) @comment
|
||||
(string_literal) @string
|
@ -1,130 +0,0 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use node_runtime::NodeRuntime;
|
||||
use serde_json::json;
|
||||
use smol::fs;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
const SERVER_PATH: &'static str =
|
||||
"node_modules/vscode-langservers-extracted/bin/vscode-css-language-server";
|
||||
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
}
|
||||
|
||||
pub struct CssLspAdapter {
|
||||
node: Arc<dyn NodeRuntime>,
|
||||
}
|
||||
|
||||
impl CssLspAdapter {
|
||||
pub fn new(node: Arc<dyn NodeRuntime>) -> Self {
|
||||
CssLspAdapter { node }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl LspAdapter for CssLspAdapter {
|
||||
async fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("vscode-css-language-server".into())
|
||||
}
|
||||
|
||||
fn short_name(&self) -> &'static str {
|
||||
"css"
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version("vscode-langservers-extracted")
|
||||
.await?,
|
||||
) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
if fs::metadata(&server_path).await.is_err() {
|
||||
self.node
|
||||
.npm_install_packages(
|
||||
&container_dir,
|
||||
&[("vscode-langservers-extracted", version.as_str())],
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &*self.node).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &*self.node).await
|
||||
}
|
||||
|
||||
async fn initialization_options(&self) -> Option<serde_json::Value> {
|
||||
Some(json!({
|
||||
"provideFormatter": true
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_cached_server_binary(
|
||||
container_dir: PathBuf,
|
||||
node: &dyn NodeRuntime,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
(|| async move {
|
||||
let mut last_version_dir = None;
|
||||
let mut entries = fs::read_dir(&container_dir).await?;
|
||||
while let Some(entry) = entries.next().await {
|
||||
let entry = entry?;
|
||||
if entry.file_type().await?.is_dir() {
|
||||
last_version_dir = Some(entry.path());
|
||||
}
|
||||
}
|
||||
let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
||||
let server_path = last_version_dir.join(SERVER_PATH);
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"missing executable in directory {:?}",
|
||||
last_version_dir
|
||||
))
|
||||
}
|
||||
})()
|
||||
.await
|
||||
.log_err()
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
("(" @open ")" @close)
|
||||
("[" @open "]" @close)
|
||||
("{" @open "}" @close)
|
@ -1,13 +0,0 @@
|
||||
name = "CSS"
|
||||
path_suffixes = ["css"]
|
||||
autoclose_before = ";:.,=}])>"
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
]
|
||||
word_characters = ["-"]
|
||||
block_comment = ["/* ", " */"]
|
||||
prettier_parser_name = "css"
|
@ -1,78 +0,0 @@
|
||||
(comment) @comment
|
||||
|
||||
[
|
||||
(tag_name)
|
||||
(nesting_selector)
|
||||
(universal_selector)
|
||||
] @tag
|
||||
|
||||
[
|
||||
"~"
|
||||
">"
|
||||
"+"
|
||||
"-"
|
||||
"*"
|
||||
"/"
|
||||
"="
|
||||
"^="
|
||||
"|="
|
||||
"~="
|
||||
"$="
|
||||
"*="
|
||||
"and"
|
||||
"or"
|
||||
"not"
|
||||
"only"
|
||||
] @operator
|
||||
|
||||
(attribute_selector (plain_value) @string)
|
||||
|
||||
(attribute_name) @attribute
|
||||
(pseudo_element_selector (tag_name) @attribute)
|
||||
(pseudo_class_selector (class_name) @attribute)
|
||||
|
||||
[
|
||||
(class_name)
|
||||
(id_name)
|
||||
(namespace_name)
|
||||
(property_name)
|
||||
(feature_name)
|
||||
] @property
|
||||
|
||||
(function_name) @function
|
||||
|
||||
(
|
||||
[
|
||||
(property_name)
|
||||
(plain_value)
|
||||
] @variable.special
|
||||
(#match? @variable.special "^--")
|
||||
)
|
||||
|
||||
[
|
||||
"@media"
|
||||
"@import"
|
||||
"@charset"
|
||||
"@namespace"
|
||||
"@supports"
|
||||
"@keyframes"
|
||||
(at_keyword)
|
||||
(to)
|
||||
(from)
|
||||
(important)
|
||||
] @keyword
|
||||
|
||||
(string_value) @string
|
||||
(color_value) @string.special
|
||||
|
||||
[
|
||||
(integer_value)
|
||||
(float_value)
|
||||
] @number
|
||||
|
||||
(unit) @type
|
||||
|
||||
[
|
||||
","
|
||||
":"
|
||||
] @punctuation.delimiter
|
@ -1 +0,0 @@
|
||||
(_ "{" "}" @end) @indent
|
@ -1,2 +0,0 @@
|
||||
(comment) @comment
|
||||
(string_value) @string
|
@ -1,542 +0,0 @@
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use gpui::{AsyncAppContext, Task};
|
||||
pub use language::*;
|
||||
use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind};
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use smol::fs::{self, File};
|
||||
use std::{
|
||||
any::Any,
|
||||
env::consts,
|
||||
ops::Deref,
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering::SeqCst},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
use util::{
|
||||
async_maybe,
|
||||
fs::remove_matching,
|
||||
github::{latest_github_release, GitHubLspBinaryVersion},
|
||||
ResultExt,
|
||||
};
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct ElixirSettings {
|
||||
pub lsp: ElixirLspSetting,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ElixirLspSetting {
|
||||
ElixirLs,
|
||||
NextLs,
|
||||
Local {
|
||||
path: String,
|
||||
arguments: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Default, Deserialize, JsonSchema)]
|
||||
pub struct ElixirSettingsContent {
|
||||
lsp: Option<ElixirLspSetting>,
|
||||
}
|
||||
|
||||
impl Settings for ElixirSettings {
|
||||
const KEY: Option<&'static str> = Some("elixir");
|
||||
|
||||
type FileContent = ElixirSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &mut gpui::AppContext,
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ElixirLspAdapter;
|
||||
|
||||
#[async_trait]
|
||||
impl LspAdapter for ElixirLspAdapter {
|
||||
async fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("elixir-ls".into())
|
||||
}
|
||||
|
||||
fn short_name(&self) -> &'static str {
|
||||
"elixir-ls"
|
||||
}
|
||||
|
||||
fn will_start_server(
|
||||
&self,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
const NOTIFICATION_MESSAGE: &str = "Could not run the elixir language server, `elixir-ls`, because `elixir` was not found.";
|
||||
|
||||
let delegate = delegate.clone();
|
||||
Some(cx.spawn(|cx| async move {
|
||||
let elixir_output = smol::process::Command::new("elixir")
|
||||
.args(["--version"])
|
||||
.output()
|
||||
.await;
|
||||
if elixir_output.is_err() {
|
||||
if DID_SHOW_NOTIFICATION
|
||||
.compare_exchange(false, true, SeqCst, SeqCst)
|
||||
.is_ok()
|
||||
{
|
||||
cx.update(|cx| {
|
||||
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
|
||||
})?
|
||||
}
|
||||
return Err(anyhow!("cannot run elixir-ls"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}))
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
let http = delegate.http_client();
|
||||
let release = latest_github_release("elixir-lsp/elixir-ls", false, http).await?;
|
||||
let version_name = release
|
||||
.name
|
||||
.strip_prefix("Release ")
|
||||
.context("Elixir-ls release name does not start with prefix")?
|
||||
.to_owned();
|
||||
|
||||
let asset_name = format!("elixir-ls-{}.zip", &version_name);
|
||||
let asset = release
|
||||
.assets
|
||||
.iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
||||
|
||||
let version = GitHubLspBinaryVersion {
|
||||
name: version_name,
|
||||
url: asset.browser_download_url.clone(),
|
||||
};
|
||||
Ok(Box::new(version) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
let zip_path = container_dir.join(format!("elixir-ls_{}.zip", version.name));
|
||||
let folder_path = container_dir.join("elixir-ls");
|
||||
let binary_path = folder_path.join("language_server.sh");
|
||||
|
||||
if fs::metadata(&binary_path).await.is_err() {
|
||||
let mut response = delegate
|
||||
.http_client()
|
||||
.get(&version.url, Default::default(), true)
|
||||
.await
|
||||
.context("error downloading release")?;
|
||||
let mut file = File::create(&zip_path)
|
||||
.await
|
||||
.with_context(|| format!("failed to create file {}", zip_path.display()))?;
|
||||
if !response.status().is_success() {
|
||||
Err(anyhow!(
|
||||
"download failed with status {}",
|
||||
response.status().to_string()
|
||||
))?;
|
||||
}
|
||||
futures::io::copy(response.body_mut(), &mut file).await?;
|
||||
|
||||
fs::create_dir_all(&folder_path)
|
||||
.await
|
||||
.with_context(|| format!("failed to create directory {}", folder_path.display()))?;
|
||||
let unzip_status = smol::process::Command::new("unzip")
|
||||
.arg(&zip_path)
|
||||
.arg("-d")
|
||||
.arg(&folder_path)
|
||||
.output()
|
||||
.await?
|
||||
.status;
|
||||
if !unzip_status.success() {
|
||||
Err(anyhow!("failed to unzip elixir-ls archive"))?;
|
||||
}
|
||||
|
||||
remove_matching(&container_dir, |entry| entry != folder_path).await;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
arguments: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary_elixir_ls(container_dir).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary_elixir_ls(container_dir).await
|
||||
}
|
||||
|
||||
async fn label_for_completion(
|
||||
&self,
|
||||
completion: &lsp::CompletionItem,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
match completion.kind.zip(completion.detail.as_ref()) {
|
||||
Some((_, detail)) if detail.starts_with("(function)") => {
|
||||
let text = detail.strip_prefix("(function) ")?;
|
||||
let filter_range = 0..text.find('(').unwrap_or(text.len());
|
||||
let source = Rope::from(format!("def {text}").as_str());
|
||||
let runs = language.highlight_text(&source, 4..4 + text.len());
|
||||
return Some(CodeLabel {
|
||||
text: text.to_string(),
|
||||
runs,
|
||||
filter_range,
|
||||
});
|
||||
}
|
||||
Some((_, detail)) if detail.starts_with("(macro)") => {
|
||||
let text = detail.strip_prefix("(macro) ")?;
|
||||
let filter_range = 0..text.find('(').unwrap_or(text.len());
|
||||
let source = Rope::from(format!("defmacro {text}").as_str());
|
||||
let runs = language.highlight_text(&source, 9..9 + text.len());
|
||||
return Some(CodeLabel {
|
||||
text: text.to_string(),
|
||||
runs,
|
||||
filter_range,
|
||||
});
|
||||
}
|
||||
Some((
|
||||
CompletionItemKind::CLASS
|
||||
| CompletionItemKind::MODULE
|
||||
| CompletionItemKind::INTERFACE
|
||||
| CompletionItemKind::STRUCT,
|
||||
_,
|
||||
)) => {
|
||||
let filter_range = 0..completion
|
||||
.label
|
||||
.find(" (")
|
||||
.unwrap_or(completion.label.len());
|
||||
let text = &completion.label[filter_range.clone()];
|
||||
let source = Rope::from(format!("defmodule {text}").as_str());
|
||||
let runs = language.highlight_text(&source, 10..10 + text.len());
|
||||
return Some(CodeLabel {
|
||||
text: completion.label.clone(),
|
||||
runs,
|
||||
filter_range,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
async fn label_for_symbol(
|
||||
&self,
|
||||
name: &str,
|
||||
kind: SymbolKind,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
let (text, filter_range, display_range) = match kind {
|
||||
SymbolKind::METHOD | SymbolKind::FUNCTION => {
|
||||
let text = format!("def {}", name);
|
||||
let filter_range = 4..4 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
SymbolKind::CLASS | SymbolKind::MODULE | SymbolKind::INTERFACE | SymbolKind::STRUCT => {
|
||||
let text = format!("defmodule {}", name);
|
||||
let filter_range = 10..10 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(CodeLabel {
|
||||
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
|
||||
text: text[display_range].to_string(),
|
||||
filter_range,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_cached_server_binary_elixir_ls(
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let server_path = container_dir.join("elixir-ls/language_server.sh");
|
||||
if server_path.exists() {
|
||||
Some(LanguageServerBinary {
|
||||
path: server_path,
|
||||
arguments: vec![],
|
||||
})
|
||||
} else {
|
||||
log::error!("missing executable in directory {:?}", server_path);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NextLspAdapter;
|
||||
|
||||
#[async_trait]
|
||||
impl LspAdapter for NextLspAdapter {
|
||||
async fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("next-ls".into())
|
||||
}
|
||||
|
||||
fn short_name(&self) -> &'static str {
|
||||
"next-ls"
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
let release =
|
||||
latest_github_release("elixir-tools/next-ls", false, delegate.http_client()).await?;
|
||||
let version = release.name.clone();
|
||||
let platform = match consts::ARCH {
|
||||
"x86_64" => "darwin_amd64",
|
||||
"aarch64" => "darwin_arm64",
|
||||
other => bail!("Running on unsupported platform: {other}"),
|
||||
};
|
||||
let asset_name = format!("next_ls_{}", platform);
|
||||
let asset = release
|
||||
.assets
|
||||
.iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
||||
let version = GitHubLspBinaryVersion {
|
||||
name: version,
|
||||
url: asset.browser_download_url.clone(),
|
||||
};
|
||||
Ok(Box::new(version) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<GitHubLspBinaryVersion>().unwrap();
|
||||
|
||||
let binary_path = container_dir.join("next-ls");
|
||||
|
||||
if fs::metadata(&binary_path).await.is_err() {
|
||||
let mut response = delegate
|
||||
.http_client()
|
||||
.get(&version.url, Default::default(), true)
|
||||
.await
|
||||
.map_err(|err| anyhow!("error downloading release: {}", err))?;
|
||||
|
||||
let mut file = smol::fs::File::create(&binary_path).await?;
|
||||
if !response.status().is_success() {
|
||||
Err(anyhow!(
|
||||
"download failed with status {}",
|
||||
response.status().to_string()
|
||||
))?;
|
||||
}
|
||||
futures::io::copy(response.body_mut(), &mut file).await?;
|
||||
|
||||
fs::set_permissions(
|
||||
&binary_path,
|
||||
<fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path,
|
||||
arguments: vec!["--stdio".into()],
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary_next(container_dir)
|
||||
.await
|
||||
.map(|mut binary| {
|
||||
binary.arguments = vec!["--stdio".into()];
|
||||
binary
|
||||
})
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary_next(container_dir)
|
||||
.await
|
||||
.map(|mut binary| {
|
||||
binary.arguments = vec!["--help".into()];
|
||||
binary
|
||||
})
|
||||
}
|
||||
|
||||
async fn label_for_completion(
|
||||
&self,
|
||||
completion: &lsp::CompletionItem,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
label_for_completion_elixir(completion, language)
|
||||
}
|
||||
|
||||
async fn label_for_symbol(
|
||||
&self,
|
||||
name: &str,
|
||||
symbol_kind: SymbolKind,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
label_for_symbol_elixir(name, symbol_kind, language)
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_cached_server_binary_next(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||
async_maybe!({
|
||||
let mut last_binary_path = None;
|
||||
let mut entries = fs::read_dir(&container_dir).await?;
|
||||
while let Some(entry) = entries.next().await {
|
||||
let entry = entry?;
|
||||
if entry.file_type().await?.is_file()
|
||||
&& entry
|
||||
.file_name()
|
||||
.to_str()
|
||||
.map_or(false, |name| name == "next-ls")
|
||||
{
|
||||
last_binary_path = Some(entry.path());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(path) = last_binary_path {
|
||||
Ok(LanguageServerBinary {
|
||||
path,
|
||||
arguments: Vec::new(),
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("no cached binary"))
|
||||
}
|
||||
})
|
||||
.await
|
||||
.log_err()
|
||||
}
|
||||
|
||||
pub struct LocalLspAdapter {
|
||||
pub path: String,
|
||||
pub arguments: Vec<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl LspAdapter for LocalLspAdapter {
|
||||
async fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("local-ls".into())
|
||||
}
|
||||
|
||||
fn short_name(&self) -> &'static str {
|
||||
"local-ls"
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
Ok(Box::new(()) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_: Box<dyn 'static + Send + Any>,
|
||||
_: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let path = shellexpand::full(&self.path)?;
|
||||
Ok(LanguageServerBinary {
|
||||
path: PathBuf::from(path.deref()),
|
||||
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
_: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
let path = shellexpand::full(&self.path).ok()?;
|
||||
Some(LanguageServerBinary {
|
||||
path: PathBuf::from(path.deref()),
|
||||
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||
let path = shellexpand::full(&self.path).ok()?;
|
||||
Some(LanguageServerBinary {
|
||||
path: PathBuf::from(path.deref()),
|
||||
arguments: self.arguments.iter().map(|arg| arg.into()).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn label_for_completion(
|
||||
&self,
|
||||
completion: &lsp::CompletionItem,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
label_for_completion_elixir(completion, language)
|
||||
}
|
||||
|
||||
async fn label_for_symbol(
|
||||
&self,
|
||||
name: &str,
|
||||
symbol: SymbolKind,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
label_for_symbol_elixir(name, symbol, language)
|
||||
}
|
||||
}
|
||||
|
||||
fn label_for_completion_elixir(
|
||||
completion: &lsp::CompletionItem,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
return Some(CodeLabel {
|
||||
runs: language.highlight_text(&completion.label.clone().into(), 0..completion.label.len()),
|
||||
text: completion.label.clone(),
|
||||
filter_range: 0..completion.label.len(),
|
||||
});
|
||||
}
|
||||
|
||||
fn label_for_symbol_elixir(
|
||||
name: &str,
|
||||
_: SymbolKind,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
Some(CodeLabel {
|
||||
runs: language.highlight_text(&name.into(), 0..name.len()),
|
||||
text: name.to_string(),
|
||||
filter_range: 0..name.len(),
|
||||
})
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
("(" @open ")" @close)
|
||||
("[" @open "]" @close)
|
||||
("{" @open "}" @close)
|
||||
("\"" @open "\"" @close)
|
||||
("do" @open "end" @close)
|
@ -1,16 +0,0 @@
|
||||
name = "Elixir"
|
||||
path_suffixes = ["ex", "exs"]
|
||||
line_comment = "# "
|
||||
autoclose_before = ";:.,=}])>"
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
]
|
||||
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
||||
|
||||
[overrides.string]
|
||||
word_characters = ["-"]
|
||||
opt_into_language_servers = ["tailwindcss-language-server"]
|
@ -1,27 +0,0 @@
|
||||
(
|
||||
(unary_operator
|
||||
operator: "@"
|
||||
operand: (call
|
||||
target: (identifier) @unary
|
||||
(#match? @unary "^(doc)$"))
|
||||
) @context
|
||||
.
|
||||
(call
|
||||
target: (identifier) @name
|
||||
(arguments
|
||||
[
|
||||
(identifier) @name
|
||||
(call
|
||||
target: (identifier) @name)
|
||||
(binary_operator
|
||||
left: (call
|
||||
target: (identifier) @name)
|
||||
operator: "when")
|
||||
])
|
||||
(#match? @name "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
|
||||
)
|
||||
|
||||
(call
|
||||
target: (identifier) @name
|
||||
(arguments (alias) @name)
|
||||
(#match? @name "^(defmodule|defprotocol)$")) @item
|
@ -1,153 +0,0 @@
|
||||
["when" "and" "or" "not" "in" "not in" "fn" "do" "end" "catch" "rescue" "after" "else"] @keyword
|
||||
|
||||
(unary_operator
|
||||
operator: "&"
|
||||
operand: (integer) @operator)
|
||||
|
||||
(operator_identifier) @operator
|
||||
|
||||
(unary_operator
|
||||
operator: _ @operator)
|
||||
|
||||
(binary_operator
|
||||
operator: _ @operator)
|
||||
|
||||
(dot
|
||||
operator: _ @operator)
|
||||
|
||||
(stab_clause
|
||||
operator: _ @operator)
|
||||
|
||||
[
|
||||
(boolean)
|
||||
(nil)
|
||||
] @constant
|
||||
|
||||
[
|
||||
(integer)
|
||||
(float)
|
||||
] @number
|
||||
|
||||
(alias) @type
|
||||
|
||||
(call
|
||||
target: (dot
|
||||
left: (atom) @type))
|
||||
|
||||
(char) @constant
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
[
|
||||
(atom)
|
||||
(quoted_atom)
|
||||
(keyword)
|
||||
(quoted_keyword)
|
||||
] @string.special.symbol
|
||||
|
||||
[
|
||||
(string)
|
||||
(charlist)
|
||||
] @string
|
||||
|
||||
(sigil
|
||||
(sigil_name) @__name__
|
||||
quoted_start: _ @string
|
||||
quoted_end: _ @string
|
||||
(#match? @__name__ "^[sS]$")) @string
|
||||
|
||||
(sigil
|
||||
(sigil_name) @__name__
|
||||
quoted_start: _ @string.regex
|
||||
quoted_end: _ @string.regex
|
||||
(#match? @__name__ "^[rR]$")) @string.regex
|
||||
|
||||
(sigil
|
||||
(sigil_name) @__name__
|
||||
quoted_start: _ @string.special
|
||||
quoted_end: _ @string.special) @string.special
|
||||
|
||||
(
|
||||
(identifier) @comment.unused
|
||||
(#match? @comment.unused "^_")
|
||||
)
|
||||
|
||||
(call
|
||||
target: [
|
||||
(identifier) @function
|
||||
(dot
|
||||
right: (identifier) @function)
|
||||
])
|
||||
|
||||
(call
|
||||
target: (identifier) @keyword
|
||||
(arguments
|
||||
[
|
||||
(identifier) @function
|
||||
(binary_operator
|
||||
left: (identifier) @function
|
||||
operator: "when")
|
||||
(binary_operator
|
||||
operator: "|>"
|
||||
right: (identifier))
|
||||
])
|
||||
(#match? @keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp)$"))
|
||||
|
||||
(binary_operator
|
||||
operator: "|>"
|
||||
right: (identifier) @function)
|
||||
|
||||
(call
|
||||
target: (identifier) @keyword
|
||||
(#match? @keyword "^(def|defdelegate|defexception|defguard|defguardp|defimpl|defmacro|defmacrop|defmodule|defn|defnp|defoverridable|defp|defprotocol|defstruct)$"))
|
||||
|
||||
(call
|
||||
target: (identifier) @keyword
|
||||
(#match? @keyword "^(alias|case|cond|else|for|if|import|quote|raise|receive|require|reraise|super|throw|try|unless|unquote|unquote_splicing|use|with)$"))
|
||||
|
||||
(
|
||||
(identifier) @constant.builtin
|
||||
(#match? @constant.builtin "^(__MODULE__|__DIR__|__ENV__|__CALLER__|__STACKTRACE__)$")
|
||||
)
|
||||
|
||||
(unary_operator
|
||||
operator: "@" @comment.doc
|
||||
operand: (call
|
||||
target: (identifier) @__attribute__ @comment.doc
|
||||
(arguments
|
||||
[
|
||||
(string)
|
||||
(charlist)
|
||||
(sigil)
|
||||
(boolean)
|
||||
] @comment.doc))
|
||||
(#match? @__attribute__ "^(moduledoc|typedoc|doc)$"))
|
||||
|
||||
(comment) @comment
|
||||
|
||||
[
|
||||
"%"
|
||||
] @punctuation
|
||||
|
||||
[
|
||||
","
|
||||
";"
|
||||
] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
"<<"
|
||||
">>"
|
||||
] @punctuation.bracket
|
||||
|
||||
(interpolation "#{" @punctuation.special "}" @punctuation.special) @embedded
|
||||
|
||||
((sigil
|
||||
(sigil_name) @_sigil_name
|
||||
(quoted_content) @embedded)
|
||||
(#eq? @_sigil_name "H"))
|
@ -1,6 +0,0 @@
|
||||
(call) @indent
|
||||
|
||||
(_ "[" "]" @end) @indent
|
||||
(_ "{" "}" @end) @indent
|
||||
(_ "(" ")" @end) @indent
|
||||
(_ "do" "end" @end) @indent
|
@ -1,7 +0,0 @@
|
||||
; Phoenix HTML template
|
||||
|
||||
((sigil
|
||||
(sigil_name) @_sigil_name
|
||||
(quoted_content) @content)
|
||||
(#eq? @_sigil_name "H")
|
||||
(#set! language "heex"))
|
@ -1,26 +0,0 @@
|
||||
(call
|
||||
target: (identifier) @context
|
||||
(arguments (alias) @name)
|
||||
(#match? @context "^(defmodule|defprotocol)$")) @item
|
||||
|
||||
(call
|
||||
target: (identifier) @context
|
||||
(arguments
|
||||
[
|
||||
(identifier) @name
|
||||
(call
|
||||
target: (identifier) @name
|
||||
(arguments
|
||||
"(" @context.extra
|
||||
_* @context.extra
|
||||
")" @context.extra))
|
||||
(binary_operator
|
||||
left: (call
|
||||
target: (identifier) @name
|
||||
(arguments
|
||||
"(" @context.extra
|
||||
_* @context.extra
|
||||
")" @context.extra))
|
||||
operator: "when")
|
||||
])
|
||||
(#match? @context "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
|
@ -1,2 +0,0 @@
|
||||
(comment) @comment
|
||||
[(string) (charlist)] @string
|
@ -1,11 +0,0 @@
|
||||
name = "Elm"
|
||||
path_suffixes = ["elm"]
|
||||
line_comment = "-- "
|
||||
block_comment = ["{- ", " -}"]
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
]
|
@ -1,72 +0,0 @@
|
||||
[
|
||||
"if"
|
||||
"then"
|
||||
"else"
|
||||
"let"
|
||||
"in"
|
||||
(case)
|
||||
(of)
|
||||
(backslash)
|
||||
(as)
|
||||
(port)
|
||||
(exposing)
|
||||
(alias)
|
||||
(import)
|
||||
(module)
|
||||
(type)
|
||||
(arrow)
|
||||
] @keyword
|
||||
|
||||
[
|
||||
(eq)
|
||||
(operator_identifier)
|
||||
(colon)
|
||||
] @operator
|
||||
|
||||
(type_annotation(lower_case_identifier) @function)
|
||||
(port_annotation(lower_case_identifier) @function)
|
||||
(function_declaration_left(lower_case_identifier) @function.definition)
|
||||
|
||||
(function_call_expr
|
||||
target: (value_expr
|
||||
name: (value_qid (lower_case_identifier) @function)))
|
||||
|
||||
(exposed_value(lower_case_identifier) @function)
|
||||
(exposed_type(upper_case_identifier) @type)
|
||||
|
||||
(field_access_expr(value_expr(value_qid)) @identifier)
|
||||
(lower_pattern) @variable
|
||||
(record_base_identifier) @identifier
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
"|"
|
||||
","
|
||||
] @punctuation.delimiter
|
||||
|
||||
(number_constant_expr) @constant
|
||||
|
||||
(type_declaration(upper_case_identifier) @type)
|
||||
(type_ref) @type
|
||||
(type_alias_declaration name: (upper_case_identifier) @type)
|
||||
|
||||
(value_expr(upper_case_qid(upper_case_identifier)) @type)
|
||||
|
||||
[
|
||||
(line_comment)
|
||||
(block_comment)
|
||||
] @comment
|
||||
|
||||
(string_escape) @string.escape
|
||||
|
||||
[
|
||||
(open_quote)
|
||||
(close_quote)
|
||||
(regular_string_part)
|
||||
(open_char)
|
||||
(close_char)
|
||||
] @string
|
@ -1,2 +0,0 @@
|
||||
((glsl_content) @content
|
||||
(#set! "language" "glsl"))
|
@ -1,22 +0,0 @@
|
||||
(type_declaration
|
||||
(type) @context
|
||||
(upper_case_identifier) @name) @item
|
||||
|
||||
(type_alias_declaration
|
||||
(type) @context
|
||||
(alias) @context
|
||||
name: (upper_case_identifier) @name) @item
|
||||
|
||||
(type_alias_declaration
|
||||
typeExpression:
|
||||
(type_expression
|
||||
part: (record_type
|
||||
(field_type
|
||||
name: (lower_case_identifier) @name) @item)))
|
||||
|
||||
(union_variant
|
||||
name: (upper_case_identifier) @name) @item
|
||||
|
||||
(value_declaration
|
||||
functionDeclarationLeft:
|
||||
(function_declaration_left(lower_case_identifier) @name)) @item
|
@ -1,8 +0,0 @@
|
||||
name = "ERB"
|
||||
path_suffixes = ["erb"]
|
||||
autoclose_before = ">})"
|
||||
brackets = [
|
||||
{ start = "<", end = ">", close = true, newline = true },
|
||||
]
|
||||
block_comment = ["<%#", "%>"]
|
||||
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
@ -1,12 +0,0 @@
|
||||
(comment_directive) @comment
|
||||
|
||||
[
|
||||
"<%#"
|
||||
"<%"
|
||||
"<%="
|
||||
"<%_"
|
||||
"<%-"
|
||||
"%>"
|
||||
"-%>"
|
||||
"_%>"
|
||||
] @keyword
|
@ -1,7 +0,0 @@
|
||||
((code) @content
|
||||
(#set! "language" "ruby")
|
||||
(#set! "combined"))
|
||||
|
||||
((content) @content
|
||||
(#set! "language" "html")
|
||||
(#set! "combined"))
|
@ -1,9 +0,0 @@
|
||||
name = "GLSL"
|
||||
path_suffixes = ["vert", "frag", "tesc", "tese", "geom", "comp"]
|
||||
line_comment = "// "
|
||||
block_comment = ["/* ", " */"]
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
]
|
@ -1,118 +0,0 @@
|
||||
"break" @keyword
|
||||
"case" @keyword
|
||||
"const" @keyword
|
||||
"continue" @keyword
|
||||
"default" @keyword
|
||||
"do" @keyword
|
||||
"else" @keyword
|
||||
"enum" @keyword
|
||||
"extern" @keyword
|
||||
"for" @keyword
|
||||
"if" @keyword
|
||||
"inline" @keyword
|
||||
"return" @keyword
|
||||
"sizeof" @keyword
|
||||
"static" @keyword
|
||||
"struct" @keyword
|
||||
"switch" @keyword
|
||||
"typedef" @keyword
|
||||
"union" @keyword
|
||||
"volatile" @keyword
|
||||
"while" @keyword
|
||||
|
||||
"#define" @keyword
|
||||
"#elif" @keyword
|
||||
"#else" @keyword
|
||||
"#endif" @keyword
|
||||
"#if" @keyword
|
||||
"#ifdef" @keyword
|
||||
"#ifndef" @keyword
|
||||
"#include" @keyword
|
||||
(preproc_directive) @keyword
|
||||
|
||||
"--" @operator
|
||||
"-" @operator
|
||||
"-=" @operator
|
||||
"->" @operator
|
||||
"=" @operator
|
||||
"!=" @operator
|
||||
"*" @operator
|
||||
"&" @operator
|
||||
"&&" @operator
|
||||
"+" @operator
|
||||
"++" @operator
|
||||
"+=" @operator
|
||||
"<" @operator
|
||||
"==" @operator
|
||||
">" @operator
|
||||
"||" @operator
|
||||
|
||||
"." @delimiter
|
||||
";" @delimiter
|
||||
|
||||
(string_literal) @string
|
||||
(system_lib_string) @string
|
||||
|
||||
(null) @constant
|
||||
(number_literal) @number
|
||||
(char_literal) @number
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function)
|
||||
(call_expression
|
||||
function: (field_expression
|
||||
field: (field_identifier) @function))
|
||||
(function_declarator
|
||||
declarator: (identifier) @function)
|
||||
(preproc_function_def
|
||||
name: (identifier) @function.special)
|
||||
|
||||
(field_identifier) @property
|
||||
(statement_identifier) @label
|
||||
(type_identifier) @type
|
||||
(primitive_type) @type
|
||||
(sized_type_specifier) @type
|
||||
|
||||
((identifier) @constant
|
||||
(#match? @constant "^[A-Z][A-Z\\d_]*$"))
|
||||
|
||||
(identifier) @variable
|
||||
|
||||
(comment) @comment
|
||||
; inherits: c
|
||||
|
||||
[
|
||||
"in"
|
||||
"out"
|
||||
"inout"
|
||||
"uniform"
|
||||
"shared"
|
||||
"layout"
|
||||
"attribute"
|
||||
"varying"
|
||||
"buffer"
|
||||
"coherent"
|
||||
"readonly"
|
||||
"writeonly"
|
||||
"precision"
|
||||
"highp"
|
||||
"mediump"
|
||||
"lowp"
|
||||
"centroid"
|
||||
"sample"
|
||||
"patch"
|
||||
"smooth"
|
||||
"flat"
|
||||
"noperspective"
|
||||
"invariant"
|
||||
"precise"
|
||||
] @type.qualifier
|
||||
|
||||
"subroutine" @keyword.function
|
||||
|
||||
(extension_storage_class) @storageclass
|
||||
|
||||
(
|
||||
(identifier) @variable.builtin
|
||||
(#match? @variable.builtin "^gl_")
|
||||
)
|
@ -1,461 +0,0 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use gpui::{AsyncAppContext, Task};
|
||||
pub use language::*;
|
||||
use lazy_static::lazy_static;
|
||||
use lsp::LanguageServerBinary;
|
||||
use regex::Regex;
|
||||
use smol::{fs, process};
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::{OsStr, OsString},
|
||||
ops::Range,
|
||||
path::PathBuf,
|
||||
str,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering::SeqCst},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
use util::{fs::remove_matching, github::latest_github_release, ResultExt};
|
||||
|
||||
fn server_binary_arguments() -> Vec<OsString> {
|
||||
vec!["-mode=stdio".into()]
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct GoLspAdapter;
|
||||
|
||||
lazy_static! {
|
||||
static ref GOPLS_VERSION_REGEX: Regex = Regex::new(r"\d+\.\d+\.\d+").unwrap();
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl super::LspAdapter for GoLspAdapter {
|
||||
async fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("gopls".into())
|
||||
}
|
||||
|
||||
fn short_name(&self) -> &'static str {
|
||||
"gopls"
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
let release = latest_github_release("golang/tools", false, delegate.http_client()).await?;
|
||||
let version: Option<String> = release.name.strip_prefix("gopls/v").map(str::to_string);
|
||||
if version.is_none() {
|
||||
log::warn!(
|
||||
"couldn't infer gopls version from github release name '{}'",
|
||||
release.name
|
||||
);
|
||||
}
|
||||
Ok(Box::new(version) as Box<_>)
|
||||
}
|
||||
|
||||
fn will_fetch_server(
|
||||
&self,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Option<Task<Result<()>>> {
|
||||
static DID_SHOW_NOTIFICATION: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
const NOTIFICATION_MESSAGE: &str =
|
||||
"Could not install the Go language server `gopls`, because `go` was not found.";
|
||||
|
||||
let delegate = delegate.clone();
|
||||
Some(cx.spawn(|cx| async move {
|
||||
let install_output = process::Command::new("go").args(["version"]).output().await;
|
||||
if install_output.is_err() {
|
||||
if DID_SHOW_NOTIFICATION
|
||||
.compare_exchange(false, true, SeqCst, SeqCst)
|
||||
.is_ok()
|
||||
{
|
||||
cx.update(|cx| {
|
||||
delegate.show_notification(NOTIFICATION_MESSAGE, cx);
|
||||
})?
|
||||
}
|
||||
return Err(anyhow!("cannot install gopls"));
|
||||
}
|
||||
Ok(())
|
||||
}))
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
container_dir: PathBuf,
|
||||
delegate: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<Option<String>>().unwrap();
|
||||
let this = *self;
|
||||
|
||||
if let Some(version) = *version {
|
||||
let binary_path = container_dir.join(&format!("gopls_{version}"));
|
||||
if let Ok(metadata) = fs::metadata(&binary_path).await {
|
||||
if metadata.is_file() {
|
||||
remove_matching(&container_dir, |entry| {
|
||||
entry != binary_path && entry.file_name() != Some(OsStr::new("gobin"))
|
||||
})
|
||||
.await;
|
||||
|
||||
return Ok(LanguageServerBinary {
|
||||
path: binary_path.to_path_buf(),
|
||||
arguments: server_binary_arguments(),
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if let Some(path) = this
|
||||
.cached_server_binary(container_dir.clone(), delegate)
|
||||
.await
|
||||
{
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
let gobin_dir = container_dir.join("gobin");
|
||||
fs::create_dir_all(&gobin_dir).await?;
|
||||
let install_output = process::Command::new("go")
|
||||
.env("GO111MODULE", "on")
|
||||
.env("GOBIN", &gobin_dir)
|
||||
.args(["install", "golang.org/x/tools/gopls@latest"])
|
||||
.output()
|
||||
.await?;
|
||||
if !install_output.status.success() {
|
||||
Err(anyhow!("failed to install gopls. Is go installed?"))?;
|
||||
}
|
||||
|
||||
let installed_binary_path = gobin_dir.join("gopls");
|
||||
let version_output = process::Command::new(&installed_binary_path)
|
||||
.arg("version")
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| anyhow!("failed to run installed gopls binary {:?}", e))?;
|
||||
let version_stdout = str::from_utf8(&version_output.stdout)
|
||||
.map_err(|_| anyhow!("gopls version produced invalid utf8"))?;
|
||||
let version = GOPLS_VERSION_REGEX
|
||||
.find(version_stdout)
|
||||
.ok_or_else(|| anyhow!("failed to parse gopls version output"))?
|
||||
.as_str();
|
||||
let binary_path = container_dir.join(&format!("gopls_{version}"));
|
||||
fs::rename(&installed_binary_path, &binary_path).await?;
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: binary_path.to_path_buf(),
|
||||
arguments: server_binary_arguments(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir)
|
||||
.await
|
||||
.map(|mut binary| {
|
||||
binary.arguments = vec!["--help".into()];
|
||||
binary
|
||||
})
|
||||
}
|
||||
|
||||
async fn label_for_completion(
|
||||
&self,
|
||||
completion: &lsp::CompletionItem,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
let label = &completion.label;
|
||||
|
||||
// Gopls returns nested fields and methods as completions.
|
||||
// To syntax highlight these, combine their final component
|
||||
// with their detail.
|
||||
let name_offset = label.rfind('.').unwrap_or(0);
|
||||
|
||||
match completion.kind.zip(completion.detail.as_ref()) {
|
||||
Some((lsp::CompletionItemKind::MODULE, detail)) => {
|
||||
let text = format!("{label} {detail}");
|
||||
let source = Rope::from(format!("import {text}").as_str());
|
||||
let runs = language.highlight_text(&source, 7..7 + text.len());
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..label.len(),
|
||||
});
|
||||
}
|
||||
Some((
|
||||
lsp::CompletionItemKind::CONSTANT | lsp::CompletionItemKind::VARIABLE,
|
||||
detail,
|
||||
)) => {
|
||||
let text = format!("{label} {detail}");
|
||||
let source =
|
||||
Rope::from(format!("var {} {}", &text[name_offset..], detail).as_str());
|
||||
let runs = adjust_runs(
|
||||
name_offset,
|
||||
language.highlight_text(&source, 4..4 + text.len()),
|
||||
);
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..label.len(),
|
||||
});
|
||||
}
|
||||
Some((lsp::CompletionItemKind::STRUCT, _)) => {
|
||||
let text = format!("{label} struct {{}}");
|
||||
let source = Rope::from(format!("type {}", &text[name_offset..]).as_str());
|
||||
let runs = adjust_runs(
|
||||
name_offset,
|
||||
language.highlight_text(&source, 5..5 + text.len()),
|
||||
);
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..label.len(),
|
||||
});
|
||||
}
|
||||
Some((lsp::CompletionItemKind::INTERFACE, _)) => {
|
||||
let text = format!("{label} interface {{}}");
|
||||
let source = Rope::from(format!("type {}", &text[name_offset..]).as_str());
|
||||
let runs = adjust_runs(
|
||||
name_offset,
|
||||
language.highlight_text(&source, 5..5 + text.len()),
|
||||
);
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..label.len(),
|
||||
});
|
||||
}
|
||||
Some((lsp::CompletionItemKind::FIELD, detail)) => {
|
||||
let text = format!("{label} {detail}");
|
||||
let source =
|
||||
Rope::from(format!("type T struct {{ {} }}", &text[name_offset..]).as_str());
|
||||
let runs = adjust_runs(
|
||||
name_offset,
|
||||
language.highlight_text(&source, 16..16 + text.len()),
|
||||
);
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..label.len(),
|
||||
});
|
||||
}
|
||||
Some((lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD, detail)) => {
|
||||
if let Some(signature) = detail.strip_prefix("func") {
|
||||
let text = format!("{label}{signature}");
|
||||
let source = Rope::from(format!("func {} {{}}", &text[name_offset..]).as_str());
|
||||
let runs = adjust_runs(
|
||||
name_offset,
|
||||
language.highlight_text(&source, 5..5 + text.len()),
|
||||
);
|
||||
return Some(CodeLabel {
|
||||
filter_range: 0..label.len(),
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
async fn label_for_symbol(
|
||||
&self,
|
||||
name: &str,
|
||||
kind: lsp::SymbolKind,
|
||||
language: &Arc<Language>,
|
||||
) -> Option<CodeLabel> {
|
||||
let (text, filter_range, display_range) = match kind {
|
||||
lsp::SymbolKind::METHOD | lsp::SymbolKind::FUNCTION => {
|
||||
let text = format!("func {} () {{}}", name);
|
||||
let filter_range = 5..5 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::STRUCT => {
|
||||
let text = format!("type {} struct {{}}", name);
|
||||
let filter_range = 5..5 + name.len();
|
||||
let display_range = 0..text.len();
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::INTERFACE => {
|
||||
let text = format!("type {} interface {{}}", name);
|
||||
let filter_range = 5..5 + name.len();
|
||||
let display_range = 0..text.len();
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::CLASS => {
|
||||
let text = format!("type {} T", name);
|
||||
let filter_range = 5..5 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::CONSTANT => {
|
||||
let text = format!("const {} = nil", name);
|
||||
let filter_range = 6..6 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::VARIABLE => {
|
||||
let text = format!("var {} = nil", name);
|
||||
let filter_range = 4..4 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
lsp::SymbolKind::MODULE => {
|
||||
let text = format!("package {}", name);
|
||||
let filter_range = 8..8 + name.len();
|
||||
let display_range = 0..filter_range.end;
|
||||
(text, filter_range, display_range)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(CodeLabel {
|
||||
runs: language.highlight_text(&text.as_str().into(), display_range.clone()),
|
||||
text: text[display_range].to_string(),
|
||||
filter_range,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_cached_server_binary(container_dir: PathBuf) -> Option<LanguageServerBinary> {
|
||||
(|| async move {
|
||||
let mut last_binary_path = None;
|
||||
let mut entries = fs::read_dir(&container_dir).await?;
|
||||
while let Some(entry) = entries.next().await {
|
||||
let entry = entry?;
|
||||
if entry.file_type().await?.is_file()
|
||||
&& entry
|
||||
.file_name()
|
||||
.to_str()
|
||||
.map_or(false, |name| name.starts_with("gopls_"))
|
||||
{
|
||||
last_binary_path = Some(entry.path());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(path) = last_binary_path {
|
||||
Ok(LanguageServerBinary {
|
||||
path,
|
||||
arguments: server_binary_arguments(),
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!("no cached binary"))
|
||||
}
|
||||
})()
|
||||
.await
|
||||
.log_err()
|
||||
}
|
||||
|
||||
fn adjust_runs(
|
||||
delta: usize,
|
||||
mut runs: Vec<(Range<usize>, HighlightId)>,
|
||||
) -> Vec<(Range<usize>, HighlightId)> {
|
||||
for (range, _) in &mut runs {
|
||||
range.start += delta;
|
||||
range.end += delta;
|
||||
}
|
||||
runs
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::languages::language;
|
||||
use gpui::Hsla;
|
||||
use theme::SyntaxTheme;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_go_label_for_completion() {
|
||||
let language = language(
|
||||
"go",
|
||||
tree_sitter_go::language(),
|
||||
Some(Arc::new(GoLspAdapter)),
|
||||
)
|
||||
.await;
|
||||
|
||||
let theme = SyntaxTheme::new_test([
|
||||
("type", Hsla::default()),
|
||||
("keyword", Hsla::default()),
|
||||
("function", Hsla::default()),
|
||||
("number", Hsla::default()),
|
||||
("property", Hsla::default()),
|
||||
]);
|
||||
language.set_theme(&theme);
|
||||
|
||||
let grammar = language.grammar().unwrap();
|
||||
let highlight_function = grammar.highlight_id_for_name("function").unwrap();
|
||||
let highlight_type = grammar.highlight_id_for_name("type").unwrap();
|
||||
let highlight_keyword = grammar.highlight_id_for_name("keyword").unwrap();
|
||||
let highlight_number = grammar.highlight_id_for_name("number").unwrap();
|
||||
let highlight_field = grammar.highlight_id_for_name("property").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
language
|
||||
.label_for_completion(&lsp::CompletionItem {
|
||||
kind: Some(lsp::CompletionItemKind::FUNCTION),
|
||||
label: "Hello".to_string(),
|
||||
detail: Some("func(a B) c.D".to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.await,
|
||||
Some(CodeLabel {
|
||||
text: "Hello(a B) c.D".to_string(),
|
||||
filter_range: 0..5,
|
||||
runs: vec![
|
||||
(0..5, highlight_function),
|
||||
(8..9, highlight_type),
|
||||
(13..14, highlight_type),
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
// Nested methods
|
||||
assert_eq!(
|
||||
language
|
||||
.label_for_completion(&lsp::CompletionItem {
|
||||
kind: Some(lsp::CompletionItemKind::METHOD),
|
||||
label: "one.two.Three".to_string(),
|
||||
detail: Some("func() [3]interface{}".to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.await,
|
||||
Some(CodeLabel {
|
||||
text: "one.two.Three() [3]interface{}".to_string(),
|
||||
filter_range: 0..13,
|
||||
runs: vec![
|
||||
(8..13, highlight_function),
|
||||
(17..18, highlight_number),
|
||||
(19..28, highlight_keyword),
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
// Nested fields
|
||||
assert_eq!(
|
||||
language
|
||||
.label_for_completion(&lsp::CompletionItem {
|
||||
kind: Some(lsp::CompletionItemKind::FIELD),
|
||||
label: "two.Three".to_string(),
|
||||
detail: Some("a.Bcd".to_string()),
|
||||
..Default::default()
|
||||
})
|
||||
.await,
|
||||
Some(CodeLabel {
|
||||
text: "two.Three a.Bcd".to_string(),
|
||||
filter_range: 0..9,
|
||||
runs: vec![(4..9, highlight_field), (12..15, highlight_type)],
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
("[" @open "]" @close)
|
||||
("{" @open "}" @close)
|
||||
("\"" @open "\"" @close)
|
@ -1,12 +0,0 @@
|
||||
name = "Go"
|
||||
path_suffixes = ["go"]
|
||||
line_comment = "// "
|
||||
autoclose_before = ";:.,=}])>"
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
{ start = "/*", end = " */", close = true, newline = false, not_in = ["comment", "string"] },
|
||||
]
|
@ -1,24 +0,0 @@
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(type_declaration
|
||||
(type_spec
|
||||
name: (_) @name)
|
||||
) @item
|
||||
)
|
||||
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(function_declaration
|
||||
name: (_) @name
|
||||
) @item
|
||||
)
|
||||
|
||||
(
|
||||
(comment)* @context
|
||||
.
|
||||
(method_declaration
|
||||
name: (_) @name
|
||||
) @item
|
||||
)
|
@ -1,107 +0,0 @@
|
||||
(identifier) @variable
|
||||
(type_identifier) @type
|
||||
(field_identifier) @property
|
||||
|
||||
(call_expression
|
||||
function: (identifier) @function)
|
||||
|
||||
(call_expression
|
||||
function: (selector_expression
|
||||
field: (field_identifier) @function.method))
|
||||
|
||||
(function_declaration
|
||||
name: (identifier) @function)
|
||||
|
||||
(method_declaration
|
||||
name: (field_identifier) @function.method)
|
||||
|
||||
[
|
||||
"--"
|
||||
"-"
|
||||
"-="
|
||||
":="
|
||||
"!"
|
||||
"!="
|
||||
"..."
|
||||
"*"
|
||||
"*"
|
||||
"*="
|
||||
"/"
|
||||
"/="
|
||||
"&"
|
||||
"&&"
|
||||
"&="
|
||||
"%"
|
||||
"%="
|
||||
"^"
|
||||
"^="
|
||||
"+"
|
||||
"++"
|
||||
"+="
|
||||
"<-"
|
||||
"<"
|
||||
"<<"
|
||||
"<<="
|
||||
"<="
|
||||
"="
|
||||
"=="
|
||||
">"
|
||||
">="
|
||||
">>"
|
||||
">>="
|
||||
"|"
|
||||
"|="
|
||||
"||"
|
||||
"~"
|
||||
] @operator
|
||||
|
||||
[
|
||||
"break"
|
||||
"case"
|
||||
"chan"
|
||||
"const"
|
||||
"continue"
|
||||
"default"
|
||||
"defer"
|
||||
"else"
|
||||
"fallthrough"
|
||||
"for"
|
||||
"func"
|
||||
"go"
|
||||
"goto"
|
||||
"if"
|
||||
"import"
|
||||
"interface"
|
||||
"map"
|
||||
"package"
|
||||
"range"
|
||||
"return"
|
||||
"select"
|
||||
"struct"
|
||||
"switch"
|
||||
"type"
|
||||
"var"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
(interpreted_string_literal)
|
||||
(raw_string_literal)
|
||||
(rune_literal)
|
||||
] @string
|
||||
|
||||
(escape_sequence) @escape
|
||||
|
||||
[
|
||||
(int_literal)
|
||||
(float_literal)
|
||||
(imaginary_literal)
|
||||
] @number
|
||||
|
||||
[
|
||||
(true)
|
||||
(false)
|
||||
(nil)
|
||||
(iota)
|
||||
] @constant.builtin
|
||||
|
||||
(comment) @comment
|
@ -1,9 +0,0 @@
|
||||
[
|
||||
(assignment_statement)
|
||||
(call_expression)
|
||||
(selector_expression)
|
||||
] @indent
|
||||
|
||||
(_ "[" "]" @end) @indent
|
||||
(_ "{" "}" @end) @indent
|
||||
(_ "(" ")" @end) @indent
|
@ -1,43 +0,0 @@
|
||||
(type_declaration
|
||||
"type" @context
|
||||
(type_spec
|
||||
name: (_) @name)) @item
|
||||
|
||||
(function_declaration
|
||||
"func" @context
|
||||
name: (identifier) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)) @item
|
||||
|
||||
(method_declaration
|
||||
"func" @context
|
||||
receiver: (parameter_list
|
||||
"(" @context
|
||||
(parameter_declaration
|
||||
type: (_) @context)
|
||||
")" @context)
|
||||
name: (field_identifier) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)) @item
|
||||
|
||||
(const_declaration
|
||||
"const" @context
|
||||
(const_spec
|
||||
name: (identifier) @name) @item)
|
||||
|
||||
(source_file
|
||||
(var_declaration
|
||||
"var" @context
|
||||
(var_spec
|
||||
name: (identifier) @name) @item))
|
||||
|
||||
(method_spec
|
||||
name: (_) @name
|
||||
parameters: (parameter_list
|
||||
"(" @context
|
||||
")" @context)) @item
|
||||
|
||||
(field_declaration
|
||||
name: (_) @name) @item
|
@ -1,6 +0,0 @@
|
||||
(comment) @comment
|
||||
[
|
||||
(interpreted_string_literal)
|
||||
(raw_string_literal)
|
||||
(rune_literal)
|
||||
] @string
|
@ -1,12 +0,0 @@
|
||||
name = "HEEX"
|
||||
path_suffixes = ["heex"]
|
||||
autoclose_before = ">})"
|
||||
brackets = [
|
||||
{ start = "<", end = ">", close = true, newline = true },
|
||||
]
|
||||
block_comment = ["<%!-- ", " --%>"]
|
||||
scope_opt_in_language_servers = ["tailwindcss-language-server"]
|
||||
|
||||
[overrides.string]
|
||||
word_characters = ["-"]
|
||||
opt_into_language_servers = ["tailwindcss-language-server"]
|
@ -1,57 +0,0 @@
|
||||
; HEEx delimiters
|
||||
[
|
||||
"/>"
|
||||
"<!"
|
||||
"<"
|
||||
"</"
|
||||
"</:"
|
||||
"<:"
|
||||
">"
|
||||
"{"
|
||||
"}"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
"<%!--"
|
||||
"<%"
|
||||
"<%#"
|
||||
"<%%="
|
||||
"<%="
|
||||
"%>"
|
||||
"--%>"
|
||||
"-->"
|
||||
"<!--"
|
||||
] @keyword
|
||||
|
||||
; HEEx operators are highlighted as such
|
||||
"=" @operator
|
||||
|
||||
; HEEx inherits the DOCTYPE tag from HTML
|
||||
(doctype) @constant
|
||||
|
||||
(comment) @comment
|
||||
|
||||
; HEEx tags and slots are highlighted as HTML
|
||||
[
|
||||
(tag_name)
|
||||
(slot_name)
|
||||
] @tag
|
||||
|
||||
; HEEx attributes are highlighted as HTML attributes
|
||||
(attribute_name) @attribute
|
||||
|
||||
; HEEx special attributes are highlighted as keywords
|
||||
(special_attribute_name) @keyword
|
||||
|
||||
[
|
||||
(attribute_value)
|
||||
(quoted_attribute_value)
|
||||
] @string
|
||||
|
||||
; HEEx components are highlighted as Elixir modules and functions
|
||||
(component_name
|
||||
[
|
||||
(module) @module
|
||||
(function) @function
|
||||
"." @punctuation.delimiter
|
||||
])
|
@ -1,13 +0,0 @@
|
||||
(
|
||||
(directive
|
||||
[
|
||||
(partial_expression_value)
|
||||
(expression_value)
|
||||
(ending_expression_value)
|
||||
] @content)
|
||||
(#set! language "elixir")
|
||||
(#set! combined)
|
||||
)
|
||||
|
||||
((expression (expression_value) @content)
|
||||
(#set! language "elixir"))
|
@ -1,4 +0,0 @@
|
||||
[
|
||||
(attribute_value)
|
||||
(quoted_attribute_value)
|
||||
] @string
|
@ -1,130 +0,0 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use node_runtime::NodeRuntime;
|
||||
use serde_json::json;
|
||||
use smol::fs;
|
||||
use std::{
|
||||
any::Any,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
||||
const SERVER_PATH: &'static str =
|
||||
"node_modules/vscode-langservers-extracted/bin/vscode-html-language-server";
|
||||
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
}
|
||||
|
||||
pub struct HtmlLspAdapter {
|
||||
node: Arc<dyn NodeRuntime>,
|
||||
}
|
||||
|
||||
impl HtmlLspAdapter {
|
||||
pub fn new(node: Arc<dyn NodeRuntime>) -> Self {
|
||||
HtmlLspAdapter { node }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl LspAdapter for HtmlLspAdapter {
|
||||
async fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("vscode-html-language-server".into())
|
||||
}
|
||||
|
||||
fn short_name(&self) -> &'static str {
|
||||
"html"
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Any + Send>> {
|
||||
Ok(Box::new(
|
||||
self.node
|
||||
.npm_package_latest_version("vscode-langservers-extracted")
|
||||
.await?,
|
||||
) as Box<_>)
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
version: Box<dyn 'static + Send + Any>,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
let version = version.downcast::<String>().unwrap();
|
||||
let server_path = container_dir.join(SERVER_PATH);
|
||||
|
||||
if fs::metadata(&server_path).await.is_err() {
|
||||
self.node
|
||||
.npm_install_packages(
|
||||
&container_dir,
|
||||
&[("vscode-langservers-extracted", version.as_str())],
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
path: self.node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &*self.node).await
|
||||
}
|
||||
|
||||
async fn installation_test_binary(
|
||||
&self,
|
||||
container_dir: PathBuf,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &*self.node).await
|
||||
}
|
||||
|
||||
async fn initialization_options(&self) -> Option<serde_json::Value> {
|
||||
Some(json!({
|
||||
"provideFormatter": true
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_cached_server_binary(
|
||||
container_dir: PathBuf,
|
||||
node: &dyn NodeRuntime,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
(|| async move {
|
||||
let mut last_version_dir = None;
|
||||
let mut entries = fs::read_dir(&container_dir).await?;
|
||||
while let Some(entry) = entries.next().await {
|
||||
let entry = entry?;
|
||||
if entry.file_type().await?.is_dir() {
|
||||
last_version_dir = Some(entry.path());
|
||||
}
|
||||
}
|
||||
let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?;
|
||||
let server_path = last_version_dir.join(SERVER_PATH);
|
||||
if server_path.exists() {
|
||||
Ok(LanguageServerBinary {
|
||||
path: node.binary_path().await?,
|
||||
arguments: server_binary_arguments(&server_path),
|
||||
})
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"missing executable in directory {:?}",
|
||||
last_version_dir
|
||||
))
|
||||
}
|
||||
})()
|
||||
.await
|
||||
.log_err()
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
("<" @open ">" @close)
|
||||
("\"" @open "\"" @close)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user