summaryrefslogtreecommitdiff
path: root/cli/lsp/tsc.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/tsc.rs')
-rw-r--r--cli/lsp/tsc.rs111
1 files changed, 82 insertions, 29 deletions
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 2873ba703..4cb93e802 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -107,6 +107,7 @@ type Request = (
Arc<StateSnapshot>,
oneshot::Sender<Result<String, AnyError>>,
CancellationToken,
+ Option<Value>,
);
#[derive(Debug, Clone, Copy, Serialize_repr)]
@@ -224,6 +225,7 @@ pub struct TsServer {
receiver: Mutex<Option<mpsc::UnboundedReceiver<Request>>>,
specifier_map: Arc<TscSpecifierMap>,
inspector_server: Mutex<Option<Arc<InspectorServer>>>,
+ pending_change: Mutex<Option<PendingChange>>,
}
impl std::fmt::Debug for TsServer {
@@ -256,6 +258,47 @@ impl Serialize for ChangeKind {
}
}
+pub struct PendingChange {
+ pub modified_scripts: Vec<(String, ChangeKind)>,
+ pub project_version: usize,
+ pub config_changed: bool,
+}
+
+impl PendingChange {
+ fn to_json(&self) -> Value {
+ json!([
+ self.modified_scripts,
+ self.project_version,
+ self.config_changed,
+ ])
+ }
+
+ fn coalesce(
+ &mut self,
+ new_version: usize,
+ modified_scripts: Vec<(String, ChangeKind)>,
+ config_changed: bool,
+ ) {
+ self.project_version = self.project_version.max(new_version);
+ self.config_changed |= config_changed;
+ for (spec, new) in modified_scripts {
+ if let Some((_, current)) =
+ self.modified_scripts.iter_mut().find(|(s, _)| s == &spec)
+ {
+ match (*current, new) {
+ (_, ChangeKind::Closed) => {
+ *current = ChangeKind::Closed;
+ }
+ (ChangeKind::Opened, ChangeKind::Modified) => {
+ *current = ChangeKind::Modified;
+ }
+ _ => {}
+ }
+ }
+ }
+ }
+}
+
impl TsServer {
pub fn new(performance: Arc<Performance>, cache: Arc<dyn HttpCache>) -> Self {
let (tx, request_rx) = mpsc::unbounded_channel::<Request>();
@@ -266,6 +309,7 @@ impl TsServer {
receiver: Mutex::new(Some(request_rx)),
specifier_map: Arc::new(TscSpecifierMap::new()),
inspector_server: Mutex::new(None),
+ pending_change: Mutex::new(None),
}
}
@@ -302,30 +346,33 @@ impl TsServer {
Ok(())
}
- pub async fn project_changed(
+ pub fn project_changed<'a>(
&self,
snapshot: Arc<StateSnapshot>,
- modified_scripts: &[(&ModuleSpecifier, ChangeKind)],
+ modified_scripts: impl IntoIterator<Item = (&'a ModuleSpecifier, ChangeKind)>,
config_changed: bool,
) {
let modified_scripts = modified_scripts
- .iter()
+ .into_iter()
.map(|(spec, change)| (self.specifier_map.denormalize(spec), change))
.collect::<Vec<_>>();
- let req = TscRequest {
- method: "$projectChanged",
- args: json!(
- [modified_scripts, snapshot.project_version, config_changed,]
- ),
- };
- self
- .request::<()>(snapshot, req)
- .await
- .map_err(|err| {
- log::error!("Failed to request to tsserver {}", err);
- LspError::invalid_request()
- })
- .ok();
+ match &mut *self.pending_change.lock() {
+ Some(pending_change) => {
+ pending_change.coalesce(
+ snapshot.project_version,
+ modified_scripts,
+ config_changed,
+ );
+ }
+ pending => {
+ let pending_change = PendingChange {
+ modified_scripts,
+ project_version: snapshot.project_version,
+ config_changed,
+ };
+ *pending = Some(pending_change);
+ }
+ }
}
pub async fn get_diagnostics(
@@ -1069,7 +1116,12 @@ impl TsServer {
let token = token.child_token();
let droppable_token = DroppableToken(token.clone());
let (tx, rx) = oneshot::channel::<Result<String, AnyError>>();
- if self.sender.send((req, snapshot, tx, token)).is_err() {
+ let change = self.pending_change.lock().take();
+ if self
+ .sender
+ .send((req, snapshot, tx, token, change.map(|c| c.to_json())))
+ .is_err()
+ {
return Err(anyhow!("failed to send request to tsc thread"));
}
let value = rx.await??;
@@ -4245,8 +4297,8 @@ fn run_tsc_thread(
tokio::select! {
biased;
(maybe_request, mut tsc_runtime) = async { (request_rx.recv().await, tsc_runtime.lock().await) } => {
- if let Some((req, state_snapshot, tx, token)) = maybe_request {
- let value = request(&mut tsc_runtime, state_snapshot, req, token.clone());
+ if let Some((req, state_snapshot, tx, token, pending_change)) = maybe_request {
+ let value = request(&mut tsc_runtime, state_snapshot, req, pending_change, token.clone());
request_signal_tx.send(()).unwrap();
let was_sent = tx.send(value).is_ok();
// Don't print the send error if the token is cancelled, it's expected
@@ -4664,6 +4716,7 @@ fn request(
runtime: &mut JsRuntime,
state_snapshot: Arc<StateSnapshot>,
request: TscRequest,
+ change: Option<Value>,
token: CancellationToken,
) -> Result<String, AnyError> {
if token.is_cancelled() {
@@ -4688,8 +4741,10 @@ fn request(
"Internal error: expected args to be array"
);
let request_src = format!(
- "globalThis.serverRequest({id}, \"{}\", {});",
- request.method, &request.args
+ "globalThis.serverRequest({id}, \"{}\", {}, {});",
+ request.method,
+ &request.args,
+ change.unwrap_or_default()
);
runtime.execute_script(located_script_name!(), request_src)?;
@@ -5221,13 +5276,11 @@ mod tests {
..snapshot.as_ref().clone()
})
};
- ts_server
- .project_changed(
- snapshot.clone(),
- &[(&specifier_dep, ChangeKind::Opened)],
- false,
- )
- .await;
+ ts_server.project_changed(
+ snapshot.clone(),
+ [(&specifier_dep, ChangeKind::Opened)],
+ false,
+ );
let specifier = resolve_url("file:///a.ts").unwrap();
let diagnostics = ts_server
.get_diagnostics(snapshot.clone(), vec![specifier], Default::default())