Merge pull request #1266 from zed-industries/fix-zombie-ra

Keith Simmons created

Fix Zombie Language Servers

Change summary

crates/lsp/src/lsp.rs             |  29 +
crates/project/src/lsp_command.rs |  14 
crates/project/src/project.rs     | 703 +++++++++++++++++++-------------
3 files changed, 448 insertions(+), 298 deletions(-)

Detailed changes

crates/lsp/src/lsp.rs 🔗

@@ -11,7 +11,7 @@ use serde_json::{json, value::RawValue, Value};
 use smol::{
     channel,
     io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader},
-    process,
+    process::{self, Child},
 };
 use std::{
     future::Future,
@@ -44,6 +44,7 @@ pub struct LanguageServer {
     io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
     output_done_rx: Mutex<Option<barrier::Receiver>>,
     root_path: PathBuf,
+    _server: Option<Child>,
 }
 
 pub struct Subscription {
@@ -118,11 +119,20 @@ impl LanguageServer {
             .stdin(Stdio::piped())
             .stdout(Stdio::piped())
             .stderr(Stdio::inherit())
+            .kill_on_drop(true)
             .spawn()?;
+
         let stdin = server.stdin.take().unwrap();
-        let stdout = server.stdout.take().unwrap();
-        let mut server =
-            Self::new_internal(server_id, stdin, stdout, root_path, cx, |notification| {
+        let stout = server.stdout.take().unwrap();
+
+        let mut server = Self::new_internal(
+            server_id,
+            stdin,
+            stout,
+            Some(server),
+            root_path,
+            cx,
+            |notification| {
                 log::info!(
                     "unhandled notification {}:\n{}",
                     notification.method,
@@ -131,7 +141,8 @@ impl LanguageServer {
                     )
                     .unwrap()
                 );
-            });
+            },
+        );
         if let Some(name) = binary_path.file_name() {
             server.name = name.to_string_lossy().to_string();
         }
@@ -142,6 +153,7 @@ impl LanguageServer {
         server_id: usize,
         stdin: Stdin,
         stdout: Stdout,
+        server: Option<Child>,
         root_path: &Path,
         cx: AsyncAppContext,
         mut on_unhandled_notification: F,
@@ -242,6 +254,7 @@ impl LanguageServer {
             io_tasks: Mutex::new(Some((input_task, output_task))),
             output_done_rx: Mutex::new(Some(output_done_rx)),
             root_path: root_path.to_path_buf(),
+            _server: server,
         }
     }
 
@@ -480,6 +493,10 @@ impl LanguageServer {
         self.server_id
     }
 
+    pub fn root_path(&self) -> &PathBuf {
+        &self.root_path
+    }
+
     pub fn request<T: request::Request>(
         &self,
         params: T::Params,
@@ -608,6 +625,7 @@ impl LanguageServer {
             0,
             stdin_writer,
             stdout_reader,
+            None,
             Path::new("/"),
             cx.clone(),
             |_| {},
@@ -617,6 +635,7 @@ impl LanguageServer {
                 0,
                 stdout_writer,
                 stdin_reader,
+                None,
                 Path::new("/"),
                 cx.clone(),
                 move |msg| {

crates/project/src/lsp_command.rs 🔗

@@ -242,7 +242,7 @@ impl LspCommand for PerformRename {
                 .read_with(&cx, |project, cx| {
                     project
                         .language_server_for_buffer(buffer.read(cx), cx)
-                        .cloned()
+                        .map(|(adapter, server)| (adapter.clone(), server.clone()))
                 })
                 .ok_or_else(|| anyhow!("no language server found for buffer"))?;
             Project::deserialize_workspace_edit(
@@ -359,7 +359,7 @@ impl LspCommand for GetDefinition {
             .read_with(&cx, |project, cx| {
                 project
                     .language_server_for_buffer(buffer.read(cx), cx)
-                    .cloned()
+                    .map(|(adapter, server)| (adapter.clone(), server.clone()))
             })
             .ok_or_else(|| anyhow!("no language server found for buffer"))?;
 
@@ -388,8 +388,8 @@ impl LspCommand for GetDefinition {
                     .update(&mut cx, |this, cx| {
                         this.open_local_buffer_via_lsp(
                             target_uri,
-                            lsp_adapter.clone(),
-                            language_server.clone(),
+                            language_server.server_id(),
+                            lsp_adapter.name(),
                             cx,
                         )
                     })
@@ -599,7 +599,7 @@ impl LspCommand for GetReferences {
             .read_with(&cx, |project, cx| {
                 project
                     .language_server_for_buffer(buffer.read(cx), cx)
-                    .cloned()
+                    .map(|(adapter, server)| (adapter.clone(), server.clone()))
             })
             .ok_or_else(|| anyhow!("no language server found for buffer"))?;
 
@@ -609,8 +609,8 @@ impl LspCommand for GetReferences {
                     .update(&mut cx, |this, cx| {
                         this.open_local_buffer_via_lsp(
                             lsp_location.uri,
-                            lsp_adapter.clone(),
-                            language_server.clone(),
+                            language_server.server_id(),
+                            lsp_adapter.name(),
                             cx,
                         )
                     })

crates/project/src/project.rs 🔗

@@ -70,14 +70,26 @@ pub struct ProjectStore {
     projects: Vec<WeakModelHandle<Project>>,
 }
 
+// Language server state is stored across 3 collections:
+//     language_servers =>
+//         a mapping from unique server id to LanguageServerState which can either be a task for a
+//         server in the process of starting, or a running server with adapter and language server arcs
+//     language_server_ids => a mapping from worktreeId and server name to the unique server id
+//     language_server_statuses => a mapping from unique server id to the current server status
+//
+// Multiple worktrees can map to the same language server for example when you jump to the definition
+// of a file in the standard library. So language_server_ids is used to look up which server is active
+// for a given worktree and language server name
+//
+// When starting a language server, first the id map is checked to make sure a server isn't already available
+// for that worktree. If there is one, it finishes early. Otherwise, a new id is allocated and and
+// the Starting variant of LanguageServerState is stored in the language_servers map.
 pub struct Project {
     worktrees: Vec<WorktreeHandle>,
     active_entry: Option<ProjectEntryId>,
     languages: Arc<LanguageRegistry>,
-    language_servers:
-        HashMap<(WorktreeId, LanguageServerName), (Arc<dyn LspAdapter>, Arc<LanguageServer>)>,
-    started_language_servers:
-        HashMap<(WorktreeId, LanguageServerName), Task<Option<Arc<LanguageServer>>>>,
+    language_servers: HashMap<usize, LanguageServerState>,
+    language_server_ids: HashMap<(WorktreeId, LanguageServerName), usize>,
     language_server_statuses: BTreeMap<usize, LanguageServerStatus>,
     language_server_settings: Arc<Mutex<serde_json::Value>>,
     last_workspace_edits_by_language_server: HashMap<usize, ProjectTransaction>,
@@ -174,6 +186,14 @@ pub enum Event {
     ContactCancelledJoinRequest(Arc<User>),
 }
 
+pub enum LanguageServerState {
+    Starting(Task<Option<Arc<LanguageServer>>>),
+    Running {
+        adapter: Arc<dyn LspAdapter>,
+        server: Arc<LanguageServer>,
+    },
+}
+
 #[derive(Serialize)]
 pub struct LanguageServerStatus {
     pub name: String,
@@ -437,7 +457,7 @@ impl Project {
                 next_entry_id: Default::default(),
                 next_diagnostic_group_id: Default::default(),
                 language_servers: Default::default(),
-                started_language_servers: Default::default(),
+                language_server_ids: Default::default(),
                 language_server_statuses: Default::default(),
                 last_workspace_edits_by_language_server: Default::default(),
                 language_server_settings: Default::default(),
@@ -536,7 +556,7 @@ impl Project {
                     }),
                 },
                 language_servers: Default::default(),
-                started_language_servers: Default::default(),
+                language_server_ids: Default::default(),
                 language_server_settings: Default::default(),
                 language_server_statuses: response
                     .language_servers
@@ -691,7 +711,7 @@ impl Project {
             if let Some(lsp_adapter) = language.lsp_adapter() {
                 if !settings.enable_language_server(Some(&language.name())) {
                     let lsp_name = lsp_adapter.name();
-                    for (worktree_id, started_lsp_name) in self.started_language_servers.keys() {
+                    for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
                         if lsp_name == *started_lsp_name {
                             language_servers_to_stop.push((*worktree_id, started_lsp_name.clone()));
                         }
@@ -1538,8 +1558,8 @@ impl Project {
     fn open_local_buffer_via_lsp(
         &mut self,
         abs_path: lsp::Url,
-        lsp_adapter: Arc<dyn LspAdapter>,
-        lsp_server: Arc<LanguageServer>,
+        language_server_id: usize,
+        language_server_name: LanguageServerName,
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<ModelHandle<Buffer>>> {
         cx.spawn(|this, mut cx| async move {
@@ -1557,9 +1577,9 @@ impl Project {
                     })
                     .await?;
                 this.update(&mut cx, |this, cx| {
-                    this.language_servers.insert(
-                        (worktree.read(cx).id(), lsp_adapter.name()),
-                        (lsp_adapter, lsp_server),
+                    this.language_server_ids.insert(
+                        (worktree.read(cx).id(), language_server_name),
+                        language_server_id,
                     );
                 });
                 (worktree, PathBuf::new())
@@ -1726,9 +1746,16 @@ impl Project {
                     if let Some(adapter) = language.lsp_adapter() {
                         language_id = adapter.id_for_language(language.name().as_ref());
                         language_server = self
-                            .language_servers
+                            .language_server_ids
                             .get(&(worktree_id, adapter.name()))
-                            .cloned();
+                            .and_then(|id| self.language_servers.get(&id))
+                            .and_then(|server_state| {
+                                if let LanguageServerState::Running { server, .. } = server_state {
+                                    Some(server.clone())
+                                } else {
+                                    None
+                                }
+                            });
                     }
                 }
 
@@ -1739,7 +1766,7 @@ impl Project {
                     }
                 }
 
-                if let Some((_, server)) = language_server {
+                if let Some(server) = language_server {
                     server
                         .notify::<lsp::notification::DidOpenTextDocument>(
                             lsp::DidOpenTextDocumentParams {
@@ -1816,9 +1843,9 @@ impl Project {
                 }
             }
             BufferEvent::Edited { .. } => {
-                let (_, language_server) = self
-                    .language_server_for_buffer(buffer.read(cx), cx)?
-                    .clone();
+                let language_server = self
+                    .language_server_for_buffer(buffer.read(cx), cx)
+                    .map(|(_, server)| server.clone())?;
                 let buffer = buffer.read(cx);
                 let file = File::from_dyn(buffer.file())?;
                 let abs_path = file.as_local()?.abs_path(cx);
@@ -1907,16 +1934,19 @@ impl Project {
     fn language_servers_for_worktree(
         &self,
         worktree_id: WorktreeId,
-    ) -> impl Iterator<Item = &(Arc<dyn LspAdapter>, Arc<LanguageServer>)> {
-        self.language_servers.iter().filter_map(
-            move |((language_server_worktree_id, _), server)| {
+    ) -> impl Iterator<Item = (&Arc<dyn LspAdapter>, &Arc<LanguageServer>)> {
+        self.language_server_ids
+            .iter()
+            .filter_map(move |((language_server_worktree_id, _), id)| {
                 if *language_server_worktree_id == worktree_id {
-                    Some(server)
-                } else {
-                    None
+                    if let Some(LanguageServerState::Running { adapter, server }) =
+                        self.language_servers.get(&id)
+                    {
+                        return Some((adapter, server));
+                    }
                 }
-            },
-        )
+                None
+            })
     }
 
     fn assign_language_to_buffer(
@@ -1960,7 +1990,8 @@ impl Project {
             return;
         };
         let key = (worktree_id, adapter.name());
-        self.started_language_servers
+
+        self.language_server_ids
             .entry(key.clone())
             .or_insert_with(|| {
                 let server_id = post_inc(&mut self.next_language_server_id);
@@ -1971,252 +2002,298 @@ impl Project {
                     self.client.http_client(),
                     cx,
                 );
-                cx.spawn_weak(|this, mut cx| async move {
-                    let language_server = language_server?.await.log_err()?;
-                    let language_server = language_server
-                        .initialize(adapter.initialization_options())
-                        .await
-                        .log_err()?;
-                    let this = this.upgrade(&cx)?;
-                    let disk_based_diagnostics_progress_token =
-                        adapter.disk_based_diagnostics_progress_token();
+                self.language_servers.insert(
+                    server_id,
+                    LanguageServerState::Starting(cx.spawn_weak(|this, mut cx| async move {
+                        let language_server = language_server?.await.log_err()?;
+                        let language_server = language_server
+                            .initialize(adapter.initialization_options())
+                            .await
+                            .log_err()?;
+                        let this = this.upgrade(&cx)?;
+                        let disk_based_diagnostics_progress_token =
+                            adapter.disk_based_diagnostics_progress_token();
 
-                    language_server
-                        .on_notification::<lsp::notification::PublishDiagnostics, _>({
-                            let this = this.downgrade();
-                            let adapter = adapter.clone();
-                            move |params, mut cx| {
-                                if let Some(this) = this.upgrade(&cx) {
-                                    this.update(&mut cx, |this, cx| {
-                                        this.on_lsp_diagnostics_published(
-                                            server_id, params, &adapter, cx,
-                                        );
-                                    });
+                        language_server
+                            .on_notification::<lsp::notification::PublishDiagnostics, _>({
+                                let this = this.downgrade();
+                                let adapter = adapter.clone();
+                                move |params, mut cx| {
+                                    if let Some(this) = this.upgrade(&cx) {
+                                        this.update(&mut cx, |this, cx| {
+                                            this.on_lsp_diagnostics_published(
+                                                server_id, params, &adapter, cx,
+                                            );
+                                        });
+                                    }
                                 }
-                            }
-                        })
-                        .detach();
+                            })
+                            .detach();
 
-                    language_server
-                        .on_request::<lsp::request::WorkspaceConfiguration, _, _>({
-                            let settings = this
-                                .read_with(&cx, |this, _| this.language_server_settings.clone());
-                            move |params, _| {
-                                let settings = settings.lock().clone();
-                                async move {
-                                    Ok(params
-                                        .items
-                                        .into_iter()
-                                        .map(|item| {
-                                            if let Some(section) = &item.section {
-                                                settings
-                                                    .get(section)
-                                                    .cloned()
-                                                    .unwrap_or(serde_json::Value::Null)
-                                            } else {
-                                                settings.clone()
-                                            }
-                                        })
-                                        .collect())
+                        language_server
+                            .on_request::<lsp::request::WorkspaceConfiguration, _, _>({
+                                let settings = this.read_with(&cx, |this, _| {
+                                    this.language_server_settings.clone()
+                                });
+                                move |params, _| {
+                                    let settings = settings.lock().clone();
+                                    async move {
+                                        Ok(params
+                                            .items
+                                            .into_iter()
+                                            .map(|item| {
+                                                if let Some(section) = &item.section {
+                                                    settings
+                                                        .get(section)
+                                                        .cloned()
+                                                        .unwrap_or(serde_json::Value::Null)
+                                                } else {
+                                                    settings.clone()
+                                                }
+                                            })
+                                            .collect())
+                                    }
                                 }
-                            }
-                        })
-                        .detach();
+                            })
+                            .detach();
 
-                    // Even though we don't have handling for these requests, respond to them to
-                    // avoid stalling any language server like `gopls` which waits for a response
-                    // to these requests when initializing.
-                    language_server
-                        .on_request::<lsp::request::WorkDoneProgressCreate, _, _>({
-                            let this = this.downgrade();
-                            move |params, mut cx| async move {
-                                if let Some(this) = this.upgrade(&cx) {
-                                    this.update(&mut cx, |this, _| {
-                                        if let Some(status) =
-                                            this.language_server_statuses.get_mut(&server_id)
-                                        {
-                                            if let lsp::NumberOrString::String(token) = params.token
+                        // Even though we don't have handling for these requests, respond to them to
+                        // avoid stalling any language server like `gopls` which waits for a response
+                        // to these requests when initializing.
+                        language_server
+                            .on_request::<lsp::request::WorkDoneProgressCreate, _, _>({
+                                let this = this.downgrade();
+                                move |params, mut cx| async move {
+                                    if let Some(this) = this.upgrade(&cx) {
+                                        this.update(&mut cx, |this, _| {
+                                            if let Some(status) =
+                                                this.language_server_statuses.get_mut(&server_id)
                                             {
-                                                status.progress_tokens.insert(token);
+                                                if let lsp::NumberOrString::String(token) =
+                                                    params.token
+                                                {
+                                                    status.progress_tokens.insert(token);
+                                                }
                                             }
-                                        }
-                                    });
+                                        });
+                                    }
+                                    Ok(())
                                 }
+                            })
+                            .detach();
+                        language_server
+                            .on_request::<lsp::request::RegisterCapability, _, _>(|_, _| async {
                                 Ok(())
-                            }
-                        })
-                        .detach();
-                    language_server
-                        .on_request::<lsp::request::RegisterCapability, _, _>(|_, _| async {
-                            Ok(())
-                        })
-                        .detach();
+                            })
+                            .detach();
 
-                    language_server
-                        .on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
-                            let this = this.downgrade();
-                            let adapter = adapter.clone();
-                            let language_server = language_server.clone();
-                            move |params, cx| {
-                                Self::on_lsp_workspace_edit(
-                                    this,
-                                    params,
-                                    server_id,
-                                    adapter.clone(),
-                                    language_server.clone(),
-                                    cx,
-                                )
-                            }
-                        })
-                        .detach();
+                        language_server
+                            .on_request::<lsp::request::ApplyWorkspaceEdit, _, _>({
+                                let this = this.downgrade();
+                                let adapter = adapter.clone();
+                                let language_server = language_server.clone();
+                                move |params, cx| {
+                                    Self::on_lsp_workspace_edit(
+                                        this,
+                                        params,
+                                        server_id,
+                                        adapter.clone(),
+                                        language_server.clone(),
+                                        cx,
+                                    )
+                                }
+                            })
+                            .detach();
 
-                    language_server
-                        .on_notification::<lsp::notification::Progress, _>({
-                            let this = this.downgrade();
-                            move |params, mut cx| {
-                                if let Some(this) = this.upgrade(&cx) {
-                                    this.update(&mut cx, |this, cx| {
-                                        this.on_lsp_progress(
-                                            params,
-                                            server_id,
-                                            disk_based_diagnostics_progress_token,
-                                            cx,
-                                        );
-                                    });
+                        language_server
+                            .on_notification::<lsp::notification::Progress, _>({
+                                let this = this.downgrade();
+                                move |params, mut cx| {
+                                    if let Some(this) = this.upgrade(&cx) {
+                                        this.update(&mut cx, |this, cx| {
+                                            this.on_lsp_progress(
+                                                params,
+                                                server_id,
+                                                disk_based_diagnostics_progress_token,
+                                                cx,
+                                            );
+                                        });
+                                    }
                                 }
+                            })
+                            .detach();
+
+                        this.update(&mut cx, |this, cx| {
+                            // If the language server for this key doesn't match the server id, don't store the
+                            // server. Which will cause it to be dropped, killing the process
+                            if this
+                                .language_server_ids
+                                .get(&key)
+                                .map(|id| id != &server_id)
+                                .unwrap_or(false)
+                            {
+                                return None;
                             }
-                        })
-                        .detach();
 
-                    this.update(&mut cx, |this, cx| {
-                        this.language_servers
-                            .insert(key.clone(), (adapter.clone(), language_server.clone()));
-                        this.language_server_statuses.insert(
-                            server_id,
-                            LanguageServerStatus {
-                                name: language_server.name().to_string(),
-                                pending_work: Default::default(),
-                                has_pending_diagnostic_updates: false,
-                                progress_tokens: Default::default(),
-                            },
-                        );
-                        language_server
-                            .notify::<lsp::notification::DidChangeConfiguration>(
-                                lsp::DidChangeConfigurationParams {
-                                    settings: this.language_server_settings.lock().clone(),
+                            // Update language_servers collection with Running variant of LanguageServerState
+                            // indicating that the server is up and running and ready
+                            this.language_servers.insert(
+                                server_id,
+                                LanguageServerState::Running {
+                                    adapter: adapter.clone(),
+                                    server: language_server.clone(),
                                 },
-                            )
-                            .ok();
+                            );
+                            this.language_server_statuses.insert(
+                                server_id,
+                                LanguageServerStatus {
+                                    name: language_server.name().to_string(),
+                                    pending_work: Default::default(),
+                                    has_pending_diagnostic_updates: false,
+                                    progress_tokens: Default::default(),
+                                },
+                            );
+                            language_server
+                                .notify::<lsp::notification::DidChangeConfiguration>(
+                                    lsp::DidChangeConfigurationParams {
+                                        settings: this.language_server_settings.lock().clone(),
+                                    },
+                                )
+                                .ok();
+
+                            if let Some(project_id) = this.shared_remote_id() {
+                                this.client
+                                    .send(proto::StartLanguageServer {
+                                        project_id,
+                                        server: Some(proto::LanguageServer {
+                                            id: server_id as u64,
+                                            name: language_server.name().to_string(),
+                                        }),
+                                    })
+                                    .log_err();
+                            }
 
-                        if let Some(project_id) = this.shared_remote_id() {
-                            this.client
-                                .send(proto::StartLanguageServer {
-                                    project_id,
-                                    server: Some(proto::LanguageServer {
-                                        id: server_id as u64,
-                                        name: language_server.name().to_string(),
-                                    }),
-                                })
-                                .log_err();
-                        }
+                            // Tell the language server about every open buffer in the worktree that matches the language.
+                            for buffer in this.opened_buffers.values() {
+                                if let Some(buffer_handle) = buffer.upgrade(cx) {
+                                    let buffer = buffer_handle.read(cx);
+                                    let file = if let Some(file) = File::from_dyn(buffer.file()) {
+                                        file
+                                    } else {
+                                        continue;
+                                    };
+                                    let language = if let Some(language) = buffer.language() {
+                                        language
+                                    } else {
+                                        continue;
+                                    };
+                                    if file.worktree.read(cx).id() != key.0
+                                        || language.lsp_adapter().map(|a| a.name())
+                                            != Some(key.1.clone())
+                                    {
+                                        continue;
+                                    }
 
-                        // Tell the language server about every open buffer in the worktree that matches the language.
-                        for buffer in this.opened_buffers.values() {
-                            if let Some(buffer_handle) = buffer.upgrade(cx) {
-                                let buffer = buffer_handle.read(cx);
-                                let file = if let Some(file) = File::from_dyn(buffer.file()) {
-                                    file
-                                } else {
-                                    continue;
-                                };
-                                let language = if let Some(language) = buffer.language() {
-                                    language
-                                } else {
-                                    continue;
-                                };
-                                if file.worktree.read(cx).id() != key.0
-                                    || language.lsp_adapter().map(|a| a.name())
-                                        != Some(key.1.clone())
-                                {
-                                    continue;
+                                    let file = file.as_local()?;
+                                    let versions = this
+                                        .buffer_snapshots
+                                        .entry(buffer.remote_id())
+                                        .or_insert_with(|| vec![(0, buffer.text_snapshot())]);
+                                    let (version, initial_snapshot) = versions.last().unwrap();
+                                    let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
+                                    let language_id =
+                                        adapter.id_for_language(language.name().as_ref());
+                                    language_server
+                                        .notify::<lsp::notification::DidOpenTextDocument>(
+                                            lsp::DidOpenTextDocumentParams {
+                                                text_document: lsp::TextDocumentItem::new(
+                                                    uri,
+                                                    language_id.unwrap_or_default(),
+                                                    *version,
+                                                    initial_snapshot.text(),
+                                                ),
+                                            },
+                                        )
+                                        .log_err()?;
+                                    buffer_handle.update(cx, |buffer, cx| {
+                                        buffer.set_completion_triggers(
+                                            language_server
+                                                .capabilities()
+                                                .completion_provider
+                                                .as_ref()
+                                                .and_then(|provider| {
+                                                    provider.trigger_characters.clone()
+                                                })
+                                                .unwrap_or(Vec::new()),
+                                            cx,
+                                        )
+                                    });
                                 }
-
-                                let file = file.as_local()?;
-                                let versions = this
-                                    .buffer_snapshots
-                                    .entry(buffer.remote_id())
-                                    .or_insert_with(|| vec![(0, buffer.text_snapshot())]);
-                                let (version, initial_snapshot) = versions.last().unwrap();
-                                let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
-                                let language_id = adapter.id_for_language(language.name().as_ref());
-                                language_server
-                                    .notify::<lsp::notification::DidOpenTextDocument>(
-                                        lsp::DidOpenTextDocumentParams {
-                                            text_document: lsp::TextDocumentItem::new(
-                                                uri,
-                                                language_id.unwrap_or_default(),
-                                                *version,
-                                                initial_snapshot.text(),
-                                            ),
-                                        },
-                                    )
-                                    .log_err()?;
-                                buffer_handle.update(cx, |buffer, cx| {
-                                    buffer.set_completion_triggers(
-                                        language_server
-                                            .capabilities()
-                                            .completion_provider
-                                            .as_ref()
-                                            .and_then(|provider| {
-                                                provider.trigger_characters.clone()
-                                            })
-                                            .unwrap_or(Vec::new()),
-                                        cx,
-                                    )
-                                });
                             }
-                        }
 
-                        cx.notify();
-                        Some(())
-                    });
+                            cx.notify();
+                            Some(language_server)
+                        })
+                    })),
+                );
 
-                    Some(language_server)
-                })
+                server_id
             });
     }
 
+    // Returns a list of all of the worktrees which no longer have a language server and the root path
+    // for the stopped server
     fn stop_language_server(
         &mut self,
         worktree_id: WorktreeId,
         adapter_name: LanguageServerName,
         cx: &mut ModelContext<Self>,
-    ) -> Task<()> {
+    ) -> Task<(Option<PathBuf>, Vec<WorktreeId>)> {
         let key = (worktree_id, adapter_name);
-        if let Some((_, language_server)) = self.language_servers.remove(&key) {
-            self.language_server_statuses
-                .remove(&language_server.server_id());
+        if let Some(server_id) = self.language_server_ids.remove(&key) {
+            // Remove other entries for this language server as well
+            let mut orphaned_worktrees = vec![worktree_id];
+            let other_keys = self.language_server_ids.keys().cloned().collect::<Vec<_>>();
+            for other_key in other_keys {
+                if self.language_server_ids.get(&other_key) == Some(&server_id) {
+                    self.language_server_ids.remove(&other_key);
+                    orphaned_worktrees.push(other_key.0);
+                }
+            }
+
+            self.language_server_statuses.remove(&server_id);
             cx.notify();
-        }
 
-        if let Some(started_language_server) = self.started_language_servers.remove(&key) {
+            let server_state = self.language_servers.remove(&server_id);
             cx.spawn_weak(|this, mut cx| async move {
-                if let Some(language_server) = started_language_server.await {
-                    if let Some(shutdown) = language_server.shutdown() {
-                        shutdown.await;
+                let mut root_path = None;
+
+                let server = match server_state {
+                    Some(LanguageServerState::Starting(started_language_server)) => {
+                        started_language_server.await
                     }
+                    Some(LanguageServerState::Running { server, .. }) => Some(server),
+                    None => None,
+                };
 
-                    if let Some(this) = this.upgrade(&cx) {
-                        this.update(&mut cx, |this, cx| {
-                            this.language_server_statuses
-                                .remove(&language_server.server_id());
-                            cx.notify();
-                        });
+                if let Some(server) = server {
+                    root_path = Some(server.root_path().clone());
+                    if let Some(shutdown) = server.shutdown() {
+                        shutdown.await;
                     }
                 }
+
+                if let Some(this) = this.upgrade(&cx) {
+                    this.update(&mut cx, |this, cx| {
+                        this.language_server_statuses.remove(&server_id);
+                        cx.notify();
+                    });
+                }
+
+                (root_path, orphaned_worktrees)
             })
         } else {
-            Task::ready(())
+            Task::ready((None, Vec::new()))
         }
     }
 
@@ -2247,7 +2324,7 @@ impl Project {
     fn restart_language_server(
         &mut self,
         worktree_id: WorktreeId,
-        worktree_path: Arc<Path>,
+        fallback_path: Arc<Path>,
         language: Arc<Language>,
         cx: &mut ModelContext<Self>,
     ) {
@@ -2257,12 +2334,33 @@ impl Project {
             return;
         };
 
-        let stop = self.stop_language_server(worktree_id, adapter.name(), cx);
+        let server_name = adapter.name();
+        let stop = self.stop_language_server(worktree_id, server_name.clone(), cx);
         cx.spawn_weak(|this, mut cx| async move {
-            stop.await;
+            let (original_root_path, orphaned_worktrees) = stop.await;
             if let Some(this) = this.upgrade(&cx) {
                 this.update(&mut cx, |this, cx| {
-                    this.start_language_server(worktree_id, worktree_path, language, cx);
+                    // Attempt to restart using original server path. Fallback to passed in
+                    // path if we could not retrieve the root path
+                    let root_path = original_root_path
+                        .map(|path_buf| Arc::from(path_buf.as_path()))
+                        .unwrap_or(fallback_path);
+
+                    this.start_language_server(worktree_id, root_path, language, cx);
+
+                    // Lookup new server id and set it for each of the orphaned worktrees
+                    if let Some(new_server_id) = this
+                        .language_server_ids
+                        .get(&(worktree_id, server_name.clone()))
+                        .cloned()
+                    {
+                        for orphaned_worktree in orphaned_worktrees {
+                            this.language_server_ids.insert(
+                                (orphaned_worktree, server_name.clone()),
+                                new_server_id.clone(),
+                            );
+                        }
+                    }
                 });
             }
         })
@@ -2498,14 +2596,16 @@ impl Project {
     }
 
     pub fn set_language_server_settings(&mut self, settings: serde_json::Value) {
-        for (_, server) in self.language_servers.values() {
-            server
-                .notify::<lsp::notification::DidChangeConfiguration>(
-                    lsp::DidChangeConfigurationParams {
-                        settings: settings.clone(),
-                    },
-                )
-                .ok();
+        for server_state in self.language_servers.values() {
+            if let LanguageServerState::Running { server, .. } = server_state {
+                server
+                    .notify::<lsp::notification::DidChangeConfiguration>(
+                        lsp::DidChangeConfigurationParams {
+                            settings: settings.clone(),
+                        },
+                    )
+                    .ok();
+            }
         }
         *self.language_server_settings.lock() = settings;
     }
@@ -2968,30 +3068,36 @@ impl Project {
     pub fn symbols(&self, query: &str, cx: &mut ModelContext<Self>) -> Task<Result<Vec<Symbol>>> {
         if self.is_local() {
             let mut requests = Vec::new();
-            for ((worktree_id, _), (lsp_adapter, language_server)) in self.language_servers.iter() {
+            for ((worktree_id, _), server_id) in self.language_server_ids.iter() {
                 let worktree_id = *worktree_id;
                 if let Some(worktree) = self
                     .worktree_for_id(worktree_id, cx)
                     .and_then(|worktree| worktree.read(cx).as_local())
                 {
-                    let lsp_adapter = lsp_adapter.clone();
-                    let worktree_abs_path = worktree.abs_path().clone();
-                    requests.push(
-                        language_server
-                            .request::<lsp::request::WorkspaceSymbol>(lsp::WorkspaceSymbolParams {
-                                query: query.to_string(),
-                                ..Default::default()
-                            })
-                            .log_err()
-                            .map(move |response| {
-                                (
-                                    lsp_adapter,
-                                    worktree_id,
-                                    worktree_abs_path,
-                                    response.unwrap_or_default(),
+                    if let Some(LanguageServerState::Running { adapter, server }) =
+                        self.language_servers.get(server_id)
+                    {
+                        let adapter = adapter.clone();
+                        let worktree_abs_path = worktree.abs_path().clone();
+                        requests.push(
+                            server
+                                .request::<lsp::request::WorkspaceSymbol>(
+                                    lsp::WorkspaceSymbolParams {
+                                        query: query.to_string(),
+                                        ..Default::default()
+                                    },
                                 )
-                            }),
-                    );
+                                .log_err()
+                                .map(move |response| {
+                                    (
+                                        adapter,
+                                        worktree_id,
+                                        worktree_abs_path,
+                                        response.unwrap_or_default(),
+                                    )
+                                }),
+                        );
+                    }
                 }
             }
 
@@ -3074,11 +3180,11 @@ impl Project {
         cx: &mut ModelContext<Self>,
     ) -> Task<Result<ModelHandle<Buffer>>> {
         if self.is_local() {
-            let (lsp_adapter, language_server) = if let Some(server) = self.language_servers.get(&(
+            let language_server_id = if let Some(id) = self.language_server_ids.get(&(
                 symbol.source_worktree_id,
                 symbol.language_server_name.clone(),
             )) {
-                server.clone()
+                *id
             } else {
                 return Task::ready(Err(anyhow!(
                     "language server for worktree and language not found"
@@ -3101,7 +3207,12 @@ impl Project {
                 return Task::ready(Err(anyhow!("invalid symbol path")));
             };
 
-            self.open_local_buffer_via_lsp(symbol_uri, lsp_adapter, language_server, cx)
+            self.open_local_buffer_via_lsp(
+                symbol_uri,
+                language_server_id,
+                symbol.language_server_name.clone(),
+                cx,
+            )
         } else if let Some(project_id) = self.remote_id() {
             let request = self.client.request(proto::OpenBufferForSymbol {
                 project_id,
@@ -3152,8 +3263,8 @@ impl Project {
 
         if worktree.read(cx).as_local().is_some() {
             let buffer_abs_path = buffer_abs_path.unwrap();
-            let (_, lang_server) =
-                if let Some(server) = self.language_server_for_buffer(source_buffer, cx) {
+            let lang_server =
+                if let Some((_, server)) = self.language_server_for_buffer(source_buffer, cx) {
                     server.clone()
                 } else {
                     return Task::ready(Ok(Default::default()));
@@ -3310,7 +3421,7 @@ impl Project {
         let buffer_id = buffer.remote_id();
 
         if self.is_local() {
-            let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx)
+            let lang_server = if let Some((_, server)) = self.language_server_for_buffer(buffer, cx)
             {
                 server.clone()
             } else {
@@ -3407,7 +3518,7 @@ impl Project {
 
         if worktree.read(cx).as_local().is_some() {
             let buffer_abs_path = buffer_abs_path.unwrap();
-            let (_, lang_server) = if let Some(server) = self.language_server_for_buffer(buffer, cx)
+            let lang_server = if let Some((_, server)) = self.language_server_for_buffer(buffer, cx)
             {
                 server.clone()
             } else {
@@ -3494,8 +3605,8 @@ impl Project {
         if self.is_local() {
             let buffer = buffer_handle.read(cx);
             let (lsp_adapter, lang_server) =
-                if let Some(server) = self.language_server_for_buffer(buffer, cx) {
-                    server.clone()
+                if let Some((adapter, server)) = self.language_server_for_buffer(buffer, cx) {
+                    (adapter.clone(), server.clone())
                 } else {
                     return Task::ready(Ok(Default::default()));
                 };
@@ -3531,8 +3642,8 @@ impl Project {
                         this,
                         edit,
                         push_to_history,
-                        lsp_adapter,
-                        lang_server,
+                        lsp_adapter.clone(),
+                        lang_server.clone(),
                         &mut cx,
                     )
                     .await
@@ -3661,8 +3772,8 @@ impl Project {
                         .update(cx, |this, cx| {
                             this.open_local_buffer_via_lsp(
                                 op.text_document.uri,
-                                lsp_adapter.clone(),
-                                language_server.clone(),
+                                language_server.server_id(),
+                                lsp_adapter.name(),
                                 cx,
                             )
                         })
@@ -3956,9 +4067,10 @@ impl Project {
         let buffer = buffer_handle.read(cx);
         if self.is_local() {
             let file = File::from_dyn(buffer.file()).and_then(File::as_local);
-            if let Some((file, (_, language_server))) =
-                file.zip(self.language_server_for_buffer(buffer, cx).cloned())
-            {
+            if let Some((file, language_server)) = file.zip(
+                self.language_server_for_buffer(buffer, cx)
+                    .map(|(_, server)| server.clone()),
+            ) {
                 let lsp_params = request.to_lsp(&file.abs_path(cx), cx);
                 return cx.spawn(|this, cx| async move {
                     if !request.check_capabilities(&language_server.capabilities()) {
@@ -5574,14 +5686,21 @@ impl Project {
         &self,
         buffer: &Buffer,
         cx: &AppContext,
-    ) -> Option<&(Arc<dyn LspAdapter>, Arc<LanguageServer>)> {
+    ) -> Option<(&Arc<dyn LspAdapter>, &Arc<LanguageServer>)> {
         if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
             let worktree_id = file.worktree_id(cx);
-            self.language_servers
-                .get(&(worktree_id, language.lsp_adapter()?.name()))
-        } else {
-            None
+            let key = (worktree_id, language.lsp_adapter()?.name());
+
+            if let Some(server_id) = self.language_server_ids.get(&key) {
+                if let Some(LanguageServerState::Running { adapter, server }) =
+                    self.language_servers.get(&server_id)
+                {
+                    return Some((adapter, server));
+                }
+            }
         }
+
+        None
     }
 }
 
@@ -5741,8 +5860,16 @@ impl Entity for Project {
         let shutdown_futures = self
             .language_servers
             .drain()
-            .filter_map(|(_, (_, server))| server.shutdown())
+            .map(|(_, server_state)| async {
+                match server_state {
+                    LanguageServerState::Running { server, .. } => server.shutdown()?.await,
+                    LanguageServerState::Starting(starting_server) => {
+                        starting_server.await?.shutdown()?.await
+                    }
+                }
+            })
             .collect::<Vec<_>>();
+
         Some(
             async move {
                 futures::future::join_all(shutdown_futures).await;
@@ -7657,6 +7784,10 @@ mod tests {
             .await
             .unwrap();
 
+        // Assert no new language server started
+        cx.foreground().run_until_parked();
+        assert!(fake_servers.try_next().is_err());
+
         assert_eq!(definitions.len(), 1);
         let definition = definitions.pop().unwrap();
         cx.update(|cx| {