<commit_before>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.

use std::{
    fmt,
    io::{self, BufReader},
    ops,
    path::PathBuf,
    process::{self, Command, Stdio},
    time::Duration,
};

use crossbeam_channel::{never, select, unbounded, Receiver, Sender};

pub use cargo_metadata::diagnostic::{
    Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion,
};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlycheckConfig {
    CargoCommand {
        command: String,
        all_targets: bool,
        all_features: bool,
        features: Vec<String>,
        extra_args: Vec<String>,
    },
    CustomCommand {
        command: String,
        args: Vec<String>,
    },
}

impl fmt::Display for FlycheckConfig {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
            FlycheckConfig::CustomCommand { command, args } => {
                write!(f, "{} {}", command, args.join(" "))
            }
        }
    }
}

/// Flycheck wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct FlycheckHandle {
    // XXX: drop order is significant
    sender: Sender<Restart>,
    thread: jod_thread::JoinHandle,
}

impl FlycheckHandle {
    pub fn spawn(
        sender: Box<dyn Fn(Message) + Send>,
        config: FlycheckConfig,
        workspace_root: PathBuf,
    ) -> FlycheckHandle {
        let actor = FlycheckActor::new(sender, config, workspace_root);
        let (sender, receiver) = unbounded::<Restart>();
        let thread = jod_thread::spawn(move || actor.run(receiver));
        FlycheckHandle { sender, thread }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        self.sender.send(Restart).unwrap();
    }
}

#[derive(Debug)]
pub enum Message {
    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic },

    /// Request check progress notification to client
    Progress(Progress),
}

#[derive(Debug)]
pub enum Progress {
    DidStart,
    DidCheckCrate(String),
    DidFinish,
    DidCancel,
}

struct Restart;

struct FlycheckActor {
    sender: Box<dyn Fn(Message) + Send>,
    config: FlycheckConfig,
    workspace_root: PathBuf,
    /// WatchThread exists to wrap around the communication needed to be able to
    /// run `cargo check` without blocking. Currently the Rust standard library
    /// doesn't provide a way to read sub-process output without blocking, so we
    /// have to wrap sub-processes output handling in a thread and pass messages
    /// back over a channel.
    // XXX: drop order is significant
    check_process: Option<(Receiver<cargo_metadata::Message>, jod_thread::JoinHandle)>,
}

enum Event {
    Restart(Restart),
    CheckEvent(Option<cargo_metadata::Message>),
}

impl FlycheckActor {
    fn new(
        sender: Box<dyn Fn(Message) + Send>,
        config: FlycheckConfig,
        workspace_root: PathBuf,
    ) -> FlycheckActor {
        FlycheckActor { sender, config, workspace_root, check_process: None }
    }
    fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
        let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan);
        select! {
            recv(inbox) -> msg => msg.ok().map(Event::Restart),
            recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
        }
    }
    fn run(mut self, inbox: Receiver<Restart>) {
        while let Some(event) = self.next_event(&inbox) {
            match event {
                Event::Restart(Restart) => {
                    while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {}
                    self.cancel_check_process();
                    self.check_process = Some(self.start_check_process());
                    self.send(Message::Progress(Progress::DidStart));
                }
                Event::CheckEvent(None) => {
                    // Watcher finished, replace it with a never channel to
                    // avoid busy-waiting.
                    assert!(self.check_process.take().is_some());
                    self.send(Message::Progress(Progress::DidFinish));
                }
                Event::CheckEvent(Some(message)) => match message {
                    cargo_metadata::Message::CompilerArtifact(msg) => {
                        self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name)));
                    }

                    cargo_metadata::Message::CompilerMessage(msg) => {
                        self.send(Message::AddDiagnostic {
                            workspace_root: self.workspace_root.clone(),
                            diagnostic: msg.message,
                        });
                    }

                    cargo_metadata::Message::BuildScriptExecuted(_)
                    | cargo_metadata::Message::BuildFinished(_)
                    | cargo_metadata::Message::TextLine(_)
                    | cargo_metadata::Message::Unknown => {}
                },
            }
        }
        // If we rerun the thread, we need to discard the previous check results first
        self.cancel_check_process();
    }
    fn cancel_check_process(&mut self) {
        if self.check_process.take().is_some() {
            self.send(Message::Progress(Progress::DidCancel));
        }
    }
    fn start_check_process(&self) -> (Receiver<cargo_metadata::Message>, jod_thread::JoinHandle) {
        let mut cmd = match &self.config {
            FlycheckConfig::CargoCommand {
                command,
                all_targets,
                all_features,
                extra_args,
                features,
            } => {
                let mut cmd = Command::new(ra_toolchain::cargo());
                cmd.arg(command);
                cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
                    .arg(self.workspace_root.join("Cargo.toml"));
                if *all_targets {
                    cmd.arg("--all-targets");
                }
                if *all_features {
                    cmd.arg("--all-features");
                } else if !features.is_empty() {
                    cmd.arg("--features");
                    cmd.arg(features.join(" "));
                }
                cmd.args(extra_args);
                cmd
            }
            FlycheckConfig::CustomCommand { command, args } => {
                let mut cmd = Command::new(command);
                cmd.args(args);
                cmd
            }
        };
        cmd.current_dir(&self.workspace_root);

        let (message_send, message_recv) = unbounded();
        let thread = jod_thread::spawn(move || {
            // If we trigger an error here, we will do so in the loop instead,
            // which will break out of the loop, and continue the shutdown
            let res = run_cargo(cmd, &mut |message| {
                // Skip certain kinds of messages to only spend time on what's useful
                match &message {
                    cargo_metadata::Message::CompilerArtifact(artifact) if artifact.fresh => {
                        return true
                    }
                    cargo_metadata::Message::BuildScriptExecuted(_)
                    | cargo_metadata::Message::Unknown => return true,
                    _ => {}
                }

                // if the send channel was closed, we want to shutdown
                message_send.send(message).is_ok()
            });

            if let Err(err) = res {
                // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
                // to display user-caused misconfiguration errors instead of just logging them here
                log::error!("Cargo watcher failed {:?}", err);
            }
        });
        (message_recv, thread)
    }

    fn send(&self, check_task: Message) {
        (self.sender)(check_task)
    }
}

fn run_cargo(
    mut command: Command,
    on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
) -> io::Result<()> {
    let child =
        command.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()).spawn()?;
    let mut child = ChildKiller(child);

    // We manually read a line at a time, instead of using serde's
    // stream deserializers, because the deserializer cannot recover
    // from an error, resulting in it getting stuck, because we try to
    // be resillient against failures.
    //
    // Because cargo only outputs one JSON object per line, we can
    // simply skip a line if it doesn't parse, which just ignores any
    // erroneus output.
    let stdout = BufReader::new(child.stdout.take().unwrap());
    let mut read_at_least_one_message = false;
    for message in cargo_metadata::Message::parse_stream(stdout) {
        let message = match message {
            Ok(message) => message,
            Err(err) => {
                log::error!("Invalid json from cargo check, ignoring ({})", err);
                continue;
            }
        };

        read_at_least_one_message = true;

        if !on_message(message) {
            break;
        }
    }

    // It is okay to ignore the result, as it only errors if the process is already dead
    let _ = child.kill();

    let exit_status = child.wait()?;
    if !exit_status.success() && !read_at_least_one_message {
        // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
        // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
        return Err(io::Error::new(
            io::ErrorKind::Other,
            format!(
                "the command produced no valid metadata (exit code: {:?}): {:?}",
                exit_status, command
            ),
        ));
    }

    Ok(())
}

struct ChildKiller(process::Child);

impl ops::Deref for ChildKiller {
    type Target = process::Child;
    fn deref(&self) -> &process::Child {
        &self.0
    }
}

impl ops::DerefMut for ChildKiller {
    fn deref_mut(&mut self) -> &mut process::Child {
        &mut self.0
    }
}

impl Drop for ChildKiller {
    fn drop(&mut self) {
        let _ = self.0.kill();
    }
}
<commit_msg>More standard pattern for Cargo<commit_after>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.

use std::{
    fmt,
    io::{self, BufReader},
    ops,
    path::PathBuf,
    process::{self, Command, Stdio},
    time::Duration,
};

use crossbeam_channel::{never, select, unbounded, Receiver, Sender};

pub use cargo_metadata::diagnostic::{
    Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion,
};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlycheckConfig {
    CargoCommand {
        command: String,
        all_targets: bool,
        all_features: bool,
        features: Vec<String>,
        extra_args: Vec<String>,
    },
    CustomCommand {
        command: String,
        args: Vec<String>,
    },
}

impl fmt::Display for FlycheckConfig {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
            FlycheckConfig::CustomCommand { command, args } => {
                write!(f, "{} {}", command, args.join(" "))
            }
        }
    }
}

/// Flycheck wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct FlycheckHandle {
    // XXX: drop order is significant
    sender: Sender<Restart>,
    thread: jod_thread::JoinHandle,
}

impl FlycheckHandle {
    pub fn spawn(
        sender: Box<dyn Fn(Message) + Send>,
        config: FlycheckConfig,
        workspace_root: PathBuf,
    ) -> FlycheckHandle {
        let actor = FlycheckActor::new(sender, config, workspace_root);
        let (sender, receiver) = unbounded::<Restart>();
        let thread = jod_thread::spawn(move || actor.run(receiver));
        FlycheckHandle { sender, thread }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        self.sender.send(Restart).unwrap();
    }
}

#[derive(Debug)]
pub enum Message {
    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic },

    /// Request check progress notification to client
    Progress(Progress),
}

#[derive(Debug)]
pub enum Progress {
    DidStart,
    DidCheckCrate(String),
    DidFinish,
    DidCancel,
}

struct Restart;

struct FlycheckActor {
    sender: Box<dyn Fn(Message) + Send>,
    config: FlycheckConfig,
    workspace_root: PathBuf,
    /// WatchThread exists to wrap around the communication needed to be able to
    /// run `cargo check` without blocking. Currently the Rust standard library
    /// doesn't provide a way to read sub-process output without blocking, so we
    /// have to wrap sub-processes output handling in a thread and pass messages
    /// back over a channel.
    check_process: Option<CargoHandle>,
}

enum Event {
    Restart(Restart),
    CheckEvent(Option<cargo_metadata::Message>),
}

impl FlycheckActor {
    fn new(
        sender: Box<dyn Fn(Message) + Send>,
        config: FlycheckConfig,
        workspace_root: PathBuf,
    ) -> FlycheckActor {
        FlycheckActor { sender, config, workspace_root, check_process: None }
    }
    fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
        let check_chan = self.check_process.as_ref().map(|cargo| &cargo.receiver);
        select! {
            recv(inbox) -> msg => msg.ok().map(Event::Restart),
            recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
        }
    }
    fn run(mut self, inbox: Receiver<Restart>) {
        while let Some(event) = self.next_event(&inbox) {
            match event {
                Event::Restart(Restart) => {
                    while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {}
                    self.cancel_check_process();
                    self.check_process = Some(self.start_check_process());
                    self.send(Message::Progress(Progress::DidStart));
                }
                Event::CheckEvent(None) => {
                    // Watcher finished, replace it with a never channel to
                    // avoid busy-waiting.
                    assert!(self.check_process.take().is_some());
                    self.send(Message::Progress(Progress::DidFinish));
                }
                Event::CheckEvent(Some(message)) => match message {
                    cargo_metadata::Message::CompilerArtifact(msg) => {
                        self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name)));
                    }

                    cargo_metadata::Message::CompilerMessage(msg) => {
                        self.send(Message::AddDiagnostic {
                            workspace_root: self.workspace_root.clone(),
                            diagnostic: msg.message,
                        });
                    }

                    cargo_metadata::Message::BuildScriptExecuted(_)
                    | cargo_metadata::Message::BuildFinished(_)
                    | cargo_metadata::Message::TextLine(_)
                    | cargo_metadata::Message::Unknown => {}
                },
            }
        }
        // If we rerun the thread, we need to discard the previous check results first
        self.cancel_check_process();
    }
    fn cancel_check_process(&mut self) {
        if self.check_process.take().is_some() {
            self.send(Message::Progress(Progress::DidCancel));
        }
    }
    fn start_check_process(&self) -> CargoHandle {
        let mut cmd = match &self.config {
            FlycheckConfig::CargoCommand {
                command,
                all_targets,
                all_features,
                extra_args,
                features,
            } => {
                let mut cmd = Command::new(ra_toolchain::cargo());
                cmd.arg(command);
                cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
                    .arg(self.workspace_root.join("Cargo.toml"));
                if *all_targets {
                    cmd.arg("--all-targets");
                }
                if *all_features {
                    cmd.arg("--all-features");
                } else if !features.is_empty() {
                    cmd.arg("--features");
                    cmd.arg(features.join(" "));
                }
                cmd.args(extra_args);
                cmd
            }
            FlycheckConfig::CustomCommand { command, args } => {
                let mut cmd = Command::new(command);
                cmd.args(args);
                cmd
            }
        };
        cmd.current_dir(&self.workspace_root);

        CargoHandle::spawn(cmd)
    }

    fn send(&self, check_task: Message) {
        (self.sender)(check_task)
    }
}

struct CargoHandle {
    receiver: Receiver<cargo_metadata::Message>,
    #[allow(unused)]
    thread: jod_thread::JoinHandle,
}

impl CargoHandle {
    fn spawn(command: Command) -> CargoHandle {
        let (sender, receiver) = unbounded();
        let actor = CargoActor::new(command, sender);
        let thread = jod_thread::spawn(move || {
            let _ = actor.run();
        });
        CargoHandle { receiver, thread }
    }
}

struct CargoActor {
    command: Command,
    sender: Sender<cargo_metadata::Message>,
}

impl CargoActor {
    fn new(command: Command, sender: Sender<cargo_metadata::Message>) -> CargoActor {
        CargoActor { command, sender }
    }
    fn run(mut self) -> io::Result<()> {
        let child = self
            .command
            .stdout(Stdio::piped())
            .stderr(Stdio::null())
            .stdin(Stdio::null())
            .spawn()?;
        let mut child = ChildKiller(child);

        // We manually read a line at a time, instead of using serde's
        // stream deserializers, because the deserializer cannot recover
        // from an error, resulting in it getting stuck, because we try to
        // be resillient against failures.
        //
        // Because cargo only outputs one JSON object per line, we can
        // simply skip a line if it doesn't parse, which just ignores any
        // erroneus output.
        let stdout = BufReader::new(child.stdout.take().unwrap());
        let mut read_at_least_one_message = false;
        for message in cargo_metadata::Message::parse_stream(stdout) {
            let message = match message {
                Ok(message) => message,
                Err(err) => {
                    log::error!("Invalid json from cargo check, ignoring ({})", err);
                    continue;
                }
            };

            read_at_least_one_message = true;

            // Skip certain kinds of messages to only spend time on what's useful
            match &message {
                cargo_metadata::Message::CompilerArtifact(artifact) if artifact.fresh => continue,
                cargo_metadata::Message::BuildScriptExecuted(_)
                | cargo_metadata::Message::Unknown => continue,
                _ => {
                    // if the send channel was closed, we want to shutdown
                    if self.sender.send(message).is_err() {
                        break;
                    }
                }
            }
        }

        // It is okay to ignore the result, as it only errors if the process is already dead
        let _ = child.kill();

        let exit_status = child.wait()?;
        if !exit_status.success() && !read_at_least_one_message {
            // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
            // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298

            // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
            // to display user-caused misconfiguration errors instead of just logging them here
            log::error!("Cargo watcher failed,the command produced no valid metadata (exit code: {:?}): {:?}", exit_status, self.command);
        }
        Ok(())
    }
}

struct ChildKiller(process::Child);

impl ops::Deref for ChildKiller {
    type Target = process::Child;
    fn deref(&self) -> &process::Child {
        &self.0
    }
}

impl ops::DerefMut for ChildKiller {
    fn deref_mut(&mut self) -> &mut process::Child {
        &mut self.0
    }
}

impl Drop for ChildKiller {
    fn drop(&mut self) {
        let _ = self.0.kill();
    }
}
<|endoftext|>


<commit_before>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
use cargo_metadata::Message;
use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
use lsp_types::{
    CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
    WorkDoneProgressEnd, WorkDoneProgressReport,
};
use std::{
    error, fmt,
    io::{BufRead, BufReader},
    path::{Path, PathBuf},
    process::{Command, Stdio},
    time::Instant,
};

mod conv;

use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};

pub use crate::conv::url_from_path_with_drive_lowercasing;

#[derive(Clone, Debug)]
pub struct CheckOptions {
    pub enable: bool,
    pub args: Vec<String>,
    pub command: String,
    pub all_targets: bool,
}

/// CheckWatcher wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct CheckWatcher {
    // XXX: drop order is significant
    cmd_send: Sender<CheckCommand>,
    handle: Option<jod_thread::JoinHandle<()>>,
    pub task_recv: Receiver<CheckTask>,
}

impl CheckWatcher {
    pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
        let options = options.clone();

        let (task_send, task_recv) = unbounded::<CheckTask>();
        let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
        let handle = jod_thread::spawn(move || {
            let mut check = CheckWatcherThread::new(options, workspace_root);
            check.run(&task_send, &cmd_recv);
        });
        CheckWatcher { task_recv, cmd_send, handle: Some(handle) }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        self.cmd_send.send(CheckCommand::Update).unwrap();
    }
}

#[derive(Debug)]
pub enum CheckTask {
    /// Request a clearing of all cached diagnostics from the check watcher
    ClearDiagnostics,

    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> },

    /// Request check progress notification to client
    Status(WorkDoneProgress),
}

pub enum CheckCommand {
    /// Request re-start of check thread
    Update,
}

struct CheckWatcherThread {
    options: CheckOptions,
    workspace_root: PathBuf,
    watcher: WatchThread,
    last_update_req: Option<Instant>,
}

impl CheckWatcherThread {
    fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherThread {
        CheckWatcherThread {
            options,
            workspace_root,
            watcher: WatchThread::dummy(),
            last_update_req: None,
        }
    }

    fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
        loop {
            select! {
                recv(&cmd_recv) -> cmd => match cmd {
                    Ok(cmd) => self.handle_command(cmd),
                    Err(RecvError) => {
                        // Command channel has closed, so shut down
                        break;
                    },
                },
                recv(self.watcher.message_recv) -> msg => match msg {
                    Ok(msg) => self.handle_message(msg, task_send),
                    Err(RecvError) => {
                        // Watcher finished, replace it with a never channel to
                        // avoid busy-waiting.
                        std::mem::replace(&mut self.watcher.message_recv, never());
                    },
                }
            };

            if self.should_recheck() {
                self.last_update_req.take();
                task_send.send(CheckTask::ClearDiagnostics).unwrap();

                // Replace with a dummy watcher first so we drop the original and wait for completion
                std::mem::replace(&mut self.watcher, WatchThread::dummy());

                // Then create the actual new watcher
                self.watcher = WatchThread::new(&self.options, &self.workspace_root);
            }
        }
    }

    fn should_recheck(&mut self) -> bool {
        if let Some(_last_update_req) = &self.last_update_req {
            // We currently only request an update on save, as we need up to
            // date source on disk for cargo check to do it's magic, so we
            // don't really need to debounce the requests at this point.
            return true;
        }
        false
    }

    fn handle_command(&mut self, cmd: CheckCommand) {
        match cmd {
            CheckCommand::Update => self.last_update_req = Some(Instant::now()),
        }
    }

    fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
        match msg {
            CheckEvent::Begin => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
                        title: "Running 'cargo check'".to_string(),
                        cancellable: Some(false),
                        message: None,
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::End => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
                        message: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
                        cancellable: Some(false),
                        message: Some(msg.target.name),
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerMessage(msg)) => {
                let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
                if map_result.is_empty() {
                    return;
                }

                for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
                    let fixes = fixes
                        .into_iter()
                        .map(|fix| {
                            CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
                        })
                        .collect();

                    task_send
                        .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
                        .unwrap();
                }
            }

            CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
            CheckEvent::Msg(Message::Unknown) => {}
        }
    }
}

#[derive(Debug)]
pub struct DiagnosticWithFixes {
    diagnostic: Diagnostic,
    fixes: Vec<CodeAction>,
}

/// WatchThread exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
/// The correct way to dispose of the thread is to drop it, on which the
/// sub-process will be killed, and the thread will be joined.
struct WatchThread {
    // XXX: drop order is significant
    message_recv: Receiver<CheckEvent>,
    _handle: Option<jod_thread::JoinHandle<()>>,
}

enum CheckEvent {
    Begin,
    Msg(cargo_metadata::Message),
    End,
}

#[derive(Debug)]
pub struct CargoError(String);

impl fmt::Display for CargoError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cargo failed: {}", self.0)
    }
}
impl error::Error for CargoError {}

pub fn run_cargo(
    args: &[String],
    current_dir: Option<&Path>,
    on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
) -> Result<(), CargoError> {
    let mut command = Command::new("cargo");
    if let Some(current_dir) = current_dir {
        command.current_dir(current_dir);
    }

    let mut child = command
        .args(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .spawn()
        .expect("couldn't launch cargo");

    // We manually read a line at a time, instead of using serde's
    // stream deserializers, because the deserializer cannot recover
    // from an error, resulting in it getting stuck, because we try to
    // be resillient against failures.
    //
    // Because cargo only outputs one JSON object per line, we can
    // simply skip a line if it doesn't parse, which just ignores any
    // erroneus output.
    let stdout = BufReader::new(child.stdout.take().unwrap());
    let mut read_at_least_one_message = false;

    for line in stdout.lines() {
        let line = match line {
            Ok(line) => line,
            Err(err) => {
                log::error!("Couldn't read line from cargo: {}", err);
                continue;
            }
        };

        let message = serde_json::from_str::<cargo_metadata::Message>(&line);
        let message = match message {
            Ok(message) => message,
            Err(err) => {
                log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
                continue;
            }
        };

        read_at_least_one_message = true;

        if !on_message(message) {
            break;
        }
    }

    // It is okay to ignore the result, as it only errors if the process is already dead
    let _ = child.kill();

    let err_msg = match child.wait() {
        Ok(exit_code) if !exit_code.success() && !read_at_least_one_message => {
            // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
            // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
            format!(
                "the command produced no valid metadata (exit code: {:?}): cargo {}",
                exit_code,
                args.join(" ")
            )
        }
        Err(err) => format!("io error: {:?}", err),
        Ok(_) => return Ok(()),
    };

    Err(CargoError(err_msg))
}

impl WatchThread {
    fn dummy() -> WatchThread {
        WatchThread { message_recv: never(), _handle: None }
    }

    fn new(options: &CheckOptions, workspace_root: &Path) -> WatchThread {
        let mut args: Vec<String> = vec![
            options.command.clone(),
            "--workspace".to_string(),
            "--message-format=json".to_string(),
            "--manifest-path".to_string(),
            format!("{}/Cargo.toml", workspace_root.display()),
        ];
        if options.all_targets {
            args.push("--all-targets".to_string());
        }
        args.extend(options.args.iter().cloned());

        let (message_send, message_recv) = unbounded();
        let workspace_root = workspace_root.to_owned();
        let handle = if options.enable {
            Some(jod_thread::spawn(move || {
                // If we trigger an error here, we will do so in the loop instead,
                // which will break out of the loop, and continue the shutdown
                let _ = message_send.send(CheckEvent::Begin);

                let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
                    // Skip certain kinds of messages to only spend time on what's useful
                    match &message {
                        Message::CompilerArtifact(artifact) if artifact.fresh => return true,
                        Message::BuildScriptExecuted(_) => return true,
                        Message::Unknown => return true,
                        _ => {}
                    }

                    // if the send channel was closed, we want to shutdown
                    message_send.send(CheckEvent::Msg(message)).is_ok()
                });

                if let Err(err) = res {
                    // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
                    // to display user-caused misconfiguration errors instead of just logging them here
                    log::error!("Cargo watcher failed {:?}", err);
                }

                // We can ignore any error here, as we are already in the progress
                // of shutting down.
                let _ = message_send.send(CheckEvent::End);
            }))
        } else {
            None
        };
        WatchThread { message_recv, _handle: handle }
    }
}
<commit_msg>Fix the endless progress bar issue<commit_after>//! cargo_check provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.
use cargo_metadata::Message;
use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
use lsp_types::{
    CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
    WorkDoneProgressEnd, WorkDoneProgressReport,
};
use std::{
    error, fmt,
    io::{BufRead, BufReader},
    path::{Path, PathBuf},
    process::{Command, Stdio},
    time::Instant,
};

mod conv;

use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};

pub use crate::conv::url_from_path_with_drive_lowercasing;

#[derive(Clone, Debug)]
pub struct CheckOptions {
    pub enable: bool,
    pub args: Vec<String>,
    pub command: String,
    pub all_targets: bool,
}

/// CheckWatcher wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct CheckWatcher {
    // XXX: drop order is significant
    cmd_send: Sender<CheckCommand>,
    handle: Option<jod_thread::JoinHandle<()>>,
    pub task_recv: Receiver<CheckTask>,
}

impl CheckWatcher {
    pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
        let options = options.clone();

        let (task_send, task_recv) = unbounded::<CheckTask>();
        let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
        let handle = jod_thread::spawn(move || {
            let mut check = CheckWatcherThread::new(options, workspace_root);
            check.run(&task_send, &cmd_recv);
        });
        CheckWatcher { task_recv, cmd_send, handle: Some(handle) }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn update(&self) {
        self.cmd_send.send(CheckCommand::Update).unwrap();
    }
}

#[derive(Debug)]
pub enum CheckTask {
    /// Request a clearing of all cached diagnostics from the check watcher
    ClearDiagnostics,

    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> },

    /// Request check progress notification to client
    Status(WorkDoneProgress),
}

pub enum CheckCommand {
    /// Request re-start of check thread
    Update,
}

struct CheckWatcherThread {
    options: CheckOptions,
    workspace_root: PathBuf,
    watcher: WatchThread,
    last_update_req: Option<Instant>,
}

impl CheckWatcherThread {
    fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherThread {
        CheckWatcherThread {
            options,
            workspace_root,
            watcher: WatchThread::dummy(),
            last_update_req: None,
        }
    }

    fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
        // If we rerun the thread, we need to discard the previous check results first
        self.clean_previous_results(task_send);

        loop {
            select! {
                recv(&cmd_recv) -> cmd => match cmd {
                    Ok(cmd) => self.handle_command(cmd),
                    Err(RecvError) => {
                        // Command channel has closed, so shut down
                        break;
                    },
                },
                recv(self.watcher.message_recv) -> msg => match msg {
                    Ok(msg) => self.handle_message(msg, task_send),
                    Err(RecvError) => {
                        // Watcher finished, replace it with a never channel to
                        // avoid busy-waiting.
                        std::mem::replace(&mut self.watcher.message_recv, never());
                    },
                }
            };

            if self.should_recheck() {
                self.last_update_req.take();
                task_send.send(CheckTask::ClearDiagnostics).unwrap();

                // Replace with a dummy watcher first so we drop the original and wait for completion
                std::mem::replace(&mut self.watcher, WatchThread::dummy());

                // Then create the actual new watcher
                self.watcher = WatchThread::new(&self.options, &self.workspace_root);
            }
        }
    }

    fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
        task_send.send(CheckTask::ClearDiagnostics).unwrap();
        task_send
            .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { message: None })))
            .unwrap();
    }

    fn should_recheck(&mut self) -> bool {
        if let Some(_last_update_req) = &self.last_update_req {
            // We currently only request an update on save, as we need up to
            // date source on disk for cargo check to do it's magic, so we
            // don't really need to debounce the requests at this point.
            return true;
        }
        false
    }

    fn handle_command(&mut self, cmd: CheckCommand) {
        match cmd {
            CheckCommand::Update => self.last_update_req = Some(Instant::now()),
        }
    }

    fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
        match msg {
            CheckEvent::Begin => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
                        title: "Running 'cargo check'".to_string(),
                        cancellable: Some(false),
                        message: None,
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::End => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
                        message: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
                task_send
                    .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
                        cancellable: Some(false),
                        message: Some(msg.target.name),
                        percentage: None,
                    })))
                    .unwrap();
            }

            CheckEvent::Msg(Message::CompilerMessage(msg)) => {
                let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root);
                if map_result.is_empty() {
                    return;
                }

                for MappedRustDiagnostic { location, diagnostic, fixes } in map_result {
                    let fixes = fixes
                        .into_iter()
                        .map(|fix| {
                            CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
                        })
                        .collect();

                    task_send
                        .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
                        .unwrap();
                }
            }

            CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
            CheckEvent::Msg(Message::Unknown) => {}
        }
    }
}

#[derive(Debug)]
pub struct DiagnosticWithFixes {
    diagnostic: Diagnostic,
    fixes: Vec<CodeAction>,
}

/// WatchThread exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
/// The correct way to dispose of the thread is to drop it, on which the
/// sub-process will be killed, and the thread will be joined.
struct WatchThread {
    // XXX: drop order is significant
    message_recv: Receiver<CheckEvent>,
    _handle: Option<jod_thread::JoinHandle<()>>,
}

enum CheckEvent {
    Begin,
    Msg(cargo_metadata::Message),
    End,
}

#[derive(Debug)]
pub struct CargoError(String);

impl fmt::Display for CargoError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Cargo failed: {}", self.0)
    }
}
impl error::Error for CargoError {}

pub fn run_cargo(
    args: &[String],
    current_dir: Option<&Path>,
    on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
) -> Result<(), CargoError> {
    let mut command = Command::new("cargo");
    if let Some(current_dir) = current_dir {
        command.current_dir(current_dir);
    }

    let mut child = command
        .args(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .spawn()
        .expect("couldn't launch cargo");

    // We manually read a line at a time, instead of using serde's
    // stream deserializers, because the deserializer cannot recover
    // from an error, resulting in it getting stuck, because we try to
    // be resillient against failures.
    //
    // Because cargo only outputs one JSON object per line, we can
    // simply skip a line if it doesn't parse, which just ignores any
    // erroneus output.
    let stdout = BufReader::new(child.stdout.take().unwrap());
    let mut read_at_least_one_message = false;

    for line in stdout.lines() {
        let line = match line {
            Ok(line) => line,
            Err(err) => {
                log::error!("Couldn't read line from cargo: {}", err);
                continue;
            }
        };

        let message = serde_json::from_str::<cargo_metadata::Message>(&line);
        let message = match message {
            Ok(message) => message,
            Err(err) => {
                log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line);
                continue;
            }
        };

        read_at_least_one_message = true;

        if !on_message(message) {
            break;
        }
    }

    // It is okay to ignore the result, as it only errors if the process is already dead
    let _ = child.kill();

    let err_msg = match child.wait() {
        Ok(exit_code) if !exit_code.success() && !read_at_least_one_message => {
            // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
            // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
            format!(
                "the command produced no valid metadata (exit code: {:?}): cargo {}",
                exit_code,
                args.join(" ")
            )
        }
        Err(err) => format!("io error: {:?}", err),
        Ok(_) => return Ok(()),
    };

    Err(CargoError(err_msg))
}

impl WatchThread {
    fn dummy() -> WatchThread {
        WatchThread { message_recv: never(), _handle: None }
    }

    fn new(options: &CheckOptions, workspace_root: &Path) -> WatchThread {
        let mut args: Vec<String> = vec![
            options.command.clone(),
            "--workspace".to_string(),
            "--message-format=json".to_string(),
            "--manifest-path".to_string(),
            format!("{}/Cargo.toml", workspace_root.display()),
        ];
        if options.all_targets {
            args.push("--all-targets".to_string());
        }
        args.extend(options.args.iter().cloned());

        let (message_send, message_recv) = unbounded();
        let workspace_root = workspace_root.to_owned();
        let handle = if options.enable {
            Some(jod_thread::spawn(move || {
                // If we trigger an error here, we will do so in the loop instead,
                // which will break out of the loop, and continue the shutdown
                let _ = message_send.send(CheckEvent::Begin);

                let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
                    // Skip certain kinds of messages to only spend time on what's useful
                    match &message {
                        Message::CompilerArtifact(artifact) if artifact.fresh => return true,
                        Message::BuildScriptExecuted(_) => return true,
                        Message::Unknown => return true,
                        _ => {}
                    }

                    // if the send channel was closed, we want to shutdown
                    message_send.send(CheckEvent::Msg(message)).is_ok()
                });

                if let Err(err) = res {
                    // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
                    // to display user-caused misconfiguration errors instead of just logging them here
                    log::error!("Cargo watcher failed {:?}", err);
                }

                // We can ignore any error here, as we are already in the progress
                // of shutting down.
                let _ = message_send.send(CheckEvent::End);
            }))
        } else {
            None
        };
        WatchThread { message_recv, _handle: handle }
    }
}
<|endoftext|>


<commit_before>//! Advertises the capabilities of the LSP Server.
use lsp_types::{
    CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
    CodeActionProviderCapability, CodeLensOptions, CompletionOptions, DeclarationCapability,
    DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern,
    FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability,
    HoverProviderCapability, ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
    SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
    SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
    TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
    WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities,
    WorkspaceServerCapabilities,
};
use serde_json::json;

use crate::config::{Config, RustfmtConfig};
use crate::semantic_tokens;

pub fn server_capabilities(config: &Config) -> ServerCapabilities {
    ServerCapabilities {
        text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
            open_close: Some(true),
            change: Some(TextDocumentSyncKind::INCREMENTAL),
            will_save: None,
            will_save_wait_until: None,
            save: Some(SaveOptions::default().into()),
        })),
        hover_provider: Some(HoverProviderCapability::Simple(true)),
        completion_provider: Some(CompletionOptions {
            resolve_provider: completions_resolve_provider(&config.caps),
            trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]),
            all_commit_characters: None,
            completion_item: None,
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        signature_help_provider: Some(SignatureHelpOptions {
            trigger_characters: Some(vec!["(".to_string(), ",".to_string()]),
            retrigger_characters: None,
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        declaration_provider: Some(DeclarationCapability::Simple(true)),
        definition_provider: Some(OneOf::Left(true)),
        type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
        implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
        references_provider: Some(OneOf::Left(true)),
        document_highlight_provider: Some(OneOf::Left(true)),
        document_symbol_provider: Some(OneOf::Left(true)),
        workspace_symbol_provider: Some(OneOf::Left(true)),
        code_action_provider: Some(code_action_capabilities(&config.caps)),
        code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
        document_formatting_provider: Some(OneOf::Left(true)),
        document_range_formatting_provider: match config.rustfmt() {
            RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
            _ => Some(OneOf::Left(false)),
        },
        document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
            first_trigger_character: "=".to_string(),
            more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]),
        }),
        selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
        folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
        rename_provider: Some(OneOf::Right(RenameOptions {
            prepare_provider: Some(true),
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        })),
        linked_editing_range_provider: None,
        document_link_provider: None,
        color_provider: None,
        execute_command_provider: None,
        workspace: Some(WorkspaceServerCapabilities {
            workspace_folders: None,
            file_operations: Some(WorkspaceFileOperationsServerCapabilities {
                did_create: None,
                will_create: None,
                did_rename: None,
                will_rename: Some(FileOperationRegistrationOptions {
                    filters: vec![
                        FileOperationFilter {
                            scheme: Some(String::from("file")),
                            pattern: FileOperationPattern {
                                glob: String::from("**/*.rs"),
                                matches: Some(FileOperationPatternKind::File),
                                options: None,
                            },
                        },
                        FileOperationFilter {
                            scheme: Some(String::from("file")),
                            pattern: FileOperationPattern {
                                glob: String::from("**"),
                                matches: Some(FileOperationPatternKind::Folder),
                                options: None,
                            },
                        },
                    ],
                }),
                did_delete: None,
                will_delete: None,
            }),
        }),
        call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
        semantic_tokens_provider: Some(
            SemanticTokensOptions {
                legend: SemanticTokensLegend {
                    token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(),
                    token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(),
                },

                full: Some(SemanticTokensFullOptions::Delta { delta: Some(true) }),
                range: Some(true),
                work_done_progress_options: Default::default(),
            }
            .into(),
        ),
        moniker_provider: None,
        experimental: Some(json!({
            "joinLines": true,
            "openCargoToml": true,
            "ssr": true,
            "onEnter": true,
            "parentModule": true,
            "hoverRange": true,
            "runnables": {
                "kinds": [ "cargo" ],
            },
            "workspaceSymbolScopeKindFiltering": true,
        })),
    }
}

fn completions_resolve_provider(client_caps: &ClientCapabilities) -> Option<bool> {
    if completion_item_edit_resolve(client_caps) {
        Some(true)
    } else {
        tracing::info!("No `additionalTextEdits` completion resolve capability was found in the client capabilities, autoimport completion is disabled");
        None
    }
}

/// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports.
pub(crate) fn completion_item_edit_resolve(caps: &ClientCapabilities) -> bool {
    (|| {
        Some(
            caps.text_document
                .as_ref()?
                .completion
                .as_ref()?
                .completion_item
                .as_ref()?
                .resolve_support
                .as_ref()?
                .properties
                .iter()
                .any(|cap_string| cap_string.as_str() == "additionalTextEdits"),
        )
    })() == Some(true)
}

fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability {
    client_caps
        .text_document
        .as_ref()
        .and_then(|it| it.code_action.as_ref())
        .and_then(|it| it.code_action_literal_support.as_ref())
        .map_or(CodeActionProviderCapability::Simple(true), |_| {
            CodeActionProviderCapability::Options(CodeActionOptions {
                // Advertise support for all built-in CodeActionKinds.
                // Ideally we would base this off of the client capabilities
                // but the client is supposed to fall back gracefully for unknown values.
                code_action_kinds: Some(vec![
                    CodeActionKind::EMPTY,
                    CodeActionKind::QUICKFIX,
                    CodeActionKind::REFACTOR,
                    CodeActionKind::REFACTOR_EXTRACT,
                    CodeActionKind::REFACTOR_INLINE,
                    CodeActionKind::REFACTOR_REWRITE,
                ]),
                resolve_provider: Some(true),
                work_done_progress_options: Default::default(),
            })
        })
}
<commit_msg>fix: add missing experimental capabilities<commit_after>//! Advertises the capabilities of the LSP Server.
use lsp_types::{
    CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
    CodeActionProviderCapability, CodeLensOptions, CompletionOptions, DeclarationCapability,
    DocumentOnTypeFormattingOptions, FileOperationFilter, FileOperationPattern,
    FileOperationPatternKind, FileOperationRegistrationOptions, FoldingRangeProviderCapability,
    HoverProviderCapability, ImplementationProviderCapability, OneOf, RenameOptions, SaveOptions,
    SelectionRangeProviderCapability, SemanticTokensFullOptions, SemanticTokensLegend,
    SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability,
    TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability,
    WorkDoneProgressOptions, WorkspaceFileOperationsServerCapabilities,
    WorkspaceServerCapabilities,
};
use serde_json::json;

use crate::config::{Config, RustfmtConfig};
use crate::semantic_tokens;

pub fn server_capabilities(config: &Config) -> ServerCapabilities {
    ServerCapabilities {
        text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
            open_close: Some(true),
            change: Some(TextDocumentSyncKind::INCREMENTAL),
            will_save: None,
            will_save_wait_until: None,
            save: Some(SaveOptions::default().into()),
        })),
        hover_provider: Some(HoverProviderCapability::Simple(true)),
        completion_provider: Some(CompletionOptions {
            resolve_provider: completions_resolve_provider(&config.caps),
            trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]),
            all_commit_characters: None,
            completion_item: None,
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        signature_help_provider: Some(SignatureHelpOptions {
            trigger_characters: Some(vec!["(".to_string(), ",".to_string()]),
            retrigger_characters: None,
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        }),
        declaration_provider: Some(DeclarationCapability::Simple(true)),
        definition_provider: Some(OneOf::Left(true)),
        type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
        implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
        references_provider: Some(OneOf::Left(true)),
        document_highlight_provider: Some(OneOf::Left(true)),
        document_symbol_provider: Some(OneOf::Left(true)),
        workspace_symbol_provider: Some(OneOf::Left(true)),
        code_action_provider: Some(code_action_capabilities(&config.caps)),
        code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
        document_formatting_provider: Some(OneOf::Left(true)),
        document_range_formatting_provider: match config.rustfmt() {
            RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
            _ => Some(OneOf::Left(false)),
        },
        document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
            first_trigger_character: "=".to_string(),
            more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]),
        }),
        selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
        folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
        rename_provider: Some(OneOf::Right(RenameOptions {
            prepare_provider: Some(true),
            work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
        })),
        linked_editing_range_provider: None,
        document_link_provider: None,
        color_provider: None,
        execute_command_provider: None,
        workspace: Some(WorkspaceServerCapabilities {
            workspace_folders: None,
            file_operations: Some(WorkspaceFileOperationsServerCapabilities {
                did_create: None,
                will_create: None,
                did_rename: None,
                will_rename: Some(FileOperationRegistrationOptions {
                    filters: vec![
                        FileOperationFilter {
                            scheme: Some(String::from("file")),
                            pattern: FileOperationPattern {
                                glob: String::from("**/*.rs"),
                                matches: Some(FileOperationPatternKind::File),
                                options: None,
                            },
                        },
                        FileOperationFilter {
                            scheme: Some(String::from("file")),
                            pattern: FileOperationPattern {
                                glob: String::from("**"),
                                matches: Some(FileOperationPatternKind::Folder),
                                options: None,
                            },
                        },
                    ],
                }),
                did_delete: None,
                will_delete: None,
            }),
        }),
        call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
        semantic_tokens_provider: Some(
            SemanticTokensOptions {
                legend: SemanticTokensLegend {
                    token_types: semantic_tokens::SUPPORTED_TYPES.to_vec(),
                    token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(),
                },

                full: Some(SemanticTokensFullOptions::Delta { delta: Some(true) }),
                range: Some(true),
                work_done_progress_options: Default::default(),
            }
            .into(),
        ),
        moniker_provider: None,
        experimental: Some(json!({
            "externalDocs": true,
            "hoverRange": true,
            "joinLines": true,
            "matchingBrace": true,
            "moveItems": true,
            "onEnter": true,
            "openCargoToml": true,
            "parentModule": true,
            "runnables": {
                "kinds": [ "cargo" ],
            },
            "ssr": true,
            "workspaceSymbolScopeKindFiltering": true,
        })),
    }
}

fn completions_resolve_provider(client_caps: &ClientCapabilities) -> Option<bool> {
    if completion_item_edit_resolve(client_caps) {
        Some(true)
    } else {
        tracing::info!("No `additionalTextEdits` completion resolve capability was found in the client capabilities, autoimport completion is disabled");
        None
    }
}

/// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports.
pub(crate) fn completion_item_edit_resolve(caps: &ClientCapabilities) -> bool {
    (|| {
        Some(
            caps.text_document
                .as_ref()?
                .completion
                .as_ref()?
                .completion_item
                .as_ref()?
                .resolve_support
                .as_ref()?
                .properties
                .iter()
                .any(|cap_string| cap_string.as_str() == "additionalTextEdits"),
        )
    })() == Some(true)
}

fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProviderCapability {
    client_caps
        .text_document
        .as_ref()
        .and_then(|it| it.code_action.as_ref())
        .and_then(|it| it.code_action_literal_support.as_ref())
        .map_or(CodeActionProviderCapability::Simple(true), |_| {
            CodeActionProviderCapability::Options(CodeActionOptions {
                // Advertise support for all built-in CodeActionKinds.
                // Ideally we would base this off of the client capabilities
                // but the client is supposed to fall back gracefully for unknown values.
                code_action_kinds: Some(vec![
                    CodeActionKind::EMPTY,
                    CodeActionKind::QUICKFIX,
                    CodeActionKind::REFACTOR,
                    CodeActionKind::REFACTOR_EXTRACT,
                    CodeActionKind::REFACTOR_INLINE,
                    CodeActionKind::REFACTOR_REWRITE,
                ]),
                resolve_provider: Some(true),
                work_done_progress_options: Default::default(),
            })
        })
}
<|endoftext|>


<commit_before>use moqt_core::basic_class::UpdateType;
use moqt_core::{BasicClass, BasicClassField};
use std::ops::Deref;

#[test]
fn basic_class() {
    unsafe {
        let mut v = BasicClass::new(1);
        assert_eq!(v.foo(), 1);
        v.set_foo(5);
        assert_eq!(v.foo(), 5);

        assert_eq!(v.int_field(), 1);
        v.set_int_field(3);
        assert_eq!(v.int_field(), 3);

        assert!(v.int_pointer_field().is_null());
        let mut p = v.int_reference_field();
        v.set_int_pointer_field(&mut p);
        v.set_int_field(4);
        assert_eq!(*v.int_pointer_field(), 4);

        assert_eq!(*v.int_reference_field(), 4);
        v.set_int_field(7);
        assert_eq!(*v.int_reference_field(), 7);
        *v.int_reference_field() = 8;
        assert_eq!(v.int_field(), 8);

        // TODO: set_int_reference_field should have int arg
        v.set_int_reference_field(&mut 9);
        assert_eq!(v.int_field(), 9);

        assert_eq!(v.class_field().get(), 42);
        assert_eq!(v.class_field_mut().set(43), 42);
        assert_eq!(v.class_field().get(), 43);

        let c = BasicClassField::new();
        v.set_class_field(&c);
        assert_eq!(v.class_field().get(), 42);
        drop(c);
        assert_eq!(v.class_field().get(), 42);
    }
}

#[test]
fn nested_enum() {
    let x: UpdateType = UpdateType::Add2;
    assert_eq!(x.to_int(), 1);
    assert_eq!(UpdateType::Mul3.to_int(), 2);
    assert_eq!(UpdateType::Div5.to_int(), 4);

    unsafe {
        let mut v = BasicClass::new(1);
        v.set_foo(1);
        v.update_foo(UpdateType::Mul3.into());
        assert_eq!(v.foo(), 3);
        v.update_foo(UpdateType::Mul3 | UpdateType::Div5);
        assert_eq!(v.foo(), 1);
    }
}

#[test]
fn vector_getters() {
    unsafe {
        let v = BasicClass::new(2);
        let mut vec = v.get_vector_int();
        assert_eq!(vec.count(), 3);
        assert_eq!(*vec.at(0), 1);
        assert_eq!(*vec.at(1), 3);
        assert_eq!(*vec.at(2), 5);

        let mut vec2 = v.get_vector_class();
        assert_eq!(vec2.count(), 3);
        assert_eq!(vec2.at(0).get(), 2);
        assert_eq!(vec2.at(1).get(), 4);
        assert_eq!(vec2.at(2).get(), 6);
    }
}
<commit_msg>Fix moqt_core test: remove Deref<commit_after>use moqt_core::basic_class::UpdateType;
use moqt_core::{BasicClass, BasicClassField};

#[test]
fn basic_class() {
    unsafe {
        let mut v = BasicClass::new(1);
        assert_eq!(v.foo(), 1);
        v.set_foo(5);
        assert_eq!(v.foo(), 5);

        assert_eq!(v.int_field(), 1);
        v.set_int_field(3);
        assert_eq!(v.int_field(), 3);

        assert!(v.int_pointer_field().is_null());
        let mut p = v.int_reference_field();
        v.set_int_pointer_field(&mut p);
        v.set_int_field(4);
        assert_eq!(*v.int_pointer_field(), 4);

        assert_eq!(*v.int_reference_field(), 4);
        v.set_int_field(7);
        assert_eq!(*v.int_reference_field(), 7);
        *v.int_reference_field() = 8;
        assert_eq!(v.int_field(), 8);

        // TODO: set_int_reference_field should have int arg
        v.set_int_reference_field(&mut 9);
        assert_eq!(v.int_field(), 9);

        assert_eq!(v.class_field().get(), 42);
        assert_eq!(v.class_field_mut().set(43), 42);
        assert_eq!(v.class_field().get(), 43);

        let c = BasicClassField::new();
        v.set_class_field(&c);
        assert_eq!(v.class_field().get(), 42);
        drop(c);
        assert_eq!(v.class_field().get(), 42);
    }
}

#[test]
fn nested_enum() {
    let x: UpdateType = UpdateType::Add2;
    assert_eq!(x.to_int(), 1);
    assert_eq!(UpdateType::Mul3.to_int(), 2);
    assert_eq!(UpdateType::Div5.to_int(), 4);

    unsafe {
        let mut v = BasicClass::new(1);
        v.set_foo(1);
        v.update_foo(UpdateType::Mul3.into());
        assert_eq!(v.foo(), 3);
        v.update_foo(UpdateType::Mul3 | UpdateType::Div5);
        assert_eq!(v.foo(), 1);
    }
}

#[test]
fn vector_getters() {
    unsafe {
        let v = BasicClass::new(2);
        let mut vec = v.get_vector_int();
        assert_eq!(vec.count(), 3);
        assert_eq!(*vec.at(0), 1);
        assert_eq!(*vec.at(1), 3);
        assert_eq!(*vec.at(2), 5);

        let mut vec2 = v.get_vector_class();
        assert_eq!(vec2.count(), 3);
        assert_eq!(vec2.at(0).get(), 2);
        assert_eq!(vec2.at(1).get(), 4);
        assert_eq!(vec2.at(2).get(), 6);
    }
}
<|endoftext|>


<commit_before>extern crate robin_core;

#[macro_use]
extern crate pest;

#[cfg(test)]
mod parser_tests {
    use robin_core::parser::{ExpressionParser, Rule};

    #[test]
    fn parse_a_left_brace() {
        parses_to!(
            parser: ExpressionParser,
            input: "{",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_a_right_brace() {
        parses_to!(
            parser: ExpressionParser,
            input: "}",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_a_left_bracket() {
        parses_to!(
            parser: ExpressionParser,
            input: "(",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_a_right_bracket() {
        parses_to!(
            parser: ExpressionParser,
            input: ")",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_a_left_square_bracket() {
        parses_to!(
            parser: ExpressionParser,
            input: "[",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_a_right_square_bracket() {
        parses_to!(
            parser: ExpressionParser,
            input: "]",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_a_fullstop() {
        parses_to!(
            parser: ExpressionParser,
            input: ".",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_a_semicolon() {
        parses_to!(
            parser: ExpressionParser,
            input: ";",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_a_comma() {
        parses_to!(
            parser: ExpressionParser,
            input: ",",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_less_than() {
        parses_to!(
            parser: ExpressionParser,
            input: "<",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_greater_than() {
        parses_to!(
            parser: ExpressionParser,
            input: ">",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_equals() {
        parses_to!(
            parser: ExpressionParser,
            input: "=",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_exclamation() {
        parses_to!(
            parser: ExpressionParser,
            input: "=",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_plus() {
        parses_to!(
            parser: ExpressionParser,
            input: "+",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_minus() {
        parses_to!(
            parser: ExpressionParser,
            input: "-",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_times() {
        parses_to!(
            parser: ExpressionParser,
            input: "*",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_divide() {
        parses_to!(
            parser: ExpressionParser,
            input: "/",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_ampersand() {
        parses_to!(
            parser: ExpressionParser,
            input: "&",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_pipe() {
        parses_to!(
            parser: ExpressionParser,
            input: "|",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_power_of() {
        parses_to!(
            parser: ExpressionParser,
            input: "^",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_tilde() {
        parses_to!(
            parser: ExpressionParser,
            input: "~",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_question_mark() {
        parses_to!(
            parser: ExpressionParser,
            input: "?",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_colon() {
        parses_to!(
            parser: ExpressionParser,
            input: ":",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_multiple_symbols() {
        parses_to!(
            parser: ExpressionParser,
            input: "?+=",
            rule: Rule::symbol_literal,
            tokens: [
                symbol_literal(0, 3)
            ]
        )
    }

    #[test]
    fn parse_a_single_identifier() {
        parses_to!(
            parser: ExpressionParser,
            input: "g",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_multiple_identifiers() {
        parses_to!(
            parser: ExpressionParser,
            input: "gbd",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 3)
            ]
        )
    }
}<commit_msg>Remove old tests and update new ones<commit_after>extern crate robin_core;

#[macro_use]
extern crate pest;

#[cfg(test)]
mod parser_tests {
    use robin_core::parser::{ExpressionParser, Rule};

    #[test]
    fn parse_a_fullstop() {
        parses_to!(
            parser: ExpressionParser,
            input: ".",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_a_semicolon() {
        parses_to!(
            parser: ExpressionParser,
            input: ";",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_a_comma() {
        parses_to!(
            parser: ExpressionParser,
            input: ",",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_less_than() {
        parses_to!(
            parser: ExpressionParser,
            input: "<",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_greater_than() {
        parses_to!(
            parser: ExpressionParser,
            input: ">",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_equals() {
        parses_to!(
            parser: ExpressionParser,
            input: "=",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_exclamation() {
        parses_to!(
            parser: ExpressionParser,
            input: "!",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_plus() {
        parses_to!(
            parser: ExpressionParser,
            input: "+",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_minus() {
        parses_to!(
            parser: ExpressionParser,
            input: "-",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_times() {
        parses_to!(
            parser: ExpressionParser,
            input: "*",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_divide() {
        parses_to!(
            parser: ExpressionParser,
            input: "/",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_ampersand() {
        parses_to!(
            parser: ExpressionParser,
            input: "&",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_pipe() {
        parses_to!(
            parser: ExpressionParser,
            input: "|",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_power_of() {
        parses_to!(
            parser: ExpressionParser,
            input: "^",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_tilde() {
        parses_to!(
            parser: ExpressionParser,
            input: "~",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_question_mark() {
        parses_to!(
            parser: ExpressionParser,
            input: "?",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_colon() {
        parses_to!(
            parser: ExpressionParser,
            input: ":",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_multiple_symbols() {
        parses_to!(
            parser: ExpressionParser,
            input: "?+=",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 3)
            ]
        )
    }

    #[test]
    fn parse_a_single_identifier() {
        parses_to!(
            parser: ExpressionParser,
            input: "g",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 1)
            ]
        )
    }

    #[test]
    fn parse_multiple_identifiers() {
        parses_to!(
            parser: ExpressionParser,
            input: "gbd",
            rule: Rule::identifier_literal,
            tokens: [
                identifier_literal(0, 3)
            ]
        )
    }
}<|endoftext|>


<commit_before>extern crate robin_core;

extern crate nom;

#[cfg(test)]
mod parser_tests {
    use nom::IResult;

    use robin_core::parser::boolean;

    #[test]
    fn parse_a_true_literal() {
        assert_eq!(boolean::boolean_literal(b"true"),
                   IResult::Done(&b""[..], true));
    }

    #[test]
    fn parse_a_false_literal() {
        assert_eq!(boolean::boolean_literal(b"false"),
                   IResult::Done(&b""[..], false));
    }
}<commit_msg>Update boolean tests<commit_after>extern crate robin_core;

#[macro_use]
extern crate pest;

#[cfg(test)]
mod parser_tests {
    use robin_core::parser::{ExpressionParser, Rule};

    #[test]
    fn parse_a_true_literal() {
        parses_to!(
            parser: ExpressionParser,
            input: "true",
            rule: Rule::boolean_literal,
            tokens: [
                boolean_literal(0, 4)
            ]
        )
    }

    #[test]
    fn parse_a_false_literal() {
        parses_to!(
            parser: ExpressionParser,
            input: "false",
            rule: Rule::boolean_literal,
            tokens: [
                boolean_literal(0, 5)
            ]
        )
    }
}
<|endoftext|>