mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-18 18:08:07 +03:00
Merge branch 'main' into kvark-linux
This commit is contained in:
commit
3a53db6502
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -2649,6 +2649,24 @@ version = "2.5.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "extension"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"collections",
|
||||||
|
"fs",
|
||||||
|
"futures 0.3.28",
|
||||||
|
"gpui",
|
||||||
|
"language",
|
||||||
|
"parking_lot 0.11.2",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"theme",
|
||||||
|
"toml",
|
||||||
|
"util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fallible-iterator"
|
name = "fallible-iterator"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -4138,6 +4156,7 @@ dependencies = [
|
|||||||
"sum_tree",
|
"sum_tree",
|
||||||
"text",
|
"text",
|
||||||
"theme",
|
"theme",
|
||||||
|
"toml",
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
"tree-sitter-elixir",
|
"tree-sitter-elixir",
|
||||||
"tree-sitter-embedded-template",
|
"tree-sitter-embedded-template",
|
||||||
@ -10589,6 +10608,7 @@ dependencies = [
|
|||||||
"diagnostics",
|
"diagnostics",
|
||||||
"editor",
|
"editor",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"extension",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
"feedback",
|
"feedback",
|
||||||
"file_finder",
|
"file_finder",
|
||||||
|
@ -21,6 +21,7 @@ members = [
|
|||||||
"crates/db",
|
"crates/db",
|
||||||
"crates/diagnostics",
|
"crates/diagnostics",
|
||||||
"crates/editor",
|
"crates/editor",
|
||||||
|
"crates/extension",
|
||||||
"crates/feature_flags",
|
"crates/feature_flags",
|
||||||
"crates/feedback",
|
"crates/feedback",
|
||||||
"crates/file_finder",
|
"crates/file_finder",
|
||||||
@ -30,12 +31,9 @@ members = [
|
|||||||
"crates/git",
|
"crates/git",
|
||||||
"crates/go_to_line",
|
"crates/go_to_line",
|
||||||
"crates/gpui",
|
"crates/gpui",
|
||||||
"crates/gpui",
|
|
||||||
"crates/gpui_macros",
|
|
||||||
"crates/gpui_macros",
|
"crates/gpui_macros",
|
||||||
"crates/install_cli",
|
"crates/install_cli",
|
||||||
"crates/journal",
|
"crates/journal",
|
||||||
"crates/journal",
|
|
||||||
"crates/language",
|
"crates/language",
|
||||||
"crates/language_selector",
|
"crates/language_selector",
|
||||||
"crates/language_tools",
|
"crates/language_tools",
|
||||||
@ -114,6 +112,7 @@ copilot_ui = { path = "crates/copilot_ui" }
|
|||||||
db = { path = "crates/db" }
|
db = { path = "crates/db" }
|
||||||
diagnostics = { path = "crates/diagnostics" }
|
diagnostics = { path = "crates/diagnostics" }
|
||||||
editor = { path = "crates/editor" }
|
editor = { path = "crates/editor" }
|
||||||
|
extension = { path = "crates/extension" }
|
||||||
feature_flags = { path = "crates/feature_flags" }
|
feature_flags = { path = "crates/feature_flags" }
|
||||||
feedback = { path = "crates/feedback" }
|
feedback = { path = "crates/feedback" }
|
||||||
file_finder = { path = "crates/file_finder" }
|
file_finder = { path = "crates/file_finder" }
|
||||||
|
@ -366,7 +366,8 @@ mod tests {
|
|||||||
use gpui::{Context, TestAppContext};
|
use gpui::{Context, TestAppContext};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{
|
use language::{
|
||||||
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig, Point,
|
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig,
|
||||||
|
LanguageMatcher, Point,
|
||||||
};
|
};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@ -675,7 +676,10 @@ mod tests {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
|
@ -172,22 +172,24 @@ pub fn generate_content_prompt(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use gpui::{AppContext, Context};
|
use gpui::{AppContext, Context};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{
|
use language::{
|
||||||
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig, Point,
|
language_settings, tree_sitter_rust, Buffer, BufferId, Language, LanguageConfig,
|
||||||
|
LanguageMatcher, Point,
|
||||||
};
|
};
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub(crate) fn rust_lang() -> Language {
|
pub(crate) fn rust_lang() -> Language {
|
||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
|
@ -19,7 +19,7 @@ use gpui::{TestAppContext, VisualContext, VisualTestContext};
|
|||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::{AllLanguageSettings, InlayHintSettings},
|
language_settings::{AllLanguageSettings, InlayHintSettings},
|
||||||
tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig,
|
tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher,
|
||||||
};
|
};
|
||||||
use rpc::RECEIVE_TIMEOUT;
|
use rpc::RECEIVE_TIMEOUT;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@ -269,7 +269,10 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -455,7 +458,10 @@ async fn test_collaborating_with_code_actions(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -668,7 +674,10 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -853,7 +862,10 @@ async fn test_language_server_statuses(cx_a: &mut TestAppContext, cx_b: &mut Tes
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -1144,7 +1156,10 @@ async fn test_on_input_format_from_host_to_guest(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -1272,7 +1287,10 @@ async fn test_on_input_format_from_guest_to_host(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -1431,7 +1449,10 @@ async fn test_mutual_editor_inlay_hint_cache_update(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -1697,7 +1718,10 @@ async fn test_inlay_hint_refresh_is_forwarded(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
|
@ -14,7 +14,7 @@ use gpui::{
|
|||||||
use language::{
|
use language::{
|
||||||
language_settings::{AllLanguageSettings, Formatter},
|
language_settings::{AllLanguageSettings, Formatter},
|
||||||
tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig,
|
tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, LanguageConfig,
|
||||||
LineEnding, OffsetRangeExt, Point, Rope,
|
LanguageMatcher, LineEnding, OffsetRangeExt, Point, Rope,
|
||||||
};
|
};
|
||||||
use live_kit_client::MacOSDisplay;
|
use live_kit_client::MacOSDisplay;
|
||||||
use lsp::LanguageServerId;
|
use lsp::LanguageServerId;
|
||||||
@ -2246,7 +2246,10 @@ async fn test_propagate_saves_and_fs_changes(
|
|||||||
let rust = Arc::new(Language::new(
|
let rust = Arc::new(Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -2254,7 +2257,10 @@ async fn test_propagate_saves_and_fs_changes(
|
|||||||
let javascript = Arc::new(Language::new(
|
let javascript = Arc::new(Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "JavaScript".into(),
|
name: "JavaScript".into(),
|
||||||
path_suffixes: vec!["js".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["js".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -3783,7 +3789,10 @@ async fn test_collaborating_with_diagnostics(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -4061,7 +4070,10 @@ async fn test_collaborating_with_lsp_progress_updates_and_diagnostics_ordering(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -4290,7 +4302,10 @@ async fn test_formatting_buffer(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -4395,7 +4410,10 @@ async fn test_prettier_formatting_buffer(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
prettier_parser_name: Some("test_parser".to_string()),
|
prettier_parser_name: Some("test_parser".to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@ -4511,7 +4529,10 @@ async fn test_definition(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -4655,7 +4676,10 @@ async fn test_references(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -4852,7 +4876,10 @@ async fn test_document_highlights(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -4955,7 +4982,10 @@ async fn test_lsp_hover(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -5051,7 +5081,10 @@ async fn test_project_symbols(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -5160,7 +5193,10 @@ async fn test_open_buffer_while_getting_definition_pointing_to_it(
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
|
@ -8,7 +8,9 @@ use editor::Bias;
|
|||||||
use fs::{repository::GitFileStatus, FakeFs, Fs as _};
|
use fs::{repository::GitFileStatus, FakeFs, Fs as _};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{BackgroundExecutor, Model, TestAppContext};
|
use gpui::{BackgroundExecutor, Model, TestAppContext};
|
||||||
use language::{range_to_lsp, FakeLspAdapter, Language, LanguageConfig, PointUtf16};
|
use language::{
|
||||||
|
range_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, PointUtf16,
|
||||||
|
};
|
||||||
use lsp::FakeLanguageServer;
|
use lsp::FakeLanguageServer;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use project::{search::SearchQuery, Project, ProjectPath};
|
use project::{search::SearchQuery, Project, ProjectPath};
|
||||||
@ -1022,7 +1024,10 @@ impl RandomizedTest for ProjectCollaborationTest {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
|
@ -1003,7 +1003,7 @@ pub mod tests {
|
|||||||
use gpui::{div, font, observe, px, AppContext, Context, Element, Hsla};
|
use gpui::{div, font, observe, px, AppContext, Context, Element, Hsla};
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
|
language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
|
||||||
Buffer, Language, LanguageConfig, SelectionGoal,
|
Buffer, Language, LanguageConfig, LanguageMatcher, SelectionGoal,
|
||||||
};
|
};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use rand::{prelude::*, Rng};
|
use rand::{prelude::*, Rng};
|
||||||
@ -1453,7 +1453,10 @@ pub mod tests {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Test".into(),
|
name: "Test".into(),
|
||||||
path_suffixes: vec![".test".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec![".test".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -1540,7 +1543,10 @@ pub mod tests {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Test".into(),
|
name: "Test".into(),
|
||||||
path_suffixes: vec![".test".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec![".test".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -1608,7 +1614,10 @@ pub mod tests {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Test".into(),
|
name: "Test".into(),
|
||||||
path_suffixes: vec![".test".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec![".test".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
|
@ -15,7 +15,8 @@ use language::{
|
|||||||
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
|
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
|
||||||
BracketPairConfig,
|
BracketPairConfig,
|
||||||
Capability::ReadWrite,
|
Capability::ReadWrite,
|
||||||
FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry, Override, Point,
|
FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageRegistry,
|
||||||
|
Override, Point,
|
||||||
};
|
};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::project_settings::{LspSettings, ProjectSettings};
|
use project::project_settings::{LspSettings, ProjectSettings};
|
||||||
@ -5077,7 +5078,10 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -5196,7 +5200,10 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -5318,7 +5325,10 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
// Enable Prettier formatting for the same buffer, and ensure
|
// Enable Prettier formatting for the same buffer, and ensure
|
||||||
// LSP is called instead of Prettier.
|
// LSP is called instead of Prettier.
|
||||||
prettier_parser_name: Some("test_parser".to_string()),
|
prettier_parser_name: Some("test_parser".to_string()),
|
||||||
@ -7747,7 +7757,10 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
brackets: BracketPairConfig {
|
brackets: BracketPairConfig {
|
||||||
pairs: vec![BracketPair {
|
pairs: vec![BracketPair {
|
||||||
start: "{".to_string(),
|
start: "{".to_string(),
|
||||||
@ -7859,7 +7872,10 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: Arc::clone(&language_name),
|
name: Arc::clone(&language_name),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -8086,7 +8102,10 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui:
|
|||||||
let mut cx = EditorLspTestContext::new(
|
let mut cx = EditorLspTestContext::new(
|
||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
path_suffixes: vec!["jsx".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["jsx".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
overrides: [(
|
overrides: [(
|
||||||
"element".into(),
|
"element".into(),
|
||||||
LanguageConfigOverride {
|
LanguageConfigOverride {
|
||||||
@ -8187,7 +8206,10 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
prettier_parser_name: Some("test_parser".to_string()),
|
prettier_parser_name: Some("test_parser".to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
@ -35,7 +35,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
|
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{BracketPair, BracketPairConfig, Language, LanguageConfig};
|
use language::{BracketPair, BracketPairConfig, Language, LanguageConfig, LanguageMatcher};
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
|
async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
|
||||||
@ -45,7 +45,10 @@ mod tests {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
brackets: BracketPairConfig {
|
brackets: BracketPairConfig {
|
||||||
pairs: vec![
|
pairs: vec![
|
||||||
BracketPair {
|
BracketPair {
|
||||||
|
@ -1240,7 +1240,7 @@ pub mod tests {
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::AllLanguageSettingsContent, Capability, FakeLspAdapter, Language,
|
language_settings::AllLanguageSettingsContent, Capability, FakeLspAdapter, Language,
|
||||||
LanguageConfig,
|
LanguageConfig, LanguageMatcher,
|
||||||
};
|
};
|
||||||
use lsp::FakeLanguageServer;
|
use lsp::FakeLanguageServer;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
@ -1529,7 +1529,10 @@ pub mod tests {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
path_suffixes: vec![path_suffix.to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec![path_suffix.to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -2209,7 +2212,10 @@ pub mod tests {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -2500,7 +2506,10 @@ pub mod tests {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -2848,7 +2857,10 @@ pub mod tests {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -3079,7 +3091,10 @@ pub mod tests {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -3319,7 +3334,10 @@ pub mod tests {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
|
@ -12,7 +12,9 @@ use collections::HashSet;
|
|||||||
use futures::Future;
|
use futures::Future;
|
||||||
use gpui::{View, ViewContext, VisualTestContext};
|
use gpui::{View, ViewContext, VisualTestContext};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
|
use language::{
|
||||||
|
point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, LanguageQueries,
|
||||||
|
};
|
||||||
use lsp::{notification, request};
|
use lsp::{notification, request};
|
||||||
use multi_buffer::ToPointUtf16;
|
use multi_buffer::ToPointUtf16;
|
||||||
use project::Project;
|
use project::Project;
|
||||||
@ -115,7 +117,10 @@ impl EditorLspTestContext {
|
|||||||
let language = Language::new(
|
let language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -160,7 +165,10 @@ impl EditorLspTestContext {
|
|||||||
let language = Language::new(
|
let language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Typescript".into(),
|
name: "Typescript".into(),
|
||||||
path_suffixes: vec!["ts".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["ts".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
brackets: language::BracketPairConfig {
|
brackets: language::BracketPairConfig {
|
||||||
pairs: vec![language::BracketPair {
|
pairs: vec![language::BracketPair {
|
||||||
start: "{".to_string(),
|
start: "{".to_string(),
|
||||||
|
28
crates/extension/Cargo.toml
Normal file
28
crates/extension/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
name = "extension"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/extension_store.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow.workspace = true
|
||||||
|
collections.workspace = true
|
||||||
|
fs.workspace = true
|
||||||
|
futures.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
language.workspace = true
|
||||||
|
parking_lot.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
theme.workspace = true
|
||||||
|
toml.workspace = true
|
||||||
|
util.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
fs = { workspace = true, features = ["test-support"] }
|
||||||
|
gpui = { workspace = true, features = ["test-support"] }
|
||||||
|
language = { workspace = true, features = ["test-support"] }
|
1
crates/extension/LICENSE-GPL
Symbolic link
1
crates/extension/LICENSE-GPL
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../LICENSE-GPL
|
344
crates/extension/src/extension_store.rs
Normal file
344
crates/extension/src/extension_store.rs
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use collections::{HashMap, HashSet};
|
||||||
|
use fs::Fs;
|
||||||
|
use futures::StreamExt as _;
|
||||||
|
use gpui::{actions, AppContext, Context, Global, Model, ModelContext, Task};
|
||||||
|
use language::{
|
||||||
|
LanguageConfig, LanguageMatcher, LanguageQueries, LanguageRegistry, QUERY_FILENAME_PREFIXES,
|
||||||
|
};
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
use theme::ThemeRegistry;
|
||||||
|
use util::{paths::EXTENSIONS_DIR, ResultExt};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod extension_store_test;
|
||||||
|
|
||||||
|
pub struct ExtensionStore {
|
||||||
|
manifest: Arc<RwLock<Manifest>>,
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
|
extensions_dir: PathBuf,
|
||||||
|
manifest_path: PathBuf,
|
||||||
|
language_registry: Arc<LanguageRegistry>,
|
||||||
|
theme_registry: Arc<ThemeRegistry>,
|
||||||
|
_watch_extensions_dir: Task<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GlobalExtensionStore(Model<ExtensionStore>);
|
||||||
|
|
||||||
|
impl Global for GlobalExtensionStore {}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Default)]
|
||||||
|
pub struct Manifest {
|
||||||
|
pub grammars: HashMap<String, GrammarManifestEntry>,
|
||||||
|
pub languages: HashMap<Arc<str>, LanguageManifestEntry>,
|
||||||
|
pub themes: HashMap<String, ThemeManifestEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, PartialOrd, Ord, Deserialize, Serialize)]
|
||||||
|
pub struct GrammarManifestEntry {
|
||||||
|
extension: String,
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct LanguageManifestEntry {
|
||||||
|
extension: String,
|
||||||
|
path: PathBuf,
|
||||||
|
matcher: LanguageMatcher,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct ThemeManifestEntry {
|
||||||
|
extension: String,
|
||||||
|
path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
actions!(zed, [ReloadExtensions]);
|
||||||
|
|
||||||
|
pub fn init(
|
||||||
|
fs: Arc<fs::RealFs>,
|
||||||
|
language_registry: Arc<LanguageRegistry>,
|
||||||
|
theme_registry: Arc<ThemeRegistry>,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) {
|
||||||
|
let store = cx.new_model(|cx| {
|
||||||
|
ExtensionStore::new(
|
||||||
|
EXTENSIONS_DIR.clone(),
|
||||||
|
fs.clone(),
|
||||||
|
language_registry.clone(),
|
||||||
|
theme_registry,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.on_action(|_: &ReloadExtensions, cx| {
|
||||||
|
let store = cx.global::<GlobalExtensionStore>().0.clone();
|
||||||
|
store
|
||||||
|
.update(cx, |store, cx| store.reload(cx))
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.set_global(GlobalExtensionStore(store));
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtensionStore {
|
||||||
|
pub fn new(
|
||||||
|
extensions_dir: PathBuf,
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
|
language_registry: Arc<LanguageRegistry>,
|
||||||
|
theme_registry: Arc<ThemeRegistry>,
|
||||||
|
cx: &mut ModelContext<Self>,
|
||||||
|
) -> Self {
|
||||||
|
let mut this = Self {
|
||||||
|
manifest: Default::default(),
|
||||||
|
extensions_dir: extensions_dir.join("installed"),
|
||||||
|
manifest_path: extensions_dir.join("manifest.json"),
|
||||||
|
fs,
|
||||||
|
language_registry,
|
||||||
|
theme_registry,
|
||||||
|
_watch_extensions_dir: Task::ready(()),
|
||||||
|
};
|
||||||
|
this._watch_extensions_dir = this.watch_extensions_dir(cx);
|
||||||
|
this.load(cx);
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(&mut self, cx: &mut ModelContext<Self>) {
|
||||||
|
let (manifest_content, manifest_metadata, extensions_metadata) =
|
||||||
|
cx.background_executor().block(async {
|
||||||
|
futures::join!(
|
||||||
|
self.fs.load(&self.manifest_path),
|
||||||
|
self.fs.metadata(&self.manifest_path),
|
||||||
|
self.fs.metadata(&self.extensions_dir),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(manifest_content) = manifest_content.log_err() {
|
||||||
|
if let Some(manifest) = serde_json::from_str(&manifest_content).log_err() {
|
||||||
|
self.manifest_updated(manifest, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let should_reload = if let (Ok(Some(manifest_metadata)), Ok(Some(extensions_metadata))) =
|
||||||
|
(manifest_metadata, extensions_metadata)
|
||||||
|
{
|
||||||
|
extensions_metadata.mtime > manifest_metadata.mtime
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_reload {
|
||||||
|
self.reload(cx).detach_and_log_err(cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn manifest_updated(&mut self, manifest: Manifest, cx: &mut ModelContext<Self>) {
|
||||||
|
for (grammar_name, grammar) in &manifest.grammars {
|
||||||
|
let mut grammar_path = self.extensions_dir.clone();
|
||||||
|
grammar_path.extend([grammar.extension.as_ref(), grammar.path.as_path()]);
|
||||||
|
self.language_registry
|
||||||
|
.register_grammar(grammar_name.clone(), grammar_path);
|
||||||
|
}
|
||||||
|
for (language_name, language) in &manifest.languages {
|
||||||
|
let mut language_path = self.extensions_dir.clone();
|
||||||
|
language_path.extend([language.extension.as_ref(), language.path.as_path()]);
|
||||||
|
self.language_registry.register_extension(
|
||||||
|
language_path.into(),
|
||||||
|
language_name.clone(),
|
||||||
|
language.matcher.clone(),
|
||||||
|
load_plugin_queries,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let fs = self.fs.clone();
|
||||||
|
let root_dir = self.extensions_dir.clone();
|
||||||
|
let theme_registry = self.theme_registry.clone();
|
||||||
|
let themes = manifest.themes.clone();
|
||||||
|
cx.background_executor()
|
||||||
|
.spawn(async move {
|
||||||
|
for theme in themes.values() {
|
||||||
|
let mut theme_path = root_dir.clone();
|
||||||
|
theme_path.extend([theme.extension.as_ref(), theme.path.as_path()]);
|
||||||
|
|
||||||
|
theme_registry
|
||||||
|
.load_user_theme(&theme_path, fs.clone())
|
||||||
|
.await
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
*self.manifest.write() = manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch_extensions_dir(&self, cx: &mut ModelContext<Self>) -> Task<()> {
|
||||||
|
let manifest = self.manifest.clone();
|
||||||
|
let fs = self.fs.clone();
|
||||||
|
let language_registry = self.language_registry.clone();
|
||||||
|
let extensions_dir = self.extensions_dir.clone();
|
||||||
|
cx.background_executor().spawn(async move {
|
||||||
|
let mut changed_languages = HashSet::default();
|
||||||
|
let mut events = fs.watch(&extensions_dir, Duration::from_millis(250)).await;
|
||||||
|
while let Some(events) = events.next().await {
|
||||||
|
changed_languages.clear();
|
||||||
|
let manifest = manifest.read();
|
||||||
|
for event in events {
|
||||||
|
for (language_name, language) in &manifest.languages {
|
||||||
|
let mut language_path = extensions_dir.clone();
|
||||||
|
language_path
|
||||||
|
.extend([language.extension.as_ref(), language.path.as_path()]);
|
||||||
|
if event.path.starts_with(&language_path) || event.path == language_path {
|
||||||
|
changed_languages.insert(language_name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
language_registry.reload_languages(&changed_languages);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reload(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||||
|
let fs = self.fs.clone();
|
||||||
|
let extensions_dir = self.extensions_dir.clone();
|
||||||
|
let manifest_path = self.manifest_path.clone();
|
||||||
|
cx.spawn(|this, mut cx| async move {
|
||||||
|
let manifest = cx
|
||||||
|
.background_executor()
|
||||||
|
.spawn(async move {
|
||||||
|
let mut manifest = Manifest::default();
|
||||||
|
|
||||||
|
let mut extension_paths = fs.read_dir(&extensions_dir).await?;
|
||||||
|
while let Some(extension_dir) = extension_paths.next().await {
|
||||||
|
let extension_dir = extension_dir?;
|
||||||
|
let Some(extension_name) =
|
||||||
|
extension_dir.file_name().and_then(OsStr::to_str)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(mut grammar_paths) =
|
||||||
|
fs.read_dir(&extension_dir.join("grammars")).await
|
||||||
|
{
|
||||||
|
while let Some(grammar_path) = grammar_paths.next().await {
|
||||||
|
let grammar_path = grammar_path?;
|
||||||
|
let Ok(relative_path) = grammar_path.strip_prefix(&extension_dir)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(grammar_name) =
|
||||||
|
grammar_path.file_stem().and_then(OsStr::to_str)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
manifest.grammars.insert(
|
||||||
|
grammar_name.into(),
|
||||||
|
GrammarManifestEntry {
|
||||||
|
extension: extension_name.into(),
|
||||||
|
path: relative_path.into(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut language_paths) =
|
||||||
|
fs.read_dir(&extension_dir.join("languages")).await
|
||||||
|
{
|
||||||
|
while let Some(language_path) = language_paths.next().await {
|
||||||
|
let language_path = language_path?;
|
||||||
|
let Ok(relative_path) = language_path.strip_prefix(&extension_dir)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let config = fs.load(&language_path.join("config.toml")).await?;
|
||||||
|
let config = ::toml::from_str::<LanguageConfig>(&config)?;
|
||||||
|
|
||||||
|
manifest.languages.insert(
|
||||||
|
config.name.clone(),
|
||||||
|
LanguageManifestEntry {
|
||||||
|
extension: extension_name.into(),
|
||||||
|
path: relative_path.into(),
|
||||||
|
matcher: config.matcher,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut theme_paths) =
|
||||||
|
fs.read_dir(&extension_dir.join("themes")).await
|
||||||
|
{
|
||||||
|
while let Some(theme_path) = theme_paths.next().await {
|
||||||
|
let theme_path = theme_path?;
|
||||||
|
let Ok(relative_path) = theme_path.strip_prefix(&extension_dir)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(theme_family) =
|
||||||
|
ThemeRegistry::read_user_theme(&theme_path, fs.clone())
|
||||||
|
.await
|
||||||
|
.log_err()
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
for theme in theme_family.themes {
|
||||||
|
let location = ThemeManifestEntry {
|
||||||
|
extension: extension_name.into(),
|
||||||
|
path: relative_path.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
manifest.themes.insert(theme.name, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.save(
|
||||||
|
&manifest_path,
|
||||||
|
&serde_json::to_string_pretty(&manifest)?.as_str().into(),
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
anyhow::Ok(manifest)
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
this.update(&mut cx, |this, cx| this.manifest_updated(manifest, cx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
|
||||||
|
let mut result = LanguageQueries::default();
|
||||||
|
if let Some(entries) = std::fs::read_dir(root_path).log_err() {
|
||||||
|
for entry in entries {
|
||||||
|
let Some(entry) = entry.log_err() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let path = entry.path();
|
||||||
|
if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
|
||||||
|
if !remainder.ends_with(".scm") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (name, query) in QUERY_FILENAME_PREFIXES {
|
||||||
|
if remainder.starts_with(name) {
|
||||||
|
if let Some(contents) = std::fs::read_to_string(&path).log_err() {
|
||||||
|
match query(&mut result) {
|
||||||
|
None => *query(&mut result) = Some(contents.into()),
|
||||||
|
Some(r) => r.to_mut().push_str(contents.as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
295
crates/extension/src/extension_store_test.rs
Normal file
295
crates/extension/src/extension_store_test.rs
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
use crate::{
|
||||||
|
ExtensionStore, GrammarManifestEntry, LanguageManifestEntry, Manifest, ThemeManifestEntry,
|
||||||
|
};
|
||||||
|
use fs::FakeFs;
|
||||||
|
use gpui::{Context, TestAppContext};
|
||||||
|
use language::{LanguageMatcher, LanguageRegistry};
|
||||||
|
use serde_json::json;
|
||||||
|
use std::{path::PathBuf, sync::Arc};
|
||||||
|
use theme::ThemeRegistry;
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_extension_store(cx: &mut TestAppContext) {
|
||||||
|
let fs = FakeFs::new(cx.executor());
|
||||||
|
|
||||||
|
fs.insert_tree(
|
||||||
|
"/the-extension-dir",
|
||||||
|
json!({
|
||||||
|
"installed": {
|
||||||
|
"zed-monokai": {
|
||||||
|
"themes": {
|
||||||
|
"monokai.json": r#"{
|
||||||
|
"name": "Monokai",
|
||||||
|
"author": "Someone",
|
||||||
|
"themes": [
|
||||||
|
{
|
||||||
|
"name": "Monokai Dark",
|
||||||
|
"appearance": "dark",
|
||||||
|
"style": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Monokai Light",
|
||||||
|
"appearance": "light",
|
||||||
|
"style": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}"#,
|
||||||
|
"monokai-pro.json": r#"{
|
||||||
|
"name": "Monokai Pro",
|
||||||
|
"author": "Someone",
|
||||||
|
"themes": [
|
||||||
|
{
|
||||||
|
"name": "Monokai Pro Dark",
|
||||||
|
"appearance": "dark",
|
||||||
|
"style": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Monokai Pro Light",
|
||||||
|
"appearance": "light",
|
||||||
|
"style": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}"#,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zed-ruby": {
|
||||||
|
"grammars": {
|
||||||
|
"ruby.wasm": "",
|
||||||
|
"embedded_template.wasm": "",
|
||||||
|
},
|
||||||
|
"languages": {
|
||||||
|
"ruby": {
|
||||||
|
"config.toml": r#"
|
||||||
|
name = "Ruby"
|
||||||
|
grammar = "ruby"
|
||||||
|
path_suffixes = ["rb"]
|
||||||
|
"#,
|
||||||
|
"highlights.scm": "",
|
||||||
|
},
|
||||||
|
"erb": {
|
||||||
|
"config.toml": r#"
|
||||||
|
name = "ERB"
|
||||||
|
grammar = "embedded_template"
|
||||||
|
path_suffixes = ["erb"]
|
||||||
|
"#,
|
||||||
|
"highlights.scm": "",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut expected_manifest = Manifest {
|
||||||
|
grammars: [
|
||||||
|
(
|
||||||
|
"embedded_template".into(),
|
||||||
|
GrammarManifestEntry {
|
||||||
|
extension: "zed-ruby".into(),
|
||||||
|
path: "grammars/embedded_template.wasm".into(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"ruby".into(),
|
||||||
|
GrammarManifestEntry {
|
||||||
|
extension: "zed-ruby".into(),
|
||||||
|
path: "grammars/ruby.wasm".into(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
languages: [
|
||||||
|
(
|
||||||
|
"ERB".into(),
|
||||||
|
LanguageManifestEntry {
|
||||||
|
extension: "zed-ruby".into(),
|
||||||
|
path: "languages/erb".into(),
|
||||||
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["erb".into()],
|
||||||
|
first_line_pattern: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Ruby".into(),
|
||||||
|
LanguageManifestEntry {
|
||||||
|
extension: "zed-ruby".into(),
|
||||||
|
path: "languages/ruby".into(),
|
||||||
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rb".into()],
|
||||||
|
first_line_pattern: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
themes: [
|
||||||
|
(
|
||||||
|
"Monokai Dark".into(),
|
||||||
|
ThemeManifestEntry {
|
||||||
|
extension: "zed-monokai".into(),
|
||||||
|
path: "themes/monokai.json".into(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Monokai Light".into(),
|
||||||
|
ThemeManifestEntry {
|
||||||
|
extension: "zed-monokai".into(),
|
||||||
|
path: "themes/monokai.json".into(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Monokai Pro Dark".into(),
|
||||||
|
ThemeManifestEntry {
|
||||||
|
extension: "zed-monokai".into(),
|
||||||
|
path: "themes/monokai-pro.json".into(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Monokai Pro Light".into(),
|
||||||
|
ThemeManifestEntry {
|
||||||
|
extension: "zed-monokai".into(),
|
||||||
|
path: "themes/monokai-pro.json".into(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let language_registry = Arc::new(LanguageRegistry::test());
|
||||||
|
let theme_registry = Arc::new(ThemeRegistry::new(Box::new(())));
|
||||||
|
|
||||||
|
let store = cx.new_model(|cx| {
|
||||||
|
ExtensionStore::new(
|
||||||
|
PathBuf::from("/the-extension-dir"),
|
||||||
|
fs.clone(),
|
||||||
|
language_registry.clone(),
|
||||||
|
theme_registry.clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.executor().run_until_parked();
|
||||||
|
store.read_with(cx, |store, _| {
|
||||||
|
let manifest = store.manifest.read();
|
||||||
|
assert_eq!(manifest.grammars, expected_manifest.grammars);
|
||||||
|
assert_eq!(manifest.languages, expected_manifest.languages);
|
||||||
|
assert_eq!(manifest.themes, expected_manifest.themes);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
language_registry.language_names(),
|
||||||
|
["ERB", "Plain Text", "Ruby"]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
theme_registry.list_names(false),
|
||||||
|
[
|
||||||
|
"Monokai Dark",
|
||||||
|
"Monokai Light",
|
||||||
|
"Monokai Pro Dark",
|
||||||
|
"Monokai Pro Light",
|
||||||
|
"One Dark",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.insert_tree(
|
||||||
|
"/the-extension-dir/installed/zed-gruvbox",
|
||||||
|
json!({
|
||||||
|
"themes": {
|
||||||
|
"gruvbox.json": r#"{
|
||||||
|
"name": "Gruvbox",
|
||||||
|
"author": "Someone Else",
|
||||||
|
"themes": [
|
||||||
|
{
|
||||||
|
"name": "Gruvbox",
|
||||||
|
"appearance": "dark",
|
||||||
|
"style": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}"#,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
expected_manifest.themes.insert(
|
||||||
|
"Gruvbox".into(),
|
||||||
|
ThemeManifestEntry {
|
||||||
|
extension: "zed-gruvbox".into(),
|
||||||
|
path: "themes/gruvbox.json".into(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
store
|
||||||
|
.update(cx, |store, cx| store.reload(cx))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
cx.executor().run_until_parked();
|
||||||
|
store.read_with(cx, |store, _| {
|
||||||
|
let manifest = store.manifest.read();
|
||||||
|
assert_eq!(manifest.grammars, expected_manifest.grammars);
|
||||||
|
assert_eq!(manifest.languages, expected_manifest.languages);
|
||||||
|
assert_eq!(manifest.themes, expected_manifest.themes);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
theme_registry.list_names(false),
|
||||||
|
[
|
||||||
|
"Gruvbox",
|
||||||
|
"Monokai Dark",
|
||||||
|
"Monokai Light",
|
||||||
|
"Monokai Pro Dark",
|
||||||
|
"Monokai Pro Light",
|
||||||
|
"One Dark",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let prev_fs_metadata_call_count = fs.metadata_call_count();
|
||||||
|
let prev_fs_read_dir_call_count = fs.read_dir_call_count();
|
||||||
|
|
||||||
|
// Create new extension store, as if Zed were restarting.
|
||||||
|
drop(store);
|
||||||
|
let store = cx.new_model(|cx| {
|
||||||
|
ExtensionStore::new(
|
||||||
|
PathBuf::from("/the-extension-dir"),
|
||||||
|
fs.clone(),
|
||||||
|
language_registry.clone(),
|
||||||
|
theme_registry.clone(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cx.executor().run_until_parked();
|
||||||
|
store.read_with(cx, |store, _| {
|
||||||
|
let manifest = store.manifest.read();
|
||||||
|
assert_eq!(manifest.grammars, expected_manifest.grammars);
|
||||||
|
assert_eq!(manifest.languages, expected_manifest.languages);
|
||||||
|
assert_eq!(manifest.themes, expected_manifest.themes);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
language_registry.language_names(),
|
||||||
|
["ERB", "Plain Text", "Ruby"]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
theme_registry.list_names(false),
|
||||||
|
[
|
||||||
|
"Gruvbox",
|
||||||
|
"Monokai Dark",
|
||||||
|
"Monokai Light",
|
||||||
|
"Monokai Pro Dark",
|
||||||
|
"Monokai Pro Light",
|
||||||
|
"One Dark",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// The on-disk manifest limits the number of FS calls that need to be made
|
||||||
|
// on startup.
|
||||||
|
assert_eq!(fs.read_dir_call_count(), prev_fs_read_dir_call_count);
|
||||||
|
assert_eq!(fs.metadata_call_count(), prev_fs_metadata_call_count + 2);
|
||||||
|
});
|
||||||
|
}
|
@ -5,7 +5,7 @@ use util::arc_cow::ArcCow;
|
|||||||
|
|
||||||
/// A shared string is an immutable string that can be cheaply cloned in GPUI
|
/// A shared string is an immutable string that can be cheaply cloned in GPUI
|
||||||
/// tasks. Essentially an abstraction over an `Arc<str>` and `&'static str`,
|
/// tasks. Essentially an abstraction over an `Arc<str>` and `&'static str`,
|
||||||
#[derive(Deref, DerefMut, Eq, PartialEq, Hash, Clone)]
|
#[derive(Deref, DerefMut, Eq, PartialEq, PartialOrd, Ord, Hash, Clone)]
|
||||||
pub struct SharedString(ArcCow<'static, str>);
|
pub struct SharedString(ArcCow<'static, str>);
|
||||||
|
|
||||||
impl Default for SharedString {
|
impl Default for SharedString {
|
||||||
|
@ -52,6 +52,7 @@ smol.workspace = true
|
|||||||
sum_tree.workspace = true
|
sum_tree.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
|
toml.workspace = true
|
||||||
tree-sitter-rust = { workspace = true, optional = true }
|
tree-sitter-rust = { workspace = true, optional = true }
|
||||||
tree-sitter-typescript = { workspace = true, optional = true }
|
tree-sitter-typescript = { workspace = true, optional = true }
|
||||||
pulldown-cmark.workspace = true
|
pulldown-cmark.workspace = true
|
||||||
|
@ -73,7 +73,10 @@ fn test_select_language() {
|
|||||||
registry.add(Arc::new(Language::new(
|
registry.add(Arc::new(Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -81,7 +84,10 @@ fn test_select_language() {
|
|||||||
registry.add(Arc::new(Language::new(
|
registry.add(Arc::new(Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Make".into(),
|
name: "Make".into(),
|
||||||
path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -2322,7 +2328,10 @@ fn ruby_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Ruby".into(),
|
name: "Ruby".into(),
|
||||||
path_suffixes: vec!["rb".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rb".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
line_comments: vec!["# ".into()],
|
line_comments: vec!["# ".into()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@ -2370,7 +2379,10 @@ fn erb_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "ERB".into(),
|
name: "ERB".into(),
|
||||||
path_suffixes: vec!["erb".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["erb".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
block_comment: Some(("<%#".into(), "%>".into())),
|
block_comment: Some(("<%#".into(), "%>".into())),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@ -2398,7 +2410,10 @@ fn rust_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -2450,7 +2465,10 @@ fn json_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Json".into(),
|
name: "Json".into(),
|
||||||
path_suffixes: vec!["js".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["js".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_json::language()),
|
Some(tree_sitter_json::language()),
|
||||||
|
@ -20,7 +20,7 @@ pub mod markdown;
|
|||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{hash_map, HashMap, HashSet};
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::{mpsc, oneshot},
|
channel::{mpsc, oneshot},
|
||||||
future::Shared,
|
future::Shared,
|
||||||
@ -33,12 +33,13 @@ use lsp::{CodeActionKind, LanguageServerBinary};
|
|||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{de, Deserialize, Deserializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
|
ffi::OsStr,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
mem,
|
mem,
|
||||||
@ -392,14 +393,13 @@ pub struct LanguageConfig {
|
|||||||
/// Human-readable name of the language.
|
/// Human-readable name of the language.
|
||||||
pub name: Arc<str>,
|
pub name: Arc<str>,
|
||||||
// The name of the grammar in a WASM bundle (experimental).
|
// The name of the grammar in a WASM bundle (experimental).
|
||||||
pub grammar_name: Option<Arc<str>>,
|
pub grammar: Option<Arc<str>>,
|
||||||
/// Given a list of `LanguageConfig`'s, the language of a file can be determined based on the path extension matching any of the `path_suffixes`.
|
/// The criteria for matching this language to a given file.
|
||||||
pub path_suffixes: Vec<String>,
|
#[serde(flatten)]
|
||||||
|
pub matcher: LanguageMatcher,
|
||||||
/// List of bracket types in a language.
|
/// List of bracket types in a language.
|
||||||
|
#[serde(default)]
|
||||||
pub brackets: BracketPairConfig,
|
pub brackets: BracketPairConfig,
|
||||||
/// A regex pattern that determines whether the language should be assigned to a file or not.
|
|
||||||
#[serde(default, deserialize_with = "deserialize_regex")]
|
|
||||||
pub first_line_pattern: Option<Regex>,
|
|
||||||
/// If set to true, auto indentation uses last non empty line to determine
|
/// If set to true, auto indentation uses last non empty line to determine
|
||||||
/// the indentation level for a new line.
|
/// the indentation level for a new line.
|
||||||
#[serde(default = "auto_indent_using_last_non_empty_line_default")]
|
#[serde(default = "auto_indent_using_last_non_empty_line_default")]
|
||||||
@ -443,6 +443,34 @@ pub struct LanguageConfig {
|
|||||||
pub prettier_parser_name: Option<String>,
|
pub prettier_parser_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, Default)]
|
||||||
|
pub struct LanguageMatcher {
|
||||||
|
/// Given a list of `LanguageConfig`'s, the language of a file can be determined based on the path extension matching any of the `path_suffixes`.
|
||||||
|
#[serde(default)]
|
||||||
|
pub path_suffixes: Vec<String>,
|
||||||
|
/// A regex pattern that determines whether the language should be assigned to a file or not.
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
serialize_with = "serialize_regex",
|
||||||
|
deserialize_with = "deserialize_regex"
|
||||||
|
)]
|
||||||
|
pub first_line_pattern: Option<Regex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const QUERY_FILENAME_PREFIXES: &[(
|
||||||
|
&str,
|
||||||
|
fn(&mut LanguageQueries) -> &mut Option<Cow<'static, str>>,
|
||||||
|
)] = &[
|
||||||
|
("highlights", |q| &mut q.highlights),
|
||||||
|
("brackets", |q| &mut q.brackets),
|
||||||
|
("outline", |q| &mut q.outline),
|
||||||
|
("indents", |q| &mut q.indents),
|
||||||
|
("embedding", |q| &mut q.embedding),
|
||||||
|
("injections", |q| &mut q.injections),
|
||||||
|
("overrides", |q| &mut q.overrides),
|
||||||
|
("redactions", |q| &mut q.redactions),
|
||||||
|
];
|
||||||
|
|
||||||
/// Tree-sitter language queries for a given language.
|
/// Tree-sitter language queries for a given language.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct LanguageQueries {
|
pub struct LanguageQueries {
|
||||||
@ -506,11 +534,10 @@ impl Default for LanguageConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: "".into(),
|
name: "".into(),
|
||||||
grammar_name: None,
|
grammar: None,
|
||||||
path_suffixes: Default::default(),
|
matcher: LanguageMatcher::default(),
|
||||||
brackets: Default::default(),
|
brackets: Default::default(),
|
||||||
auto_indent_using_last_non_empty_line: auto_indent_using_last_non_empty_line_default(),
|
auto_indent_using_last_non_empty_line: auto_indent_using_last_non_empty_line_default(),
|
||||||
first_line_pattern: Default::default(),
|
|
||||||
increase_indent_pattern: Default::default(),
|
increase_indent_pattern: Default::default(),
|
||||||
decrease_indent_pattern: Default::default(),
|
decrease_indent_pattern: Default::default(),
|
||||||
autoclose_before: Default::default(),
|
autoclose_before: Default::default(),
|
||||||
@ -538,6 +565,16 @@ fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialize_regex<S>(regex: &Option<Regex>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
match regex {
|
||||||
|
Some(regex) => serializer.serialize_str(regex.as_str()),
|
||||||
|
None => serializer.serialize_none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub struct FakeLspAdapter {
|
pub struct FakeLspAdapter {
|
||||||
@ -702,22 +739,29 @@ type AvailableLanguageId = usize;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AvailableLanguage {
|
struct AvailableLanguage {
|
||||||
id: AvailableLanguageId,
|
id: AvailableLanguageId,
|
||||||
config: LanguageConfig,
|
name: Arc<str>,
|
||||||
grammar: AvailableGrammar,
|
source: AvailableLanguageSource,
|
||||||
lsp_adapters: Vec<Arc<dyn LspAdapter>>,
|
lsp_adapters: Vec<Arc<dyn LspAdapter>>,
|
||||||
loaded: bool,
|
loaded: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
enum AvailableGrammar {
|
enum AvailableGrammar {
|
||||||
Native {
|
Loaded(tree_sitter::Language),
|
||||||
grammar: tree_sitter::Language,
|
Loading(Vec<oneshot::Sender<Result<tree_sitter::Language>>>),
|
||||||
|
Unloaded(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum AvailableLanguageSource {
|
||||||
|
BuiltIn {
|
||||||
asset_dir: &'static str,
|
asset_dir: &'static str,
|
||||||
get_queries: fn(&str) -> LanguageQueries,
|
get_queries: fn(&str) -> LanguageQueries,
|
||||||
|
config: LanguageConfig,
|
||||||
},
|
},
|
||||||
Wasm {
|
Extension {
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
get_queries: fn(&Path) -> LanguageQueries,
|
get_queries: fn(&Path) -> LanguageQueries,
|
||||||
|
matcher: LanguageMatcher,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,6 +781,7 @@ struct LanguageRegistryState {
|
|||||||
next_language_server_id: usize,
|
next_language_server_id: usize,
|
||||||
languages: Vec<Arc<Language>>,
|
languages: Vec<Arc<Language>>,
|
||||||
available_languages: Vec<AvailableLanguage>,
|
available_languages: Vec<AvailableLanguage>,
|
||||||
|
grammars: HashMap<String, AvailableGrammar>,
|
||||||
next_available_language_id: AvailableLanguageId,
|
next_available_language_id: AvailableLanguageId,
|
||||||
loading_languages: HashMap<AvailableLanguageId, Vec<oneshot::Sender<Result<Arc<Language>>>>>,
|
loading_languages: HashMap<AvailableLanguageId, Vec<oneshot::Sender<Result<Arc<Language>>>>>,
|
||||||
subscription: (watch::Sender<()>, watch::Receiver<()>),
|
subscription: (watch::Sender<()>, watch::Receiver<()>),
|
||||||
@ -758,6 +803,7 @@ impl LanguageRegistry {
|
|||||||
next_language_server_id: 0,
|
next_language_server_id: 0,
|
||||||
languages: vec![PLAIN_TEXT.clone()],
|
languages: vec![PLAIN_TEXT.clone()],
|
||||||
available_languages: Default::default(),
|
available_languages: Default::default(),
|
||||||
|
grammars: Default::default(),
|
||||||
next_available_language_id: 0,
|
next_available_language_id: 0,
|
||||||
loading_languages: Default::default(),
|
loading_languages: Default::default(),
|
||||||
subscription: watch::channel(),
|
subscription: watch::channel(),
|
||||||
@ -787,20 +833,24 @@ impl LanguageRegistry {
|
|||||||
self.state.write().reload();
|
self.state.write().reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clear out the given languages and reload them from scratch.
|
||||||
|
pub fn reload_languages(&self, languages: &HashSet<Arc<str>>) {
|
||||||
|
self.state.write().reload_languages(languages);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register(
|
pub fn register(
|
||||||
&self,
|
&self,
|
||||||
asset_dir: &'static str,
|
asset_dir: &'static str,
|
||||||
config: LanguageConfig,
|
config: LanguageConfig,
|
||||||
grammar: tree_sitter::Language,
|
|
||||||
lsp_adapters: Vec<Arc<dyn LspAdapter>>,
|
lsp_adapters: Vec<Arc<dyn LspAdapter>>,
|
||||||
get_queries: fn(&str) -> LanguageQueries,
|
get_queries: fn(&str) -> LanguageQueries,
|
||||||
) {
|
) {
|
||||||
let state = &mut *self.state.write();
|
let state = &mut *self.state.write();
|
||||||
state.available_languages.push(AvailableLanguage {
|
state.available_languages.push(AvailableLanguage {
|
||||||
id: post_inc(&mut state.next_available_language_id),
|
id: post_inc(&mut state.next_available_language_id),
|
||||||
config,
|
name: config.name.clone(),
|
||||||
grammar: AvailableGrammar::Native {
|
source: AvailableLanguageSource::BuiltIn {
|
||||||
grammar,
|
config,
|
||||||
get_queries,
|
get_queries,
|
||||||
asset_dir,
|
asset_dir,
|
||||||
},
|
},
|
||||||
@ -809,28 +859,63 @@ impl LanguageRegistry {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_wasm(
|
pub fn register_extension(
|
||||||
&self,
|
&self,
|
||||||
path: Arc<Path>,
|
path: Arc<Path>,
|
||||||
config: LanguageConfig,
|
name: Arc<str>,
|
||||||
|
matcher: LanguageMatcher,
|
||||||
get_queries: fn(&Path) -> LanguageQueries,
|
get_queries: fn(&Path) -> LanguageQueries,
|
||||||
) {
|
) {
|
||||||
let state = &mut *self.state.write();
|
let state = &mut *self.state.write();
|
||||||
|
let source = AvailableLanguageSource::Extension {
|
||||||
|
path,
|
||||||
|
get_queries,
|
||||||
|
matcher,
|
||||||
|
};
|
||||||
|
for existing_language in &mut state.available_languages {
|
||||||
|
if existing_language.name == name
|
||||||
|
&& matches!(
|
||||||
|
existing_language.source,
|
||||||
|
AvailableLanguageSource::Extension { .. }
|
||||||
|
)
|
||||||
|
{
|
||||||
|
existing_language.source = source;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
state.available_languages.push(AvailableLanguage {
|
state.available_languages.push(AvailableLanguage {
|
||||||
id: post_inc(&mut state.next_available_language_id),
|
id: post_inc(&mut state.next_available_language_id),
|
||||||
config,
|
name,
|
||||||
grammar: AvailableGrammar::Wasm { path, get_queries },
|
source,
|
||||||
lsp_adapters: Vec::new(),
|
lsp_adapters: Vec::new(),
|
||||||
loaded: false,
|
loaded: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_grammars(
|
||||||
|
&self,
|
||||||
|
grammars: impl IntoIterator<Item = (impl Into<String>, tree_sitter::Language)>,
|
||||||
|
) {
|
||||||
|
self.state.write().grammars.extend(
|
||||||
|
grammars
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, grammar)| (name.into(), AvailableGrammar::Loaded(grammar))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_grammar(&self, name: String, path: PathBuf) {
|
||||||
|
self.state
|
||||||
|
.write()
|
||||||
|
.grammars
|
||||||
|
.insert(name, AvailableGrammar::Unloaded(path));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn language_names(&self) -> Vec<String> {
|
pub fn language_names(&self) -> Vec<String> {
|
||||||
let state = self.state.read();
|
let state = self.state.read();
|
||||||
let mut result = state
|
let mut result = state
|
||||||
.available_languages
|
.available_languages
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|l| l.loaded.not().then_some(l.config.name.to_string()))
|
.filter_map(|l| l.loaded.not().then_some(l.name.to_string()))
|
||||||
.chain(state.languages.iter().map(|l| l.config.name.to_string()))
|
.chain(state.languages.iter().map(|l| l.config.name.to_string()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
result.sort_unstable_by_key(|language_name| language_name.to_lowercase());
|
result.sort_unstable_by_key(|language_name| language_name.to_lowercase());
|
||||||
@ -873,7 +958,7 @@ impl LanguageRegistry {
|
|||||||
name: &str,
|
name: &str,
|
||||||
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
|
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
|
||||||
let name = UniCase::new(name);
|
let name = UniCase::new(name);
|
||||||
self.get_or_load_language(|config| UniCase::new(config.name.as_ref()) == name)
|
self.get_or_load_language(|language_name, _| UniCase::new(language_name) == name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn language_for_name_or_extension(
|
pub fn language_for_name_or_extension(
|
||||||
@ -881,8 +966,8 @@ impl LanguageRegistry {
|
|||||||
string: &str,
|
string: &str,
|
||||||
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
|
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
|
||||||
let string = UniCase::new(string);
|
let string = UniCase::new(string);
|
||||||
self.get_or_load_language(|config| {
|
self.get_or_load_language(|name, config| {
|
||||||
UniCase::new(config.name.as_ref()) == string
|
UniCase::new(name) == string
|
||||||
|| config
|
|| config
|
||||||
.path_suffixes
|
.path_suffixes
|
||||||
.iter()
|
.iter()
|
||||||
@ -899,7 +984,7 @@ impl LanguageRegistry {
|
|||||||
let filename = path.file_name().and_then(|name| name.to_str());
|
let filename = path.file_name().and_then(|name| name.to_str());
|
||||||
let extension = path.extension_or_hidden_file_name();
|
let extension = path.extension_or_hidden_file_name();
|
||||||
let path_suffixes = [extension, filename];
|
let path_suffixes = [extension, filename];
|
||||||
self.get_or_load_language(|config| {
|
self.get_or_load_language(|_, config| {
|
||||||
let path_matches = config
|
let path_matches = config
|
||||||
.path_suffixes
|
.path_suffixes
|
||||||
.iter()
|
.iter()
|
||||||
@ -919,7 +1004,7 @@ impl LanguageRegistry {
|
|||||||
|
|
||||||
fn get_or_load_language(
|
fn get_or_load_language(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
callback: impl Fn(&LanguageConfig) -> bool,
|
callback: impl Fn(&str, &LanguageMatcher) -> bool,
|
||||||
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
|
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
@ -927,52 +1012,60 @@ impl LanguageRegistry {
|
|||||||
if let Some(language) = state
|
if let Some(language) = state
|
||||||
.languages
|
.languages
|
||||||
.iter()
|
.iter()
|
||||||
.find(|language| callback(&language.config))
|
.find(|language| callback(language.config.name.as_ref(), &language.config.matcher))
|
||||||
{
|
{
|
||||||
let _ = tx.send(Ok(language.clone()));
|
let _ = tx.send(Ok(language.clone()));
|
||||||
} else if let Some(executor) = self.executor.clone() {
|
} else if let Some(executor) = self.executor.clone() {
|
||||||
if let Some(language) = state
|
if let Some(language) = state
|
||||||
.available_languages
|
.available_languages
|
||||||
.iter()
|
.iter()
|
||||||
.find(|l| !l.loaded && callback(&l.config))
|
.rfind(|l| {
|
||||||
|
!l.loaded
|
||||||
|
&& match &l.source {
|
||||||
|
AvailableLanguageSource::BuiltIn { config, .. } => {
|
||||||
|
callback(l.name.as_ref(), &config.matcher)
|
||||||
|
}
|
||||||
|
AvailableLanguageSource::Extension { matcher, .. } => {
|
||||||
|
callback(l.name.as_ref(), &matcher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
let txs = state
|
match state.loading_languages.entry(language.id) {
|
||||||
.loading_languages
|
hash_map::Entry::Occupied(mut entry) => entry.get_mut().push(tx),
|
||||||
.entry(language.id)
|
hash_map::Entry::Vacant(entry) => {
|
||||||
.or_insert_with(|| {
|
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
executor
|
executor
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
let id = language.id;
|
let id = language.id;
|
||||||
let name = language.config.name.clone();
|
let name = language.name.clone();
|
||||||
let language = async {
|
let language = async {
|
||||||
let (grammar, queries) = match language.grammar {
|
let (config, queries) = match language.source {
|
||||||
AvailableGrammar::Native {
|
AvailableLanguageSource::BuiltIn {
|
||||||
grammar,
|
|
||||||
asset_dir,
|
asset_dir,
|
||||||
get_queries,
|
get_queries,
|
||||||
} => (grammar, (get_queries)(asset_dir)),
|
config,
|
||||||
AvailableGrammar::Wasm { path, get_queries } => {
|
} => (config, (get_queries)(asset_dir)),
|
||||||
let grammar_name =
|
AvailableLanguageSource::Extension {
|
||||||
&language.config.grammar_name.as_ref().ok_or_else(
|
path,
|
||||||
|| anyhow!("missing grammar name"),
|
get_queries,
|
||||||
)?;
|
..
|
||||||
let mut wasm_path = path.join(grammar_name.as_ref());
|
} => {
|
||||||
wasm_path.set_extension("wasm");
|
let config = std::fs::read(path.join("config.toml"));
|
||||||
let wasm_bytes = std::fs::read(&wasm_path)?;
|
let config: LanguageConfig =
|
||||||
let grammar = PARSER.with(|parser| {
|
::toml::from_slice(&config?)?;
|
||||||
let mut parser = parser.borrow_mut();
|
(config, get_queries(path.as_ref()))
|
||||||
let mut store = parser.take_wasm_store().unwrap();
|
|
||||||
let grammar =
|
|
||||||
store.load_language(&grammar_name, &wasm_bytes);
|
|
||||||
parser.set_wasm_store(store).unwrap();
|
|
||||||
grammar
|
|
||||||
})?;
|
|
||||||
(grammar, get_queries(path.as_ref()))
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Language::new(language.config, Some(grammar))
|
|
||||||
|
let grammar = if let Some(grammar) = config.grammar.clone() {
|
||||||
|
Some(this.get_or_load_grammar(grammar).await?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Language::new(config, grammar)
|
||||||
.with_lsp_adapters(language.lsp_adapters)
|
.with_lsp_adapters(language.lsp_adapters)
|
||||||
.await
|
.await
|
||||||
.with_queries(queries)
|
.with_queries(queries)
|
||||||
@ -1009,10 +1102,9 @@ impl LanguageRegistry {
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
entry.insert(vec![tx]);
|
||||||
Vec::new()
|
}
|
||||||
});
|
}
|
||||||
txs.push(tx);
|
|
||||||
} else {
|
} else {
|
||||||
let _ = tx.send(Err(anyhow!("language not found")));
|
let _ = tx.send(Err(anyhow!("language not found")));
|
||||||
}
|
}
|
||||||
@ -1023,6 +1115,65 @@ impl LanguageRegistry {
|
|||||||
rx.unwrap()
|
rx.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_or_load_grammar(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
name: Arc<str>,
|
||||||
|
) -> UnwrapFuture<oneshot::Receiver<Result<tree_sitter::Language>>> {
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
let mut state = self.state.write();
|
||||||
|
|
||||||
|
if let Some(grammar) = state.grammars.get_mut(name.as_ref()) {
|
||||||
|
match grammar {
|
||||||
|
AvailableGrammar::Loaded(grammar) => {
|
||||||
|
tx.send(Ok(grammar.clone())).ok();
|
||||||
|
}
|
||||||
|
AvailableGrammar::Loading(txs) => {
|
||||||
|
txs.push(tx);
|
||||||
|
}
|
||||||
|
AvailableGrammar::Unloaded(wasm_path) => {
|
||||||
|
if let Some(executor) = &self.executor {
|
||||||
|
let this = self.clone();
|
||||||
|
let wasm_path = wasm_path.clone();
|
||||||
|
executor
|
||||||
|
.spawn(async move {
|
||||||
|
let wasm_bytes = std::fs::read(&wasm_path)?;
|
||||||
|
let grammar_name = wasm_path
|
||||||
|
.file_stem()
|
||||||
|
.and_then(OsStr::to_str)
|
||||||
|
.ok_or_else(|| anyhow!("invalid grammar filename"))?;
|
||||||
|
let grammar = PARSER.with(|parser| {
|
||||||
|
let mut parser = parser.borrow_mut();
|
||||||
|
let mut store = parser.take_wasm_store().unwrap();
|
||||||
|
let grammar = store.load_language(&grammar_name, &wasm_bytes);
|
||||||
|
parser.set_wasm_store(store).unwrap();
|
||||||
|
grammar
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if let Some(AvailableGrammar::Loading(txs)) =
|
||||||
|
this.state.write().grammars.insert(
|
||||||
|
name.to_string(),
|
||||||
|
AvailableGrammar::Loaded(grammar.clone()),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for tx in txs {
|
||||||
|
tx.send(Ok(grammar.clone())).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
anyhow::Ok(())
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
*grammar = AvailableGrammar::Loading(vec![tx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tx.send(Err(anyhow!("no such grammar {}", name))).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
rx.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_vec(&self) -> Vec<Arc<Language>> {
|
pub fn to_vec(&self) -> Vec<Arc<Language>> {
|
||||||
self.state.read().languages.iter().cloned().collect()
|
self.state.read().languages.iter().cloned().collect()
|
||||||
}
|
}
|
||||||
@ -1206,6 +1357,19 @@ impl LanguageRegistryState {
|
|||||||
*self.subscription.0.borrow_mut() = ();
|
*self.subscription.0.borrow_mut() = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reload_languages(&mut self, languages: &HashSet<Arc<str>>) {
|
||||||
|
self.languages
|
||||||
|
.retain(|language| !languages.contains(&language.config.name));
|
||||||
|
self.version += 1;
|
||||||
|
self.reload_count += 1;
|
||||||
|
for language in &mut self.available_languages {
|
||||||
|
if languages.contains(&language.name) {
|
||||||
|
language.loaded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*self.subscription.0.borrow_mut() = ();
|
||||||
|
}
|
||||||
|
|
||||||
/// Mark the given language a having been loaded, so that the
|
/// Mark the given language a having been loaded, so that the
|
||||||
/// language registry won't try to load it again.
|
/// language registry won't try to load it again.
|
||||||
fn mark_language_loaded(&mut self, id: AvailableLanguageId) {
|
fn mark_language_loaded(&mut self, id: AvailableLanguageId) {
|
||||||
@ -1720,7 +1884,7 @@ impl Language {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn path_suffixes(&self) -> &[String] {
|
pub fn path_suffixes(&self) -> &[String] {
|
||||||
&self.config.path_suffixes
|
&self.config.matcher.path_suffixes
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_autoclose_before(&self, c: char) -> bool {
|
pub fn should_autoclose_before(&self, c: char) -> bool {
|
||||||
@ -1911,6 +2075,33 @@ impl CodeLabel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Ord for LanguageMatcher {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.path_suffixes.cmp(&other.path_suffixes).then_with(|| {
|
||||||
|
self.first_line_pattern
|
||||||
|
.as_ref()
|
||||||
|
.map(Regex::as_str)
|
||||||
|
.cmp(&other.first_line_pattern.as_ref().map(Regex::as_str))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for LanguageMatcher {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for LanguageMatcher {}
|
||||||
|
|
||||||
|
impl PartialEq for LanguageMatcher {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.path_suffixes == other.path_suffixes
|
||||||
|
&& self.first_line_pattern.as_ref().map(Regex::as_str)
|
||||||
|
== other.first_line_pattern.as_ref().map(Regex::as_str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
impl Default for FakeLspAdapter {
|
impl Default for FakeLspAdapter {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -2034,11 +2225,12 @@ mod tests {
|
|||||||
"/javascript",
|
"/javascript",
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "JavaScript".into(),
|
name: "JavaScript".into(),
|
||||||
path_suffixes: vec!["js".into()],
|
matcher: LanguageMatcher {
|
||||||
first_line_pattern: Some(Regex::new(r"\bnode\b").unwrap()),
|
path_suffixes: vec!["js".into()],
|
||||||
|
first_line_pattern: Some(Regex::new(r"\bnode\b").unwrap()),
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
tree_sitter_typescript::language_tsx(),
|
|
||||||
vec![],
|
vec![],
|
||||||
|_| Default::default(),
|
|_| Default::default(),
|
||||||
);
|
);
|
||||||
@ -2067,14 +2259,21 @@ mod tests {
|
|||||||
let mut languages = LanguageRegistry::test();
|
let mut languages = LanguageRegistry::test();
|
||||||
languages.set_executor(cx.executor());
|
languages.set_executor(cx.executor());
|
||||||
let languages = Arc::new(languages);
|
let languages = Arc::new(languages);
|
||||||
|
languages.add_grammars([
|
||||||
|
("json", tree_sitter_json::language()),
|
||||||
|
("rust", tree_sitter_rust::language()),
|
||||||
|
]);
|
||||||
languages.register(
|
languages.register(
|
||||||
"/JSON",
|
"/JSON",
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "JSON".into(),
|
name: "JSON".into(),
|
||||||
path_suffixes: vec!["json".into()],
|
grammar: Some("json".into()),
|
||||||
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["json".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
tree_sitter_json::language(),
|
|
||||||
vec![],
|
vec![],
|
||||||
|_| Default::default(),
|
|_| Default::default(),
|
||||||
);
|
);
|
||||||
@ -2082,10 +2281,13 @@ mod tests {
|
|||||||
"/rust",
|
"/rust",
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".into()],
|
grammar: Some("rust".into()),
|
||||||
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
tree_sitter_rust::language(),
|
|
||||||
vec![],
|
vec![],
|
||||||
|_| Default::default(),
|
|_| Default::default(),
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::LanguageConfig;
|
use crate::{LanguageConfig, LanguageMatcher};
|
||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
use std::{env, ops::Range, sync::Arc};
|
use std::{env, ops::Range, sync::Arc};
|
||||||
use text::{Buffer, BufferId};
|
use text::{Buffer, BufferId};
|
||||||
@ -1092,7 +1092,10 @@ fn html_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "HTML".into(),
|
name: "HTML".into(),
|
||||||
path_suffixes: vec!["html".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["html".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_html::language()),
|
Some(tree_sitter_html::language()),
|
||||||
@ -1111,7 +1114,10 @@ fn ruby_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Ruby".into(),
|
name: "Ruby".into(),
|
||||||
path_suffixes: vec!["rb".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rb".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_ruby::language()),
|
Some(tree_sitter_ruby::language()),
|
||||||
@ -1130,7 +1136,10 @@ fn erb_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "ERB".into(),
|
name: "ERB".into(),
|
||||||
path_suffixes: vec!["erb".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["erb".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_embedded_template::language()),
|
Some(tree_sitter_embedded_template::language()),
|
||||||
@ -1163,7 +1172,10 @@ fn rust_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -1189,7 +1201,10 @@ fn markdown_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Markdown".into(),
|
name: "Markdown".into(),
|
||||||
path_suffixes: vec!["md".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["md".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_markdown::language()),
|
Some(tree_sitter_markdown::language()),
|
||||||
@ -1209,7 +1224,10 @@ fn elixir_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Elixir".into(),
|
name: "Elixir".into(),
|
||||||
path_suffixes: vec!["ex".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["ex".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_elixir::language()),
|
Some(tree_sitter_elixir::language()),
|
||||||
@ -1226,7 +1244,10 @@ fn heex_lang() -> Language {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "HEEx".into(),
|
name: "HEEx".into(),
|
||||||
path_suffixes: vec!["heex".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["heex".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_heex::language()),
|
Some(tree_sitter_heex::language()),
|
||||||
|
@ -5,7 +5,9 @@ use crate::lsp_log::LogMenuItem;
|
|||||||
use super::*;
|
use super::*;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{Context, TestAppContext, VisualTestContext};
|
use gpui::{Context, TestAppContext, VisualTestContext};
|
||||||
use language::{tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig, LanguageServerName};
|
use language::{
|
||||||
|
tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig, LanguageMatcher, LanguageServerName,
|
||||||
|
};
|
||||||
use project::{FakeFs, Project};
|
use project::{FakeFs, Project};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
@ -21,7 +23,10 @@ async fn test_lsp_logs(cx: &mut TestAppContext) {
|
|||||||
let mut rust_language = Language::new(
|
let mut rust_language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
|
@ -5,7 +5,7 @@ use gpui::AppContext;
|
|||||||
use language::{
|
use language::{
|
||||||
language_settings::{AllLanguageSettings, LanguageSettingsContent},
|
language_settings::{AllLanguageSettings, LanguageSettingsContent},
|
||||||
tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig,
|
tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig,
|
||||||
LineEnding, OffsetRangeExt, Point, ToPoint,
|
LanguageMatcher, LineEnding, OffsetRangeExt, Point, ToPoint,
|
||||||
};
|
};
|
||||||
use lsp::Url;
|
use lsp::Url;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
@ -149,7 +149,10 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut rust_language = Language::new(
|
let mut rust_language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -157,7 +160,10 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut json_language = Language::new(
|
let mut json_language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "JSON".into(),
|
name: "JSON".into(),
|
||||||
path_suffixes: vec!["json".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["json".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
@ -535,7 +541,10 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -970,7 +979,10 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -1102,7 +1114,10 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
|||||||
let progress_token = "the-progress-token";
|
let progress_token = "the-progress-token";
|
||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
@ -1183,7 +1198,10 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp
|
|||||||
|
|
||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
@ -1272,7 +1290,10 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T
|
|||||||
|
|
||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
@ -1322,7 +1343,10 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut rust = Language::new(
|
let mut rust = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: Arc::from("Rust"),
|
name: Arc::from("Rust"),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
@ -1336,7 +1360,10 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut js = Language::new(
|
let mut js = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: Arc::from("JavaScript"),
|
name: Arc::from("JavaScript"),
|
||||||
path_suffixes: vec!["js".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["js".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
@ -1451,7 +1478,10 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -1862,7 +1892,10 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -2249,7 +2282,10 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
@ -2350,7 +2386,10 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "TypeScript".into(),
|
name: "TypeScript".into(),
|
||||||
path_suffixes: vec!["ts".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["ts".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_typescript::language_typescript()),
|
Some(tree_sitter_typescript::language_typescript()),
|
||||||
@ -2447,7 +2486,10 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "TypeScript".into(),
|
name: "TypeScript".into(),
|
||||||
path_suffixes: vec!["ts".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["ts".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_typescript::language_typescript()),
|
Some(tree_sitter_typescript::language_typescript()),
|
||||||
@ -2513,7 +2555,10 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "TypeScript".into(),
|
name: "TypeScript".into(),
|
||||||
path_suffixes: vec!["ts".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["ts".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
@ -2816,14 +2861,18 @@ async fn test_save_as(cx: &mut gpui::TestAppContext) {
|
|||||||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||||
|
|
||||||
let languages = project.update(cx, |project, _| project.languages().clone());
|
let languages = project.update(cx, |project, _| project.languages().clone());
|
||||||
|
languages.add_grammars([("rust", tree_sitter_rust::language())]);
|
||||||
languages.register(
|
languages.register(
|
||||||
"/some/path",
|
"/some/path",
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".into()],
|
grammar: Some("rust".into()),
|
||||||
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
tree_sitter_rust::language(),
|
|
||||||
vec![],
|
vec![],
|
||||||
|_| Default::default(),
|
|_| Default::default(),
|
||||||
);
|
);
|
||||||
@ -3649,7 +3698,10 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
|
@ -260,7 +260,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use gpui::{TestAppContext, VisualContext};
|
use gpui::{TestAppContext, VisualContext};
|
||||||
use language::{FakeLspAdapter, Language, LanguageConfig};
|
use language::{FakeLspAdapter, Language, LanguageConfig, LanguageMatcher};
|
||||||
use project::FakeFs;
|
use project::FakeFs;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
@ -273,7 +273,10 @@ mod tests {
|
|||||||
let mut language = Language::new(
|
let mut language = Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
use ai::test::FakeEmbeddingProvider;
|
use ai::test::FakeEmbeddingProvider;
|
||||||
|
|
||||||
use gpui::{Task, TestAppContext};
|
use gpui::{Task, TestAppContext};
|
||||||
use language::{Language, LanguageConfig, LanguageRegistry, ToOffset};
|
use language::{Language, LanguageConfig, LanguageMatcher, LanguageRegistry, ToOffset};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use project::{project_settings::ProjectSettings, FakeFs, Fs, Project};
|
use project::{project_settings::ProjectSettings, FakeFs, Fs, Project};
|
||||||
@ -1251,7 +1251,10 @@ fn js_lang() -> Arc<Language> {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Javascript".into(),
|
name: "Javascript".into(),
|
||||||
path_suffixes: vec!["js".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["js".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_typescript::language_tsx()),
|
Some(tree_sitter_typescript::language_tsx()),
|
||||||
@ -1343,7 +1346,10 @@ fn rust_lang() -> Arc<Language> {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
collapsed_placeholder: " /* ... */ ".to_string(),
|
collapsed_placeholder: " /* ... */ ".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@ -1393,7 +1399,10 @@ fn json_lang() -> Arc<Language> {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "JSON".into(),
|
name: "JSON".into(),
|
||||||
path_suffixes: vec!["json".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["json".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_json::language()),
|
Some(tree_sitter_json::language()),
|
||||||
@ -1421,7 +1430,10 @@ fn toml_lang() -> Arc<Language> {
|
|||||||
Arc::new(Language::new(
|
Arc::new(Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "TOML".into(),
|
name: "TOML".into(),
|
||||||
path_suffixes: vec!["toml".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["toml".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_toml::language()),
|
Some(tree_sitter_toml::language()),
|
||||||
@ -1433,7 +1445,10 @@ fn cpp_lang() -> Arc<Language> {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "CPP".into(),
|
name: "CPP".into(),
|
||||||
path_suffixes: vec!["cpp".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["cpp".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_cpp::language()),
|
Some(tree_sitter_cpp::language()),
|
||||||
@ -1513,7 +1528,10 @@ fn lua_lang() -> Arc<Language> {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Lua".into(),
|
name: "Lua".into(),
|
||||||
path_suffixes: vec!["lua".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["lua".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
collapsed_placeholder: "--[ ... ]--".to_string(),
|
collapsed_placeholder: "--[ ... ]--".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@ -1542,7 +1560,10 @@ fn php_lang() -> Arc<Language> {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "PHP".into(),
|
name: "PHP".into(),
|
||||||
path_suffixes: vec!["php".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["php".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
collapsed_placeholder: "/* ... */".into(),
|
collapsed_placeholder: "/* ... */".into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@ -1597,7 +1618,10 @@ fn ruby_lang() -> Arc<Language> {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Ruby".into(),
|
name: "Ruby".into(),
|
||||||
path_suffixes: vec!["rb".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rb".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
collapsed_placeholder: "# ...".to_string(),
|
collapsed_placeholder: "# ...".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@ -1638,7 +1662,10 @@ fn elixir_lang() -> Arc<Language> {
|
|||||||
Language::new(
|
Language::new(
|
||||||
LanguageConfig {
|
LanguageConfig {
|
||||||
name: "Elixir".into(),
|
name: "Elixir".into(),
|
||||||
path_suffixes: vec!["rs".into()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".into()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_elixir::language()),
|
Some(tree_sitter_elixir::language()),
|
||||||
|
@ -194,7 +194,9 @@ impl ThemeRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_names(&self, _staff: bool) -> Vec<SharedString> {
|
pub fn list_names(&self, _staff: bool) -> Vec<SharedString> {
|
||||||
self.state.read().themes.keys().cloned().collect()
|
let mut names = self.state.read().themes.keys().cloned().collect::<Vec<_>>();
|
||||||
|
names.sort();
|
||||||
|
names
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list(&self, _staff: bool) -> Vec<ThemeMeta> {
|
pub fn list(&self, _staff: bool) -> Vec<ThemeMeta> {
|
||||||
@ -263,11 +265,17 @@ impl ThemeRegistry {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the user theme from the specified path and adds it to the registry.
|
pub async fn read_user_theme(theme_path: &Path, fs: Arc<dyn Fs>) -> Result<ThemeFamilyContent> {
|
||||||
pub async fn load_user_theme(&self, theme_path: &Path, fs: Arc<dyn Fs>) -> Result<()> {
|
|
||||||
let reader = fs.open_sync(&theme_path).await?;
|
let reader = fs.open_sync(&theme_path).await?;
|
||||||
let theme = serde_json_lenient::from_reader(reader)?;
|
let theme = serde_json_lenient::from_reader(reader)?;
|
||||||
|
|
||||||
|
Ok(theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads the user theme from the specified path and adds it to the registry.
|
||||||
|
pub async fn load_user_theme(&self, theme_path: &Path, fs: Arc<dyn Fs>) -> Result<()> {
|
||||||
|
let theme = Self::read_user_theme(theme_path, fs).await?;
|
||||||
|
|
||||||
self.insert_user_theme_families([theme]);
|
self.insert_user_theme_families([theme]);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
cmp::Ordering,
|
||||||
fmt::{self, Debug},
|
fmt::{self, Debug},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
@ -18,6 +19,18 @@ impl<'a, T: ?Sized + PartialEq> PartialEq for ArcCow<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + PartialOrd> PartialOrd for ArcCow<'a, T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
self.as_ref().partial_cmp(other.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: ?Sized + Ord> Ord for ArcCow<'a, T> {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.as_ref().cmp(other.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, T: ?Sized + Eq> Eq for ArcCow<'a, T> {}
|
impl<'a, T: ?Sized + Eq> Eq for ArcCow<'a, T> {}
|
||||||
|
|
||||||
impl<'a, T: ?Sized + Hash> Hash for ArcCow<'a, T> {
|
impl<'a, T: ?Sized + Hash> Hash for ArcCow<'a, T> {
|
||||||
|
@ -22,6 +22,11 @@ lazy_static::lazy_static! {
|
|||||||
} else {
|
} else {
|
||||||
CONFIG_DIR.join("support")
|
CONFIG_DIR.join("support")
|
||||||
};
|
};
|
||||||
|
pub static ref EXTENSIONS_DIR: PathBuf = if cfg!(target_os="macos") {
|
||||||
|
HOME.join("Library/Application Support/Zed")
|
||||||
|
} else {
|
||||||
|
CONFIG_DIR.join("extensions")
|
||||||
|
};
|
||||||
pub static ref PLUGINS_DIR: PathBuf = if cfg!(target_os="macos") {
|
pub static ref PLUGINS_DIR: PathBuf = if cfg!(target_os="macos") {
|
||||||
HOME.join("Library/Application Support/Zed/plugins")
|
HOME.join("Library/Application Support/Zed/plugins")
|
||||||
} else {
|
} else {
|
||||||
|
@ -44,6 +44,7 @@ db.workspace = true
|
|||||||
diagnostics.workspace = true
|
diagnostics.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
|
extension.workspace = true
|
||||||
feature_flags.workspace = true
|
feature_flags.workspace = true
|
||||||
feedback.workspace = true
|
feedback.workspace = true
|
||||||
file_finder.workspace = true
|
file_finder.workspace = true
|
||||||
|
@ -4,8 +4,8 @@ pub use language::*;
|
|||||||
use node_runtime::NodeRuntime;
|
use node_runtime::NodeRuntime;
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::{borrow::Cow, fs, path::Path, str, sync::Arc};
|
use std::{str, sync::Arc};
|
||||||
use util::{asset_str, paths::PLUGINS_DIR, ResultExt};
|
use util::asset_str;
|
||||||
|
|
||||||
use self::{deno::DenoSettings, elixir::ElixirSettings};
|
use self::{deno::DenoSettings, elixir::ElixirSettings};
|
||||||
|
|
||||||
@ -62,30 +62,69 @@ pub fn init(
|
|||||||
ElixirSettings::register(cx);
|
ElixirSettings::register(cx);
|
||||||
DenoSettings::register(cx);
|
DenoSettings::register(cx);
|
||||||
|
|
||||||
let language = |name, grammar, adapters| {
|
languages.add_grammars([
|
||||||
languages.register(name, load_config(name), grammar, adapters, load_queries)
|
("bash", tree_sitter_bash::language()),
|
||||||
|
("beancount", tree_sitter_beancount::language()),
|
||||||
|
("c", tree_sitter_c::language()),
|
||||||
|
("c_sharp", tree_sitter_c_sharp::language()),
|
||||||
|
("cpp", tree_sitter_cpp::language()),
|
||||||
|
("css", tree_sitter_css::language()),
|
||||||
|
("elixir", tree_sitter_elixir::language()),
|
||||||
|
("elm", tree_sitter_elm::language()),
|
||||||
|
(
|
||||||
|
"embedded_template",
|
||||||
|
tree_sitter_embedded_template::language(),
|
||||||
|
),
|
||||||
|
("erlang", tree_sitter_erlang::language()),
|
||||||
|
("gitcommit", tree_sitter_gitcommit::language()),
|
||||||
|
("gleam", tree_sitter_gleam::language()),
|
||||||
|
("glsl", tree_sitter_glsl::language()),
|
||||||
|
("go", tree_sitter_go::language()),
|
||||||
|
("gomod", tree_sitter_gomod::language()),
|
||||||
|
("gowork", tree_sitter_gowork::language()),
|
||||||
|
("haskell", tree_sitter_haskell::language()),
|
||||||
|
("hcl", tree_sitter_hcl::language()),
|
||||||
|
("heex", tree_sitter_heex::language()),
|
||||||
|
("html", tree_sitter_html::language()),
|
||||||
|
("json", tree_sitter_json::language()),
|
||||||
|
("lua", tree_sitter_lua::language()),
|
||||||
|
("markdown", tree_sitter_markdown::language()),
|
||||||
|
("nix", tree_sitter_nix::language()),
|
||||||
|
("nu", tree_sitter_nu::language()),
|
||||||
|
("ocaml", tree_sitter_ocaml::language_ocaml()),
|
||||||
|
(
|
||||||
|
"ocaml_interface",
|
||||||
|
tree_sitter_ocaml::language_ocaml_interface(),
|
||||||
|
),
|
||||||
|
("php", tree_sitter_php::language_php()),
|
||||||
|
("proto", tree_sitter_proto::language()),
|
||||||
|
("purescript", tree_sitter_purescript::language()),
|
||||||
|
("python", tree_sitter_python::language()),
|
||||||
|
("racket", tree_sitter_racket::language()),
|
||||||
|
("ruby", tree_sitter_ruby::language()),
|
||||||
|
("rust", tree_sitter_rust::language()),
|
||||||
|
("scheme", tree_sitter_scheme::language()),
|
||||||
|
("svelte", tree_sitter_svelte::language()),
|
||||||
|
("toml", tree_sitter_toml::language()),
|
||||||
|
("tsx", tree_sitter_typescript::language_tsx()),
|
||||||
|
("typescript", tree_sitter_typescript::language_typescript()),
|
||||||
|
("uiua", tree_sitter_uiua::language()),
|
||||||
|
("vue", tree_sitter_vue::language()),
|
||||||
|
("yaml", tree_sitter_yaml::language()),
|
||||||
|
("zig", tree_sitter_zig::language()),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let language = |name: &'static str, adapters| {
|
||||||
|
languages.register(name, load_config(name), adapters, load_queries)
|
||||||
};
|
};
|
||||||
|
|
||||||
language("bash", tree_sitter_bash::language(), vec![]);
|
language("bash", vec![]);
|
||||||
language("beancount", tree_sitter_beancount::language(), vec![]);
|
language("beancount", vec![]);
|
||||||
language(
|
language("c", vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>]);
|
||||||
"c",
|
language("cpp", vec![Arc::new(c::CLspAdapter)]);
|
||||||
tree_sitter_c::language(),
|
language("csharp", vec![Arc::new(csharp::OmniSharpAdapter {})]);
|
||||||
vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>],
|
|
||||||
);
|
|
||||||
language(
|
|
||||||
"cpp",
|
|
||||||
tree_sitter_cpp::language(),
|
|
||||||
vec![Arc::new(c::CLspAdapter)],
|
|
||||||
);
|
|
||||||
language(
|
|
||||||
"csharp",
|
|
||||||
tree_sitter_c_sharp::language(),
|
|
||||||
vec![Arc::new(csharp::OmniSharpAdapter {})],
|
|
||||||
);
|
|
||||||
language(
|
language(
|
||||||
"css",
|
"css",
|
||||||
tree_sitter_css::language(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(css::CssLspAdapter::new(node_runtime.clone())),
|
Arc::new(css::CssLspAdapter::new(node_runtime.clone())),
|
||||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
@ -95,53 +134,32 @@ pub fn init(
|
|||||||
match &ElixirSettings::get(None, cx).lsp {
|
match &ElixirSettings::get(None, cx).lsp {
|
||||||
elixir::ElixirLspSetting::ElixirLs => language(
|
elixir::ElixirLspSetting::ElixirLs => language(
|
||||||
"elixir",
|
"elixir",
|
||||||
tree_sitter_elixir::language(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(elixir::ElixirLspAdapter),
|
Arc::new(elixir::ElixirLspAdapter),
|
||||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
elixir::ElixirLspSetting::NextLs => language(
|
elixir::ElixirLspSetting::NextLs => {
|
||||||
"elixir",
|
language("elixir", vec![Arc::new(elixir::NextLspAdapter)])
|
||||||
tree_sitter_elixir::language(),
|
}
|
||||||
vec![Arc::new(elixir::NextLspAdapter)],
|
|
||||||
),
|
|
||||||
elixir::ElixirLspSetting::Local { path, arguments } => language(
|
elixir::ElixirLspSetting::Local { path, arguments } => language(
|
||||||
"elixir",
|
"elixir",
|
||||||
tree_sitter_elixir::language(),
|
|
||||||
vec![Arc::new(elixir::LocalLspAdapter {
|
vec![Arc::new(elixir::LocalLspAdapter {
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
arguments: arguments.clone(),
|
arguments: arguments.clone(),
|
||||||
})],
|
})],
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
language("gitcommit", tree_sitter_gitcommit::language(), vec![]);
|
language("gitcommit", vec![]);
|
||||||
language(
|
language("erlang", vec![Arc::new(erlang::ErlangLspAdapter)]);
|
||||||
"erlang",
|
|
||||||
tree_sitter_erlang::language(),
|
|
||||||
vec![Arc::new(erlang::ErlangLspAdapter)],
|
|
||||||
);
|
|
||||||
|
|
||||||
language(
|
language("gleam", vec![Arc::new(gleam::GleamLspAdapter)]);
|
||||||
"gleam",
|
language("go", vec![Arc::new(go::GoLspAdapter)]);
|
||||||
tree_sitter_gleam::language(),
|
language("gomod", vec![]);
|
||||||
vec![Arc::new(gleam::GleamLspAdapter)],
|
language("gowork", vec![]);
|
||||||
);
|
language("zig", vec![Arc::new(zig::ZlsAdapter)]);
|
||||||
language(
|
|
||||||
"go",
|
|
||||||
tree_sitter_go::language(),
|
|
||||||
vec![Arc::new(go::GoLspAdapter)],
|
|
||||||
);
|
|
||||||
language("gomod", tree_sitter_gomod::language(), vec![]);
|
|
||||||
language("gowork", tree_sitter_gowork::language(), vec![]);
|
|
||||||
language(
|
|
||||||
"zig",
|
|
||||||
tree_sitter_zig::language(),
|
|
||||||
vec![Arc::new(zig::ZlsAdapter)],
|
|
||||||
);
|
|
||||||
language(
|
language(
|
||||||
"heex",
|
"heex",
|
||||||
tree_sitter_heex::language(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(elixir::ElixirLspAdapter),
|
Arc::new(elixir::ElixirLspAdapter),
|
||||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
@ -149,48 +167,32 @@ pub fn init(
|
|||||||
);
|
);
|
||||||
language(
|
language(
|
||||||
"json",
|
"json",
|
||||||
tree_sitter_json::language(),
|
|
||||||
vec![Arc::new(json::JsonLspAdapter::new(
|
vec![Arc::new(json::JsonLspAdapter::new(
|
||||||
node_runtime.clone(),
|
node_runtime.clone(),
|
||||||
languages.clone(),
|
languages.clone(),
|
||||||
))],
|
))],
|
||||||
);
|
);
|
||||||
language("markdown", tree_sitter_markdown::language(), vec![]);
|
language("markdown", vec![]);
|
||||||
language(
|
language(
|
||||||
"python",
|
"python",
|
||||||
tree_sitter_python::language(),
|
|
||||||
vec![Arc::new(python::PythonLspAdapter::new(
|
vec![Arc::new(python::PythonLspAdapter::new(
|
||||||
node_runtime.clone(),
|
node_runtime.clone(),
|
||||||
))],
|
))],
|
||||||
);
|
);
|
||||||
language(
|
language("rust", vec![Arc::new(rust::RustLspAdapter)]);
|
||||||
"rust",
|
language("toml", vec![Arc::new(toml::TaploLspAdapter)]);
|
||||||
tree_sitter_rust::language(),
|
|
||||||
vec![Arc::new(rust::RustLspAdapter)],
|
|
||||||
);
|
|
||||||
language(
|
|
||||||
"toml",
|
|
||||||
tree_sitter_toml::language(),
|
|
||||||
vec![Arc::new(toml::TaploLspAdapter)],
|
|
||||||
);
|
|
||||||
match &DenoSettings::get(None, cx).enable {
|
match &DenoSettings::get(None, cx).enable {
|
||||||
true => {
|
true => {
|
||||||
language(
|
language(
|
||||||
"tsx",
|
"tsx",
|
||||||
tree_sitter_typescript::language_tsx(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(deno::DenoLspAdapter::new()),
|
Arc::new(deno::DenoLspAdapter::new()),
|
||||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
language(
|
language("typescript", vec![Arc::new(deno::DenoLspAdapter::new())]);
|
||||||
"typescript",
|
|
||||||
tree_sitter_typescript::language_typescript(),
|
|
||||||
vec![Arc::new(deno::DenoLspAdapter::new())],
|
|
||||||
);
|
|
||||||
language(
|
language(
|
||||||
"javascript",
|
"javascript",
|
||||||
tree_sitter_typescript::language_tsx(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(deno::DenoLspAdapter::new()),
|
Arc::new(deno::DenoLspAdapter::new()),
|
||||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
@ -200,7 +202,6 @@ pub fn init(
|
|||||||
false => {
|
false => {
|
||||||
language(
|
language(
|
||||||
"tsx",
|
"tsx",
|
||||||
tree_sitter_typescript::language_tsx(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
||||||
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
||||||
@ -209,7 +210,6 @@ pub fn init(
|
|||||||
);
|
);
|
||||||
language(
|
language(
|
||||||
"typescript",
|
"typescript",
|
||||||
tree_sitter_typescript::language_typescript(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
||||||
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
||||||
@ -217,7 +217,6 @@ pub fn init(
|
|||||||
);
|
);
|
||||||
language(
|
language(
|
||||||
"javascript",
|
"javascript",
|
||||||
tree_sitter_typescript::language_tsx(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
|
||||||
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
|
||||||
@ -227,47 +226,31 @@ pub fn init(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
language(
|
language("haskell", vec![Arc::new(haskell::HaskellLanguageServer {})]);
|
||||||
"haskell",
|
|
||||||
tree_sitter_haskell::language(),
|
|
||||||
vec![Arc::new(haskell::HaskellLanguageServer {})],
|
|
||||||
);
|
|
||||||
language(
|
language(
|
||||||
"html",
|
"html",
|
||||||
tree_sitter_html::language(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(html::HtmlLspAdapter::new(node_runtime.clone())),
|
Arc::new(html::HtmlLspAdapter::new(node_runtime.clone())),
|
||||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
language(
|
language("ruby", vec![Arc::new(ruby::RubyLanguageServer)]);
|
||||||
"ruby",
|
|
||||||
tree_sitter_ruby::language(),
|
|
||||||
vec![Arc::new(ruby::RubyLanguageServer)],
|
|
||||||
);
|
|
||||||
language(
|
language(
|
||||||
"erb",
|
"erb",
|
||||||
tree_sitter_embedded_template::language(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(ruby::RubyLanguageServer),
|
Arc::new(ruby::RubyLanguageServer),
|
||||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
language("scheme", tree_sitter_scheme::language(), vec![]);
|
language("scheme", vec![]);
|
||||||
language("racket", tree_sitter_racket::language(), vec![]);
|
language("racket", vec![]);
|
||||||
language(
|
language("lua", vec![Arc::new(lua::LuaLspAdapter)]);
|
||||||
"lua",
|
|
||||||
tree_sitter_lua::language(),
|
|
||||||
vec![Arc::new(lua::LuaLspAdapter)],
|
|
||||||
);
|
|
||||||
language(
|
language(
|
||||||
"yaml",
|
"yaml",
|
||||||
tree_sitter_yaml::language(),
|
|
||||||
vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))],
|
vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))],
|
||||||
);
|
);
|
||||||
language(
|
language(
|
||||||
"svelte",
|
"svelte",
|
||||||
tree_sitter_svelte::language(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(svelte::SvelteLspAdapter::new(node_runtime.clone())),
|
Arc::new(svelte::SvelteLspAdapter::new(node_runtime.clone())),
|
||||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
@ -275,7 +258,6 @@ pub fn init(
|
|||||||
);
|
);
|
||||||
language(
|
language(
|
||||||
"php",
|
"php",
|
||||||
tree_sitter_php::language_php(),
|
|
||||||
vec![
|
vec![
|
||||||
Arc::new(php::IntelephenseLspAdapter::new(node_runtime.clone())),
|
Arc::new(php::IntelephenseLspAdapter::new(node_runtime.clone())),
|
||||||
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
|
||||||
@ -287,62 +269,24 @@ pub fn init(
|
|||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
language(
|
language(
|
||||||
"purescript",
|
"purescript",
|
||||||
tree_sitter_purescript::language(),
|
|
||||||
vec![Arc::new(purescript::PurescriptLspAdapter::new(
|
vec![Arc::new(purescript::PurescriptLspAdapter::new(
|
||||||
node_runtime.clone(),
|
node_runtime.clone(),
|
||||||
))],
|
))],
|
||||||
);
|
);
|
||||||
language(
|
language(
|
||||||
"elm",
|
"elm",
|
||||||
tree_sitter_elm::language(),
|
|
||||||
vec![Arc::new(elm::ElmLspAdapter::new(node_runtime.clone()))],
|
vec![Arc::new(elm::ElmLspAdapter::new(node_runtime.clone()))],
|
||||||
);
|
);
|
||||||
language("glsl", tree_sitter_glsl::language(), vec![]);
|
language("glsl", vec![]);
|
||||||
language("nix", tree_sitter_nix::language(), vec![]);
|
language("nix", vec![]);
|
||||||
language(
|
language("nu", vec![Arc::new(nu::NuLanguageServer {})]);
|
||||||
"nu",
|
language("ocaml", vec![Arc::new(ocaml::OCamlLspAdapter)]);
|
||||||
tree_sitter_nu::language(),
|
language("ocaml-interface", vec![Arc::new(ocaml::OCamlLspAdapter)]);
|
||||||
vec![Arc::new(nu::NuLanguageServer {})],
|
language("vue", vec![Arc::new(vue::VueLspAdapter::new(node_runtime))]);
|
||||||
);
|
language("uiua", vec![Arc::new(uiua::UiuaLanguageServer {})]);
|
||||||
language(
|
language("proto", vec![]);
|
||||||
"ocaml",
|
language("terraform", vec![]);
|
||||||
tree_sitter_ocaml::language_ocaml(),
|
language("hcl", vec![]);
|
||||||
vec![Arc::new(ocaml::OCamlLspAdapter)],
|
|
||||||
);
|
|
||||||
language(
|
|
||||||
"ocaml-interface",
|
|
||||||
tree_sitter_ocaml::language_ocaml_interface(),
|
|
||||||
vec![Arc::new(ocaml::OCamlLspAdapter)],
|
|
||||||
);
|
|
||||||
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 {})],
|
|
||||||
);
|
|
||||||
language("proto", tree_sitter_proto::language(), vec![]);
|
|
||||||
language("terraform", tree_sitter_hcl::language(), vec![]);
|
|
||||||
language("hcl", tree_sitter_hcl::language(), vec![]);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
languages.register_wasm(
|
|
||||||
path.into(),
|
|
||||||
::toml::from_slice(&config).unwrap(),
|
|
||||||
load_plugin_queries,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
@ -370,20 +314,6 @@ fn load_config(name: &str) -> LanguageConfig {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
const QUERY_FILENAME_PREFIXES: &[(
|
|
||||||
&str,
|
|
||||||
fn(&mut LanguageQueries) -> &mut Option<Cow<'static, str>>,
|
|
||||||
)] = &[
|
|
||||||
("highlights", |q| &mut q.highlights),
|
|
||||||
("brackets", |q| &mut q.brackets),
|
|
||||||
("outline", |q| &mut q.outline),
|
|
||||||
("indents", |q| &mut q.indents),
|
|
||||||
("embedding", |q| &mut q.embedding),
|
|
||||||
("injections", |q| &mut q.injections),
|
|
||||||
("overrides", |q| &mut q.overrides),
|
|
||||||
("redactions", |q| &mut q.redactions),
|
|
||||||
];
|
|
||||||
|
|
||||||
fn load_queries(name: &str) -> LanguageQueries {
|
fn load_queries(name: &str) -> LanguageQueries {
|
||||||
let mut result = LanguageQueries::default();
|
let mut result = LanguageQueries::default();
|
||||||
for path in LanguageDir::iter() {
|
for path in LanguageDir::iter() {
|
||||||
@ -404,32 +334,3 @@ fn load_queries(name: &str) -> LanguageQueries {
|
|||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_plugin_queries(root_path: &Path) -> LanguageQueries {
|
|
||||||
let mut result = LanguageQueries::default();
|
|
||||||
if let Some(entries) = fs::read_dir(root_path).log_err() {
|
|
||||||
for entry in entries {
|
|
||||||
let Some(entry) = entry.log_err() else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let path = entry.path();
|
|
||||||
if let Some(remainder) = path.strip_prefix(root_path).ok().and_then(|p| p.to_str()) {
|
|
||||||
if !remainder.ends_with(".scm") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (name, query) in QUERY_FILENAME_PREFIXES {
|
|
||||||
if remainder.starts_with(name) {
|
|
||||||
if let Some(contents) = fs::read_to_string(&path).log_err() {
|
|
||||||
match query(&mut result) {
|
|
||||||
None => *query(&mut result) = Some(contents.into()),
|
|
||||||
Some(r) => r.to_mut().push_str(contents.as_ref()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Shell Script"
|
name = "Shell Script"
|
||||||
|
grammar = "bash"
|
||||||
path_suffixes = ["sh", "bash", "bashrc", "bash_profile", "bash_aliases", "bash_logout", "profile", "zsh", "zshrc", "zshenv", "zsh_profile", "zsh_aliases", "zsh_histfile", "zlogin", "zprofile", ".env"]
|
path_suffixes = ["sh", "bash", "bashrc", "bash_profile", "bash_aliases", "bash_logout", "profile", "zsh", "zshrc", "zshenv", "zsh_profile", "zsh_aliases", "zsh_histfile", "zlogin", "zprofile", ".env"]
|
||||||
line_comments = ["# "]
|
line_comments = ["# "]
|
||||||
first_line_pattern = "^#!.*\\b(?:ba|z)?sh\\b"
|
first_line_pattern = "^#!.*\\b(?:ba|z)?sh\\b"
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
name = "Beancount"
|
name = "Beancount"
|
||||||
|
grammar = "beancount"
|
||||||
path_suffixes = ["beancount"]
|
path_suffixes = ["beancount"]
|
||||||
brackets = [{ start = "\"", end = "\"", close = false, newline = false }]
|
brackets = [{ start = "\"", end = "\"", close = false, newline = false }]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "C"
|
name = "C"
|
||||||
|
grammar = "c"
|
||||||
path_suffixes = ["c"]
|
path_suffixes = ["c"]
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "C++"
|
name = "C++"
|
||||||
|
grammar = "cpp"
|
||||||
path_suffixes = ["cc", "cpp", "h", "hpp", "cxx", "hxx", "inl"]
|
path_suffixes = ["cc", "cpp", "h", "hpp", "cxx", "hxx", "inl"]
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "CSharp"
|
name = "CSharp"
|
||||||
|
grammar = "c_sharp"
|
||||||
path_suffixes = ["cs"]
|
path_suffixes = ["cs"]
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "CSS"
|
name = "CSS"
|
||||||
|
grammar = "css"
|
||||||
path_suffixes = ["css"]
|
path_suffixes = ["css"]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
brackets = [
|
brackets = [
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Elixir"
|
name = "Elixir"
|
||||||
|
grammar = "elixir"
|
||||||
path_suffixes = ["ex", "exs"]
|
path_suffixes = ["ex", "exs"]
|
||||||
line_comments = ["# "]
|
line_comments = ["# "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Elm"
|
name = "Elm"
|
||||||
|
grammar = "elm"
|
||||||
path_suffixes = ["elm"]
|
path_suffixes = ["elm"]
|
||||||
line_comments = ["-- "]
|
line_comments = ["-- "]
|
||||||
block_comment = ["{- ", " -}"]
|
block_comment = ["{- ", " -}"]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "ERB"
|
name = "ERB"
|
||||||
|
grammar = "embedded_template"
|
||||||
path_suffixes = ["erb"]
|
path_suffixes = ["erb"]
|
||||||
autoclose_before = ">})"
|
autoclose_before = ">})"
|
||||||
brackets = [
|
brackets = [
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Erlang"
|
name = "Erlang"
|
||||||
|
grammar = "erlang"
|
||||||
# TODO: support parsing rebar.config files
|
# TODO: support parsing rebar.config files
|
||||||
# # https://github.com/WhatsApp/tree-sitter-erlang/issues/3
|
# # https://github.com/WhatsApp/tree-sitter-erlang/issues/3
|
||||||
path_suffixes = ["erl", "hrl", "app.src", "escript", "xrl", "yrl", "Emakefile", "rebar.config"]
|
path_suffixes = ["erl", "hrl", "app.src", "escript", "xrl", "yrl", "Emakefile", "rebar.config"]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Git Commit"
|
name = "Git Commit"
|
||||||
|
grammar = "git_commit"
|
||||||
path_suffixes = [
|
path_suffixes = [
|
||||||
# Refer to https://github.com/neovim/neovim/blob/master/runtime/lua/vim/filetype.lua#L1286-L1290
|
# Refer to https://github.com/neovim/neovim/blob/master/runtime/lua/vim/filetype.lua#L1286-L1290
|
||||||
"TAG_EDITMSG",
|
"TAG_EDITMSG",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Gleam"
|
name = "Gleam"
|
||||||
|
grammar = "gleam"
|
||||||
path_suffixes = ["gleam"]
|
path_suffixes = ["gleam"]
|
||||||
line_comments = ["// ", "/// "]
|
line_comments = ["// ", "/// "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "GLSL"
|
name = "GLSL"
|
||||||
|
grammar = "glsl"
|
||||||
path_suffixes = ["vert", "frag", "tesc", "tese", "geom", "comp"]
|
path_suffixes = ["vert", "frag", "tesc", "tese", "geom", "comp"]
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
block_comment = ["/* ", " */"]
|
block_comment = ["/* ", " */"]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Go"
|
name = "Go"
|
||||||
|
grammar = "go"
|
||||||
path_suffixes = ["go"]
|
path_suffixes = ["go"]
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Go Mod"
|
name = "Go Mod"
|
||||||
|
grammar = "go"
|
||||||
path_suffixes = ["mod"]
|
path_suffixes = ["mod"]
|
||||||
line_comments = ["//"]
|
line_comments = ["//"]
|
||||||
autoclose_before = ")"
|
autoclose_before = ")"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Go Work"
|
name = "Go Work"
|
||||||
|
grammar = "go_work"
|
||||||
path_suffixes = ["work"]
|
path_suffixes = ["work"]
|
||||||
line_comments = ["//"]
|
line_comments = ["//"]
|
||||||
autoclose_before = ")"
|
autoclose_before = ")"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Haskell"
|
name = "Haskell"
|
||||||
|
grammar = "haskell"
|
||||||
path_suffixes = ["hs"]
|
path_suffixes = ["hs"]
|
||||||
autoclose_before = ",=)}]"
|
autoclose_before = ",=)}]"
|
||||||
line_comments = ["-- "]
|
line_comments = ["-- "]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "HCL"
|
name = "HCL"
|
||||||
|
grammar = "hcl"
|
||||||
path_suffixes = ["hcl"]
|
path_suffixes = ["hcl"]
|
||||||
line_comments = ["# ", "// "]
|
line_comments = ["# ", "// "]
|
||||||
block_comment = ["/*", "*/"]
|
block_comment = ["/*", "*/"]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "HEEX"
|
name = "HEEX"
|
||||||
|
grammar = "heex"
|
||||||
path_suffixes = ["heex"]
|
path_suffixes = ["heex"]
|
||||||
autoclose_before = ">})"
|
autoclose_before = ">})"
|
||||||
brackets = [
|
brackets = [
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "HTML"
|
name = "HTML"
|
||||||
|
grammar = "html"
|
||||||
path_suffixes = ["html", "htm", "shtml"]
|
path_suffixes = ["html", "htm", "shtml"]
|
||||||
autoclose_before = ">})"
|
autoclose_before = ">})"
|
||||||
block_comment = ["<!-- ", " -->"]
|
block_comment = ["<!-- ", " -->"]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "JavaScript"
|
name = "JavaScript"
|
||||||
|
grammar = "tsx"
|
||||||
path_suffixes = ["js", "jsx", "mjs", "cjs"]
|
path_suffixes = ["js", "jsx", "mjs", "cjs"]
|
||||||
first_line_pattern = '^#!.*\bnode\b'
|
first_line_pattern = '^#!.*\bnode\b'
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "JSON"
|
name = "JSON"
|
||||||
|
grammar = "json"
|
||||||
path_suffixes = ["json"]
|
path_suffixes = ["json"]
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
autoclose_before = ",]}"
|
autoclose_before = ",]}"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Lua"
|
name = "Lua"
|
||||||
|
grammar = "lua"
|
||||||
path_suffixes = ["lua"]
|
path_suffixes = ["lua"]
|
||||||
line_comments = ["-- "]
|
line_comments = ["-- "]
|
||||||
autoclose_before = ",]}"
|
autoclose_before = ",]}"
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
name = "Markdown"
|
name = "Markdown"
|
||||||
|
grammar = "markdown"
|
||||||
path_suffixes = ["md", "mdx"]
|
path_suffixes = ["md", "mdx"]
|
||||||
|
word_characters = ["-"]
|
||||||
brackets = [
|
brackets = [
|
||||||
{ start = "{", end = "}", close = true, newline = true },
|
{ start = "{", end = "}", close = true, newline = true },
|
||||||
{ start = "[", end = "]", close = true, newline = true },
|
{ start = "[", end = "]", close = true, newline = true },
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Nix"
|
name = "Nix"
|
||||||
|
grammar = "nix"
|
||||||
path_suffixes = ["nix"]
|
path_suffixes = ["nix"]
|
||||||
line_comments = ["# "]
|
line_comments = ["# "]
|
||||||
block_comment = ["/* ", " */"]
|
block_comment = ["/* ", " */"]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Nu"
|
name = "Nu"
|
||||||
|
grammar = "nu"
|
||||||
path_suffixes = ["nu"]
|
path_suffixes = ["nu"]
|
||||||
line_comments = ["# "]
|
line_comments = ["# "]
|
||||||
autoclose_before = ";:.,=}])>` \n\t\""
|
autoclose_before = ";:.,=}])>` \n\t\""
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "OCaml Interface"
|
name = "OCaml Interface"
|
||||||
|
grammar = "ocaml_interface"
|
||||||
path_suffixes = ["mli"]
|
path_suffixes = ["mli"]
|
||||||
block_comment = ["(* ", "*)"]
|
block_comment = ["(* ", "*)"]
|
||||||
autoclose_before = ";,=)}"
|
autoclose_before = ";,=)}"
|
||||||
@ -8,6 +9,6 @@ brackets = [
|
|||||||
{ start = "[", end = "]", close = true, newline = true },
|
{ start = "[", end = "]", close = true, newline = true },
|
||||||
{ start = "(", end = ")", close = true, newline = true },
|
{ start = "(", end = ")", close = true, newline = true },
|
||||||
{ start = "sig", end = " end", close = true, newline = true },
|
{ start = "sig", end = " end", close = true, newline = true },
|
||||||
# HACK: For some reason `object` alone does not work
|
# HACK: For some reason `object` alone does not work
|
||||||
{ start = "object ", end = "end", close = true, newline = true },
|
{ start = "object ", end = "end", close = true, newline = true },
|
||||||
]
|
]
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
name = "OCaml"
|
name = "OCaml"
|
||||||
|
grammar = "ocaml"
|
||||||
path_suffixes = ["ml"]
|
path_suffixes = ["ml"]
|
||||||
block_comment = ["(* ", "*)"]
|
block_comment = ["(* ", "*)"]
|
||||||
autoclose_before = ";,=)}]"
|
autoclose_before = ";,=)}]"
|
||||||
brackets = [
|
brackets = [
|
||||||
{ start = "{", end = "}", close = true, newline = true },
|
{ start = "{", end = "}", close = true, newline = true },
|
||||||
{ start = "<", end = ">", close = true, newline = true },
|
{ start = "<", end = ">", close = true, newline = true },
|
||||||
{ start = "[", end = "]", close = true, newline = true },
|
{ start = "[", end = "]", close = true, newline = true },
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "PHP"
|
name = "PHP"
|
||||||
|
grammar = "php"
|
||||||
path_suffixes = ["php"]
|
path_suffixes = ["php"]
|
||||||
first_line_pattern = '^#!.*php'
|
first_line_pattern = '^#!.*php'
|
||||||
line_comments = ["// ", "# "]
|
line_comments = ["// ", "# "]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "proto"
|
name = "proto"
|
||||||
|
grammar = "proto"
|
||||||
path_suffixes = ["proto"]
|
path_suffixes = ["proto"]
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "PureScript"
|
name = "PureScript"
|
||||||
|
grammar = "purescript"
|
||||||
path_suffixes = ["purs"]
|
path_suffixes = ["purs"]
|
||||||
autoclose_before = ",=)}]"
|
autoclose_before = ",=)}]"
|
||||||
line_comments = ["-- "]
|
line_comments = ["-- "]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Python"
|
name = "Python"
|
||||||
|
grammar = "python"
|
||||||
path_suffixes = ["py", "pyi", "mpy"]
|
path_suffixes = ["py", "pyi", "mpy"]
|
||||||
first_line_pattern = '^#!.*\bpython[0-9.]*\b'
|
first_line_pattern = '^#!.*\bpython[0-9.]*\b'
|
||||||
line_comments = ["# "]
|
line_comments = ["# "]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Racket"
|
name = "Racket"
|
||||||
|
grammar = "racket"
|
||||||
path_suffixes = ["rkt"]
|
path_suffixes = ["rkt"]
|
||||||
line_comments = ["; "]
|
line_comments = ["; "]
|
||||||
autoclose_before = "])"
|
autoclose_before = "])"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Ruby"
|
name = "Ruby"
|
||||||
|
grammar = "ruby"
|
||||||
path_suffixes = [
|
path_suffixes = [
|
||||||
"rb",
|
"rb",
|
||||||
"Gemfile",
|
"Gemfile",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Rust"
|
name = "Rust"
|
||||||
|
grammar = "rust"
|
||||||
path_suffixes = ["rs"]
|
path_suffixes = ["rs"]
|
||||||
line_comments = ["// ", "/// ", "//! "]
|
line_comments = ["// ", "/// ", "//! "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Scheme"
|
name = "Scheme"
|
||||||
|
grammar = "scheme"
|
||||||
path_suffixes = ["scm", "ss"]
|
path_suffixes = ["scm", "ss"]
|
||||||
line_comments = ["; "]
|
line_comments = ["; "]
|
||||||
autoclose_before = "])"
|
autoclose_before = "])"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Svelte"
|
name = "Svelte"
|
||||||
|
grammar = "svelte"
|
||||||
path_suffixes = ["svelte"]
|
path_suffixes = ["svelte"]
|
||||||
block_comment = ["<!-- ", " -->"]
|
block_comment = ["<!-- ", " -->"]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Terraform"
|
name = "Terraform"
|
||||||
|
grammar = "terraform"
|
||||||
path_suffixes = ["tf", "tfvars"]
|
path_suffixes = ["tf", "tfvars"]
|
||||||
line_comments = ["# ", "// "]
|
line_comments = ["# ", "// "]
|
||||||
block_comment = ["/*", "*/"]
|
block_comment = ["/*", "*/"]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "TOML"
|
name = "TOML"
|
||||||
|
grammar = "toml"
|
||||||
path_suffixes = ["Cargo.lock", "toml"]
|
path_suffixes = ["Cargo.lock", "toml"]
|
||||||
line_comments = ["# "]
|
line_comments = ["# "]
|
||||||
autoclose_before = ",]}"
|
autoclose_before = ",]}"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "TSX"
|
name = "TSX"
|
||||||
|
grammar = "tsx"
|
||||||
path_suffixes = ["tsx"]
|
path_suffixes = ["tsx"]
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "TypeScript"
|
name = "TypeScript"
|
||||||
|
grammar = "typescript"
|
||||||
path_suffixes = ["ts", "cts", "d.cts", "d.mts", "mts"]
|
path_suffixes = ["ts", "cts", "d.cts", "d.mts", "mts"]
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Uiua"
|
name = "Uiua"
|
||||||
|
grammar = "uiua"
|
||||||
path_suffixes = ["ua"]
|
path_suffixes = ["ua"]
|
||||||
line_comments = ["# "]
|
line_comments = ["# "]
|
||||||
autoclose_before = ")]}\""
|
autoclose_before = ")]}\""
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Vue.js"
|
name = "Vue.js"
|
||||||
|
grammar = "vue"
|
||||||
path_suffixes = ["vue"]
|
path_suffixes = ["vue"]
|
||||||
block_comment = ["<!-- ", " -->"]
|
block_comment = ["<!-- ", " -->"]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "YAML"
|
name = "YAML"
|
||||||
|
grammar = "yaml"
|
||||||
path_suffixes = ["yml", "yaml"]
|
path_suffixes = ["yml", "yaml"]
|
||||||
line_comments = ["# "]
|
line_comments = ["# "]
|
||||||
autoclose_before = ",]}"
|
autoclose_before = ",]}"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
name = "Zig"
|
name = "Zig"
|
||||||
|
grammar = "zig"
|
||||||
path_suffixes = ["zig"]
|
path_suffixes = ["zig"]
|
||||||
line_comments = ["// "]
|
line_comments = ["// "]
|
||||||
autoclose_before = ";:.,=}])>"
|
autoclose_before = ";:.,=}])>"
|
||||||
|
@ -48,7 +48,7 @@ use theme::{ActiveTheme, SystemAppearance, ThemeRegistry, ThemeSettings};
|
|||||||
use util::{
|
use util::{
|
||||||
async_maybe,
|
async_maybe,
|
||||||
http::{self, HttpClient, ZedHttpClient},
|
http::{self, HttpClient, ZedHttpClient},
|
||||||
paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR, PLUGINS_DIR},
|
paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR},
|
||||||
ResultExt,
|
ResultExt,
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@ -174,6 +174,8 @@ fn main() {
|
|||||||
);
|
);
|
||||||
assistant::init(cx);
|
assistant::init(cx);
|
||||||
|
|
||||||
|
extension::init(fs.clone(), languages.clone(), ThemeRegistry::global(cx), cx);
|
||||||
|
|
||||||
load_user_themes_in_background(fs.clone(), cx);
|
load_user_themes_in_background(fs.clone(), cx);
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
watch_themes(fs.clone(), cx);
|
watch_themes(fs.clone(), cx);
|
||||||
@ -982,20 +984,13 @@ fn watch_themes(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
|
|||||||
.detach()
|
.detach()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
async fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>) {
|
async fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>) {
|
||||||
let reload_debounce = Duration::from_millis(250);
|
let reload_debounce = Duration::from_millis(250);
|
||||||
|
|
||||||
let mut events = fs.watch(PLUGINS_DIR.as_ref(), reload_debounce).await;
|
let mut events = fs
|
||||||
|
.watch("crates/zed/src/languages".as_ref(), reload_debounce)
|
||||||
#[cfg(debug_assertions)]
|
.await;
|
||||||
{
|
|
||||||
events = futures::stream::select(
|
|
||||||
events,
|
|
||||||
fs.watch("crates/zed/src/languages".as_ref(), reload_debounce)
|
|
||||||
.await,
|
|
||||||
)
|
|
||||||
.boxed();
|
|
||||||
}
|
|
||||||
|
|
||||||
while (events.next().await).is_some() {
|
while (events.next().await).is_some() {
|
||||||
languages.reload();
|
languages.reload();
|
||||||
@ -1025,3 +1020,6 @@ fn watch_file_types(fs: Arc<dyn fs::Fs>, cx: &mut AppContext) {
|
|||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
fn watch_file_types(_fs: Arc<dyn fs::Fs>, _cx: &mut AppContext) {}
|
fn watch_file_types(_fs: Arc<dyn fs::Fs>, _cx: &mut AppContext) {}
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
async fn watch_languages(_fs: Arc<dyn fs::Fs>, _languages: Arc<LanguageRegistry>) {}
|
||||||
|
@ -739,7 +739,7 @@ mod tests {
|
|||||||
actions, Action, AnyWindowHandle, AppContext, AssetSource, Entity, TestAppContext,
|
actions, Action, AnyWindowHandle, AppContext, AssetSource, Entity, TestAppContext,
|
||||||
VisualTestContext, WindowHandle,
|
VisualTestContext, WindowHandle,
|
||||||
};
|
};
|
||||||
use language::LanguageRegistry;
|
use language::{LanguageMatcher, LanguageRegistry};
|
||||||
use project::{project_settings::ProjectSettings, Project, ProjectPath};
|
use project::{project_settings::ProjectSettings, Project, ProjectPath};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use settings::{handle_settings_file_changes, watch_config_file, SettingsStore};
|
use settings::{handle_settings_file_changes, watch_config_file, SettingsStore};
|
||||||
@ -2742,7 +2742,10 @@ mod tests {
|
|||||||
Arc::new(language::Language::new(
|
Arc::new(language::Language::new(
|
||||||
language::LanguageConfig {
|
language::LanguageConfig {
|
||||||
name: "Rust".into(),
|
name: "Rust".into(),
|
||||||
path_suffixes: vec!["rs".to_string()],
|
matcher: LanguageMatcher {
|
||||||
|
path_suffixes: vec!["rs".to_string()],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Some(tree_sitter_rust::language()),
|
Some(tree_sitter_rust::language()),
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
If you have the development environment already setup, you can skip to [Launching Zed](#launching-zed)
|
If you have the development environment already setup, you can skip to [Launching Zed](#launching-zed)
|
||||||
|
|
||||||
### Using OPAM
|
### Using OPAM
|
||||||
Opam is the official package manager for OCaml and is highly recommended for getting started with OCaml. To get started using Opam, please follow the instructions provided [here](https://opam.ocaml.org/doc/Install.html).
|
Opam is the official package manager for OCaml and is highly recommended for getting started with OCaml. To get started using Opam, please follow the instructions provided [here](https://ocaml.org/install).
|
||||||
|
|
||||||
Once you install opam and setup a switch with your development environment as per the instructions, you can proceed.
|
Once you install opam and setup a switch with your development environment as per the instructions, you can proceed.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user