Repository: gluon-lang/gluon
Pull request #691 (2019-02-15 23:15:39)

Improve the language server support


Patch:
diff --git a/base/src/symbol.rs b/base/src/symbol.rs
index f3e545bf..05b72c4e 100644
--- a/base/src/symbol.rs
+++ b/base/src/symbol.rs
@@ -109,6 +109,18 @@ impl PartialEq for Symbol {
     }
 }
 
+impl PartialEq<SymbolRef> for Symbol {
+    fn eq(&self, other: &SymbolRef) -> bool {
+        **self == *other
+    }
+}
+
+impl PartialEq<Symbol> for SymbolRef {
+    fn eq(&self, other: &Symbol) -> bool {
+        *self == **other
+    }
+}
+
 impl PartialOrd for Symbol {
     fn partial_cmp(&self, other: &Symbol) -> Option<Ordering> {
         (**self).partial_cmp(other)
diff --git a/completion/src/lib.rs b/completion/src/lib.rs
index 491ac07e..a2e1860d 100644
--- a/completion/src/lib.rs
+++ b/completion/src/lib.rs
@@ -788,19 +788,19 @@ where
     }
 }
 
-pub trait Extract: Sized {
+pub trait Extract<'a>: Sized {
     type Output;
-    fn extract(self, found: &Found) -> Result<Self::Output, ()>;
-    fn match_extract(self, match_: &Match) -> Result<Self::Output, ()>;
+    fn extract(self, found: &Found<'a>) -> Result<Self::Output, ()>;
+    fn match_extract(self, match_: &Match<'a>) -> Result<Self::Output, ()>;
 }
 
 #[derive(Clone, Copy)]
 pub struct TypeAt<'a> {
     pub env: &'a TypeEnv<Type = ArcType>,
 }
-impl<'a> Extract for TypeAt<'a> {
+impl<'a> Extract<'a> for TypeAt<'a> {
     type Output = Either<ArcKind, ArcType>;
-    fn extract(self, found: &Found) -> Result<Self::Output, ()> {
+    fn extract(self, found: &Found<'a>) -> Result<Self::Output, ()> {
         match found.match_ {
             Some(ref match_) => self.match_extract(match_),
             None => self.match_extract(found.enclosing_match()),
@@ -825,30 +825,31 @@ impl<'a> Extract for TypeAt<'a> {
 
 #[derive(Clone, Copy)]
 pub struct IdentAt;
-impl Extract for IdentAt {
-    type Output = Symbol;
-    fn extract(self, found: &Found) -> Result<Self::Output, ()> {
+impl<'a> Extract<'a> for IdentAt {
+    type Output = &'a SymbolRef;
+    fn extract(self, found: &Found<'a>) -> Result<Self::Output, ()> {
         match found.match_ {
             Some(ref match_) => self.match_extract(match_),
             None => self.match_extract(found.enclosing_match()),
         }
     }
 
-    fn match_extract(self, found: &Match) -> Result<Self::Output, ()> {
-        Ok(match *found {
-            Match::Expr(&Spanned {
-                value: Expr::Ident(ref id),
+    fn match_extract(self, found: &Match<'a>) -> Result<Self::Output, ()> {
+        Ok(match found {
+            Match::Expr(Spanned {
+                value: Expr::Ident(id),
                 ..
             })
-            | Match::Pattern(&Spanned {
-                value: Pattern::Ident(ref id),
+            | Match::Pattern(Spanned {
+                value: Pattern::Ident(id),
                 ..
-            }) => id.name.clone(),
-            Match::Ident(_, id, _) => id.clone(),
-            Match::Pattern(&Spanned {
-                value: Pattern::As(ref id, _),
+            }) => &id.name,
+            Match::Ident(_, id, _) => id,
+            Match::Pattern(Spanned {
+                value: Pattern::As(id, _),
                 ..
-            }) => id.value.clone(),
+            }) => &id.value,
+            Match::Type(_, id, _) => id,
             _ => return Err(()),
         })
     }
@@ -856,7 +857,7 @@ impl Extract for IdentAt {
 
 #[derive(Copy, Clone)]
 pub struct SpanAt;
-impl Extract for SpanAt {
+impl<'a> Extract<'a> for SpanAt {
     type Output = Span<BytePos>;
     fn extract(self, found: &Found) -> Result<Self::Output, ()> {
         match found.match_ {
@@ -882,13 +883,13 @@ macro_rules! tuple_extract {
 macro_rules! tuple_extract_ {
     ($($id: ident)*) => {
         #[allow(non_snake_case)]
-        impl<$($id : Extract),*> Extract for ( $($id),* ) {
+        impl<'a, $($id : Extract<'a>),*> Extract<'a> for ( $($id),* ) {
             type Output = ( $($id::Output),* );
-            fn extract(self, found: &Found) -> Result<Self::Output, ()> {
+            fn extract(self, found: &Found<'a>) -> Result<Self::Output, ()> {
                 let ( $($id),* ) = self;
                 Ok(( $( $id.extract(found)? ),* ))
             }
-            fn match_extract(self, found: &Match) -> Result<Self::Output, ()> {
+            fn match_extract(self, found: &Match<'a>) -> Result<Self::Output, ()> {
                 let ( $($id),* ) = self;
                 Ok(( $( $id.match_extract(found)? ),* ))
             }
@@ -898,14 +899,14 @@ macro_rules! tuple_extract_ {
 
 tuple_extract! {A B C D E F G H}
 
-pub fn completion<T>(
+pub fn completion<'a, T>(
     extract: T,
     source_span: Span<BytePos>,
-    expr: &SpannedExpr<Symbol>,
+    expr: &'a SpannedExpr<Symbol>,
     pos: BytePos,
 ) -> Result<T::Output, ()>
 where
-    T: Extract,
+    T: Extract<'a>,
 {
     let found = complete_at((), source_span, expr, pos)?;
     extract.extract(&found)
@@ -931,16 +932,16 @@ pub fn find_all_symbols(
 ) -> Result<(String, Vec<Span<BytePos>>), ()> {
     let extract = IdentAt;
     completion(extract, source_span, expr, pos).map(|symbol| {
-        struct ExtractIdents {
+        struct ExtractIdents<'b> {
             result: Vec<Span<BytePos>>,
-            symbol: Symbol,
+            symbol: &'b SymbolRef,
         }
-        impl<'a> Visitor<'a> for ExtractIdents {
+        impl<'a, 'b> Visitor<'a> for ExtractIdents<'b> {
             type Ident = Symbol;
 
             fn visit_expr(&mut self, e: &'a SpannedExpr<Self::Ident>) {
                 match e.value {
-                    Expr::Ident(ref id) if id.name == self.symbol => {
+                    Expr::Ident(ref id) if id.name == *self.symbol => {
                         self.result.push(e.span);
                     }
                     _ => walk_expr(self, e),
@@ -949,11 +950,11 @@ pub fn find_all_symbols(
 
             fn visit_pattern(&mut self, p: &'a SpannedPattern<Self::Ident>) {
                 match p.value {
-                    Pattern::As(ref id, ref pat) if id.value == self.symbol => {
+                    Pattern::As(ref id, ref pat) if id.value == *self.symbol => {
                         self.result.push(p.span);
                         walk_pattern(self, &pat.value);
                     }
-                    Pattern::Ident(ref id) if id.name == self.symbol => {
+                    Pattern::Ident(ref id) if id.name == *self.symbol => {
                         self.result.push(p.span);
                     }
                     _ => walk_pattern(self, &p.value),
@@ -973,20 +974,27 @@ pub fn symbol(
     source_span: Span<BytePos>,
     expr: &SpannedExpr<Symbol>,
     pos: BytePos,
-) -> Result<Symbol, ()> {
+) -> Result<&SymbolRef, ()> {
     let extract = IdentAt;
     completion(extract, source_span, expr, pos)
 }
 
+pub type SpCompletionSymbol<'a> = Spanned<CompletionSymbol<'a>, BytePos>;
+
 #[derive(Debug, PartialEq)]
-pub enum CompletionSymbol<'a> {
+pub struct CompletionSymbol<'a> {
+    pub name: &'a Symbol,
+    pub content: CompletionSymbolContent<'a>,
+    pub children: Vec<SpCompletionSymbol<'a>>,
+}
+
+#[derive(Debug, PartialEq)]
+pub enum CompletionSymbolContent<'a> {
     Value {
-        name: &'a Symbol,
         typ: &'a ArcType,
         expr: &'a SpannedExpr<Symbol>,
     },
     Type {
-        name: &'a Symbol,
         alias: &'a AliasData<Symbol, AstType<Symbol>>,
     },
 }
@@ -994,54 +1002,81 @@ pub enum CompletionSymbol<'a> {
 pub fn all_symbols(
     source_span: Span<BytePos>,
     expr: &SpannedExpr<Symbol>,
-) -> Vec<Spanned<CompletionSymbol, BytePos>> {
+) -> Vec<SpCompletionSymbol> {
     struct AllIdents<'a> {
         source_span: Span<BytePos>,
         result: Vec<Spanned<CompletionSymbol<'a>, BytePos>>,
     }
+
     impl<'a> Visitor<'a> for AllIdents<'a> {
         type Ident = Symbol;
 
         fn visit_expr(&mut self, e: &'a SpannedExpr<Self::Ident>) {
             if self.source_span.contains(e.span) {
-                match e.value {
-                    Expr::TypeBindings(ref binds, _) => {
+                let source_span = self.source_span;
+                match &e.value {
+                    Expr::TypeBindings(binds, expr) => {
                         self.result.extend(binds.iter().map(|bind| {
                             pos::spanned(
                                 bind.name.span,
-                                CompletionSymbol::Type {
-                                    name: &bind.name.value,
-                                    alias: &bind.alias.value,
+                                CompletionSymbol {
+                                    name: bind
+                                        .finalized_alias
+                                        .as_ref()
+                                        .map(|alias| &alias.name)
+                                        .unwrap_or(&bind.name.value),
+                                    content: CompletionSymbolContent::Type {
+                                        alias: &bind.alias.value,
+                                    },
+                                    children: Vec::new(),
                                 },
                             )
                         }));
+
+                        self.visit_expr(expr);
                     }
-                    Expr::LetBindings(ref binds, _) => {
-                        self.result
-                            .extend(binds.iter().flat_map(|bind| match bind.name.value {
-                                Pattern::Ident(ref id) => Some(pos::spanned(
+                    Expr::LetBindings(binds, expr) => {
+                        self.result.extend(binds.iter().flat_map(|bind| {
+                            let children = idents_of(source_span, &bind.expr);
+                            match bind.name.value {
+                                Pattern::Ident(ref id) => vec![pos::spanned(
                                     bind.name.span,
-                                    CompletionSymbol::Value {
+                                    CompletionSymbol {
                                         name: &id.name,
-                                        typ: &id.typ,
-                                        expr: &bind.expr,
+                                        content: CompletionSymbolContent::Value {
+                                            typ: &id.typ,
+                                            expr: &bind.expr,
+                                        },
+                                        children,
                                     },
-                                )),
-                                _ => None,
-                            }))
+                                )],
+                                _ => children,
+                            }
+                        }));
+
+                        self.visit_expr(expr);
                     }
-                    _ => (),
+                    _ => walk_expr(self, e),
                 }
+            } else {
+                walk_expr(self, e);
             }
-            walk_expr(self, e)
         }
     }
-    let mut visitor = AllIdents {
-        source_span,
-        result: Vec::new(),
-    };
-    visitor.visit_expr(expr);
-    visitor.result
+
+    fn idents_of(
+        source_span: Span<BytePos>,
+        expr: &SpannedExpr<Symbol>,
+    ) -> Vec<Spanned<CompletionSymbol, BytePos>> {
+        let mut visitor = AllIdents {
+            source_span,
+            result: Vec::new(),
+        };
+        visitor.visit_expr(expr);
+        visitor.result
+    }
+
+    idents_of(source_span, expr)
 }
 
 pub fn suggest<T>(
diff --git a/scripts/install_mdbook.sh b/scripts/install_mdbook.sh
index a6aeccb2..9314944b 100755
--- a/scripts/install_mdbook.sh
+++ b/scripts/install_mdbook.sh
@@ -3,7 +3,7 @@
 set -ex
 
 if [[ $1 == *"apple"* ]]; then
-    TARGET=$1
+    exit 0
 else
     TARGET='x86_64-unknown-linux-musl'
 fi


Test patch:
diff --git a/completion/tests/completion.rs b/completion/tests/completion.rs
index 72cfb246..6968c2d5 100644
--- a/completion/tests/completion.rs
+++ b/completion/tests/completion.rs
@@ -8,12 +8,13 @@ extern crate gluon_check as check;
 extern crate gluon_completion as completion;
 extern crate gluon_parser as parser;
 
-use crate::base::ast::Argument;
-use crate::base::kind::{ArcKind, Kind};
-use crate::base::metadata::Metadata;
-use crate::base::metadata::{Comment, CommentType};
-use crate::base::pos::{BytePos, Span};
-use crate::base::types::{ArcType, Field, Type};
+use crate::base::{
+    ast::Argument,
+    kind::{ArcKind, Kind},
+    metadata::{Comment, CommentType, Metadata},
+    pos::{BytePos, Span},
+    types::{ArcType, Field, Type},
+};
 
 use either::Either;
 
@@ -61,6 +62,13 @@ fn find_type_loc(s: &str, line: usize, column: usize) -> Result<ArcType, ()> {
     find_span_type(s, pos).map(|t| t.1.right().expect("Type"))
 }
 
+fn symbol(s: &str, pos: BytePos) -> Result<String, ()> {
+    let (expr, result) = support::typecheck_expr(s);
+    assert!(result.is_ok(), "{}", result.unwrap_err());
+
+    completion::symbol(expr.span, &expr, pos).map(|s| s.declared_name().to_string())
+}
+
 fn get_metadata(s: &str, pos: BytePos) -> Option<Metadata> {
     let env = MockEnv::new();
 
@@ -545,7 +553,8 @@ let { x, y } = { x = 1, y = 2 }
 
     let symbols = completion::all_symbols(expr.span, &expr);
 
-    assert_eq!(symbols.len(), 4);
+    assert_eq!(symbols.len(), 3);
+    assert_eq!(symbols[1].value.children.len(), 1);
 }
 
 #[test]
@@ -608,3 +617,17 @@ let f x : Int -> Int = x
 
     assert_eq!(result, expected);
 }
+
+#[test]
+fn type_symbol() {
+    let _ = env_logger::try_init();
+
+    let text = r#"
+type Test a = | Test a
+let x : Test Int = Test 1
+1.0
+"#;
+    let result = symbol(text, loc(text, 2, 10));
+
+    assert_eq!(result, Ok("Test".into()));
+}