cairo-language-server 2.20.0

The Cairo Language Server
Documentation
// +--------------------------------------------------------+
// | Code adopted from:                                     |
// | Repository: https://github.com/astral-sh/ruff          |
// | File: `crates/ruff_server/src/server/schedule/task.rs` |
// | Commit: 46a457318d8d259376a2b458b3f814b9b795fe69       |
// +--------------------------------------------------------+

use lsp_server::RequestId;
use serde::Serialize;
use tracing::error;

use crate::lsp::result::LSPError;
use crate::server::client::{Notifier, Requester, Responder};
use crate::state::{MetaState, State, StateSnapshot};

type LocalMutFn<'s> = Box<dyn FnOnce(&mut State, Notifier, &mut Requester<'_>, Responder) + 's>;
type LocalFn<'s> = Box<dyn FnOnce(&State, MetaState, Notifier, &mut Requester<'_>, Responder) + 's>;

type BackgroundFn = Box<dyn FnOnce(Notifier, Responder) + Send + 'static>;

pub type BackgroundFnBuilder<'s> = Box<dyn FnOnce(&State, MetaState) -> BackgroundFn + 's>;

/// Describes how the task should be run.
#[derive(Clone, Copy, Debug, Default)]
pub enum BackgroundSchedule {
    /// The task should be run on the general high-priority background
    /// thread.
    LatencySensitive,
    /// The task should be run on a regular-priority background thread.
    #[default]
    Worker,
}

/// A [`Task`] is a future that has not yet started, and it is the job of
/// the [`super::Scheduler`] to make that happen, via [`super::Scheduler::dispatch`].
/// A task can either run on the main thread (in other words, the same thread as the
/// scheduler) or it can run in a background thread. The main difference between
/// the two is that background threads only have a read-only snapshot of the session,
/// while local tasks have exclusive access and can modify it as they please. Keep in mind that
/// local tasks will **block** the main event loop, so only use local tasks if you **need**
/// mutable state access, or you need the absolute lowest latency possible.
pub enum Task<'s> {
    Fmt(BackgroundFnBuilder<'s>),
    Background(BackgroundTaskBuilder<'s>),
    Sync(SyncTask<'s>),
    SyncMut(SyncMutTask<'s>),
}

// The reason why this isn't just a 'static background closure
// is because we need to take a snapshot of the state before sending
// this task to the background. The inner closure can't take the state
// as an immutable reference since it's used mutably elsewhere. So instead,
// a background task is built using an outer closure that borrows the state to take a snapshot
// that the inner closure can capture. This builder closure has a lifetime linked to the scheduler.
// When the task is dispatched, the scheduler runs the synchronous builder, which takes the state
// as a reference, to create the inner 'static closure. That closure is then moved to a background
// task pool.
pub struct BackgroundTaskBuilder<'s> {
    pub schedule: BackgroundSchedule,
    pub builder: BackgroundFnBuilder<'s>,
}

pub struct SyncMutTask<'s> {
    pub func: LocalMutFn<'s>,
}

pub struct SyncTask<'s> {
    pub func: LocalFn<'s>,
}

impl<'s> Task<'s> {
    /// Creates a new fmt task.
    pub fn fmt(
        func: impl FnOnce(&State, MetaState) -> Box<dyn FnOnce(Notifier, Responder) + Send + 'static>
        + 's,
    ) -> Self {
        Self::Fmt(Box::new(func))
    }

    /// Creates a new background task.
    pub fn background(
        schedule: BackgroundSchedule,
        func: impl FnOnce(&State, MetaState) -> Box<dyn FnOnce(Notifier, Responder) + Send + 'static>
        + 's,
    ) -> Self {
        Self::Background(BackgroundTaskBuilder { schedule, builder: Box::new(func) })
    }

    /// Creates a new local task.
    pub fn local_mut(
        func: impl FnOnce(&mut State, Notifier, &mut Requester<'_>, Responder) + 's,
    ) -> Self {
        Self::SyncMut(SyncMutTask { func: Box::new(func) })
    }

    /// Creates a new local task without access to the mutable state.
    pub fn local(
        func: impl FnOnce(&State, MetaState, Notifier, &mut Requester<'_>, Responder) + 's,
    ) -> Self {
        Self::Sync(SyncTask { func: Box::new(func) })
    }

    /// Creates a local task that immediately responds with the provided `request`.
    pub fn immediate<R>(id: RequestId, result: Result<R, LSPError>) -> Self
    where
        R: Serialize + Send + 'static,
    {
        Self::local(move |_, _, _, _, responder| {
            if let Err(err) = responder.respond(id, result) {
                error!("unable to send immediate response: {err:?}");
            }
        })
    }

    /// Creates a local task that does nothing.
    pub fn nothing() -> Self {
        Self::local(move |_, _, _, _, _| {})
    }
}

pub trait Handler: FnOnce(StateSnapshot, MetaState, Notifier, Responder) + Send + 'static {}

impl<T> Handler for T where T: FnOnce(StateSnapshot, MetaState, Notifier, Responder) + Send + 'static
{}

#[derive(Debug, Clone, Copy)]
pub enum RetryTaskInfo {
    Fmt,
    Background(BackgroundSchedule),
}

impl RetryTaskInfo {
    pub fn task(self, request_handler: impl Handler) -> Task<'static> {
        let builder: BackgroundFnBuilder = Box::new(|state: &State, meta_state| {
            let state_snapshot = state.snapshot();
            Box::new(|notifier, responder| {
                request_handler(state_snapshot, meta_state, notifier, responder)
            })
        });
        match self {
            Self::Fmt => Task::Fmt(builder),
            Self::Background(schedule) => {
                Task::Background(BackgroundTaskBuilder { schedule, builder })
            }
        }
    }
}