Skip to content

Commit

Permalink
Improve type-on-hover (#187)
Browse files Browse the repository at this point in the history
* Split CollectInfo implementation for expressions

* Restructure info collection

* Rename Info to HoverInfo

* Put LSP hover functionality in its own file

* Replace Option<Span> by Span in HoverInfo

* Split HoverInfo into HoverInfo and HoverInfoContent

* Add special hover info for variables

* Add hover information for type constructors

* Add hover information for calls and dotcalls

* Add hover information for type universe

* Add hover information for holes

* Add hover info for Anno; remove GenericInfo
  • Loading branch information
BinderDavid authored Apr 27, 2024
1 parent 7e23539 commit 7ffbf4c
Show file tree
Hide file tree
Showing 8 changed files with 433 additions and 184 deletions.
8 changes: 4 additions & 4 deletions lang/query/src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ use rust_lapper::Lapper;

use syntax::common::*;

use super::info::{Info, Item};
use super::info::{HoverInfo, Item};

#[derive(Default)]
pub struct Index {
pub(crate) index_enabled: HashSet<FileId>,
pub(crate) info_index_by_id: HashMap<FileId, Lapper<u32, Info>>,
pub(crate) info_index_by_id: HashMap<FileId, Lapper<u32, HoverInfo>>,
pub(crate) item_index_by_id: HashMap<FileId, Lapper<u32, Item>>,
}

Expand Down Expand Up @@ -55,14 +55,14 @@ impl<'a> IndexViewMut<'a> {
self.index.item_index_by_id.insert(self.file_id, Lapper::new(vec![]));
}

pub fn set(&mut self, info_index: Lapper<u32, Info>, item_index: Lapper<u32, Item>) {
pub fn set(&mut self, info_index: Lapper<u32, HoverInfo>, item_index: Lapper<u32, Item>) {
self.index.info_index_by_id.insert(self.file_id, info_index);
self.index.item_index_by_id.insert(self.file_id, item_index);
}
}

impl<'a> IndexView<'a> {
pub fn infos(&self) -> &Lapper<u32, Info> {
pub fn infos(&self) -> &Lapper<u32, HoverInfo> {
&self.index.info_index_by_id[&self.file_id]
}

Expand Down
273 changes: 149 additions & 124 deletions lang/query/src/info.rs → lang/query/src/info/collect.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::rc::Rc;

use codespan::Span;
use printer::PrintToString;
use rust_lapper::{Interval, Lapper};

use syntax::ctx::values::{Binder as TypeCtxBinder, TypeCtx};
use printer::PrintToString;
use syntax::tst::{self};

pub fn collect_info(prg: &tst::Prg) -> (Lapper<u32, Info>, Lapper<u32, Item>) {
use super::data::*;

/// Traverse the program and collect information for the LSP server.
pub fn collect_info(prg: &tst::Prg) -> (Lapper<u32, HoverInfo>, Lapper<u32, Item>) {
let mut c = InfoCollector::default();

prg.collect_info(&mut c);
Expand All @@ -17,72 +19,51 @@ pub fn collect_info(prg: &tst::Prg) -> (Lapper<u32, Info>, Lapper<u32, Item>) {
(info_lapper, item_lapper)
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Info {
pub typ: String,
pub span: Option<Span>,
pub ctx: Option<Ctx>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ctx {
pub bound: Vec<Vec<Binder>>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Binder {
pub name: String,
pub typ: String,
}

#[derive(PartialEq, Eq, Clone)]
pub enum Item {
Data(String),
Codata(String),
Def { name: String, type_name: String },
Codef { name: String, type_name: String },
}

#[derive(Default)]
struct InfoCollector {
info_spans: Vec<Interval<u32, Info>>,
info_spans: Vec<Interval<u32, HoverInfo>>,
item_spans: Vec<Interval<u32, Item>>,
}

impl From<tst::TypeInfo> for Info {
fn from(info: tst::TypeInfo) -> Self {
Info { typ: info.typ.print_to_string(None), ctx: info.ctx.map(Into::into), span: info.span }
impl InfoCollector {
fn add_hover_content(&mut self, span: Span, content: HoverInfoContent) {
let info = Interval {
start: span.start().into(),
stop: span.end().into(),
val: HoverInfo { span, content },
};
self.info_spans.push(info)
}
}

impl From<TypeCtx> for Ctx {
fn from(ctx: TypeCtx) -> Self {
let bound =
ctx.bound.into_iter().map(|tel| tel.into_iter().map(Into::into).collect()).collect();
Ctx { bound }
}
/// Every syntax node which implements this trait can be traversed and
/// make source-code indexed information available for the LSP server.
trait CollectInfo {
fn collect_info(&self, _collector: &mut InfoCollector) {}
}

impl From<TypeCtxBinder> for Binder {
fn from(binder: TypeCtxBinder) -> Self {
Binder { name: binder.name, typ: binder.typ.print_to_string(None) }
// Generic implementations
//
//

impl<T: CollectInfo> CollectInfo for Rc<T> {
fn collect_info(&self, collector: &mut InfoCollector) {
(**self).collect_info(collector)
}
}

impl Item {
pub fn type_name(&self) -> &str {
impl<T: CollectInfo> CollectInfo for Option<T> {
fn collect_info(&self, collector: &mut InfoCollector) {
match self {
Item::Data(name) => name,
Item::Codata(name) => name,
Item::Def { type_name, .. } => type_name,
Item::Codef { type_name, .. } => type_name,
None => (),
Some(x) => x.collect_info(collector),
}
}
}

trait CollectInfo {
fn collect_info(&self, _collector: &mut InfoCollector) {}
}
// Traversing a module and toplevel declarations
//
//

impl CollectInfo for tst::Prg {
fn collect_info(&self, collector: &mut InfoCollector) {
Expand Down Expand Up @@ -189,107 +170,151 @@ impl CollectInfo for tst::Let {
}
}

impl CollectInfo for tst::Match {
// Traversing expressions and collection information
//
//

impl CollectInfo for tst::Exp {
fn collect_info(&self, collector: &mut InfoCollector) {
let tst::Match { cases, .. } = self;
for case in cases.iter() {
case.collect_info(collector)
match self {
tst::Exp::Variable(e) => e.collect_info(collector),
tst::Exp::TypCtor(e) => e.collect_info(collector),
tst::Exp::Call(e) => e.collect_info(collector),
tst::Exp::DotCall(e) => e.collect_info(collector),
tst::Exp::Hole(e) => e.collect_info(collector),
tst::Exp::Type(e) => e.collect_info(collector),
tst::Exp::Anno(e) => e.collect_info(collector),
tst::Exp::LocalMatch(e) => e.collect_info(collector),
tst::Exp::LocalComatch(e) => e.collect_info(collector),
}
}
}

impl CollectInfo for tst::Case {
impl CollectInfo for tst::Variable {
fn collect_info(&self, collector: &mut InfoCollector) {
let tst::Case { body, .. } = self;
body.collect_info(collector)
let tst::Variable { info, .. } = self;
if let Some(span) = info.span {
let content = HoverInfoContent::VariableInfo(VariableInfo {
typ: info.typ.print_to_string(None),
});
collector.add_hover_content(span, content)
}
}
}

impl CollectInfo for tst::Args {
impl CollectInfo for tst::TypCtor {
fn collect_info(&self, collector: &mut InfoCollector) {
let tst::Args { args } = self;
for arg in args.iter() {
arg.collect_info(collector)
let tst::TypCtor { info, args, .. } = self;
if let Some(span) = info.span {
let content = HoverInfoContent::TypeCtorInfo(TypeCtorInfo {
typ: info.typ.print_to_string(None),
});
collector.add_hover_content(span, content)
}
args.collect_info(collector)
}
}

impl CollectInfo for tst::Exp {
impl CollectInfo for tst::Call {
fn collect_info(&self, collector: &mut InfoCollector) {
match self {
tst::Exp::Variable(tst::Variable { info, .. }) => info.collect_info(collector),
tst::Exp::TypCtor(tst::TypCtor { info, name: _, args }) => {
info.collect_info(collector);
args.collect_info(collector)
}
tst::Exp::Call(tst::Call { info, name: _, args }) => {
info.collect_info(collector);
args.collect_info(collector)
}
tst::Exp::DotCall(tst::DotCall { info, exp, name: _, args }) => {
info.collect_info(collector);
exp.collect_info(collector);
args.collect_info(collector)
}
tst::Exp::Hole(tst::Hole { info }) => info.collect_info(collector),
tst::Exp::Type(tst::Type { info }) => info.collect_info(collector),
tst::Exp::Anno(tst::Anno { info, exp, typ }) => {
info.collect_info(collector);
exp.collect_info(collector);
typ.collect_info(collector)
}
tst::Exp::LocalMatch(tst::LocalMatch {
info: _,
ctx: _,
name: _,
on_exp,
motive: _,
ret_typ,
body,
}) => {
on_exp.collect_info(collector);
ret_typ.as_exp().collect_info(collector);
body.collect_info(collector)
}
tst::Exp::LocalComatch(tst::LocalComatch {
info: _,
ctx: _,
name: _,
is_lambda_sugar: _,
body,
}) => body.collect_info(collector),
let tst::Call { info, args, .. } = self;
if let Some(span) = info.span {
let content =
HoverInfoContent::CallInfo(CallInfo { typ: info.typ.print_to_string(None) });
collector.add_hover_content(span, content)
}
args.collect_info(collector)
}
}

impl CollectInfo for tst::TypeInfo {
impl CollectInfo for tst::DotCall {
fn collect_info(&self, collector: &mut InfoCollector) {
let tst::TypeInfo { typ, span, ctx } = self;
if let Some(span) = span {
let info = Interval {
start: span.start().into(),
stop: span.end().into(),
val: Info {
typ: typ.print_to_string(None),
span: Some(*span),
ctx: ctx.clone().map(Into::into),
},
};
collector.info_spans.push(info)
let tst::DotCall { info, exp, args, .. } = self;
if let Some(span) = info.span {
let content =
HoverInfoContent::DotCallInfo(DotCallInfo { typ: info.typ.print_to_string(None) });
collector.add_hover_content(span, content)
}
exp.collect_info(collector);
args.collect_info(collector)
}
}
impl<T: CollectInfo> CollectInfo for Rc<T> {

impl CollectInfo for tst::Hole {
fn collect_info(&self, collector: &mut InfoCollector) {
(**self).collect_info(collector)
let tst::Hole { info } = self;
if let Some(span) = info.span {
let content = HoverInfoContent::HoleInfo(HoleInfo {
goal: info.typ.print_to_string(None),
ctx: info.ctx.clone().map(Into::into),
});
collector.add_hover_content(span, content)
}
}
}

impl<T: CollectInfo> CollectInfo for Option<T> {
impl CollectInfo for tst::Type {
fn collect_info(&self, collector: &mut InfoCollector) {
match self {
None => (),
Some(x) => x.collect_info(collector),
let tst::Type { info } = self;
if let Some(span) = info.span {
let content = HoverInfoContent::TypeUnivInfo(TypeUnivInfo {
typ: info.typ.print_to_string(None),
});
collector.add_hover_content(span, content)
}
}
}

impl CollectInfo for tst::Anno {
fn collect_info(&self, collector: &mut InfoCollector) {
let tst::Anno { info, exp, typ } = self;
if let Some(span) = info.span {
let content =
HoverInfoContent::AnnoInfo(AnnoInfo { typ: info.typ.print_to_string(None) });
collector.add_hover_content(span, content)
}
exp.collect_info(collector);
typ.collect_info(collector)
}
}

impl CollectInfo for tst::LocalMatch {
fn collect_info(&self, collector: &mut InfoCollector) {
let tst::LocalMatch { on_exp, ret_typ, body, .. } = self;
on_exp.collect_info(collector);
ret_typ.as_exp().collect_info(collector);
body.collect_info(collector)
}
}

impl CollectInfo for tst::LocalComatch {
fn collect_info(&self, collector: &mut InfoCollector) {
let tst::LocalComatch { body, .. } = self;
body.collect_info(collector)
}
}

impl CollectInfo for tst::Match {
fn collect_info(&self, collector: &mut InfoCollector) {
let tst::Match { cases, .. } = self;
for case in cases.iter() {
case.collect_info(collector)
}
}
}

impl CollectInfo for tst::Case {
fn collect_info(&self, collector: &mut InfoCollector) {
let tst::Case { body, .. } = self;
body.collect_info(collector)
}
}

impl CollectInfo for tst::Args {
fn collect_info(&self, collector: &mut InfoCollector) {
let tst::Args { args } = self;
for arg in args.iter() {
arg.collect_info(collector)
}
}
}
Loading

0 comments on commit 7ffbf4c

Please sign in to comment.