Skip to content

Commit

Permalink
Add links to hover documentation (FuelLabs#4532)
Browse files Browse the repository at this point in the history
## Description

Closes FuelLabs#2851
FuelLabs#2852

Related, this code is needed for the links to work:
FuelLabs/sway-vscode-plugin#152

There are two types of links: go to definition and go to
implementations.

It should be working for:
- structs
- enums
- traits
- variables
- functions
- function parameters
- struct fields

Note: it isn't working for ABI at the moment. 

<img width="500" alt="image"
src="https://user-images.githubusercontent.com/47993817/236133554-aa79b4ed-f2db-4de4-baf9-edae42b6d44a.png">

<img width="617" alt="image"
src="https://user-images.githubusercontent.com/47993817/236133705-d9990ea1-61d6-4abc-a8f8-ee41d8a5658a.png">

<img width="1138" alt="image"
src="https://user-images.githubusercontent.com/47993817/236133746-0fdf1ede-ff49-46ce-bb2a-41ce18503c6e.png">

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] I have added tests that prove my fix is effective or that my
feature works.
- [ ] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [ ] I have requested a review from the relevant team or maintainers.

---------

Co-authored-by: IGI-111 <[email protected]>
Co-authored-by: Kaya Gökalp <[email protected]>
Co-authored-by: Joshua Batty <[email protected]>
  • Loading branch information
4 people authored May 10, 2023
1 parent 1532078 commit 3e84cc9
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 31 deletions.
2 changes: 1 addition & 1 deletion forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2579,7 +2579,7 @@ pub fn check(
)
.unwrap(),
);
lib_namespace_map.insert(node, namespace);
lib_namespace_map.insert(node, namespace.module().clone());
}

source_map.insert_dependency(manifest.dir());
Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/language/call_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl CallPath {
///
/// Paths to _external_ libraries such `std::lib1::lib2::my_obj` are considered full already
/// and are left unchanged since `std` is a root of the package `std`.
pub fn to_fullpath(&self, namespace: &mut Namespace) -> CallPath {
pub fn to_fullpath(&self, namespace: &Namespace) -> CallPath {
if self.is_absolute {
return self.clone();
}
Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/language/ty/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
pub struct TyModule {
pub span: Span,
pub submodules: Vec<(ModName, TySubmodule)>,
pub namespace: namespace::Module,
pub namespace: namespace::Namespace,
pub all_nodes: Vec<TyAstNode>,
pub attributes: transform::AttributesMap,
}
Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/semantic_analysis/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl ty::TyModule {
typed_nodes_res.map(|all_nodes| Self {
span: span.clone(),
submodules,
namespace: ctx.namespace.module().clone(),
namespace: ctx.namespace.clone(),
all_nodes,
attributes: attributes.clone(),
})
Expand Down
26 changes: 25 additions & 1 deletion sway-core/src/semantic_analysis/namespace/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use crate::{
decl_engine::*,
engine_threading::Engines,
error::*,
language::ty::{self, TyStorageDecl},
language::{
ty::{self, TyDecl, TyStorageDecl},
CallPath,
},
namespace::*,
type_system::*,
};
Expand Down Expand Up @@ -167,6 +170,27 @@ impl Items {
self.implemented_traits.get_items_for_type(engines, type_id)
}

pub fn get_impl_spans_for_decl(&self, engines: Engines<'_>, ty_decl: &TyDecl) -> Vec<Span> {
ty_decl
.return_type(engines)
.value
.map(|type_id| {
self.implemented_traits
.get_impl_spans_for_type(engines, &type_id)
})
.unwrap_or_default()
}

pub fn get_impl_spans_for_type(&self, engines: Engines<'_>, type_id: &TypeId) -> Vec<Span> {
self.implemented_traits
.get_impl_spans_for_type(engines, type_id)
}

pub fn get_impl_spans_for_trait_name(&self, trait_name: &CallPath) -> Vec<Span> {
self.implemented_traits
.get_impl_spans_for_trait_name(trait_name)
}

pub fn get_methods_for_type(
&self,
engines: Engines<'_>,
Expand Down
8 changes: 7 additions & 1 deletion sway-core/src/semantic_analysis/namespace/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use super::{
items::{GlobImport, Items, SymbolMap},
root::Root,
trait_map::TraitMap,
ModuleName, Path,
ModuleName, Path, PathBuf,
};

use sway_ast::ItemConst;
Expand Down Expand Up @@ -56,6 +56,11 @@ pub struct Module {
/// Indicates whether the module is external to the current package. External modules are
/// imported in the `Forc.toml` file.
pub is_external: bool,
/// An absolute path from the `root` that represents the module location.
///
/// When this is the root module, this is equal to `[]`. When this is a
/// submodule of the root called "foo", this would be equal to `[foo]`.
pub mod_path: PathBuf,
}

impl Default for Module {
Expand All @@ -67,6 +72,7 @@ impl Default for Module {
name: Default::default(),
span: Default::default(),
is_external: Default::default(),
mod_path: Default::default(),
}
}
}
Expand Down
104 changes: 93 additions & 11 deletions sway-core/src/semantic_analysis/namespace/trait_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,17 @@ impl OrdWithEngines for TraitKey {
/// Map of name to [TyImplItem](ty::TyImplItem)
type TraitItems = im::HashMap<String, TyImplItem>;

#[derive(Clone, Debug)]
struct TraitValue {
trait_items: TraitItems,
/// The span of the entire impl block.
impl_span: Span,
}

#[derive(Clone, Debug)]
struct TraitEntry {
key: TraitKey,
value: TraitItems,
value: TraitValue,
}

/// Map of trait name and type to [TraitItems].
Expand Down Expand Up @@ -144,7 +151,11 @@ impl TraitMap {
name: map_trait_name,
type_id: map_type_id,
},
value: map_trait_items,
value:
TraitValue {
trait_items: map_trait_items,
..
},
} in self.trait_impls.iter()
{
let CallPath {
Expand Down Expand Up @@ -233,7 +244,7 @@ impl TraitMap {
};

// even if there is a conflicting definition, add the trait anyway
self.insert_inner(trait_name, type_id, trait_items, engines);
self.insert_inner(trait_name, impl_span.clone(), type_id, trait_items, engines);

if errors.is_empty() {
ok((), warnings, errors)
Expand All @@ -245,6 +256,7 @@ impl TraitMap {
fn insert_inner(
&mut self,
trait_name: TraitName,
impl_span: Span,
type_id: TypeId,
trait_methods: TraitItems,
engines: Engines<'_>,
Expand All @@ -253,10 +265,11 @@ impl TraitMap {
name: trait_name,
type_id,
};
let entry = TraitEntry {
key,
value: trait_methods,
let value = TraitValue {
trait_items: trait_methods,
impl_span,
};
let entry = TraitEntry { key, value };
let trait_impls: TraitImpls = vec![entry];
let trait_map = TraitMap { trait_impls };

Expand Down Expand Up @@ -362,7 +375,10 @@ impl TraitMap {
.binary_search_by(|se| se.key.cmp(&oe.key, engines));

match pos {
Ok(pos) => self.trait_impls[pos].value.extend(oe.value.into_iter()),
Ok(pos) => self.trait_impls[pos]
.value
.trait_items
.extend(oe.value.trait_items.into_iter()),
Err(pos) => self.trait_impls.insert(pos, oe),
}
}
Expand Down Expand Up @@ -588,14 +604,19 @@ impl TraitMap {
name: map_trait_name,
type_id: map_type_id,
},
value: map_trait_items,
value:
TraitValue {
trait_items: map_trait_items,
impl_span,
},
} in self.trait_impls.iter()
{
for type_id in all_types.iter_mut() {
let type_info = type_engine.get(*type_id);
if !type_info.can_change(decl_engine) && *type_id == *map_type_id {
trait_map.insert_inner(
map_trait_name.clone(),
impl_span.clone(),
*type_id,
map_trait_items.clone(),
engines,
Expand Down Expand Up @@ -631,7 +652,13 @@ impl TraitMap {
}
})
.collect();
trait_map.insert_inner(map_trait_name.clone(), *type_id, trait_items, engines);
trait_map.insert_inner(
map_trait_name.clone(),
impl_span.clone(),
*type_id,
trait_items,
engines,
);
}
}
}
Expand Down Expand Up @@ -663,13 +690,68 @@ impl TraitMap {
}
for entry in self.trait_impls.iter() {
if are_equal_minus_dynamic_types(engines, type_id, entry.key.type_id) {
let mut trait_items = entry.value.values().cloned().collect::<Vec<_>>();
let mut trait_items = entry
.value
.trait_items
.values()
.cloned()
.collect::<Vec<_>>();
items.append(&mut trait_items);
}
}
items
}

/// Find the spans of all impls for the given type.
///
/// Notes:
/// - equivalency is defined (1) based on whether the types contains types
/// that are dynamic and can change and (2) whether the types hold
/// equivalency after (1) is fulfilled
/// - this method does not translate types from the found entries to the
/// `type_id` (like in `filter_by_type()`). This is because the only
/// entries that qualify as hits are equivalents of `type_id`
pub(crate) fn get_impl_spans_for_type(
&self,
engines: Engines<'_>,
type_id: &TypeId,
) -> Vec<Span> {
let type_engine = engines.te();
let mut spans = vec![];
// small performance gain in bad case
if type_engine
.get(*type_id)
.eq(&TypeInfo::ErrorRecovery, engines)
{
return spans;
}
for entry in self.trait_impls.iter() {
if are_equal_minus_dynamic_types(engines, *type_id, entry.key.type_id) {
spans.push(entry.value.impl_span.clone());
}
}
spans
}

/// Find the entries in `self` with trait name `trait_name` and return the
/// spans of the impls.
pub(crate) fn get_impl_spans_for_trait_name(&self, trait_name: &CallPath) -> Vec<Span> {
self.trait_impls
.iter()
.filter_map(|entry| {
let map_trait_name = CallPath {
prefixes: entry.key.name.prefixes.clone(),
suffix: entry.key.name.suffix.name.clone(),
is_absolute: entry.key.name.is_absolute,
};
if &map_trait_name == trait_name {
return Some(entry.value.impl_span.clone());
}
None
})
.collect()
}

/// Find the entries in `self` that are equivalent to `type_id` with trait
/// name `trait_name`.
///
Expand Down Expand Up @@ -704,7 +786,7 @@ impl TraitMap {
if &map_trait_name == trait_name
&& are_equal_minus_dynamic_types(engines, type_id, e.key.type_id)
{
let mut trait_items = e.value.values().cloned().collect::<Vec<_>>();
let mut trait_items = e.value.trait_items.values().cloned().collect::<Vec<_>>();
items.append(&mut trait_items);
}
}
Expand Down
47 changes: 46 additions & 1 deletion sway-lsp/src/capabilities/hover/hover_link_contents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ use crate::{
utils::document::get_url_from_span,
};
use std::sync::Arc;
use sway_core::{language::CallPath, Engines, TypeId, TypeInfo};
use sway_core::{
language::{
ty::{TyDecl, TyTraitDecl},
CallPath,
},
Engines, TypeId, TypeInfo,
};

use sway_types::{Span, Spanned};
use tower_lsp::lsp_types::{Range, Url};
Expand Down Expand Up @@ -72,4 +78,43 @@ impl<'a> HoverLinkContents<'a> {
});
};
}

/// Adds all implementations of the given [TyTraitDecl] to the list of implementations.
pub fn add_implementations_for_trait(&mut self, trait_decl: &TyTraitDecl) {
if let Some(namespace) = self.session.namespace() {
let call_path = CallPath::from(trait_decl.name.clone()).to_fullpath(&namespace);
let impl_spans = namespace.get_impl_spans_for_trait_name(&call_path);
self.add_implementations(&trait_decl.span(), impl_spans);
}
}

/// Adds implementations of the given type to the list of implementations using the [TyDecl].
pub fn add_implementations_for_decl(&mut self, ty_decl: &TyDecl) {
if let Some(namespace) = self.session.namespace() {
let impl_spans = namespace.get_impl_spans_for_decl(self.engines, ty_decl);
self.add_implementations(&ty_decl.span(), impl_spans);
}
}

/// Adds implementations of the given type to the list of implementations using the [TypeId].
pub fn add_implementations_for_type(&mut self, decl_span: &Span, type_id: &TypeId) {
if let Some(namespace) = self.session.namespace() {
let impl_spans = namespace.get_impl_spans_for_type(self.engines, type_id);
self.add_implementations(decl_span, impl_spans);
}
}

/// Adds implementations to the list of implementation spans, with the declaration span first.
/// Ensure that all paths are converted to workspace paths before adding them.
fn add_implementations(&mut self, decl_span: &Span, mut impl_spans: Vec<Span>) {
let mut all_spans = vec![decl_span.clone()];
all_spans.append(&mut impl_spans);
all_spans.dedup();
all_spans.iter().for_each(|span| {
let span_result = self.session.sync.temp_to_workspace_span(span);
if let Ok(span) = span_result {
self.implementations.push(span);
}
});
}
}
Loading

0 comments on commit 3e84cc9

Please sign in to comment.