From 4f5f608b499ad50aa66b2213e3b92dd2fe61ed68 Mon Sep 17 00:00:00 2001
From: Zac Pullar-Strecker <zacmps@gmail.com>
Date: Mon, 24 Aug 2020 20:26:45 +1200
Subject: [PATCH] Reorganise code

---
 Cargo.lock                        |   7 +-
 crates/ra_hir/Cargo.toml          |   3 +
 crates/ra_hir/src/code_model.rs   | 104 ++++++++++++-
 crates/ra_hir/src/lib.rs          |   2 +
 crates/ra_hir/src/link_rewrite.rs | 226 ++++++++++++++++++++++++++++
 crates/ra_ide/Cargo.toml          |   6 +-
 crates/ra_ide/src/link_rewrite.rs | 241 ++----------------------------
 crates/ra_ide_db/Cargo.toml       |   1 -
 crates/ra_ide_db/src/defs.rs      |  17 ---
 9 files changed, 347 insertions(+), 260 deletions(-)
 create mode 100644 crates/ra_hir/src/link_rewrite.rs

diff --git a/Cargo.lock b/Cargo.lock
index 2e2bc9e106e..08d122e77d0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -988,8 +988,10 @@ dependencies = [
  "ra_hir_ty",
  "ra_prof",
  "ra_syntax",
+ "ra_tt",
  "rustc-hash",
  "stdx",
+ "url",
 ]
 
 [[package]]
@@ -1081,18 +1083,14 @@ dependencies = [
  "ra_db",
  "ra_fmt",
  "ra_hir",
- "ra_hir_def",
  "ra_ide_db",
- "ra_parser",
  "ra_prof",
  "ra_ssr",
  "ra_syntax",
  "ra_text_edit",
- "ra_tt",
  "rustc-hash",
  "stdx",
  "test_utils",
- "url",
 ]
 
 [[package]]
@@ -1105,7 +1103,6 @@ dependencies = [
  "once_cell",
  "ra_db",
  "ra_hir",
- "ra_hir_def",
  "ra_prof",
  "ra_syntax",
  "ra_text_edit",
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index c260bb193cd..a8c4875d4e4 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -16,6 +16,8 @@ arrayvec = "0.5.1"
 
 itertools = "0.9.0"
 
+url = "2.1.1"
+
 stdx = { path = "../stdx" }
 ra_syntax = { path = "../ra_syntax" }
 ra_db = { path = "../ra_db" }
@@ -23,3 +25,4 @@ ra_prof = { path = "../ra_prof" }
 hir_expand = { path = "../ra_hir_expand", package = "ra_hir_expand" }
 hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
 hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" }
+ra_tt = { path = "../ra_tt" }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 8284ab757b5..8154e9bf6cb 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -16,7 +16,7 @@ use hir_def::{
     type_ref::{Mutability, TypeRef},
     AdtId, AssocContainerId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule,
     ImplId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId,
-    TraitId, TypeAliasId, TypeParamId, UnionId,
+    TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
 };
 use hir_expand::{
     diagnostics::DiagnosticSink,
@@ -35,12 +35,14 @@ use ra_syntax::{
     ast::{self, AttrsOwner, NameOwner},
     AstNode,
 };
+use ra_tt::{Ident, Leaf, Literal, TokenTree};
 use rustc_hash::FxHashSet;
 use stdx::impl_from;
 
 use crate::{
     db::{DefDatabase, HirDatabase},
     has_source::HasSource,
+    link_rewrite::Resolvable,
     HirDisplay, InFile, Name,
 };
 
@@ -120,6 +122,33 @@ impl Crate {
     pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
         db.crate_graph().iter().map(|id| Crate { id }).collect()
     }
+
+    /// Try to get the root URL of the documentation of a crate.
+    pub fn get_doc_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> {
+        // Look for #![doc(html_root_url = "...")]
+        let attrs = db.attrs(AttrDef::from(self.root_module(db)?).into());
+        let doc_attr_q = attrs.by_key("doc");
+
+        let doc_url = if doc_attr_q.exists() {
+            doc_attr_q.tt_values().map(|tt| {
+            let name = tt.token_trees.iter()
+                .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url"))
+                .skip(2)
+                .next();
+
+            match name {
+                Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text),
+                _ => None
+            }
+        }).flat_map(|t| t).next().map(|s| s.to_string())
+        } else {
+            None
+        };
+
+        doc_url
+            .map(|s| s.trim_matches('"').trim_end_matches("/").to_owned() + "/")
+            .map(|s| s.to_string())
+    }
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -1709,3 +1738,76 @@ pub trait HasVisibility {
         vis.is_visible_from(db.upcast(), module.id)
     }
 }
+
+impl Resolvable for ModuleDef {
+    fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> {
+        Some(match self {
+            ModuleDef::Module(m) => ModuleId::from(m.clone()).resolver(db),
+            ModuleDef::Function(f) => FunctionId::from(f.clone()).resolver(db),
+            ModuleDef::Adt(adt) => AdtId::from(adt.clone()).resolver(db),
+            ModuleDef::EnumVariant(ev) => {
+                GenericDefId::from(GenericDef::from(ev.clone())).resolver(db)
+            }
+            ModuleDef::Const(c) => GenericDefId::from(GenericDef::from(c.clone())).resolver(db),
+            ModuleDef::Static(s) => StaticId::from(s.clone()).resolver(db),
+            ModuleDef::Trait(t) => TraitId::from(t.clone()).resolver(db),
+            ModuleDef::TypeAlias(t) => ModuleId::from(t.module(db)).resolver(db),
+            // FIXME: This should be a resolver relative to `std/core`
+            ModuleDef::BuiltinType(_t) => None?,
+        })
+    }
+
+    fn try_into_module_def(self) -> Option<ModuleDef> {
+        Some(self)
+    }
+}
+
+impl Resolvable for TypeParam {
+    fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> {
+        Some(Into::<ModuleId>::into(self.module(db)).resolver(db))
+    }
+
+    fn try_into_module_def(self) -> Option<ModuleDef> {
+        None
+    }
+}
+
+impl Resolvable for MacroDef {
+    fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> {
+        Some(Into::<ModuleId>::into(self.module(db)?).resolver(db))
+    }
+
+    fn try_into_module_def(self) -> Option<ModuleDef> {
+        None
+    }
+}
+
+impl Resolvable for Field {
+    fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> {
+        Some(Into::<VariantId>::into(Into::<VariantDef>::into(self.parent_def(db))).resolver(db))
+    }
+
+    fn try_into_module_def(self) -> Option<ModuleDef> {
+        None
+    }
+}
+
+impl Resolvable for ImplDef {
+    fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> {
+        Some(Into::<ModuleId>::into(self.module(db)).resolver(db))
+    }
+
+    fn try_into_module_def(self) -> Option<ModuleDef> {
+        None
+    }
+}
+
+impl Resolvable for Local {
+    fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver> {
+        Some(Into::<ModuleId>::into(self.module(db)).resolver(db))
+    }
+
+    fn try_into_module_def(self) -> Option<ModuleDef> {
+        None
+    }
+}
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index e2d13dbfdc8..b33293a18d9 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -27,6 +27,7 @@ pub mod diagnostics;
 
 mod from_id;
 mod code_model;
+mod link_rewrite;
 
 mod has_source;
 
@@ -38,6 +39,7 @@ pub use crate::{
         Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
     },
     has_source::HasSource,
+    link_rewrite::resolve_doc_link,
     semantics::{original_range, PathResolution, Semantics, SemanticsScope},
 };
 
diff --git a/crates/ra_hir/src/link_rewrite.rs b/crates/ra_hir/src/link_rewrite.rs
new file mode 100644
index 00000000000..a66f1e6fc71
--- /dev/null
+++ b/crates/ra_hir/src/link_rewrite.rs
@@ -0,0 +1,226 @@
+//! Resolves and rewrites links in markdown documentation for hovers/completion windows.
+
+use std::iter::once;
+
+use itertools::Itertools;
+use url::Url;
+
+use crate::{db::HirDatabase, Adt, AsName, Crate, Hygiene, ItemInNs, ModPath, ModuleDef};
+use hir_def::{db::DefDatabase, resolver::Resolver};
+use ra_syntax::ast::Path;
+
+pub fn resolve_doc_link<T: Resolvable + Clone, D: DefDatabase + HirDatabase>(
+    db: &D,
+    definition: &T,
+    link_text: &str,
+    link_target: &str,
+) -> Option<(String, String)> {
+    try_resolve_intra(db, definition, link_text, &link_target).or_else(|| {
+        if let Some(definition) = definition.clone().try_into_module_def() {
+            try_resolve_path(db, &definition, &link_target)
+                .map(|target| (target, link_text.to_string()))
+        } else {
+            None
+        }
+    })
+}
+
+/// Try to resolve path to local documentation via intra-doc-links (i.e. `super::gateway::Shard`).
+///
+/// See [RFC1946](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md).
+fn try_resolve_intra<T: Resolvable, D: DefDatabase + HirDatabase>(
+    db: &D,
+    definition: &T,
+    link_text: &str,
+    link_target: &str,
+) -> Option<(String, String)> {
+    // Set link_target for implied shortlinks
+    let link_target =
+        if link_target.is_empty() { link_text.trim_matches('`') } else { link_target };
+
+    // Namespace disambiguation
+    let namespace = Namespace::from_intra_spec(link_target);
+
+    // Strip prefixes/suffixes
+    let link_target = strip_prefixes_suffixes(link_target);
+
+    // Parse link as a module path
+    let path = Path::parse(link_target).ok()?;
+    let modpath = ModPath::from_src(path, &Hygiene::new_unhygienic()).unwrap();
+
+    // Resolve it relative to symbol's location (according to the RFC this should consider small scopes)
+    let resolver = definition.resolver(db)?;
+
+    let resolved = resolver.resolve_module_path_in_items(db, &modpath);
+    let (defid, namespace) = match namespace {
+        // FIXME: .or(resolved.macros)
+        None => resolved
+            .types
+            .map(|t| (t.0, Namespace::Types))
+            .or(resolved.values.map(|t| (t.0, Namespace::Values)))?,
+        Some(ns @ Namespace::Types) => (resolved.types?.0, ns),
+        Some(ns @ Namespace::Values) => (resolved.values?.0, ns),
+        // FIXME:
+        Some(Namespace::Macros) => None?,
+    };
+
+    // Get the filepath of the final symbol
+    let def: ModuleDef = defid.into();
+    let module = def.module(db)?;
+    let krate = module.krate();
+    let ns = match namespace {
+        Namespace::Types => ItemInNs::Types(defid),
+        Namespace::Values => ItemInNs::Values(defid),
+        // FIXME:
+        Namespace::Macros => None?,
+    };
+    let import_map = db.import_map(krate.into());
+    let path = import_map.path_of(ns)?;
+
+    Some((
+        get_doc_url(db, &krate)?
+            .join(&format!("{}/", krate.display_name(db)?))
+            .ok()?
+            .join(&path.segments.iter().map(|name| name.to_string()).join("/"))
+            .ok()?
+            .join(&get_symbol_filename(db, &def)?)
+            .ok()?
+            .into_string(),
+        strip_prefixes_suffixes(link_text).to_string(),
+    ))
+}
+
+/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`).
+fn try_resolve_path(db: &dyn HirDatabase, moddef: &ModuleDef, link_target: &str) -> Option<String> {
+    if !link_target.contains("#") && !link_target.contains(".html") {
+        return None;
+    }
+    let ns = ItemInNs::Types(moddef.clone().into());
+
+    let module = moddef.module(db)?;
+    let krate = module.krate();
+    let import_map = db.import_map(krate.into());
+    let base = once(format!("{}", krate.display_name(db)?))
+        .chain(import_map.path_of(ns)?.segments.iter().map(|name| format!("{}", name)))
+        .join("/");
+
+    get_doc_url(db, &krate)
+        .and_then(|url| url.join(&base).ok())
+        .and_then(|url| {
+            get_symbol_filename(db, moddef).as_deref().map(|f| url.join(f).ok()).flatten()
+        })
+        .and_then(|url| url.join(link_target).ok())
+        .map(|url| url.into_string())
+}
+
+// Strip prefixes, suffixes, and inline code marks from the given string.
+fn strip_prefixes_suffixes(mut s: &str) -> &str {
+    s = s.trim_matches('`');
+
+    [
+        (TYPES.0.iter(), TYPES.1.iter()),
+        (VALUES.0.iter(), VALUES.1.iter()),
+        (MACROS.0.iter(), MACROS.1.iter()),
+    ]
+    .iter()
+    .for_each(|(prefixes, suffixes)| {
+        prefixes.clone().for_each(|prefix| s = s.trim_start_matches(*prefix));
+        suffixes.clone().for_each(|suffix| s = s.trim_end_matches(*suffix));
+    });
+    let s = s.trim_start_matches("@").trim();
+    s
+}
+
+fn get_doc_url(db: &dyn HirDatabase, krate: &Crate) -> Option<Url> {
+    krate
+        .get_doc_url(db)
+        .or_else(||
+        // Fallback to docs.rs
+        // FIXME: Specify an exact version here. This may be difficult, as multiple versions of the same crate could exist.
+        Some(format!("https://docs.rs/{}/*/", krate.display_name(db)?)))
+        .and_then(|s| Url::parse(&s).ok())
+}
+
+/// Get the filename and extension generated for a symbol by rustdoc.
+///
+/// Example: `struct.Shard.html`
+fn get_symbol_filename(db: &dyn HirDatabase, definition: &ModuleDef) -> Option<String> {
+    Some(match definition {
+        ModuleDef::Adt(adt) => match adt {
+            Adt::Struct(s) => format!("struct.{}.html", s.name(db)),
+            Adt::Enum(e) => format!("enum.{}.html", e.name(db)),
+            Adt::Union(u) => format!("union.{}.html", u.name(db)),
+        },
+        ModuleDef::Module(_) => "index.html".to_string(),
+        ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)),
+        ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)),
+        ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t.as_name()),
+        ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)),
+        ModuleDef::EnumVariant(ev) => {
+            format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db))
+        }
+        ModuleDef::Const(c) => format!("const.{}.html", c.name(db)?),
+        ModuleDef::Static(s) => format!("static.{}.html", s.name(db)?),
+    })
+}
+
+#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
+enum Namespace {
+    Types,
+    Values,
+    Macros,
+}
+
+static TYPES: ([&str; 7], [&str; 0]) =
+    (["type", "struct", "enum", "mod", "trait", "union", "module"], []);
+static VALUES: ([&str; 8], [&str; 1]) =
+    (["value", "function", "fn", "method", "const", "static", "mod", "module"], ["()"]);
+static MACROS: ([&str; 1], [&str; 1]) = (["macro"], ["!"]);
+
+impl Namespace {
+    /// Extract the specified namespace from an intra-doc-link if one exists.
+    ///
+    /// # Examples
+    ///
+    /// * `struct MyStruct` -> `Namespace::Types`
+    /// * `panic!` -> `Namespace::Macros`
+    /// * `fn@from_intra_spec` -> `Namespace::Values`
+    fn from_intra_spec(s: &str) -> Option<Self> {
+        [
+            (Namespace::Types, (TYPES.0.iter(), TYPES.1.iter())),
+            (Namespace::Values, (VALUES.0.iter(), VALUES.1.iter())),
+            (Namespace::Macros, (MACROS.0.iter(), MACROS.1.iter())),
+        ]
+        .iter()
+        .filter(|(_ns, (prefixes, suffixes))| {
+            prefixes
+                .clone()
+                .map(|prefix| {
+                    s.starts_with(*prefix)
+                        && s.chars()
+                            .nth(prefix.len() + 1)
+                            .map(|c| c == '@' || c == ' ')
+                            .unwrap_or(false)
+                })
+                .any(|cond| cond)
+                || suffixes
+                    .clone()
+                    .map(|suffix| {
+                        s.starts_with(*suffix)
+                            && s.chars()
+                                .nth(suffix.len() + 1)
+                                .map(|c| c == '@' || c == ' ')
+                                .unwrap_or(false)
+                    })
+                    .any(|cond| cond)
+        })
+        .map(|(ns, (_, _))| *ns)
+        .next()
+    }
+}
+
+/// Sealed trait used solely for the generic bound on [`resolve_doc_link`].
+pub trait Resolvable {
+    fn resolver<D: DefDatabase + HirDatabase>(&self, db: &D) -> Option<Resolver>;
+    fn try_into_module_def(self) -> Option<ModuleDef>;
+}
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 485aa9081e9..de606f57ed4 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -17,10 +17,9 @@ indexmap = "1.3.2"
 itertools = "0.9.0"
 log = "0.4.8"
 rustc-hash = "1.1.0"
-url = "2.1.1"
+oorandom = "11.1.2"
 pulldown-cmark-to-cmark = "5.0.0"
 pulldown-cmark = {version = "0.7.2", default-features = false}
-oorandom = "11.1.2"
 
 stdx = { path = "../stdx" }
 
@@ -34,9 +33,6 @@ ra_prof = { path = "../ra_prof" }
 test_utils = { path = "../test_utils" }
 ra_assists = { path = "../ra_assists" }
 ra_ssr = { path = "../ra_ssr" }
-ra_hir_def = { path = "../ra_hir_def" }
-ra_tt = { path = "../ra_tt" }
-ra_parser = { path = "../ra_parser" }
 
 # ra_ide should depend only on the top-level `hir` package. if you need
 # something from some `hir_xxx` subpackage, reexport the API via `hir`.
diff --git a/crates/ra_ide/src/link_rewrite.rs b/crates/ra_ide/src/link_rewrite.rs
index 37d695bb88f..94d2c31c2ee 100644
--- a/crates/ra_ide/src/link_rewrite.rs
+++ b/crates/ra_ide/src/link_rewrite.rs
@@ -1,17 +1,10 @@
-//! Resolves and rewrites links in markdown documentation for hovers/completion windows.
+//! This is a wrapper around [`hir::link_rewrite`] connecting it to the markdown parser.
 
-use std::iter::once;
-
-use itertools::Itertools;
 use pulldown_cmark::{CowStr, Event, Options, Parser, Tag};
 use pulldown_cmark_to_cmark::{cmark_with_options, Options as CmarkOptions};
-use url::Url;
 
-use hir::{Adt, AsName, AttrDef, Crate, Hygiene, ItemInNs, ModPath, ModuleDef};
-use ra_hir_def::db::DefDatabase;
+use hir::resolve_doc_link;
 use ra_ide_db::{defs::Definition, RootDatabase};
-use ra_syntax::ast::Path;
-use ra_tt::{Ident, Leaf, Literal, TokenTree};
 
 /// Rewrite documentation links in markdown to point to an online host (e.g. docs.rs)
 pub fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition) -> String {
@@ -31,9 +24,14 @@ pub fn rewrite_links(db: &RootDatabase, markdown: &str, definition: &Definition)
             // Two posibilities:
             // * path-based links: `../../module/struct.MyStruct.html`
             // * module-based links (AKA intra-doc links): `super::super::module::MyStruct`
-            let resolved = try_resolve_intra(db, definition, title, &target).or_else(|| {
-                try_resolve_path(db, definition, &target).map(|target| (target, title.to_string()))
-            });
+            let resolved = match definition {
+                Definition::ModuleDef(t) => resolve_doc_link(db, t, title, target),
+                Definition::Macro(t) => resolve_doc_link(db, t, title, target),
+                Definition::Field(t) => resolve_doc_link(db, t, title, target),
+                Definition::SelfType(t) => resolve_doc_link(db, t, title, target),
+                Definition::Local(t) => resolve_doc_link(db, t, title, target),
+                Definition::TypeParam(t) => resolve_doc_link(db, t, title, target),
+            };
 
             match resolved {
                 Some((target, title)) => (target, title),
@@ -79,222 +77,3 @@ fn map_links<'e>(
         _ => evt,
     })
 }
-
-#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
-enum Namespace {
-    Types,
-    Values,
-    Macros,
-}
-
-static TYPES: ([&str; 7], [&str; 0]) =
-    (["type", "struct", "enum", "mod", "trait", "union", "module"], []);
-static VALUES: ([&str; 8], [&str; 1]) =
-    (["value", "function", "fn", "method", "const", "static", "mod", "module"], ["()"]);
-static MACROS: ([&str; 1], [&str; 1]) = (["macro"], ["!"]);
-
-impl Namespace {
-    /// Extract the specified namespace from an intra-doc-link if one exists.
-    ///
-    /// # Examples
-    ///
-    /// * `struct MyStruct` -> `Namespace::Types`
-    /// * `panic!` -> `Namespace::Macros`
-    /// * `fn@from_intra_spec` -> `Namespace::Values`
-    fn from_intra_spec(s: &str) -> Option<Self> {
-        [
-            (Namespace::Types, (TYPES.0.iter(), TYPES.1.iter())),
-            (Namespace::Values, (VALUES.0.iter(), VALUES.1.iter())),
-            (Namespace::Macros, (MACROS.0.iter(), MACROS.1.iter())),
-        ]
-        .iter()
-        .filter(|(_ns, (prefixes, suffixes))| {
-            prefixes
-                .clone()
-                .map(|prefix| {
-                    s.starts_with(*prefix)
-                        && s.chars()
-                            .nth(prefix.len() + 1)
-                            .map(|c| c == '@' || c == ' ')
-                            .unwrap_or(false)
-                })
-                .any(|cond| cond)
-                || suffixes
-                    .clone()
-                    .map(|suffix| {
-                        s.starts_with(*suffix)
-                            && s.chars()
-                                .nth(suffix.len() + 1)
-                                .map(|c| c == '@' || c == ' ')
-                                .unwrap_or(false)
-                    })
-                    .any(|cond| cond)
-        })
-        .map(|(ns, (_, _))| *ns)
-        .next()
-    }
-}
-
-// Strip prefixes, suffixes, and inline code marks from the given string.
-fn strip_prefixes_suffixes(mut s: &str) -> &str {
-    s = s.trim_matches('`');
-
-    [
-        (TYPES.0.iter(), TYPES.1.iter()),
-        (VALUES.0.iter(), VALUES.1.iter()),
-        (MACROS.0.iter(), MACROS.1.iter()),
-    ]
-    .iter()
-    .for_each(|(prefixes, suffixes)| {
-        prefixes.clone().for_each(|prefix| s = s.trim_start_matches(*prefix));
-        suffixes.clone().for_each(|suffix| s = s.trim_end_matches(*suffix));
-    });
-    s.trim_start_matches("@").trim()
-}
-
-/// Try to resolve path to local documentation via intra-doc-links (i.e. `super::gateway::Shard`).
-///
-/// See [RFC1946](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md).
-fn try_resolve_intra(
-    db: &RootDatabase,
-    definition: &Definition,
-    link_text: &str,
-    link_target: &str,
-) -> Option<(String, String)> {
-    // Set link_target for implied shortlinks
-    let link_target =
-        if link_target.is_empty() { link_text.trim_matches('`') } else { link_target };
-
-    // Namespace disambiguation
-    let namespace = Namespace::from_intra_spec(link_target);
-
-    // Strip prefixes/suffixes
-    let link_target = strip_prefixes_suffixes(link_target);
-
-    // Parse link as a module path
-    let path = Path::parse(link_target).ok()?;
-    let modpath = ModPath::from_src(path, &Hygiene::new_unhygienic()).unwrap();
-
-    // Resolve it relative to symbol's location (according to the RFC this should consider small scopes)
-    let resolver = definition.resolver(db)?;
-
-    let resolved = resolver.resolve_module_path_in_items(db, &modpath);
-    let (defid, namespace) = match namespace {
-        // FIXME: .or(resolved.macros)
-        None => resolved
-            .types
-            .map(|t| (t.0, Namespace::Types))
-            .or(resolved.values.map(|t| (t.0, Namespace::Values)))?,
-        Some(ns @ Namespace::Types) => (resolved.types?.0, ns),
-        Some(ns @ Namespace::Values) => (resolved.values?.0, ns),
-        // FIXME:
-        Some(Namespace::Macros) => None?,
-    };
-
-    // Get the filepath of the final symbol
-    let def: ModuleDef = defid.into();
-    let module = def.module(db)?;
-    let krate = module.krate();
-    let ns = match namespace {
-        Namespace::Types => ItemInNs::Types(defid),
-        Namespace::Values => ItemInNs::Values(defid),
-        // FIXME:
-        Namespace::Macros => None?,
-    };
-    let import_map = db.import_map(krate.into());
-    let path = import_map.path_of(ns)?;
-
-    Some((
-        get_doc_url(db, &krate)?
-            .join(&format!("{}/", krate.display_name(db)?))
-            .ok()?
-            .join(&path.segments.iter().map(|name| name.to_string()).join("/"))
-            .ok()?
-            .join(&get_symbol_filename(db, &Definition::ModuleDef(def))?)
-            .ok()?
-            .into_string(),
-        strip_prefixes_suffixes(link_text).to_string(),
-    ))
-}
-
-/// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`).
-fn try_resolve_path(db: &RootDatabase, definition: &Definition, link: &str) -> Option<String> {
-    if !link.contains("#") && !link.contains(".html") {
-        return None;
-    }
-    let ns = if let Definition::ModuleDef(moddef) = definition {
-        ItemInNs::Types(moddef.clone().into())
-    } else {
-        return None;
-    };
-    let module = definition.module(db)?;
-    let krate = module.krate();
-    let import_map = db.import_map(krate.into());
-    let base = once(format!("{}", krate.display_name(db)?))
-        .chain(import_map.path_of(ns)?.segments.iter().map(|name| format!("{}", name)))
-        .join("/");
-
-    get_doc_url(db, &krate)
-        .and_then(|url| url.join(&base).ok())
-        .and_then(|url| {
-            get_symbol_filename(db, definition).as_deref().map(|f| url.join(f).ok()).flatten()
-        })
-        .and_then(|url| url.join(link).ok())
-        .map(|url| url.into_string())
-}
-
-/// Try to get the root URL of the documentation of a crate.
-fn get_doc_url(db: &RootDatabase, krate: &Crate) -> Option<Url> {
-    // Look for #![doc(html_root_url = "...")]
-    let attrs = db.attrs(AttrDef::from(krate.root_module(db)?).into());
-    let doc_attr_q = attrs.by_key("doc");
-
-    let doc_url = if doc_attr_q.exists() {
-        doc_attr_q.tt_values().map(|tt| {
-            let name = tt.token_trees.iter()
-                .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url"))
-                .skip(2)
-                .next();
-
-            match name {
-                Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text),
-                _ => None
-            }
-        }).flat_map(|t| t).next().map(|s| s.to_string())
-    } else {
-        // Fallback to docs.rs
-        // FIXME: Specify an exact version here (from Cargo.lock)
-        Some(format!("https://docs.rs/{}/*", krate.display_name(db)?))
-    };
-
-    doc_url
-        .map(|s| s.trim_matches('"').trim_end_matches("/").to_owned() + "/")
-        .and_then(|s| Url::parse(&s).ok())
-}
-
-/// Get the filename and extension generated for a symbol by rustdoc.
-///
-/// Example: `struct.Shard.html`
-fn get_symbol_filename(db: &RootDatabase, definition: &Definition) -> Option<String> {
-    Some(match definition {
-        Definition::ModuleDef(def) => match def {
-            ModuleDef::Adt(adt) => match adt {
-                Adt::Struct(s) => format!("struct.{}.html", s.name(db)),
-                Adt::Enum(e) => format!("enum.{}.html", e.name(db)),
-                Adt::Union(u) => format!("union.{}.html", u.name(db)),
-            },
-            ModuleDef::Module(_) => "index.html".to_string(),
-            ModuleDef::Trait(t) => format!("trait.{}.html", t.name(db)),
-            ModuleDef::TypeAlias(t) => format!("type.{}.html", t.name(db)),
-            ModuleDef::BuiltinType(t) => format!("primitive.{}.html", t.as_name()),
-            ModuleDef::Function(f) => format!("fn.{}.html", f.name(db)),
-            ModuleDef::EnumVariant(ev) => {
-                format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db))
-            }
-            ModuleDef::Const(c) => format!("const.{}.html", c.name(db)?),
-            ModuleDef::Static(s) => format!("static.{}.html", s.name(db)?),
-        },
-        Definition::Macro(m) => format!("macro.{}.html", m.name(db)?),
-        _ => None?,
-    })
-}
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml
index f345f1de8a0..2716a38cc99 100644
--- a/crates/ra_ide_db/Cargo.toml
+++ b/crates/ra_ide_db/Cargo.toml
@@ -26,7 +26,6 @@ ra_text_edit = { path = "../ra_text_edit" }
 ra_db = { path = "../ra_db" }
 ra_prof = { path = "../ra_prof" }
 test_utils = { path = "../test_utils" }
-ra_hir_def = { path = "../ra_hir_def" }
 
 # ra_ide should depend only on the top-level `hir` package. if you need
 # something from some `hir_xxx` subpackage, reexport the API via `hir`.
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index 80c99935d79..df56f2d9e60 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -6,7 +6,6 @@
 // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
 
 use hir::{
-    db::{DefDatabase, HirDatabase},
     Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution,
     Semantics, TypeParam, Visibility,
 };
@@ -17,7 +16,6 @@ use ra_syntax::{
 };
 
 use crate::RootDatabase;
-use ra_hir_def::resolver::{HasResolver, Resolver};
 
 // FIXME: a more precise name would probably be `Symbol`?
 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
@@ -78,21 +76,6 @@ impl Definition {
         };
         Some(name)
     }
-
-    pub fn resolver<D: HirDatabase + DefDatabase>(&self, db: &D) -> Option<Resolver> {
-        use hir::VariantDef;
-        use ra_hir_def::*;
-        Some(match self {
-            Definition::ModuleDef(def) => def.resolver(db)?,
-            Definition::Field(field) => {
-                Into::<VariantId>::into(Into::<VariantDef>::into(field.parent_def(db))).resolver(db)
-            }
-            Definition::Macro(m) => Into::<ModuleId>::into(m.module(db)?).resolver(db),
-            Definition::SelfType(imp) => Into::<ImplId>::into(imp.clone()).resolver(db),
-            Definition::Local(local) => Into::<DefWithBodyId>::into(local.parent(db)).resolver(db),
-            Definition::TypeParam(tp) => Into::<ModuleId>::into(tp.module(db)).resolver(db),
-        })
-    }
 }
 
 #[derive(Debug)]