summaryrefslogtreecommitdiff
path: root/cli/lsp/language_server.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r--cli/lsp/language_server.rs217
1 files changed, 217 insertions, 0 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index e22934dc3..aa5286609 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -52,6 +52,7 @@ use super::tsc::TsServer;
use super::utils;
lazy_static! {
+ static ref ABSTRACT_MODIFIER: Regex = Regex::new(r"\babstract\b").unwrap();
static ref EXPORT_MODIFIER: Regex = Regex::new(r"\bexport\b").unwrap();
}
@@ -1037,6 +1038,30 @@ impl Inner {
navigation_tree.walk(&|i, mp| {
let mut code_lenses = cl.borrow_mut();
+ // TSC Implementations Code Lens
+ if self.config.settings.enabled_code_lens_implementations() {
+ let source = CodeLensSource::Implementations;
+ match i.kind {
+ tsc::ScriptElementKind::InterfaceElement => {
+ code_lenses.push(i.to_code_lens(&line_index, &specifier, &source));
+ }
+ tsc::ScriptElementKind::ClassElement
+ | tsc::ScriptElementKind::MemberFunctionElement
+ | tsc::ScriptElementKind::MemberVariableElement
+ | tsc::ScriptElementKind::MemberGetAccessorElement
+ | tsc::ScriptElementKind::MemberSetAccessorElement => {
+ if ABSTRACT_MODIFIER.is_match(&i.kind_modifiers) {
+ code_lenses.push(i.to_code_lens(
+ &line_index,
+ &specifier,
+ &source,
+ ));
+ }
+ }
+ _ => (),
+ }
+ }
+
// TSC References Code Lens
if self.config.settings.enabled_code_lens_references() {
let source = CodeLensSource::References;
@@ -1124,6 +1149,83 @@ impl Inner {
let code_lens_data: CodeLensData = serde_json::from_value(data)
.map_err(|err| LspError::invalid_params(err.to_string()))?;
let code_lens = match code_lens_data.source {
+ CodeLensSource::Implementations => {
+ let line_index =
+ self.get_line_index_sync(&code_lens_data.specifier).unwrap();
+ let req = tsc::RequestMethod::GetImplementation((
+ code_lens_data.specifier.clone(),
+ line_index.offset_tsc(params.range.start)?,
+ ));
+ let res =
+ 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 {
+ let implementation_specifier = ModuleSpecifier::resolve_url(
+ &implementation.document_span.file_name,
+ )
+ .map_err(|err| {
+ error!("Invalid specifier returned from TypeScript: {}", err);
+ LspError::internal_error()
+ })?;
+ let implementation_location =
+ implementation.to_location(&line_index);
+ if !(implementation_specifier == code_lens_data.specifier
+ && implementation_location.range.start == params.range.start)
+ {
+ locations.push(implementation_location);
+ }
+ }
+ let command = if !locations.is_empty() {
+ let title = if locations.len() > 1 {
+ format!("{} implementations", locations.len())
+ } else {
+ "1 implementation".to_string()
+ };
+ Command {
+ title,
+ command: "deno.showReferences".to_string(),
+ arguments: Some(vec![
+ serde_json::to_value(code_lens_data.specifier).unwrap(),
+ serde_json::to_value(params.range.start).unwrap(),
+ serde_json::to_value(locations).unwrap(),
+ ]),
+ }
+ } else {
+ Command {
+ title: "0 implementations".to_string(),
+ command: "".to_string(),
+ arguments: None,
+ }
+ };
+ CodeLens {
+ range: params.range,
+ command: Some(command),
+ data: None,
+ }
+ } else {
+ let command = Command {
+ title: "0 implementations".to_string(),
+ command: "".to_string(),
+ arguments: None,
+ };
+ CodeLens {
+ range: params.range,
+ command: Some(command),
+ data: None,
+ }
+ }
+ }
CodeLensSource::References => {
let line_index =
self.get_line_index_sync(&code_lens_data.specifier).unwrap();
@@ -2373,6 +2475,121 @@ mod tests {
}
#[tokio::test]
+ async fn test_code_lens_impl_request() {
+ let mut harness = LspTestHarness::new(vec![
+ ("initialize_request.json", LspResponse::RequestAny),
+ ("initialized_notification.json", LspResponse::None),
+ ("did_open_notification_cl_impl.json", LspResponse::None),
+ (
+ "code_lens_request.json",
+ LspResponse::Request(
+ 2,
+ json!([
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 10,
+ },
+ "end": {
+ "line": 0,
+ "character": 11,
+ }
+ },
+ "data": {
+ "specifier": "file:///a/file.ts",
+ "source": "implementations",
+ },
+ },
+ {
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 10,
+ },
+ "end": {
+ "line": 0,
+ "character": 11,
+ }
+ },
+ "data": {
+ "specifier": "file:///a/file.ts",
+ "source": "references",
+ },
+ },
+ {
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 6,
+ },
+ "end": {
+ "line": 4,
+ "character": 7,
+ }
+ },
+ "data": {
+ "specifier": "file:///a/file.ts",
+ "source": "references",
+ },
+ },
+ ]),
+ ),
+ ),
+ (
+ "code_lens_resolve_request_impl.json",
+ LspResponse::Request(
+ 4,
+ json!({
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 10,
+ },
+ "end": {
+ "line": 0,
+ "character": 11,
+ }
+ },
+ "command": {
+ "title": "1 implementation",
+ "command": "deno.showReferences",
+ "arguments": [
+ "file:///a/file.ts",
+ {
+ "line": 0,
+ "character": 10,
+ },
+ [
+ {
+ "uri": "file:///a/file.ts",
+ "range": {
+ "start": {
+ "line": 4,
+ "character": 6,
+ },
+ "end": {
+ "line": 4,
+ "character": 7,
+ }
+ }
+ }
+ ],
+ ]
+ }
+ }),
+ ),
+ ),
+ (
+ "shutdown_request.json",
+ LspResponse::Request(3, json!(null)),
+ ),
+ ("exit_notification.json", LspResponse::None),
+ ]);
+ harness.run().await;
+ }
+
+ #[tokio::test]
async fn test_code_actions() {
let mut harness = LspTestHarness::new(vec![
("initialize_request.json", LspResponse::RequestAny),