diff options
author | Kitson Kelly <me@kitsonkelly.com> | 2020-12-07 21:46:39 +1100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-07 21:46:39 +1100 |
commit | 301d3e4b6849d24154ac2d65c00a9b30223d000e (patch) | |
tree | ab3bc074493e6c9be8d1875233bc141bdc0da3b4 /cli/lsp/utils.rs | |
parent | c8e9b2654ec0d54c77bb3f49fa31c3986203d517 (diff) |
feat: add mvp language server (#8515)
Resolves #8400
Diffstat (limited to 'cli/lsp/utils.rs')
-rw-r--r-- | cli/lsp/utils.rs | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/cli/lsp/utils.rs b/cli/lsp/utils.rs new file mode 100644 index 000000000..0c3d5a635 --- /dev/null +++ b/cli/lsp/utils.rs @@ -0,0 +1,114 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +use deno_core::error::custom_error; +use deno_core::error::AnyError; +use deno_core::serde_json::Value; +use deno_core::url::Position; +use deno_core::url::Url; +use deno_core::ModuleSpecifier; +use lsp_server::Notification; +use serde::de::DeserializeOwned; +use std::error::Error; +use std::fmt; + +// TODO(@kitsonk) support actually supporting cancellation requests from the +// client. + +pub struct Canceled { + _private: (), +} + +impl Canceled { + #[allow(unused)] + pub fn new() -> Self { + Self { _private: () } + } + + #[allow(unused)] + pub fn throw() -> ! { + std::panic::resume_unwind(Box::new(Canceled::new())) + } +} + +impl fmt::Display for Canceled { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "cancelled") + } +} + +impl fmt::Debug for Canceled { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Canceled") + } +} + +impl Error for Canceled {} + +pub fn from_json<T: DeserializeOwned>( + what: &'static str, + json: Value, +) -> Result<T, AnyError> { + let response = T::deserialize(&json).map_err(|err| { + custom_error( + "DeserializeFailed", + format!("Failed to deserialize {}: {}; {}", what, err, json), + ) + })?; + Ok(response) +} + +pub fn is_canceled(e: &(dyn Error + 'static)) -> bool { + e.downcast_ref::<Canceled>().is_some() +} + +pub fn notification_is<N: lsp_types::notification::Notification>( + notification: &Notification, +) -> bool { + notification.method == N::METHOD +} + +/// Normalizes a file name returned from the TypeScript compiler into a URI that +/// should be sent by the language server to the client. +pub fn normalize_file_name(file_name: &str) -> Result<Url, AnyError> { + let specifier_str = if file_name.starts_with("file://") { + file_name.to_string() + } else { + format!("deno:///{}", file_name.replacen("://", "/", 1)) + }; + Url::parse(&specifier_str).map_err(|err| err.into()) +} + +/// Normalize URLs from the client, where "virtual" `deno:///` URLs are +/// converted into proper module specifiers. +pub fn normalize_url(url: Url) -> ModuleSpecifier { + if url.scheme() == "deno" + && (url.path().starts_with("/http") || url.path().starts_with("/asset")) + { + let specifier_str = url[Position::BeforePath..] + .replacen("/", "", 1) + .replacen("/", "://", 1); + if let Ok(specifier) = + percent_encoding::percent_decode_str(&specifier_str).decode_utf8() + { + if let Ok(specifier) = ModuleSpecifier::resolve_url(&specifier) { + return specifier; + } + } + } + ModuleSpecifier::from(url) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_normalize_url() { + let fixture = Url::parse("deno:///https/deno.land/x/mod.ts").unwrap(); + let actual = normalize_url(fixture); + assert_eq!( + actual, + ModuleSpecifier::resolve_url("https://deno.land/x/mod.ts").unwrap() + ); + } +} |