Repository: helix-editor/helix
File: helix-lsp/src/lib.rs (Rust)

Code before:
            }
        };

        Ok(notification)
    }
}

#[derive(Debug)]
enum Clients {
    /// A set of running clients
    Clients(Vec<Arc<Client>>),
    /// A marker that clients for a language server were manually stopped
    /// and should not be automatically restarted.
    Tombstone,
}

impl Clients {
    fn new(client: Arc<Client>) -> Self {
        Self::Clients(vec![client])
    }
}

#[derive(Debug)]
pub struct Registry {
    inner: SlotMap<LanguageServerId, Arc<Client>>,
    inner_by_name: HashMap<LanguageServerName, Clients>,
    syn_loader: Arc<ArcSwap<helix_core::syntax::Loader>>,
    pub incoming: SelectAll<UnboundedReceiverStream<(LanguageServerId, Call)>>,
    pub file_event_handler: file_event::Handler,
}

impl Registry {
    pub fn new(syn_loader: Arc<ArcSwap<helix_core::syntax::Loader>>) -> Self {
        Self {
            inner: SlotMap::with_key(),
            inner_by_name: HashMap::new(),
            syn_loader,
            incoming: SelectAll::new(),
            file_event_handler: file_event::Handler::new(),
        }
    }

    pub fn get_by_id(&self, id: LanguageServerId) -> Option<&Arc<Client>> {
        self.inner.get(id)
    }

    pub fn remove_by_id(&mut self, id: LanguageServerId) {
        let Some(client) = self.inner.remove(id) else {
            log::debug!("client was already removed");
            return;
        };

Review comment (suggestion):
I wonder if we really need a new enum. Ii think it would be sufficient to have the following:

* not in hashmap: no lsp started yet.
* in hashmap and vec length >= 1: lsp is active (either add a new client or reuse an existing one)
* in hashmap and vec length == 0: We can only endup here if `:lsp-stop` is called, don't start a LSP instance for this document.

This would not require adding a new type and would really only require two changes:
* leave behind an empty list in the `stop` function
* `Registry::get`: don't start the LSP if the list is empty

it's a bit of a hack but it would be a much smaller change.

Code after:
            }
        };

        Ok(notification)
    }
}

#[derive(Debug)]
pub struct Registry {
    inner: SlotMap<LanguageServerId, Arc<Client>>,
    inner_by_name: HashMap<LanguageServerName, Vec<Arc<Client>>>,
    syn_loader: Arc<ArcSwap<helix_core::syntax::Loader>>,
    pub incoming: SelectAll<UnboundedReceiverStream<(LanguageServerId, Call)>>,
    pub file_event_handler: file_event::Handler,
}

impl Registry {
    pub fn new(syn_loader: Arc<ArcSwap<helix_core::syntax::Loader>>) -> Self {
        Self {
            inner: SlotMap::with_key(),
            inner_by_name: HashMap::new(),
            syn_loader,
            incoming: SelectAll::new(),
            file_event_handler: file_event::Handler::new(),
        }
    }

    pub fn get_by_id(&self, id: LanguageServerId) -> Option<&Arc<Client>> {
        self.inner.get(id)
    }

    pub fn remove_by_id(&mut self, id: LanguageServerId) {
        let Some(client) = self.inner.remove(id) else {
            log::debug!("client was already removed");
            return;
        };
        self.file_event_handler.remove_client(id);
        let instances = self
            .inner_by_name
            .get_mut(client.name())
            .expect("inner and inner_by_name must be synced");
        instances.retain(|ls| id != ls.id());
        if instances.is_empty() {
            self.inner_by_name.remove(client.name());
        }
    }

    fn start_client(
        &mut self,
        name: String,
        ls_config: &LanguageConfiguration,