ZIm/crates/languages/src/lib.rs
Nathan Sobo 6fca1d2b0b
Eliminate GPUI View, ViewContext, and WindowContext types (#22632)
There's still a bit more work to do on this, but this PR is compiling
(with warnings) after eliminating the key types. When the tasks below
are complete, this will be the new narrative for GPUI:

- `Entity<T>` - This replaces `View<T>`/`Model<T>`. It represents a unit
of state, and if `T` implements `Render`, then `Entity<T>` implements
`Element`.
- `&mut App` This replaces `AppContext` and represents the app.
- `&mut Context<T>` This replaces `ModelContext` and derefs to `App`. It
is provided by the framework when updating an entity.
- `&mut Window` Broken out of `&mut WindowContext` which no longer
exists. Every method that once took `&mut WindowContext` now takes `&mut
Window, &mut App` and every method that took `&mut ViewContext<T>` now
takes `&mut Window, &mut Context<T>`

Not pictured here are the two other failed attempts. It's been quite a
month!

Tasks:

- [x] Remove `View`, `ViewContext`, `WindowContext` and thread through
`Window`
- [x] [@cole-miller @mikayla-maki] Redraw window when entities change
- [x] [@cole-miller @mikayla-maki] Get examples and Zed running
- [x] [@cole-miller @mikayla-maki] Fix Zed rendering
- [x] [@mikayla-maki] Fix todo! macros and comments
- [x] Fix a bug where the editor would not be redrawn because of view
caching
- [x] remove publicness window.notify() and replace with
`AppContext::notify`
- [x] remove `observe_new_window_models`, replace with
`observe_new_models` with an optional window
- [x] Fix a bug where the project panel would not be redrawn because of
the wrong refresh() call being used
- [x] Fix the tests
- [x] Fix warnings by eliminating `Window` params or using `_`
- [x] Fix conflicts
- [x] Simplify generic code where possible
- [x] Rename types
- [ ] Update docs

### issues post merge

- [x] Issues switching between normal and insert mode
- [x] Assistant re-rendering failure
- [x] Vim test failures
- [x] Mac build issue



Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Joseph <joseph@zed.dev>
Co-authored-by: max <max@zed.dev>
Co-authored-by: Michael Sloan <michael@zed.dev>
Co-authored-by: Mikayla Maki <mikaylamaki@Mikaylas-MacBook-Pro.local>
Co-authored-by: Mikayla <mikayla.c.maki@gmail.com>
Co-authored-by: joão <joao@zed.dev>
2025-01-26 03:02:45 +00:00

365 lines
12 KiB
Rust

use anyhow::Context as _;
use gpui::{App, UpdateGlobal};
use json::json_task_context;
pub use language::*;
use lsp::LanguageServerName;
use node_runtime::NodeRuntime;
use python::{PythonContextProvider, PythonToolchainProvider};
use rust_embed::RustEmbed;
use settings::SettingsStore;
use smol::stream::StreamExt;
use std::{str, sync::Arc};
use typescript::typescript_task_context;
use util::{asset_str, ResultExt};
use crate::{bash::bash_task_context, go::GoContextProvider, rust::RustContextProvider};
mod bash;
mod c;
mod css;
mod go;
mod json;
mod python;
mod rust;
mod tailwind;
mod typescript;
mod vtsls;
mod yaml;
#[derive(RustEmbed)]
#[folder = "src/"]
#[exclude = "*.rs"]
struct LanguageDir;
pub fn init(languages: Arc<LanguageRegistry>, node_runtime: NodeRuntime, cx: &mut App) {
#[cfg(feature = "load-grammars")]
languages.register_native_grammars([
("bash", tree_sitter_bash::LANGUAGE),
("c", tree_sitter_c::LANGUAGE),
("cpp", tree_sitter_cpp::LANGUAGE),
("css", tree_sitter_css::LANGUAGE),
("diff", tree_sitter_diff::LANGUAGE),
("go", tree_sitter_go::LANGUAGE),
("gomod", tree_sitter_go_mod::LANGUAGE),
("gowork", tree_sitter_gowork::LANGUAGE),
("jsdoc", tree_sitter_jsdoc::LANGUAGE),
("json", tree_sitter_json::LANGUAGE),
("jsonc", tree_sitter_json::LANGUAGE),
("markdown", tree_sitter_md::LANGUAGE),
("markdown-inline", tree_sitter_md::INLINE_LANGUAGE),
("python", tree_sitter_python::LANGUAGE),
("regex", tree_sitter_regex::LANGUAGE),
("rust", tree_sitter_rust::LANGUAGE),
("tsx", tree_sitter_typescript::LANGUAGE_TSX),
("typescript", tree_sitter_typescript::LANGUAGE_TYPESCRIPT),
("yaml", tree_sitter_yaml::LANGUAGE),
]);
macro_rules! language {
($name:literal) => {
let config = load_config($name);
languages.register_language(
config.name.clone(),
config.grammar.clone(),
config.matcher.clone(),
config.hidden,
Arc::new(move || {
Ok(LoadedLanguage {
config: config.clone(),
queries: load_queries($name),
context_provider: None,
toolchain_provider: None,
})
}),
);
};
($name:literal, $adapters:expr) => {
let config = load_config($name);
// typeck helper
let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
for adapter in adapters {
languages.register_lsp_adapter(config.name.clone(), adapter);
}
languages.register_language(
config.name.clone(),
config.grammar.clone(),
config.matcher.clone(),
config.hidden,
Arc::new(move || {
Ok(LoadedLanguage {
config: config.clone(),
queries: load_queries($name),
context_provider: None,
toolchain_provider: None,
})
}),
);
};
($name:literal, $adapters:expr, $context_provider:expr) => {
let config = load_config($name);
// typeck helper
let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
for adapter in adapters {
languages.register_lsp_adapter(config.name.clone(), adapter);
}
languages.register_language(
config.name.clone(),
config.grammar.clone(),
config.matcher.clone(),
config.hidden,
Arc::new(move || {
Ok(LoadedLanguage {
config: config.clone(),
queries: load_queries($name),
context_provider: Some(Arc::new($context_provider)),
toolchain_provider: None,
})
}),
);
};
($name:literal, $adapters:expr, $context_provider:expr, $toolchain_provider:expr) => {
let config = load_config($name);
// typeck helper
let adapters: Vec<Arc<dyn LspAdapter>> = $adapters;
for adapter in adapters {
languages.register_lsp_adapter(config.name.clone(), adapter);
}
languages.register_language(
config.name.clone(),
config.grammar.clone(),
config.matcher.clone(),
config.hidden,
Arc::new(move || {
Ok(LoadedLanguage {
config: config.clone(),
queries: load_queries($name),
context_provider: Some(Arc::new($context_provider)),
toolchain_provider: Some($toolchain_provider),
})
}),
);
};
}
language!("bash", Vec::new(), bash_task_context());
language!("c", vec![Arc::new(c::CLspAdapter) as Arc<dyn LspAdapter>]);
language!("cpp", vec![Arc::new(c::CLspAdapter)]);
language!(
"css",
vec![Arc::new(css::CssLspAdapter::new(node_runtime.clone())),]
);
language!("diff");
language!("go", vec![Arc::new(go::GoLspAdapter)], GoContextProvider);
language!("gomod", vec![Arc::new(go::GoLspAdapter)], GoContextProvider);
language!(
"gowork",
vec![Arc::new(go::GoLspAdapter)],
GoContextProvider
);
language!(
"json",
vec![
Arc::new(json::JsonLspAdapter::new(
node_runtime.clone(),
languages.clone(),
)),
Arc::new(json::NodeVersionAdapter)
],
json_task_context()
);
language!(
"jsonc",
vec![Arc::new(json::JsonLspAdapter::new(
node_runtime.clone(),
languages.clone(),
))],
json_task_context()
);
language!("markdown");
language!("markdown-inline");
language!(
"python",
vec![
Arc::new(python::PythonLspAdapter::new(node_runtime.clone(),)),
Arc::new(python::PyLspAdapter::new())
],
PythonContextProvider,
Arc::new(PythonToolchainProvider::default()) as Arc<dyn ToolchainLister>
);
language!(
"rust",
vec![Arc::new(rust::RustLspAdapter)],
RustContextProvider
);
language!(
"tsx",
vec![
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
],
typescript_task_context()
);
language!(
"typescript",
vec![
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
],
typescript_task_context()
);
language!(
"javascript",
vec![
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())),
Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
],
typescript_task_context()
);
language!(
"jsdoc",
vec![
Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone(),)),
Arc::new(vtsls::VtslsLspAdapter::new(node_runtime.clone()))
]
);
language!("regex");
language!(
"yaml",
vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))]
);
// Register globally available language servers.
//
// This will allow users to add support for a built-in language server (e.g., Tailwind)
// for a given language via the `language_servers` setting:
//
// ```json
// {
// "languages": {
// "My Language": {
// "language_servers": ["tailwindcss-language-server", "..."]
// }
// }
// }
// ```
languages.register_available_lsp_adapter(
LanguageServerName("tailwindcss-language-server".into()),
{
let node_runtime = node_runtime.clone();
move || Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone()))
},
);
languages.register_available_lsp_adapter(LanguageServerName("eslint".into()), {
let node_runtime = node_runtime.clone();
move || Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone()))
});
// Register Tailwind for the existing languages that should have it by default.
//
// This can be driven by the `language_servers` setting once we have a way for
// extensions to provide their own default value for that setting.
let tailwind_languages = [
"Astro",
"CSS",
"ERB",
"HEEX",
"HTML",
"JavaScript",
"PHP",
"Svelte",
"TSX",
"Vue.js",
];
for language in tailwind_languages {
languages.register_lsp_adapter(
language.into(),
Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
);
}
let eslint_languages = ["TSX", "TypeScript", "JavaScript", "Vue.js", "Svelte"];
for language in eslint_languages {
languages.register_lsp_adapter(
language.into(),
Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())),
);
}
let mut subscription = languages.subscribe();
let mut prev_language_settings = languages.language_settings();
cx.spawn(|cx| async move {
while subscription.next().await.is_some() {
let language_settings = languages.language_settings();
if language_settings != prev_language_settings {
cx.update(|cx| {
SettingsStore::update_global(cx, |settings, cx| {
settings
.set_extension_settings(language_settings.clone(), cx)
.log_err();
});
})?;
prev_language_settings = language_settings;
}
}
anyhow::Ok(())
})
.detach();
}
#[cfg(any(test, feature = "test-support"))]
pub fn language(name: &str, grammar: tree_sitter::Language) -> Arc<Language> {
Arc::new(
Language::new(load_config(name), Some(grammar))
.with_queries(load_queries(name))
.unwrap(),
)
}
fn load_config(name: &str) -> LanguageConfig {
let config_toml = String::from_utf8(
LanguageDir::get(&format!("{}/config.toml", name))
.unwrap_or_else(|| panic!("missing config for language {:?}", name))
.data
.to_vec(),
)
.unwrap();
#[allow(unused_mut)]
let mut config: LanguageConfig = ::toml::from_str(&config_toml)
.with_context(|| format!("failed to load config.toml for language {name:?}"))
.unwrap();
#[cfg(not(any(feature = "load-grammars", test)))]
{
config = LanguageConfig {
name: config.name,
matcher: config.matcher,
..Default::default()
}
}
config
}
fn load_queries(name: &str) -> LanguageQueries {
let mut result = LanguageQueries::default();
for path in LanguageDir::iter() {
if let Some(remainder) = path.strip_prefix(name).and_then(|p| p.strip_prefix('/')) {
if !remainder.ends_with(".scm") {
continue;
}
for (name, query) in QUERY_FILENAME_PREFIXES {
if remainder.starts_with(name) {
let contents = asset_str::<LanguageDir>(path.as_ref());
match query(&mut result) {
None => *query(&mut result) = Some(contents),
Some(r) => r.to_mut().push_str(contents.as_ref()),
}
}
}
}
}
result
}