2339 lines · 87.5 KB · Rust
RawDownload
mod input_handler; pub use lsp_types::request::*;pub use lsp_types::*; use anyhow::{Context as _, Result, anyhow};use collections::{BTreeMap, HashMap};use futures::{    AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, Future, FutureExt, StreamExt,    channel::oneshot::{self, Canceled},    future::{self, Either},    io::{BufReader, BufWriter},    select,};use gpui::{App, AppContext as _, AsyncApp, BackgroundExecutor, SharedString, Task};use notification::DidChangeWorkspaceFolders;use parking_lot::{Mutex, RwLock};use postage::{barrier, prelude::Stream};use schemars::JsonSchema;use serde::{Deserialize, Serialize, de::DeserializeOwned};use serde_json::{Value, json, value::RawValue};use util::command::{Child, Stdio}; use gpui_util::{ResultExt, TryFutureExt};use std::path::Path;use std::{    any::TypeId,    collections::BTreeSet,    ffi::{OsStr, OsString},    fmt,    io::Write,    ops::DerefMut,    path::PathBuf,    pin::Pin,    sync::{        Arc, Weak,        atomic::{AtomicI32, Ordering::SeqCst},    },    task::Poll,    time::{Duration, Instant},};use util::{ConnectionResult, redact}; const JSON_RPC_VERSION: &str = "2.0";const CONTENT_LEN_HEADER: &str = "Content-Length: "; /// The default amount of time to wait while initializing or fetching LSP servers, in seconds.////// Should not be used (in favor of DEFAULT_LSP_REQUEST_TIMEOUT) and is exported solely for use inside ProjectSettings defaults.pub const DEFAULT_LSP_REQUEST_TIMEOUT_SECS: u64 = 120;/// A timeout representing the value of [DEFAULT_LSP_REQUEST_TIMEOUT_SECS].////// Should **only be used** in tests and as a fallback when a corresponding config value cannot be obtained!pub const DEFAULT_LSP_REQUEST_TIMEOUT: Duration =    Duration::from_secs(DEFAULT_LSP_REQUEST_TIMEOUT_SECS); /// The shutdown timeout for LSP servers (including Prettier/Copilot).const SERVER_SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5); pub fn workspace_folder_for_uri(uri: Uri) -> WorkspaceFolder {    let name = uri        .to_file_path()        .ok()        .map(|path| {            let name = path.file_name().unwrap_or(path.as_os_str());            name.to_string_lossy().into_owned()        })        .filter(|name| !name.is_empty())        .or_else(|| {            uri.path_segments()                .and_then(|mut segments| segments.rfind(|segment| !segment.is_empty()))                .map(str::to_owned)        })        .unwrap_or_else(|| uri.as_str().to_owned());     WorkspaceFolder { uri, name }} type NotificationHandler = Box<dyn Send + FnMut(Option<RequestId>, Value, &mut AsyncApp)>;type PendingRespondTasks = Arc<Mutex<HashMap<RequestId, Task<()>>>>;type ResponseHandler = Box<dyn Send + FnOnce(Result<String, Error>) -> Task<()>>;type IoHandler = Box<dyn Send + FnMut(IoKind, &str)>; /// Kind of language server stdio given to an IO handler.#[derive(Debug, Clone, Copy)]pub enum IoKind {    StdOut,    StdIn,    StdErr,} /// Represents a launchable language server. This can either be a standalone binary or the path/// to a runtime with arguments to instruct it to launch the actual language server file.#[derive(Clone, Serialize)]pub struct LanguageServerBinary {    pub path: PathBuf,    pub arguments: Vec<OsString>,    pub env: Option<HashMap<String, String>>,} /// Configures the search (and installation) of language servers.#[derive(Debug, Clone)]pub struct LanguageServerBinaryOptions {    /// Whether the adapter should look at the users system    pub allow_path_lookup: bool,    /// Whether the adapter should download its own version    pub allow_binary_download: bool,    /// Whether the adapter should download a pre-release version    pub pre_release: bool,} struct NotificationSerializer(Box<dyn FnOnce() -> String + Send + Sync>); /// A running language server process.pub struct LanguageServer {    server_id: LanguageServerId,    next_id: AtomicI32,    outbound_tx: async_channel::Sender<String>,    notification_tx: async_channel::Sender<NotificationSerializer>,    name: LanguageServerName,    version: Option<SharedString>,    process_name: Arc<str>,    binary: LanguageServerBinary,    capabilities: RwLock<ServerCapabilities>,    /// Configuration sent to the server, stored for display in the language server logs    /// buffer. This is represented as the message sent to the LSP in order to avoid cloning it (can    /// be large in cases like sending schemas to the json server).    configuration: Arc<DidChangeConfigurationParams>,    code_action_kinds: Option<Vec<CodeActionKind>>,    notification_handlers: Arc<Mutex<HashMap<&'static str, NotificationHandler>>>,    response_handlers: Arc<Mutex<Option<HashMap<RequestId, ResponseHandler>>>>,    /// Tasks spawned by `on_custom_request` to compute responses. Tracked so that    /// incoming `$/cancelRequest` notifications can cancel them by dropping the task.    pending_respond_tasks: PendingRespondTasks,    io_handlers: Arc<Mutex<HashMap<i32, IoHandler>>>,    executor: BackgroundExecutor,    #[allow(clippy::type_complexity)]    io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,    output_done_rx: Mutex<Option<barrier::Receiver>>,    server: Arc<Mutex<Option<Child>>>,    workspace_folders: Option<Arc<Mutex<BTreeSet<Uri>>>>,    root_uri: Uri,} #[derive(Clone, Debug, PartialEq, Eq, Hash)]pub enum LanguageServerSelector {    Id(LanguageServerId),    Name(LanguageServerName),} /// Identifies a running language server.#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]#[repr(transparent)]pub struct LanguageServerId(pub usize); impl LanguageServerId {    pub fn from_proto(id: u64) -> Self {        Self(id as usize)    }     pub fn to_proto(self) -> u64 {        self.0 as u64    }} /// A name of a language server.#[derive(    Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize, JsonSchema,)]#[serde(transparent)]pub struct LanguageServerName(pub SharedString); impl std::fmt::Display for LanguageServerName {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        std::fmt::Display::fmt(&self.0, f)    }} impl AsRef<str> for LanguageServerName {    fn as_ref(&self) -> &str {        self.0.as_ref()    }} impl AsRef<OsStr> for LanguageServerName {    fn as_ref(&self) -> &OsStr {        self.0.as_ref().as_ref()    }} impl LanguageServerName {    pub const fn new_static(s: &'static str) -> Self {        Self(SharedString::new_static(s))    }     pub fn from_proto(s: String) -> Self {        Self(s.into())    }} impl<'a> From<&'a str> for LanguageServerName {    fn from(str: &'a str) -> LanguageServerName {        LanguageServerName(str.to_string().into())    }} impl PartialEq<str> for LanguageServerName {    fn eq(&self, other: &str) -> bool {        self.0 == other    }} /// Handle to a language server RPC activity subscription.pub enum Subscription {    Notification {        method: &'static str,        notification_handlers: Option<Weak<Mutex<HashMap<&'static str, NotificationHandler>>>>,    },    Io {        id: i32,        io_handlers: Option<Weak<Mutex<HashMap<i32, IoHandler>>>>,    },} /// Language server protocol RPC request message ID.////// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage)#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]#[serde(untagged)]pub enum RequestId {    Int(i32),    Str(String),} fn is_unit<T: 'static>(_: &T) -> bool {    TypeId::of::<T>() == TypeId::of::<()>()} /// Language server protocol RPC request message.////// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#requestMessage)#[derive(Serialize, Deserialize)]pub struct Request<'a, T>where    T: 'static,{    jsonrpc: &'static str,    id: RequestId,    method: &'a str,    #[serde(default, skip_serializing_if = "is_unit")]    params: T,} /// Language server protocol RPC request response message before it is deserialized into a concrete type.#[derive(Serialize, Deserialize)]struct AnyResponse<'a> {    jsonrpc: &'a str,    id: RequestId,    #[serde(default)]    error: Option<Error>,    #[serde(borrow)]    result: Option<&'a RawValue>,} /// Language server protocol RPC request response message.////// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#responseMessage)#[derive(Serialize)]struct Response<T> {    jsonrpc: &'static str,    id: RequestId,    #[serde(flatten)]    value: LspResult<T>,} #[derive(Serialize)]#[serde(rename_all = "snake_case")]enum LspResult<T> {    #[serde(rename = "result")]    Ok(Option<T>),    Error(Option<Error>),} /// Language server protocol RPC notification message.////// [LSP Specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#notificationMessage)#[derive(Serialize, Deserialize)]struct Notification<'a, T>where    T: 'static,{    jsonrpc: &'static str,    #[serde(borrow)]    method: &'a str,    #[serde(default, skip_serializing_if = "is_unit")]    params: T,} /// Language server RPC notification message before it is deserialized into a concrete type.#[derive(Debug, Clone, Deserialize)]struct NotificationOrRequest {    #[serde(default)]    id: Option<RequestId>,    method: String,    #[serde(default)]    params: Option<Value>,} #[derive(Debug, Serialize, Deserialize)]struct Error {    code: i64,    message: String,    #[serde(default)]    data: Option<serde_json::Value>,} pub trait LspRequestFuture<O>: Future<Output = ConnectionResult<O>> {    fn id(&self) -> i32;} struct LspRequest<F> {    id: i32,    request: F,} impl<F> LspRequest<F> {    pub fn new(id: i32, request: F) -> Self {        Self { id, request }    }} impl<F: Future> Future for LspRequest<F> {    type Output = F::Output;     fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {        // SAFETY: This is standard pin projection, we're pinned so our fields must be pinned.        let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().request) };        inner.poll(cx)    }} impl<F, O> LspRequestFuture<O> for LspRequest<F>where    F: Future<Output = ConnectionResult<O>>,{    fn id(&self) -> i32 {        self.id    }} /// Combined capabilities of the server and the adapter.#[derive(Debug, Clone)]pub struct AdapterServerCapabilities {    // Reported capabilities by the server    pub server_capabilities: ServerCapabilities,    // List of code actions supported by the LspAdapter matching the server    pub code_action_kinds: Option<Vec<CodeActionKind>>,} // See the VSCode docs [1] and the LSP Spec [2]//// [1]: https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#standard-token-types-and-modifiers// [2]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#semanticTokenTypespub const SEMANTIC_TOKEN_TYPES: &[SemanticTokenType] = &[    SemanticTokenType::NAMESPACE,    SemanticTokenType::CLASS,    SemanticTokenType::ENUM,    SemanticTokenType::INTERFACE,    SemanticTokenType::STRUCT,    SemanticTokenType::TYPE_PARAMETER,    SemanticTokenType::TYPE,    SemanticTokenType::PARAMETER,    SemanticTokenType::VARIABLE,    SemanticTokenType::PROPERTY,    SemanticTokenType::ENUM_MEMBER,    SemanticTokenType::DECORATOR,    SemanticTokenType::FUNCTION,    SemanticTokenType::METHOD,    SemanticTokenType::MACRO,    SemanticTokenType::new("label"), // Not in the spec, but in the docs.    SemanticTokenType::COMMENT,    SemanticTokenType::STRING,    SemanticTokenType::KEYWORD,    SemanticTokenType::NUMBER,    SemanticTokenType::REGEXP,    SemanticTokenType::OPERATOR,    SemanticTokenType::MODIFIER, // Only in the spec, not in the docs.    // Language specific things below.    // C#    SemanticTokenType::EVENT,    // Rust    SemanticTokenType::new("lifetime"),];pub const SEMANTIC_TOKEN_MODIFIERS: &[SemanticTokenModifier] = &[    SemanticTokenModifier::DECLARATION,    SemanticTokenModifier::DEFINITION,    SemanticTokenModifier::READONLY,    SemanticTokenModifier::STATIC,    SemanticTokenModifier::DEPRECATED,    SemanticTokenModifier::ABSTRACT,    SemanticTokenModifier::ASYNC,    SemanticTokenModifier::MODIFICATION,    SemanticTokenModifier::DOCUMENTATION,    SemanticTokenModifier::DEFAULT_LIBRARY,    // Language specific things below.    // Rust    SemanticTokenModifier::new("constant"),]; impl LanguageServer {    /// Starts a language server process.    /// A request_timeout of zero or Duration::MAX indicates an indefinite timeout.    pub fn new(        stderr_capture: Arc<Mutex<Option<String>>>,        server_id: LanguageServerId,        server_name: LanguageServerName,        binary: LanguageServerBinary,        root_path: &Path,        code_action_kinds: Option<Vec<CodeActionKind>>,        workspace_folders: Option<Arc<Mutex<BTreeSet<Uri>>>>,        cx: &mut AsyncApp,    ) -> Result<Self> {        let working_dir = if root_path.is_dir() {            root_path        } else {            root_path.parent().unwrap_or_else(|| Path::new("/"))        };        let root_uri = Uri::from_file_path(&working_dir)            .map_err(|()| anyhow!("{working_dir:?} is not a valid URI"))?;        log::info!(            "starting language server process. binary path: \            {:?}, working directory: {:?}, args: {:?}",            binary.path,            working_dir,            &binary.arguments        );        let mut command = util::command::new_command(&binary.path);        command            .current_dir(working_dir)            .args(&binary.arguments)            .envs(binary.env.clone().unwrap_or_default())            .stdin(Stdio::piped())            .stdout(Stdio::piped())            .stderr(Stdio::piped())            .kill_on_drop(true);         let mut server = command            .spawn()            .with_context(|| format!("failed to spawn command {command:?}",))?;         let stdin = server.stdin.take().unwrap();        let stdout = server.stdout.take().unwrap();        let stderr = server.stderr.take().unwrap();        let server = Self::new_internal(            server_id,            server_name,            stdin,            stdout,            Some(stderr),            stderr_capture,            Some(server),            code_action_kinds,            binary,            root_uri,            workspace_folders,            cx,            move |notification| {                log::info!(                    "Language server with id {} sent unhandled notification {}:\n{}",                    server_id,                    notification.method,                    serde_json::to_string_pretty(&notification.params).unwrap(),                );                false            },        );         Ok(server)    }     fn new_internal<Stdin, Stdout, Stderr, F>(        server_id: LanguageServerId,        server_name: LanguageServerName,        stdin: Stdin,        stdout: Stdout,        stderr: Option<Stderr>,        stderr_capture: Arc<Mutex<Option<String>>>,        server: Option<Child>,        code_action_kinds: Option<Vec<CodeActionKind>>,        binary: LanguageServerBinary,        root_uri: Uri,        workspace_folders: Option<Arc<Mutex<BTreeSet<Uri>>>>,        cx: &mut AsyncApp,        on_unhandled_notification: F,    ) -> Self    where        Stdin: AsyncWrite + Unpin + Send + 'static,        Stdout: AsyncRead + Unpin + Send + 'static,        Stderr: AsyncRead + Unpin + Send + 'static,        F: Fn(&NotificationOrRequest) -> bool + 'static + Send + Sync + Clone,

Lines 1–500 of 2339.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t