1800 lines · 58.9 KB · Rust
RawDownload
//! The `language` crate provides a large chunk of Zed's language-related//! features (the other big contributors being project and lsp crates that revolve around LSP features).//! Namely, this crate://! - Provides [`Language`], [`Grammar`] and [`LanguageRegistry`] types that//! use Tree-sitter to provide syntax highlighting to the editor; note though that `language` doesn't perform the highlighting by itself. It only maps ranges in a buffer to colors. Treesitter is also used for buffer outlines (lists of symbols in a buffer)//! - Exposes [`LanguageConfig`] that describes how constructs (like brackets or line comments) should be handled by the editor for a source file of a particular language.//!//! Notably we do *not* assign a single language to a single file; in real world a single file can consist of multiple programming languages - HTML is a good example of that - and `language` crate tends to reflect that status quo in its API.mod buffer;mod diagnostic_set;mod highlight_map;mod language_registry;pub mod language_settings;mod outline;pub mod proto;mod syntax_map;mod task_context; #[cfg(test)]mod buffer_tests;pub mod markdown; use crate::language_settings::SoftWrap;use anyhow::{anyhow, Context, Result};use async_trait::async_trait;use collections::{HashMap, HashSet};use futures::Future;use gpui::{AppContext, AsyncAppContext, Model, SharedString, Task};pub use highlight_map::HighlightMap;use http_client::HttpClient;use lazy_static::lazy_static;use lsp::{CodeActionKind, LanguageServerBinary};use parking_lot::Mutex;use regex::Regex;use schemars::{    gen::SchemaGenerator,    schema::{InstanceType, Schema, SchemaObject},    JsonSchema,};use serde::{de, Deserialize, Deserializer, Serialize, Serializer};use serde_json::Value;use smol::future::FutureExt as _;use std::num::NonZeroU32;use std::{    any::Any,    ffi::OsStr,    fmt::Debug,    hash::Hash,    mem,    ops::{DerefMut, Range},    path::{Path, PathBuf},    pin::Pin,    str,    sync::{        atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst},        Arc,    },};use syntax_map::{QueryCursorHandle, SyntaxSnapshot};use task::RunnableTag;pub use task_context::{ContextProvider, RunnableRange};use theme::SyntaxTheme;use tree_sitter::{self, wasmtime, Query, QueryCursor, WasmStore};use util::serde::default_true; pub use buffer::Operation;pub use buffer::*;pub use diagnostic_set::DiagnosticEntry;pub use language_registry::{    LanguageNotFound, LanguageQueries, LanguageRegistry, LanguageServerBinaryStatus,    PendingLanguageServer, QUERY_FILENAME_PREFIXES,};pub use lsp::LanguageServerId;pub use outline::*;pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer};pub use text::{AnchorRangeExt, LineEnding};pub use tree_sitter::{Node, Parser, Tree, TreeCursor}; /// Initializes the `language` crate.////// This should be called before making use of items from the create.pub fn init(cx: &mut AppContext) {    language_settings::init(cx);} static QUERY_CURSORS: Mutex<Vec<QueryCursor>> = Mutex::new(vec![]);static PARSERS: Mutex<Vec<Parser>> = Mutex::new(vec![]); pub fn with_parser<F, R>(func: F) -> Rwhere    F: FnOnce(&mut Parser) -> R,{    let mut parser = PARSERS.lock().pop().unwrap_or_else(|| {        let mut parser = Parser::new();        parser            .set_wasm_store(WasmStore::new(WASM_ENGINE.clone()).unwrap())            .unwrap();        parser    });    parser.set_included_ranges(&[]).unwrap();    let result = func(&mut parser);    PARSERS.lock().push(parser);    result} pub fn with_query_cursor<F, R>(func: F) -> Rwhere    F: FnOnce(&mut QueryCursor) -> R,{    let mut cursor = QueryCursorHandle::new();    func(cursor.deref_mut())} lazy_static! {    static ref NEXT_LANGUAGE_ID: AtomicUsize = Default::default();    static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default();    static ref WASM_ENGINE: wasmtime::Engine = {        wasmtime::Engine::new(&wasmtime::Config::new()).unwrap()    };     /// A shared grammar for plain text, exposed for reuse by downstream crates.    pub static ref PLAIN_TEXT: Arc<Language> = Arc::new(Language::new(        LanguageConfig {            name: "Plain Text".into(),            soft_wrap: Some(SoftWrap::EditorWidth),            ..Default::default()        },        None,    ));} /// Types that represent a position in a buffer, and can be converted into/// an LSP position, to send to a language server.pub trait ToLspPosition {    /// Converts the value into an LSP position.    fn to_lsp_position(self) -> lsp::Position;} /// A name of a language server.#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]pub struct LanguageServerName(pub Arc<str>); #[derive(Debug, Clone, PartialEq, Eq, Hash)]pub struct Location {    pub buffer: Model<Buffer>,    pub range: Range<Anchor>,} /// Represents a Language Server, with certain cached sync properties./// Uses [`LspAdapter`] under the hood, but calls all 'static' methods/// once at startup, and caches the results.pub struct CachedLspAdapter {    pub name: LanguageServerName,    pub disk_based_diagnostic_sources: Vec<String>,    pub disk_based_diagnostics_progress_token: Option<String>,    language_ids: HashMap<String, String>,    pub adapter: Arc<dyn LspAdapter>,    pub reinstall_attempt_count: AtomicU64,    cached_binary: futures::lock::Mutex<Option<LanguageServerBinary>>,} impl CachedLspAdapter {    pub fn new(adapter: Arc<dyn LspAdapter>) -> Arc<Self> {        let name = adapter.name();        let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources();        let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token();        let language_ids = adapter.language_ids();         Arc::new(CachedLspAdapter {            name,            disk_based_diagnostic_sources,            disk_based_diagnostics_progress_token,            language_ids,            adapter,            cached_binary: Default::default(),            reinstall_attempt_count: AtomicU64::new(0),        })    }     pub async fn get_language_server_command(        self: Arc<Self>,        language: Arc<Language>,        container_dir: Arc<Path>,        delegate: Arc<dyn LspAdapterDelegate>,        cx: &mut AsyncAppContext,    ) -> Result<LanguageServerBinary> {        let cached_binary = self.cached_binary.lock().await;        self.adapter            .clone()            .get_language_server_command(language, container_dir, delegate, cached_binary, cx)            .await    }     pub fn will_start_server(        &self,        delegate: &Arc<dyn LspAdapterDelegate>,        cx: &mut AsyncAppContext,    ) -> Option<Task<Result<()>>> {        self.adapter.will_start_server(delegate, cx)    }     pub fn can_be_reinstalled(&self) -> bool {        self.adapter.can_be_reinstalled()    }     pub async fn installation_test_binary(        &self,        container_dir: PathBuf,    ) -> Option<LanguageServerBinary> {        self.adapter.installation_test_binary(container_dir).await    }     pub fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {        self.adapter.code_action_kinds()    }     pub fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) {        self.adapter.process_diagnostics(params)    }     pub async fn process_completions(&self, completion_items: &mut [lsp::CompletionItem]) {        self.adapter.process_completions(completion_items).await    }     pub async fn labels_for_completions(        &self,        completion_items: &[lsp::CompletionItem],        language: &Arc<Language>,    ) -> Result<Vec<Option<CodeLabel>>> {        self.adapter            .clone()            .labels_for_completions(completion_items, language)            .await    }     pub async fn labels_for_symbols(        &self,        symbols: &[(String, lsp::SymbolKind)],        language: &Arc<Language>,    ) -> Result<Vec<Option<CodeLabel>>> {        self.adapter            .clone()            .labels_for_symbols(symbols, language)            .await    }     pub fn language_id(&self, language: &Language) -> String {        self.language_ids            .get(language.name().as_ref())            .cloned()            .unwrap_or_else(|| language.lsp_id())    }     #[cfg(any(test, feature = "test-support"))]    fn as_fake(&self) -> Option<&FakeLspAdapter> {        self.adapter.as_fake()    }} /// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application// e.g. to display a notification or fetch data from the web.#[async_trait]pub trait LspAdapterDelegate: Send + Sync {    fn show_notification(&self, message: &str, cx: &mut AppContext);    fn http_client(&self) -> Arc<dyn HttpClient>;    fn worktree_id(&self) -> u64;    fn worktree_root_path(&self) -> &Path;    fn update_status(&self, language: LanguageServerName, status: LanguageServerBinaryStatus);     async fn which(&self, command: &OsStr) -> Option<PathBuf>;    async fn shell_env(&self) -> HashMap<String, String>;    async fn read_text_file(&self, path: PathBuf) -> Result<String>;} #[async_trait(?Send)]pub trait LspAdapter: 'static + Send + Sync {    fn name(&self) -> LanguageServerName;     fn get_language_server_command<'a>(        self: Arc<Self>,        language: Arc<Language>,        container_dir: Arc<Path>,        delegate: Arc<dyn LspAdapterDelegate>,        mut cached_binary: futures::lock::MutexGuard<'a, Option<LanguageServerBinary>>,        cx: &'a mut AsyncAppContext,    ) -> Pin<Box<dyn 'a + Future<Output = Result<LanguageServerBinary>>>> {        async move {            // First we check whether the adapter can give us a user-installed binary.            // If so, we do *not* want to cache that, because each worktree might give us a different            // binary:            //            //      worktree 1: user-installed at `.bin/gopls`            //      worktree 2: user-installed at `~/bin/gopls`            //      worktree 3: no gopls found in PATH -> fallback to Zed installation            //            // We only want to cache when we fall back to the global one,            // because we don't want to download and overwrite our global one            // for each worktree we might have open.            if let Some(binary) = self.check_if_user_installed(delegate.as_ref(), cx).await {                log::info!(                    "found user-installed language server for {}. path: {:?}, arguments: {:?}",                    language.name(),                    binary.path,                    binary.arguments                );                return Ok(binary);            }             if let Some(cached_binary) = cached_binary.as_ref() {                return Ok(cached_binary.clone());            }             if !container_dir.exists() {                smol::fs::create_dir_all(&container_dir)                    .await                    .context("failed to create container directory")?;            }             let mut binary = try_fetch_server_binary(self.as_ref(), &delegate, container_dir.to_path_buf(), cx).await;             if let Err(error) = binary.as_ref() {                if let Some(prev_downloaded_binary) = self                    .cached_server_binary(container_dir.to_path_buf(), delegate.as_ref())                    .await                {                    log::info!(                        "failed to fetch newest version of language server {:?}. falling back to using {:?}",                        self.name(),                        prev_downloaded_binary.path                    );                    binary = Ok(prev_downloaded_binary);                } else {                    delegate.update_status(                        self.name(),                        LanguageServerBinaryStatus::Failed {                            error: format!("{error:?}"),                        },                    );                }            }             if let Ok(binary) = &binary {                *cached_binary = Some(binary.clone());            }             binary        }        .boxed_local()    }     async fn check_if_user_installed(        &self,        _: &dyn LspAdapterDelegate,        _: &AsyncAppContext,    ) -> Option<LanguageServerBinary> {        None    }     async fn fetch_latest_server_version(        &self,        delegate: &dyn LspAdapterDelegate,    ) -> Result<Box<dyn 'static + Send + Any>>;     fn will_fetch_server(        &self,        _: &Arc<dyn LspAdapterDelegate>,        _: &mut AsyncAppContext,    ) -> Option<Task<Result<()>>> {        None    }     fn will_start_server(        &self,        _: &Arc<dyn LspAdapterDelegate>,        _: &mut AsyncAppContext,    ) -> Option<Task<Result<()>>> {        None    }     async fn fetch_server_binary(        &self,        latest_version: Box<dyn 'static + Send + Any>,        container_dir: PathBuf,        delegate: &dyn LspAdapterDelegate,    ) -> Result<LanguageServerBinary>;     async fn cached_server_binary(        &self,        container_dir: PathBuf,        delegate: &dyn LspAdapterDelegate,    ) -> Option<LanguageServerBinary>;     /// Returns `true` if a language server can be reinstalled.    ///    /// If language server initialization fails, a reinstallation will be attempted unless the value returned from this method is `false`.    ///    /// Implementations that rely on software already installed on user's system    /// should have [`can_be_reinstalled`](Self::can_be_reinstalled) return `false`.    fn can_be_reinstalled(&self) -> bool {        true    }     async fn installation_test_binary(        &self,        container_dir: PathBuf,    ) -> Option<LanguageServerBinary>;     fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}     /// Post-processes completions provided by the language server.    async fn process_completions(&self, _: &mut [lsp::CompletionItem]) {}     async fn labels_for_completions(        self: Arc<Self>,        completions: &[lsp::CompletionItem],        language: &Arc<Language>,    ) -> Result<Vec<Option<CodeLabel>>> {        let mut labels = Vec::new();        for (ix, completion) in completions.into_iter().enumerate() {            let label = self.label_for_completion(completion, language).await;            if let Some(label) = label {                labels.resize(ix + 1, None);                *labels.last_mut().unwrap() = Some(label);            }        }        Ok(labels)    }     async fn label_for_completion(        &self,        _: &lsp::CompletionItem,        _: &Arc<Language>,    ) -> Option<CodeLabel> {        None    }     async fn labels_for_symbols(        self: Arc<Self>,        symbols: &[(String, lsp::SymbolKind)],        language: &Arc<Language>,    ) -> Result<Vec<Option<CodeLabel>>> {        let mut labels = Vec::new();        for (ix, (name, kind)) in symbols.into_iter().enumerate() {            let label = self.label_for_symbol(name, *kind, language).await;            if let Some(label) = label {                labels.resize(ix + 1, None);                *labels.last_mut().unwrap() = Some(label);            }        }        Ok(labels)    }     async fn label_for_symbol(        &self,        _: &str,        _: lsp::SymbolKind,        _: &Arc<Language>,    ) -> Option<CodeLabel> {        None    }     /// Returns initialization options that are going to be sent to a LSP server as a part of [`lsp::InitializeParams`]    async fn initialization_options(        self: Arc<Self>,        _: &Arc<dyn LspAdapterDelegate>,    ) -> Result<Option<Value>> {        Ok(None)    }     async fn workspace_configuration(        self: Arc<Self>,        _: &Arc<dyn LspAdapterDelegate>,        _cx: &mut AsyncAppContext,    ) -> Result<Value> {        Ok(serde_json::json!({}))    }     /// Returns a list of code actions supported by a given LspAdapter    fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {        Some(vec![            CodeActionKind::EMPTY,            CodeActionKind::QUICKFIX,            CodeActionKind::REFACTOR,            CodeActionKind::REFACTOR_EXTRACT,            CodeActionKind::SOURCE,        ])    }     fn disk_based_diagnostic_sources(&self) -> Vec<String> {        Default::default()    }     fn disk_based_diagnostics_progress_token(&self) -> Option<String> {        None    }     fn language_ids(&self) -> HashMap<String, String> {        Default::default()    } 

Lines 1–500 of 1800.