Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SchemaDocument find references LSP support #4670

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions compiler/crates/graphql-syntax/src/node/type_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,45 @@ pub enum TypeSystemDefinition {
}

impl TypeSystemDefinition {
/// Returns the span for the entire type definition.
pub fn span(&self) -> Span {
match self {
TypeSystemDefinition::SchemaDefinition(_extension) => Span::empty(), // Not implemented
TypeSystemDefinition::SchemaExtension(_extension) => Span::empty(), // Not implemented
TypeSystemDefinition::SchemaDefinition(definition) => definition.span,
TypeSystemDefinition::SchemaExtension(extension) => extension.span,
TypeSystemDefinition::ObjectTypeDefinition(definition) => definition.span,
TypeSystemDefinition::ObjectTypeExtension(extension) => extension.span,
TypeSystemDefinition::InterfaceTypeDefinition(definition) => definition.span,
TypeSystemDefinition::InterfaceTypeExtension(extension) => extension.span,
TypeSystemDefinition::UnionTypeDefinition(definition) => definition.span,
TypeSystemDefinition::UnionTypeExtension(extension) => extension.span,
TypeSystemDefinition::DirectiveDefinition(definition) => definition.span,
TypeSystemDefinition::InputObjectTypeDefinition(definition) => definition.span,
TypeSystemDefinition::InputObjectTypeExtension(extension) => extension.span,
TypeSystemDefinition::EnumTypeDefinition(definition) => definition.span,
TypeSystemDefinition::EnumTypeExtension(extension) => extension.span,
TypeSystemDefinition::ScalarTypeDefinition(definition) => definition.span,
TypeSystemDefinition::ScalarTypeExtension(extension) => extension.span,
}
}

/// Returns the span for the type definition name
/// or the span for the entire definition, if a name is non-existent.
pub fn name_span(&self) -> Span {
match self {
TypeSystemDefinition::SchemaDefinition(definition) => definition.span,
TypeSystemDefinition::SchemaExtension(extension) => extension.span,
TypeSystemDefinition::ObjectTypeDefinition(definition) => definition.name.span,
TypeSystemDefinition::ObjectTypeExtension(extension) => extension.name.span,
TypeSystemDefinition::ObjectTypeDefinition(extension) => extension.name.span,
TypeSystemDefinition::InterfaceTypeDefinition(extension) => extension.name.span,
TypeSystemDefinition::InterfaceTypeDefinition(definition) => definition.name.span,
TypeSystemDefinition::InterfaceTypeExtension(extension) => extension.name.span,
TypeSystemDefinition::UnionTypeDefinition(extension) => extension.name.span,
TypeSystemDefinition::UnionTypeDefinition(definition) => definition.name.span,
TypeSystemDefinition::UnionTypeExtension(extension) => extension.name.span,
TypeSystemDefinition::DirectiveDefinition(extension) => extension.name.span,
TypeSystemDefinition::InputObjectTypeDefinition(extension) => extension.name.span,
TypeSystemDefinition::DirectiveDefinition(definition) => definition.name.span,
TypeSystemDefinition::InputObjectTypeDefinition(definition) => definition.name.span,
TypeSystemDefinition::InputObjectTypeExtension(extension) => extension.name.span,
TypeSystemDefinition::EnumTypeDefinition(extension) => extension.name.span,
TypeSystemDefinition::EnumTypeDefinition(definition) => definition.name.span,
TypeSystemDefinition::EnumTypeExtension(extension) => extension.name.span,
TypeSystemDefinition::ScalarTypeDefinition(extension) => extension.name.span,
TypeSystemDefinition::ScalarTypeDefinition(definition) => definition.name.span,
TypeSystemDefinition::ScalarTypeExtension(extension) => extension.name.span,
}
}
Expand Down
204 changes: 169 additions & 35 deletions compiler/crates/relay-lsp/src/node_resolution_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ use graphql_syntax::Argument;
use graphql_syntax::Directive;
use graphql_syntax::ExecutableDefinition;
use graphql_syntax::ExecutableDocument;
use graphql_syntax::FieldDefinition;
use graphql_syntax::FragmentDefinition;
use graphql_syntax::FragmentSpread;
use graphql_syntax::InlineFragment;
use graphql_syntax::LinkedField;
use graphql_syntax::List;
use graphql_syntax::OperationDefinition;
use graphql_syntax::ScalarField;
use graphql_syntax::SchemaDocument;
use graphql_syntax::Selection;
use graphql_syntax::TypeCondition;
use intern::string_key::StringKey;
use schema::TypeSystemDefinition;

use crate::lsp_runtime_error::LSPRuntimeError;
use crate::lsp_runtime_error::LSPRuntimeResult;
Expand All @@ -30,6 +33,7 @@ pub use type_path::TypePathItem;

#[derive(Debug, Clone, PartialEq)]
pub enum NodeKind {
SchemaDocument,
OperationDefinition(OperationDefinition),
FragmentDefinition(FragmentDefinition),
FieldName,
Expand Down Expand Up @@ -93,6 +97,94 @@ fn type_condition_at_position(
Some(NodeKind::TypeCondition(type_condition.type_.value))
}

pub fn create_node_resolution_info_for_schema_document(
document: SchemaDocument,
position_span: Span,
) -> LSPRuntimeResult<NodeResolutionInfo> {
let definition = document
.definitions
.iter()
.find(|definition| definition.span().contains(position_span))
.ok_or(LSPRuntimeError::ExpectedError)?;

let mut node_resolution_info = NodeResolutionInfo::new(NodeKind::SchemaDocument);

match definition {
TypeSystemDefinition::ObjectTypeDefinition(object_type) => {
build_node_resolution_info_from_field_definitions(
&object_type.fields,
TypePathItem::ObjectType {
name: object_type.name.value,
},
position_span,
&mut node_resolution_info,
);

Ok(node_resolution_info)
}
TypeSystemDefinition::ObjectTypeExtension(object_type) => {
build_node_resolution_info_from_field_definitions(
&object_type.fields,
TypePathItem::ObjectType {
name: object_type.name.value,
},
position_span,
&mut node_resolution_info,
);

Ok(node_resolution_info)
}
TypeSystemDefinition::InterfaceTypeDefinition(interface_type) => {
build_node_resolution_info_from_field_definitions(
&interface_type.fields,
TypePathItem::InterfaceType {
name: interface_type.name.value,
},
position_span,
&mut node_resolution_info,
);

Ok(node_resolution_info)
}
TypeSystemDefinition::InterfaceTypeExtension(interface_type) => {
build_node_resolution_info_from_field_definitions(
&interface_type.fields,
TypePathItem::InterfaceType {
name: interface_type.name.value,
},
position_span,
&mut node_resolution_info,
);

Ok(node_resolution_info)
}
_ => Err(LSPRuntimeError::ExpectedError),
}
}

fn build_node_resolution_info_from_field_definitions(
fields: &Option<List<FieldDefinition>>,
type_path_item: TypePathItem,
position_span: Span,
node_resolution_info: &mut NodeResolutionInfo,
) {
if let Some(fields) = &fields {
if let Some(field) = fields
.items
.iter()
.find(|item| item.span.contains(position_span))
{
node_resolution_info.kind = NodeKind::FieldName;
node_resolution_info.type_path.add_type(type_path_item);
node_resolution_info
.type_path
.add_type(TypePathItem::FieldDefinition {
name: field.name.value,
});
}
}
}

pub fn create_node_resolution_info(
document: ExecutableDocument,
position_span: Span,
Expand Down Expand Up @@ -306,17 +398,19 @@ mod test {
use common::SourceLocationKey;
use common::Span;
use graphql_syntax::parse_executable;
use graphql_syntax::parse_schema_document;
use intern::string_key::Intern;

use super::create_node_resolution_info;
use super::create_node_resolution_info_for_schema_document;
use super::NodeKind;
use super::NodeResolutionInfo;

fn parse_and_get_node_info(source: &str, pos: u32) -> NodeResolutionInfo {
fn parse_and_get_node_info(source: &str, sub_str: &str) -> NodeResolutionInfo {
let document =
parse_executable(source, SourceLocationKey::standalone("/test/file")).unwrap();

// Select the `uri` field
let pos = source.find(sub_str).unwrap() as u32;
let position_span = Span {
start: pos,
end: pos,
Expand All @@ -325,19 +419,59 @@ mod test {
create_node_resolution_info(document, position_span).unwrap()
}

fn parse_and_get_schema_document_node_info(source: &str, sub_str: &str) -> NodeResolutionInfo {
let document =
parse_schema_document(source, SourceLocationKey::standalone("/test/file")).unwrap();

let pos = source.find(sub_str).unwrap() as u32;
let position_span = Span {
start: pos,
end: pos,
};

create_node_resolution_info_for_schema_document(document, position_span).unwrap()
}

#[test]
fn create_node_resolution_info_for_schema_document_object_type_field() {
let node_resolution_info = parse_and_get_schema_document_node_info(
r#"
type Object {
uri: String
}
"#,
"uri",
);

assert_eq!(node_resolution_info.kind, NodeKind::FieldName);
}

#[test]
fn create_node_resolution_info_for_schema_document_interface_field() {
let node_resolution_info = parse_and_get_schema_document_node_info(
r#"
interface Interface {
uri: String
}
"#,
"uri",
);

assert_eq!(node_resolution_info.kind, NodeKind::FieldName);
}

#[test]
fn create_node_resolution_info_test() {
let node_resolution_info = parse_and_get_node_info(
r#"
fragment User_data on User {
name
profile_picture {
uri
fragment User_data on User {
name
profile_picture {
uri
}
}
}
"#,
// Select the `uri` field
117,
"#,
"uri",
);

assert_eq!(node_resolution_info.kind, NodeKind::FieldName);
Expand All @@ -347,15 +481,18 @@ mod test {
fn create_node_resolution_info_test_position_outside() {
let document = parse_executable(
r#"
fragment User_data on User {
name
}
"#,
fragment User_data on User {
name
}
"#,
SourceLocationKey::standalone("/test/file"),
)
.unwrap();
// Position is outside of the document
let position_span = Span { start: 86, end: 87 };
let position_span = Span {
start: 1000,
end: 1001,
};
let result = create_node_resolution_info(document, position_span);
assert!(result.is_err());
}
Expand All @@ -364,12 +501,11 @@ mod test {
fn create_node_resolution_info_fragment_def_name() {
let node_resolution_info = parse_and_get_node_info(
r#"
fragment User_data on User {
name
}
"#,
// Select the `User_data` fragment name
26,
fragment User_data on User {
name
}
"#,
"User_data",
);

match node_resolution_info.kind {
Expand All @@ -384,12 +520,11 @@ mod test {
fn create_node_resolution_info_fragment_def_type_condition() {
let node_resolution_info = parse_and_get_node_info(
r#"
fragment User_data on User {
name
}
"#,
// Select the `User` type in fragment declaration
35,
fragment Test on User {
name
}
"#,
"User",
);

assert_eq!(
Expand All @@ -402,15 +537,14 @@ mod test {
fn create_node_resolution_info_inline_fragment_type_condition() {
let node_resolution_info = parse_and_get_node_info(
r#"
fragment User_data on User {
name
... on User {
id
fragment Test on Person {
name
... on User {
id
}
}
}
"#,
// Select the `User` type in fragment declaration
84,
"#,
"User",
);

assert_eq!(
Expand Down
Loading
Loading