summaryrefslogtreecommitdiff
path: root/support/texlab/src/diagnostics/debouncer.rs
blob: 4c836368568d1357b40ebd69ed3d5311cc9f9c31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use std::{
    sync::Arc,
    thread::{self, JoinHandle},
    time::{Duration, Instant},
};

use crossbeam_channel::Sender;
use dashmap::DashMap;
use lsp_types::Url;

use crate::{Document, Workspace};

pub enum DiagnosticsMessage {
    Analyze {
        workspace: Workspace,
        document: Document,
    },
    Shutdown,
}

pub struct DiagnosticsDebouncer {
    pub sender: Sender<DiagnosticsMessage>,
    handle: Option<JoinHandle<()>>,
}

impl DiagnosticsDebouncer {
    pub fn launch<A>(action: A) -> Self
    where
        A: Fn(Workspace, Document) + Send + Clone + 'static,
    {
        let (sender, receiver) = crossbeam_channel::unbounded();

        let handle = thread::spawn(move || {
            let pool = threadpool::Builder::new().build();
            let last_task_time_by_uri: Arc<DashMap<Arc<Url>, Instant>> = Arc::default();
            while let Ok(DiagnosticsMessage::Analyze {
                workspace,
                document,
            }) = receiver.recv()
            {
                let delay = workspace
                    .environment
                    .options
                    .diagnostics_delay
                    .unwrap_or(300);

                if let Some(time) = last_task_time_by_uri.get(&document.uri) {
                    if time.elapsed().as_millis() < u128::from(delay) {
                        continue;
                    }
                }

                let last_task_time_by_uri = Arc::clone(&last_task_time_by_uri);
                let action = action.clone();
                pool.execute(move || {
                    thread::sleep(Duration::from_millis(delay));
                    last_task_time_by_uri.insert(Arc::clone(&document.uri), Instant::now());
                    action(workspace, document);
                });
            }
        });

        Self {
            sender,
            handle: Some(handle),
        }
    }
}

impl Drop for DiagnosticsDebouncer {
    fn drop(&mut self) {
        self.sender.send(DiagnosticsMessage::Shutdown).unwrap();
        self.handle.take().unwrap().join().unwrap();
    }
}