diff options
-rw-r--r-- | cli/lsp/language_server.rs | 239 | ||||
-rw-r--r-- | cli/lsp/tsc.rs | 142 |
2 files changed, 187 insertions, 194 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index d167b53bb..e6880f0df 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -227,14 +227,13 @@ impl Inner { self.performance.measure(mark); Ok(navigation_tree.clone()) } else { - let res = self + let navigation_tree: tsc::NavigationTree = self .ts_server .request( self.snapshot(), tsc::RequestMethod::GetNavigationTree(specifier.clone()), ) .await?; - let navigation_tree: tsc::NavigationTree = serde_json::from_value(res)?; self .navigation_trees .insert(specifier.clone(), navigation_tree.clone()); @@ -512,7 +511,7 @@ impl Inner { warn!("{}", ignored_options); } } - self + let _ok: bool = self .ts_server .request(self.snapshot(), tsc::RequestMethod::Configure(tsconfig)) .await?; @@ -588,7 +587,7 @@ impl Inner { } if capabilities.code_action_provider.is_some() { - let res = self + let fixable_diagnostics: Vec<String> = self .ts_server .request(self.snapshot(), tsc::RequestMethod::GetSupportedCodeFixes) .await @@ -596,11 +595,6 @@ impl Inner { error!("Unable to get fixable diagnostics: {}", err); LspError::internal_error() })?; - let fixable_diagnostics: Vec<String> = - from_value(res).map_err(|err| { - error!("Unable to get fixable diagnostics: {}", err); - LspError::internal_error() - })?; self.ts_fixable_diagnostics = fixable_diagnostics; } @@ -877,11 +871,14 @@ impl Inner { specifier, line_index.offset_tsc(params.text_document_position_params.position)?, )); - // TODO(lucacasonato): handle error correctly - let res = self.ts_server.request(self.snapshot(), req).await.unwrap(); - // TODO(lucacasonato): handle error correctly - let maybe_quick_info: Option<tsc::QuickInfo> = - serde_json::from_value(res).unwrap(); + let maybe_quick_info: Option<tsc::QuickInfo> = self + .ts_server + .request(self.snapshot(), req) + .await + .map_err(|err| { + error!("Unable to get quick info: {}", err); + LspError::internal_error() + })?; if let Some(quick_info) = maybe_quick_info { let hover = quick_info.to_hover(&line_index); self.performance.measure(mark); @@ -953,18 +950,13 @@ impl Inner { line_index.offset_tsc(diagnostic.range.end)?, codes, )); - let res = + let actions: Vec<tsc::CodeFixAction> = self.ts_server.request(self.snapshot(), req).await.map_err( |err| { error!("Error getting actions from TypeScript: {}", err); LspError::internal_error() }, )?; - let actions: Vec<tsc::CodeFixAction> = - from_value(res).map_err(|err| { - error!("Cannot decode actions from TypeScript: {}", err); - LspError::internal_error() - })?; for action in actions { code_actions .add_ts_fix_action(&action, diagnostic, self) @@ -1005,46 +997,42 @@ impl Inner { params: CodeAction, ) -> LspResult<CodeAction> { let mark = self.performance.mark("code_action_resolve"); - let result = - if let Some(data) = params.data.clone() { - let code_action_data: CodeActionData = - from_value(data).map_err(|err| { - error!("Unable to decode code action data: {}", err); - LspError::invalid_params("The CodeAction's data is invalid.") - })?; - let req = tsc::RequestMethod::GetCombinedCodeFix(( - code_action_data.specifier, - json!(code_action_data.fix_id.clone()), - )); - let res = self.ts_server.request(self.snapshot(), req).await.map_err( - |err| { - error!("Unable to get combined fix from TypeScript: {}", err); - LspError::internal_error() - }, - )?; - let combined_code_actions: tsc::CombinedCodeActions = - from_value(res).map_err(|err| { - error!("Cannot decode combined actions from TypeScript: {}", err); - LspError::internal_error() - })?; - if combined_code_actions.commands.is_some() { - error!("Deno does not support code actions with commands."); - Err(LspError::invalid_request()) - } else { - let mut code_action = params.clone(); - code_action.edit = - ts_changes_to_edit(&combined_code_actions.changes, self) - .await - .map_err(|err| { - error!("Unable to convert changes to edits: {}", err); - LspError::internal_error() - })?; - Ok(code_action) - } + let result = if let Some(data) = params.data.clone() { + let code_action_data: CodeActionData = + from_value(data).map_err(|err| { + error!("Unable to decode code action data: {}", err); + LspError::invalid_params("The CodeAction's data is invalid.") + })?; + let req = tsc::RequestMethod::GetCombinedCodeFix(( + code_action_data.specifier, + json!(code_action_data.fix_id.clone()), + )); + let combined_code_actions: tsc::CombinedCodeActions = self + .ts_server + .request(self.snapshot(), req) + .await + .map_err(|err| { + error!("Unable to get combined fix from TypeScript: {}", err); + LspError::internal_error() + })?; + if combined_code_actions.commands.is_some() { + error!("Deno does not support code actions with commands."); + Err(LspError::invalid_request()) } else { - // The code action doesn't need to be resolved - Ok(params) - }; + let mut code_action = params.clone(); + code_action.edit = + ts_changes_to_edit(&combined_code_actions.changes, self) + .await + .map_err(|err| { + error!("Unable to convert changes to edits: {}", err); + LspError::internal_error() + })?; + Ok(code_action) + } + } else { + // The code action doesn't need to be resolved + Ok(params) + }; self.performance.measure(mark); result } @@ -1191,18 +1179,13 @@ impl Inner { code_lens_data.specifier.clone(), line_index.offset_tsc(params.range.start)?, )); - let res = + let maybe_implementations: Option<Vec<tsc::ImplementationLocation>> = self.ts_server.request(self.snapshot(), req).await.map_err( |err| { error!("Error processing TypeScript request: {}", err); LspError::internal_error() }, )?; - let maybe_implementations: Option<Vec<tsc::ImplementationLocation>> = - serde_json::from_value(res).map_err(|err| { - error!("Error deserializing response: {}", err); - LspError::internal_error() - })?; if let Some(implementations) = maybe_implementations { let mut locations = Vec::new(); for implementation in implementations { @@ -1275,18 +1258,13 @@ impl Inner { code_lens_data.specifier.clone(), line_index.offset_tsc(params.range.start)?, )); - let res = + let maybe_references: Option<Vec<tsc::ReferenceEntry>> = self.ts_server.request(self.snapshot(), req).await.map_err( |err| { error!("Error processing TypeScript request: {}", err); LspError::internal_error() }, )?; - let maybe_references: Option<Vec<tsc::ReferenceEntry>> = - serde_json::from_value(res).map_err(|err| { - error!("Error deserializing response: {}", err); - LspError::internal_error() - })?; if let Some(references) = maybe_references { let mut locations = Vec::new(); for reference in references { @@ -1393,11 +1371,14 @@ impl Inner { line_index.offset_tsc(params.text_document_position_params.position)?, files_to_search, )); - // TODO(lucacasonato): handle error correctly - let res = self.ts_server.request(self.snapshot(), req).await.unwrap(); - // TODO(lucacasonato): handle error correctly - let maybe_document_highlights: Option<Vec<tsc::DocumentHighlights>> = - serde_json::from_value(res).unwrap(); + let maybe_document_highlights: Option<Vec<tsc::DocumentHighlights>> = self + .ts_server + .request(self.snapshot(), req) + .await + .map_err(|err| { + error!("Unable to get document highlights from TypeScript: {}", err); + LspError::internal_error() + })?; if let Some(document_highlights) = maybe_document_highlights { let result = document_highlights @@ -1437,11 +1418,14 @@ impl Inner { specifier, line_index.offset_tsc(params.text_document_position.position)?, )); - // TODO(lucacasonato): handle error correctly - let res = self.ts_server.request(self.snapshot(), req).await.unwrap(); - // TODO(lucacasonato): handle error correctly - let maybe_references: Option<Vec<tsc::ReferenceEntry>> = - serde_json::from_value(res).unwrap(); + let maybe_references: Option<Vec<tsc::ReferenceEntry>> = self + .ts_server + .request(self.snapshot(), req) + .await + .map_err(|err| { + error!("Unable to get references from TypeScript: {}", err); + LspError::internal_error() + })?; if let Some(references) = maybe_references { let mut results = Vec::new(); @@ -1489,11 +1473,14 @@ impl Inner { specifier, line_index.offset_tsc(params.text_document_position_params.position)?, )); - // TODO(lucacasonato): handle error correctly - let res = self.ts_server.request(self.snapshot(), req).await.unwrap(); - // TODO(lucacasonato): handle error correctly - let maybe_definition: Option<tsc::DefinitionInfoAndBoundSpan> = - serde_json::from_value(res).unwrap(); + let maybe_definition: Option<tsc::DefinitionInfoAndBoundSpan> = self + .ts_server + .request(self.snapshot(), req) + .await + .map_err(|err| { + error!("Unable to get definition from TypeScript: {}", err); + LspError::internal_error() + })?; if let Some(definition) = maybe_definition { let results = definition.to_definition(&line_index, self).await; @@ -1535,11 +1522,14 @@ impl Inner { ..Default::default() }, )); - // TODO(lucacasonato): handle error correctly - let res = self.ts_server.request(self.snapshot(), req).await.unwrap(); - // TODO(lucacasonato): handle error correctly - let maybe_completion_info: Option<tsc::CompletionInfo> = - serde_json::from_value(res).unwrap(); + let maybe_completion_info: Option<tsc::CompletionInfo> = self + .ts_server + .request(self.snapshot(), req) + .await + .map_err(|err| { + error!("Unable to get completion info from TypeScript: {}", err); + LspError::internal_error() + })?; if let Some(completions) = maybe_completion_info { let results = completions.into_completion_response(&line_index); @@ -1576,20 +1566,13 @@ impl Inner { specifier, line_index.offset_tsc(params.text_document_position_params.position)?, )); - let res = - self - .ts_server - .request(self.snapshot(), req) - .await - .map_err(|err| { - error!("Failed to request to tsserver {}", err); - LspError::invalid_request() - })?; - - let maybe_implementations: Option<Vec<tsc::ImplementationLocation>> = serde_json::from_value(res) + let maybe_implementations: Option<Vec<tsc::ImplementationLocation>> = self + .ts_server + .request(self.snapshot(), req) + .await .map_err(|err| { - error!("Failed to deserialized tsserver response to Vec<ImplementationLocation> {}", err); - LspError::internal_error() + error!("Failed to request to tsserver {}", err); + LspError::invalid_request() })?; let result = if let Some(implementations) = maybe_implementations { @@ -1638,26 +1621,14 @@ impl Inner { false, )); - let res = - self - .ts_server - .request(self.snapshot(), req) - .await - .map_err(|err| { - error!("Failed to request to tsserver {}", err); - LspError::invalid_request() - })?; - - let maybe_locations = serde_json::from_value::< - Option<Vec<tsc::RenameLocation>>, - >(res) - .map_err(|err| { - error!( - "Failed to deserialize tsserver response to Vec<RenameLocation> {}", - err - ); - LspError::internal_error() - })?; + let maybe_locations: Option<Vec<tsc::RenameLocation>> = self + .ts_server + .request(self.snapshot(), req) + .await + .map_err(|err| { + error!("Failed to request to tsserver {}", err); + LspError::invalid_request() + })?; if let Some(locations) = maybe_locations { let rename_locations = tsc::RenameLocations { locations }; @@ -1751,19 +1722,13 @@ impl Inner { line_index.offset_tsc(params.text_document_position_params.position)?, options, )); - let res = - self - .ts_server - .request(self.snapshot(), req) - .await - .map_err(|err| { - error!("Failed to request to tsserver: {}", err); - LspError::invalid_request() - })?; - let maybe_signature_help_items: Option<tsc::SignatureHelpItems> = - serde_json::from_value(res).map_err(|err| { - error!("Failed to deserialize tsserver response: {}", err); - LspError::internal_error() + let maybe_signature_help_items: Option<tsc::SignatureHelpItems> = self + .ts_server + .request(self.snapshot(), req) + .await + .map_err(|err| { + error!("Failed to request to tsserver: {}", err); + LspError::invalid_request() })?; if let Some(signature_help_items) = maybe_signature_help_items { diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 8adcb7f2b..65f706cbe 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -19,6 +19,7 @@ use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::json_op_sync; use deno_core::resolve_url; +use deno_core::serde::de; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; use deno_core::serde_json; @@ -71,16 +72,19 @@ impl TsServer { Self(tx) } - pub async fn request( + pub async fn request<R>( &self, snapshot: StateSnapshot, req: RequestMethod, - ) -> Result<Value, AnyError> { + ) -> Result<R, AnyError> + where + R: de::DeserializeOwned, + { let (tx, rx) = oneshot::channel::<Result<Value, AnyError>>(); if self.0.send((req, snapshot, tx)).is_err() { return Err(anyhow!("failed to send request to tsc thread")); } - rx.await? + rx.await?.map(|v| serde_json::from_value::<R>(v).unwrap()) } } @@ -1104,9 +1108,11 @@ fn cache_snapshot( Ok(()) } -fn op<F>(op_fn: F) -> Box<OpFn> +fn op<F, V, R>(op_fn: F) -> Box<OpFn> where - F: Fn(&mut State, Value) -> Result<Value, AnyError> + 'static, + F: Fn(&mut State, V) -> Result<R, AnyError> + 'static, + V: de::DeserializeOwned, + R: Serialize, { json_op_sync(move |s, args, _bufs| { let state = s.borrow_mut::<State>(); @@ -1123,14 +1129,17 @@ struct SourceSnapshotArgs { /// The language service is dropping a reference to a source file snapshot, and /// we can drop our version of that document. -fn dispose(state: &mut State, args: Value) -> Result<Value, AnyError> { +#[allow(clippy::unnecessary_wraps)] +fn dispose( + state: &mut State, + args: SourceSnapshotArgs, +) -> Result<bool, AnyError> { let mark = state.state_snapshot.performance.mark("op_dispose"); - let v: SourceSnapshotArgs = serde_json::from_value(args)?; state .snapshots - .remove(&(v.specifier.into(), v.version.into())); + .remove(&(args.specifier.into(), args.version.into())); state.state_snapshot.performance.measure(mark); - Ok(json!(true)) + Ok(true) } #[derive(Debug, Deserialize)] @@ -1144,18 +1153,20 @@ struct GetChangeRangeArgs { /// The language service wants to compare an old snapshot with a new snapshot to /// determine what source hash changed. -fn get_change_range(state: &mut State, args: Value) -> Result<Value, AnyError> { +fn get_change_range( + state: &mut State, + args: GetChangeRangeArgs, +) -> Result<Value, AnyError> { let mark = state.state_snapshot.performance.mark("op_get_change_range"); - let v: GetChangeRangeArgs = serde_json::from_value(args.clone())?; - cache_snapshot(state, v.specifier.clone(), v.version.clone())?; + cache_snapshot(state, args.specifier.clone(), args.version.clone())?; if let Some(current) = state .snapshots - .get(&(v.specifier.clone().into(), v.version.into())) + .get(&(args.specifier.clone().into(), args.version.clone().into())) { - if let Some(prev) = state - .snapshots - .get(&(v.specifier.clone().into(), v.old_version.clone().into())) - { + if let Some(prev) = state.snapshots.get(&( + args.specifier.clone().into(), + args.old_version.clone().into(), + )) { state.state_snapshot.performance.measure(mark); Ok(text::get_range_change(prev, current)) } else { @@ -1168,7 +1179,7 @@ fn get_change_range(state: &mut State, args: Value) -> Result<Value, AnyError> { Ok(json!({ "span": { "start": 0, - "length": v.old_length, + "length": args.old_length, }, "newLength": new_length, })) @@ -1178,31 +1189,33 @@ fn get_change_range(state: &mut State, args: Value) -> Result<Value, AnyError> { Err(custom_error( "MissingSnapshot", format!( - "The current snapshot version is missing.\n Args: \"{}\"", + "The current snapshot version is missing.\n Args: \"{:?}\"", args ), )) } } -fn get_length(state: &mut State, args: Value) -> Result<Value, AnyError> { +fn get_length( + state: &mut State, + args: SourceSnapshotArgs, +) -> Result<usize, AnyError> { let mark = state.state_snapshot.performance.mark("op_get_length"); - let v: SourceSnapshotArgs = serde_json::from_value(args)?; - let specifier = resolve_url(&v.specifier)?; + let specifier = resolve_url(&args.specifier)?; if let Some(Some(asset)) = state.state_snapshot.assets.get(&specifier) { - Ok(json!(asset.length)) + Ok(asset.length) } else if state.state_snapshot.documents.contains_key(&specifier) { - cache_snapshot(state, v.specifier.clone(), v.version.clone())?; + cache_snapshot(state, args.specifier.clone(), args.version.clone())?; let content = state .snapshots - .get(&(v.specifier.into(), v.version.into())) + .get(&(args.specifier.into(), args.version.into())) .unwrap(); state.state_snapshot.performance.measure(mark); - Ok(json!(content.encode_utf16().count())) + Ok(content.encode_utf16().count()) } else { let sources = &mut state.state_snapshot.sources; state.state_snapshot.performance.measure(mark); - Ok(json!(sources.get_length_utf16(&specifier).unwrap())) + Ok(sources.get_length_utf16(&specifier).unwrap()) } } @@ -1215,39 +1228,40 @@ struct GetTextArgs { end: usize, } -fn get_text(state: &mut State, args: Value) -> Result<Value, AnyError> { +fn get_text(state: &mut State, args: GetTextArgs) -> Result<String, AnyError> { let mark = state.state_snapshot.performance.mark("op_get_text"); - let v: GetTextArgs = serde_json::from_value(args)?; - let specifier = resolve_url(&v.specifier)?; + let specifier = resolve_url(&args.specifier)?; let content = if let Some(Some(content)) = state.state_snapshot.assets.get(&specifier) { content.text.clone() } else if state.state_snapshot.documents.contains_key(&specifier) { - cache_snapshot(state, v.specifier.clone(), v.version.clone())?; + cache_snapshot(state, args.specifier.clone(), args.version.clone())?; state .snapshots - .get(&(v.specifier.into(), v.version.into())) + .get(&(args.specifier.into(), args.version.into())) .unwrap() .clone() } else { state.state_snapshot.sources.get_source(&specifier).unwrap() }; state.state_snapshot.performance.measure(mark); - Ok(json!(text::slice(&content, v.start..v.end))) + Ok(text::slice(&content, args.start..args.end).to_string()) } -fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> { +fn resolve( + state: &mut State, + args: ResolveArgs, +) -> Result<Vec<Option<(String, String)>>, AnyError> { let mark = state.state_snapshot.performance.mark("op_resolve"); - let v: ResolveArgs = serde_json::from_value(args)?; - let mut resolved = Vec::<Option<(String, String)>>::new(); - let referrer = resolve_url(&v.base)?; + let mut resolved = Vec::new(); + let referrer = resolve_url(&args.base)?; let sources = &mut state.state_snapshot.sources; if state.state_snapshot.documents.contains_key(&referrer) { if let Some(dependencies) = state.state_snapshot.documents.dependencies(&referrer) { - for specifier in &v.specifiers { + for specifier in &args.specifiers { if specifier.starts_with("asset:///") { resolved.push(Some(( specifier.clone(), @@ -1297,7 +1311,7 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> { } } } else if sources.contains_key(&referrer) { - for specifier in &v.specifiers { + for specifier in &args.specifiers { if let Some((resolved_specifier, media_type)) = sources.resolve_import(specifier, &referrer) { @@ -1321,17 +1335,29 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> { } state.state_snapshot.performance.measure(mark); - Ok(json!(resolved)) + Ok(resolved) } -fn respond(state: &mut State, args: Value) -> Result<Value, AnyError> { - state.response = Some(serde_json::from_value(args)?); - Ok(json!(true)) +#[allow(clippy::unnecessary_wraps)] +fn respond(state: &mut State, args: Response) -> Result<bool, AnyError> { + state.response = Some(args); + Ok(true) } #[allow(clippy::unnecessary_wraps)] -fn script_names(state: &mut State, _args: Value) -> Result<Value, AnyError> { - Ok(json!(state.state_snapshot.documents.open_specifiers())) +fn script_names( + state: &mut State, + _args: Value, +) -> Result<Vec<ModuleSpecifier>, AnyError> { + Ok( + state + .state_snapshot + .documents + .open_specifiers() + .into_iter() + .cloned() + .collect(), + ) } #[derive(Debug, Deserialize)] @@ -1340,29 +1366,31 @@ struct ScriptVersionArgs { specifier: String, } -fn script_version(state: &mut State, args: Value) -> Result<Value, AnyError> { +fn script_version( + state: &mut State, + args: ScriptVersionArgs, +) -> Result<Option<String>, AnyError> { let mark = state.state_snapshot.performance.mark("op_script_version"); - let v: ScriptVersionArgs = serde_json::from_value(args)?; - let specifier = resolve_url(&v.specifier)?; + let specifier = resolve_url(&args.specifier)?; if specifier.scheme() == "asset" { return if state.state_snapshot.assets.contains_key(&specifier) { - Ok(json!("1")) + Ok(Some("1".to_string())) } else { - Ok(json!(null)) + Ok(None) }; } else if let Some(version) = state.state_snapshot.documents.version(&specifier) { - return Ok(json!(version.to_string())); + return Ok(Some(version.to_string())); } else { let sources = &mut state.state_snapshot.sources; if let Some(version) = sources.get_script_version(&specifier) { - return Ok(json!(version)); + return Ok(Some(version)); } } state.state_snapshot.performance.measure(mark); - Ok(json!(null)) + Ok(None) } #[derive(Debug, Deserialize)] @@ -1371,10 +1399,10 @@ struct SetAssetArgs { text: Option<String>, } -fn set_asset(state: &mut State, args: Value) -> Result<Value, AnyError> { - let v: SetAssetArgs = serde_json::from_value(args)?; - state.asset = v.text; - Ok(json!(true)) +#[allow(clippy::unnecessary_wraps)] +fn set_asset(state: &mut State, args: SetAssetArgs) -> Result<bool, AnyError> { + state.asset = args.text; + Ok(true) } /// Create and setup a JsRuntime based on a snapshot. It is expected that the |