summaryrefslogtreecommitdiff
path: root/cli/lsp/utils.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/utils.rs')
-rw-r--r--cli/lsp/utils.rs114
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()
+ );
+ }
+}