Skip to content

Commit

Permalink
lower interface implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
andyyu2004 committed Apr 25, 2022
1 parent 7c7fe19 commit 84c6215
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 31 deletions.
14 changes: 14 additions & 0 deletions src/gqls-ide/src/implementation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use gqls_db::DefDatabase;
use tree_sitter::Point;
use vfs::FileId;

use crate::{Analysis, Location};

impl Analysis {
pub fn implementations(&self, file: FileId, at: Point) -> Vec<Location> {
todo!()
}
}

#[cfg(test)]
mod tests;
1 change: 1 addition & 0 deletions src/gqls-ide/src/implementation/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions src/gqls-ide/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

mod def;
mod edit;
mod implementation;
mod macros;
mod references;
mod resolve;
Expand Down
4 changes: 4 additions & 0 deletions src/gqls-ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,12 @@ pub enum ItemKind {
DirectiveDefinition(Idx<DirectiveDefinition>),
}

pub type Implementations = Vec<Ty>;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeDefinition {
directives: Directives,
implementations: Option<Implementations>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand All @@ -71,6 +74,7 @@ pub struct DirectiveDefinition {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeExtension {
directives: Directives,
implementations: Option<Implementations>,
}

#[derive(Clone, Eq)]
Expand Down
45 changes: 34 additions & 11 deletions src/gqls-ir/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl BodyCtxt {
fn lower_union_member_types(&mut self, node: Node<'_>) -> Vec<Ty> {
assert_eq!(node.kind(), NodeKind::UNION_MEMBER_TYPES);
node.children_of_kind(&mut node.walk(), NodeKind::NAMED_TYPE)
.filter_map(|node| self.lower_named_type(node))
.map(|node| self.lower_named_type(node))
.collect()
}

Expand Down Expand Up @@ -126,12 +126,12 @@ impl BodyCtxt {
assert_eq!(node.kind(), NodeKind::TYPE);
let ty = node.sole_named_child();
let kind = match ty.kind() {
NodeKind::NAMED_TYPE => return self.lower_named_type(ty),
NodeKind::NAMED_TYPE => return Some(self.lower_named_type(ty)),
NodeKind::LIST_TYPE => TyKind::List(self.lower_type(ty.sole_named_child())?),
NodeKind::NON_NULL_TYPE => {
let inner = ty.sole_named_child();
match inner.kind() {
NodeKind::NAMED_TYPE => TyKind::NonNull(self.lower_named_type(inner)?),
NodeKind::NAMED_TYPE => TyKind::NonNull(self.lower_named_type(inner)),
NodeKind::LIST_TYPE => TyKind::NonNull(self.lower_list_type(inner)?),
_ => unreachable!(),
}
Expand All @@ -141,12 +141,6 @@ impl BodyCtxt {
Some(Box::new(Type { range: ty.range(), kind }))
}

fn lower_named_type(&mut self, node: Node<'_>) -> Option<Ty> {
assert_eq!(node.kind(), NodeKind::NAMED_TYPE);
let kind = TyKind::Named(Name::new(self, node));
Some(Box::new(Type { range: node.range(), kind }))
}

fn lower_list_type(&mut self, node: Node<'_>) -> Option<Ty> {
assert_eq!(node.kind(), NodeKind::LIST_TYPE);
let kind = TyKind::List(self.lower_type(node.sole_named_child())?);
Expand Down Expand Up @@ -203,7 +197,13 @@ impl ItemCtxt {
};
let name = Name::new(self, name_node);
let directives = self.lower_directives_of(typedef);
(name, ItemKind::TypeDefinition(self.types.alloc(TypeDefinition { directives })))
let implementations = self.try_lower_implementations_of(typedef);
(
name,
ItemKind::TypeDefinition(
self.types.alloc(TypeDefinition { directives, implementations }),
),
)
}
NodeKind::TYPE_EXTENSION => {
let type_ext = def.sole_named_child();
Expand All @@ -213,7 +213,13 @@ impl ItemCtxt {
};
let name = Name::new(self, name_node);
let directives = self.lower_directives_of(type_ext);
(name, ItemKind::TypeExtension(self.type_exts.alloc(TypeExtension { directives })))
let implementations = self.try_lower_implementations_of(type_ext);
(
name,
ItemKind::TypeExtension(
self.type_exts.alloc(TypeExtension { directives, implementations }),
),
)
}
NodeKind::DIRECTIVE_DEFINITION => {
let name = Name::new(self, def.name_node()?);
Expand All @@ -224,6 +230,17 @@ impl ItemCtxt {
};
Some(Item { range: def.range(), name, kind })
}

fn try_lower_implementations_of(&mut self, node: Node<'_>) -> Option<Implementations> {
let implementations = node.child_of_kind(NodeKind::IMPLEMENTS_INTERFACES)?;
let cursor = &mut implementations.walk();
Some(
implementations
.children_of_kind(cursor, NodeKind::NAMED_TYPE)
.map(|node| self.lower_named_type(node))
.collect(),
)
}
}

trait LowerCtxt: HasText {
Expand All @@ -250,6 +267,12 @@ trait LowerCtxt: HasText {
let name = Name::new(self, node.name_node()?);
Some(Directive { range: node.range(), name })
}

fn lower_named_type(&mut self, node: Node<'_>) -> Ty {
assert_eq!(node.kind(), NodeKind::NAMED_TYPE);
let kind = TyKind::Named(Name::new(self, node));
Box::new(Type { range: node.range(), kind })
}
}

impl<C: HasText> LowerCtxt for C {
Expand Down
54 changes: 34 additions & 20 deletions src/gqls-ir/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fn test_definitions() {
foo: Foo
}
extend type Bar {
extend type Bar implements Iface {
i: Int! @qux
}
Expand Down Expand Up @@ -194,7 +194,7 @@ fn test_definitions() {
name: Bar,
range: Range {
start_byte: 174,
end_byte: 226,
end_byte: 243,
start_point: Point {
row: 13,
column: 8,
Expand All @@ -211,8 +211,8 @@ fn test_definitions() {
Item {
name: qux,
range: Range {
start_byte: 236,
end_byte: 279,
start_byte: 253,
end_byte: 296,
start_point: Point {
row: 17,
column: 8,
Expand All @@ -229,8 +229,8 @@ fn test_definitions() {
Item {
name: S,
range: Range {
start_byte: 289,
end_byte: 302,
start_byte: 306,
end_byte: 319,
start_point: Point {
row: 19,
column: 8,
Expand All @@ -247,8 +247,8 @@ fn test_definitions() {
Item {
name: U,
range: Range {
start_byte: 312,
end_byte: 336,
start_byte: 329,
end_byte: 353,
start_point: Point {
row: 21,
column: 8,
Expand All @@ -265,8 +265,8 @@ fn test_definitions() {
Item {
name: I,
range: Range {
start_byte: 346,
end_byte: 396,
start_byte: 363,
end_byte: 413,
start_point: Point {
row: 23,
column: 8,
Expand All @@ -283,8 +283,8 @@ fn test_definitions() {
Item {
name: Iface,
range: Range {
start_byte: 406,
end_byte: 464,
start_byte: 423,
end_byte: 481,
start_point: Point {
row: 27,
column: 8,
Expand Down Expand Up @@ -321,19 +321,22 @@ fn test_definitions() {
name: qux,
},
],
implementations: None,
},
TypeDefinition {
directives: [],
implementations: None,
},
TypeDefinition {
directives: [],
implementations: None,
},
TypeDefinition {
directives: [
Directive {
range: Range {
start_byte: 298,
end_byte: 302,
start_byte: 315,
end_byte: 319,
start_point: Point {
row: 19,
column: 17,
Expand All @@ -346,13 +349,14 @@ fn test_definitions() {
name: qux,
},
],
implementations: None,
},
TypeDefinition {
directives: [
Directive {
range: Range {
start_byte: 320,
end_byte: 324,
start_byte: 337,
end_byte: 341,
start_point: Point {
row: 21,
column: 16,
Expand All @@ -365,13 +369,14 @@ fn test_definitions() {
name: qux,
},
],
implementations: None,
},
TypeDefinition {
directives: [
Directive {
range: Range {
start_byte: 354,
end_byte: 358,
start_byte: 371,
end_byte: 375,
start_point: Point {
row: 23,
column: 16,
Expand All @@ -384,13 +389,14 @@ fn test_definitions() {
name: qux,
},
],
implementations: None,
},
TypeDefinition {
directives: [
Directive {
range: Range {
start_byte: 422,
end_byte: 426,
start_byte: 439,
end_byte: 443,
start_point: Point {
row: 27,
column: 24,
Expand All @@ -403,6 +409,7 @@ fn test_definitions() {
name: qux,
},
],
implementations: None,
},
],
},
Expand All @@ -417,6 +424,11 @@ fn test_definitions() {
data: [
TypeExtension {
directives: [],
implementations: Some(
[
Iface,
],
),
},
],
},
Expand Down Expand Up @@ -491,6 +503,7 @@ fn test_definitions() {
data: [
TypeDefinition {
directives: [],
implementations: None,
},
TypeDefinition {
directives: [
Expand All @@ -510,6 +523,7 @@ fn test_definitions() {
name: foo,
},
],
implementations: None,
},
],
},
Expand Down
24 changes: 24 additions & 0 deletions src/gqls-parse/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,30 @@ fn test_parse_empty_type() {
);
}

#[test]
fn test_parse_implements_interface() {
test(
"type Foo implements Bar {}",
expect![[
r#"(document (item (type_definition (object_type_definition (name) (implements_interfaces (named_type (name))) (fields_definition)))))"#
]],
);

test(
"type Foo implements Bar & Bar {}",
expect![[
r#"(document (item (type_definition (object_type_definition (name) (implements_interfaces (implements_interfaces (named_type (name))) (named_type (name))) (fields_definition)))))"#
]],
);

test(
"type Foo implements & Bar & Bar {}",
expect![[
r#"(document (item (type_definition (object_type_definition (name) (implements_interfaces (implements_interfaces (named_type (name))) (named_type (name))) (fields_definition)))))"#
]],
);
}

#[test]
fn test_parse_union() {
test(
Expand Down
14 changes: 14 additions & 0 deletions src/gqls/src/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub fn capabilities() -> ServerCapabilities {
references_provider: Some(OneOf::Left(true)),
document_symbol_provider: Some(OneOf::Left(true)),
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
workspace: Some(WorkspaceServerCapabilities {
workspace_folders: Some(WorkspaceFoldersServerCapabilities {
supported: Some(true),
Expand Down Expand Up @@ -197,6 +198,19 @@ impl LanguageServer for Gqls {
})
}

async fn goto_implementation(
&self,
params: GotoDefinitionParams,
) -> jsonrpc::Result<Option<GotoDefinitionResponse>> {
let position = params.text_document_position_params;
self.with_ide(|ide| {
let path = ide.path(&position.text_document.uri)?;
let analysis = ide.analysis();
let locations = analysis.implementations(path, position.position.convert());
Ok(convert::locations_to_goto_definition_response(&locations))
})
}

async fn references(&self, params: ReferenceParams) -> jsonrpc::Result<Option<Vec<Location>>> {
let position = params.text_document_position;
self.with_ide(|ide| {
Expand Down
2 changes: 2 additions & 0 deletions tree-sitter-graphql/grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ module.exports = grammar({
seq("{", repeat1($.enum_value_definition), "}"),
enum_value_definition: ($) =>
seq(optional($.description), $.enum_value, optional($.directives)),
implements_interfaces: ($) =>
seq("implements", optional("&"), sepBy1($.named_type, "&")),
implements_interfaces: ($) =>
choice(
seq($.implements_interfaces, "&", $.named_type),
Expand Down

0 comments on commit 84c6215

Please sign in to comment.