Skip to content

Commit 251f488

Browse files
committed
Track analysis progress with notifications
1 parent a9bd5c8 commit 251f488

File tree

10 files changed

+213
-13
lines changed

10 files changed

+213
-13
lines changed

src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,10 @@ impl Config {
9797
response.pop_front().as_ref().and_then(Value::as_bool).unwrap_or_default();
9898
state.config.enable_proc_macros =
9999
response.pop_front().as_ref().and_then(Value::as_bool).unwrap_or(false);
100-
101100
debug!("reloaded configuration: {:#?}", state.config);
102101

103102
state.proc_macro_controller.on_config_change(&mut state.db, &state.config);
103+
state.analysis_progress_controller.on_config_change(&state.config);
104104
})
105105
};
106106

File renamed without changes.

src/ide/analysis_progress.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use std::sync::atomic::{AtomicBool, Ordering};
2+
use std::sync::{Arc, Mutex};
3+
4+
use crate::config::Config;
5+
use crate::lsp::ext::{DiagnosticsCalculationFinish, DiagnosticsCalculationStart};
6+
use crate::server::client::Notifier;
7+
8+
/// A struct that allows to track procmacro requests.
9+
#[derive(Clone)]
10+
pub struct ProcMacroRequestTracker {
11+
procmacro_request_submitted: Arc<AtomicBool>,
12+
}
13+
14+
impl ProcMacroRequestTracker {
15+
pub fn new() -> Self {
16+
Self { procmacro_request_submitted: Arc::new(AtomicBool::new(false)) }
17+
}
18+
19+
/// Signals that a request to proc macro server was made during the current generation of
20+
/// diagnostics.
21+
pub fn register_procmacro_request(&self) {
22+
self.procmacro_request_submitted.store(true, Ordering::SeqCst);
23+
}
24+
25+
pub fn reset(&self) {
26+
self.procmacro_request_submitted.store(false, Ordering::SeqCst);
27+
}
28+
29+
pub fn get_did_submit_procmacro_request(&self) -> bool {
30+
self.procmacro_request_submitted.load(Ordering::SeqCst)
31+
}
32+
}
33+
34+
#[derive(Clone)]
35+
pub(crate) struct AnalysisProgressController {
36+
state: Arc<Mutex<AnalysisProgressControllerState>>,
37+
request_tracker: ProcMacroRequestTracker,
38+
}
39+
40+
impl AnalysisProgressController {
41+
pub fn on_config_change(&self, config: &Config) {
42+
self.state.lock().unwrap().on_config_change(config)
43+
}
44+
45+
pub fn try_start_analysis(&self) {
46+
self.request_tracker.reset();
47+
self.state.lock().unwrap().try_start_analysis()
48+
}
49+
50+
pub fn try_stop_analysis(&self) {
51+
self.state
52+
.lock()
53+
.unwrap()
54+
.try_stop_analysis(self.request_tracker.get_did_submit_procmacro_request());
55+
}
56+
}
57+
58+
impl AnalysisProgressController {
59+
pub fn new(notifier: Notifier, request_tracker: ProcMacroRequestTracker) -> Self {
60+
Self {
61+
request_tracker,
62+
state: Arc::new(Mutex::new(AnalysisProgressControllerState::new(notifier))),
63+
}
64+
}
65+
}
66+
67+
/// Controller used to send notifications to the client about analysis progress.
68+
/// Uses information provided from other controllers (diagnostics controller, procmacro controller)
69+
/// to assess if diagnostics are in fact calculated.
70+
#[derive(Clone)]
71+
pub struct AnalysisProgressControllerState {
72+
notifier: Notifier,
73+
/// Indicates that a notification was sent and analysis (i.e. macro expansion) is taking place.
74+
analysis_in_progress: bool,
75+
/// Loaded asynchronously from config
76+
procmacros_enabled: Option<bool>,
77+
}
78+
79+
impl AnalysisProgressControllerState {
80+
pub fn new(notifier: Notifier) -> Self {
81+
Self { notifier, analysis_in_progress: false, procmacros_enabled: None }
82+
}
83+
84+
pub fn on_config_change(&mut self, config: &Config) {
85+
self.procmacros_enabled = Some(config.enable_proc_macros);
86+
}
87+
88+
fn try_start_analysis(&mut self) {
89+
if !self.analysis_in_progress {
90+
self.analysis_in_progress = true;
91+
self.notifier.notify::<DiagnosticsCalculationStart>(());
92+
}
93+
}
94+
95+
fn try_stop_analysis(&mut self, did_submit_procmacro_request: bool) {
96+
let config_not_loaded = self.procmacros_enabled.is_none();
97+
if (!did_submit_procmacro_request
98+
|| config_not_loaded
99+
|| (self.procmacros_enabled == Some(false)))
100+
&& self.analysis_in_progress
101+
{
102+
self.analysis_in_progress = false;
103+
self.notifier.notify::<DiagnosticsCalculationFinish>(());
104+
}
105+
}
106+
}

src/ide/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod analysis_progress;
12
pub mod code_actions;
23
pub mod completion;
34
pub mod formatter;

src/lang/diagnostics/mod.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use tracing::{error, trace};
1010

1111
use self::project_diagnostics::ProjectDiagnostics;
1212
use self::refresh::{clear_old_diagnostics, refresh_diagnostics};
13+
use self::trigger::trigger;
14+
use crate::ide::analysis_progress::AnalysisProgressController;
1315
use crate::lang::diagnostics::file_batches::{batches, find_primary_files, find_secondary_files};
1416
use crate::lang::lsp::LsProtoGroup;
1517
use crate::server::client::Notifier;
@@ -41,9 +43,10 @@ pub struct DiagnosticsController {
4143

4244
impl DiagnosticsController {
4345
/// Creates a new diagnostics controller.
44-
pub fn new(notifier: Notifier) -> Self {
46+
pub fn new(notifier: Notifier, analysis_progress_tracker: AnalysisProgressController) -> Self {
4547
let (trigger, receiver) = trigger::trigger();
46-
let (thread, parallelism) = DiagnosticsControllerThread::spawn(receiver, notifier);
48+
let (thread, parallelism) =
49+
DiagnosticsControllerThread::spawn(receiver, notifier, analysis_progress_tracker);
4750
Self {
4851
trigger,
4952
_thread: thread,
@@ -53,8 +56,7 @@ impl DiagnosticsController {
5356

5457
/// Schedules diagnostics refreshing on snapshot(s) of the current state.
5558
pub fn refresh(&self, state: &State) {
56-
let state_snapshots = StateSnapshots::new(state, &self.state_snapshots_props);
57-
self.trigger.activate(state_snapshots);
59+
self.trigger.activate(StateSnapshots::new(state, &self.state_snapshots_props));
5860
}
5961
}
6062

@@ -64,6 +66,7 @@ struct DiagnosticsControllerThread {
6466
notifier: Notifier,
6567
pool: thread::Pool,
6668
project_diagnostics: ProjectDiagnostics,
69+
analysis_progress_controller: AnalysisProgressController,
6770
worker_handles: Vec<TaskHandle>,
6871
}
6972

@@ -73,10 +76,12 @@ impl DiagnosticsControllerThread {
7376
fn spawn(
7477
receiver: trigger::Receiver<StateSnapshots>,
7578
notifier: Notifier,
79+
analysis_progress_controller: AnalysisProgressController,
7680
) -> (JoinHandle, NonZero<usize>) {
7781
let mut this = Self {
7882
receiver,
7983
notifier,
84+
analysis_progress_controller,
8085
pool: thread::Pool::new(),
8186
project_diagnostics: ProjectDiagnostics::new(),
8287
worker_handles: Vec::new(),
@@ -96,6 +101,8 @@ impl DiagnosticsControllerThread {
96101
fn event_loop(&mut self) {
97102
while let Some(state_snapshots) = self.receiver.wait() {
98103
assert!(self.worker_handles.is_empty());
104+
self.analysis_progress_controller.try_start_analysis();
105+
99106
if let Err(err) = catch_unwind(AssertUnwindSafe(|| {
100107
self.diagnostics_controller_tick(state_snapshots);
101108
})) {
@@ -108,6 +115,7 @@ impl DiagnosticsControllerThread {
108115
}
109116

110117
self.join_and_clear_workers();
118+
self.analysis_progress_controller.try_stop_analysis();
111119
}
112120
}
113121

src/lang/proc_macros/client/mod.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::collections::{HashMap, VecDeque};
2+
use std::fmt::{Debug, Formatter};
23
use std::sync::{Mutex, MutexGuard};
34

45
use anyhow::{Context, Result, anyhow, ensure};
@@ -16,8 +17,10 @@ use scarb_proc_macro_server_types::methods::expand::{
1617
pub use status::ClientStatus;
1718
use tracing::error;
1819

20+
use crate::id_generator;
21+
use crate::ide::analysis_progress::ProcMacroRequestTracker;
22+
1923
pub mod connection;
20-
mod id_generator;
2124
pub mod status;
2225

2326
#[derive(Debug)]
@@ -27,21 +30,26 @@ pub enum RequestParams {
2730
Inline(ExpandInlineMacroParams),
2831
}
2932

30-
#[derive(Debug)]
3133
pub struct ProcMacroClient {
3234
connection: ProcMacroServerConnection,
3335
id_generator: id_generator::IdGenerator,
3436
requests_params: Mutex<HashMap<RequestId, RequestParams>>,
3537
error_channel: Sender<()>,
38+
proc_macro_request_tracker: ProcMacroRequestTracker,
3639
}
3740

3841
impl ProcMacroClient {
39-
pub fn new(connection: ProcMacroServerConnection, error_channel: Sender<()>) -> Self {
42+
pub fn new(
43+
connection: ProcMacroServerConnection,
44+
error_channel: Sender<()>,
45+
proc_macro_request_tracker: ProcMacroRequestTracker,
46+
) -> Self {
4047
Self {
4148
connection,
4249
id_generator: Default::default(),
4350
requests_params: Default::default(),
4451
error_channel,
52+
proc_macro_request_tracker,
4553
}
4654
}
4755

@@ -146,6 +154,7 @@ impl ProcMacroClient {
146154

147155
match self.send_request_untracked::<M>(id, &params) {
148156
Ok(()) => {
157+
self.proc_macro_request_tracker.register_procmacro_request();
149158
requests_params.insert(id, map(params));
150159
}
151160
Err(err) => {
@@ -162,6 +171,17 @@ impl ProcMacroClient {
162171
}
163172
}
164173

174+
impl Debug for ProcMacroClient {
175+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
176+
f.debug_struct("ProcMacroClient")
177+
.field("connection", &self.connection)
178+
.field("id_generator", &self.id_generator)
179+
.field("requests_params", &self.requests_params)
180+
.field("error_channel", &self.error_channel)
181+
.finish_non_exhaustive()
182+
}
183+
}
184+
165185
pub struct Responses<'a> {
166186
responses: MutexGuard<'a, VecDeque<RpcResponse>>,
167187
requests: MutexGuard<'a, HashMap<RequestId, RequestParams>>,

src/lang/proc_macros/controller.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use super::client::connection::ProcMacroServerConnection;
1919
use super::client::status::ClientStatus;
2020
use super::client::{ProcMacroClient, RequestParams};
2121
use crate::config::Config;
22+
use crate::ide::analysis_progress::ProcMacroRequestTracker;
2223
use crate::lang::db::AnalysisDatabase;
2324
use crate::lang::proc_macros::db::ProcMacroGroup;
2425
use crate::lang::proc_macros::plugins::proc_macro_plugin_suite;
@@ -53,17 +54,23 @@ pub struct ProcMacroClientController {
5354
plugin_suite: Option<PluginSuite>,
5455
initialization_retries: RateLimiter<NotKeyed, InMemoryState, QuantaClock>,
5556
channels: ProcMacroChannels,
57+
proc_macro_request_tracker: ProcMacroRequestTracker,
5658
}
5759

5860
impl ProcMacroClientController {
5961
pub fn channels(&mut self) -> ProcMacroChannels {
6062
self.channels.clone()
6163
}
6264

63-
pub fn new(scarb: ScarbToolchain, notifier: Notifier) -> Self {
65+
pub fn new(
66+
scarb: ScarbToolchain,
67+
notifier: Notifier,
68+
proc_macro_request_tracker: ProcMacroRequestTracker,
69+
) -> Self {
6470
Self {
6571
scarb,
6672
notifier,
73+
proc_macro_request_tracker,
6774
plugin_suite: Default::default(),
6875
initialization_retries: RateLimiter::direct(
6976
Quota::with_period(Duration::from_secs(
@@ -176,10 +183,10 @@ impl ProcMacroClientController {
176183
self.channels.response_sender.clone(),
177184
),
178185
self.channels.error_sender.clone(),
186+
self.proc_macro_request_tracker.clone(),
179187
);
180188

181189
client.start_initialize();
182-
183190
db.set_proc_macro_client_status(ClientStatus::Starting(Arc::new(client)));
184191
}
185192
Err(err) => {

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ use crate::state::State;
7171

7272
mod config;
7373
mod env_config;
74+
mod id_generator;
7475
mod ide;
7576
mod lang;
7677
pub mod lsp;

src/lsp/ext.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,45 @@ pub mod testing {
8888
const METHOD: &'static str = "cairo/projectUpdatingFinished";
8989
}
9090
}
91+
92+
#[derive(Debug)]
93+
pub struct ScarbPathMissing {}
94+
95+
impl Notification for ScarbPathMissing {
96+
type Params = ();
97+
const METHOD: &'static str = "scarb/could-not-find-scarb-executable";
98+
}
99+
100+
#[derive(Debug)]
101+
pub struct ScarbResolvingStart {}
102+
103+
impl Notification for ScarbResolvingStart {
104+
type Params = ();
105+
const METHOD: &'static str = "scarb/resolving-start";
106+
}
107+
108+
#[derive(Debug)]
109+
pub struct ScarbResolvingFinish {}
110+
111+
impl Notification for ScarbResolvingFinish {
112+
type Params = ();
113+
const METHOD: &'static str = "scarb/resolving-finish";
114+
}
115+
116+
/// Notifies about diagnostics generation which is beginning to calculate
117+
#[derive(Debug)]
118+
pub struct DiagnosticsCalculationStart;
119+
120+
impl Notification for DiagnosticsCalculationStart {
121+
type Params = ();
122+
const METHOD: &'static str = "cairo/diagnosticsCalculationStart";
123+
}
124+
125+
/// Notifies about diagnostics generation which ended calculating
126+
#[derive(Debug)]
127+
pub struct DiagnosticsCalculationFinish;
128+
129+
impl Notification for DiagnosticsCalculationFinish {
130+
type Params = ();
131+
const METHOD: &'static str = "cairo/diagnosticsCalculationFinish";
132+
}

0 commit comments

Comments
 (0)