<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for providing the completion language feature
use crate::lsp::{
    CompletionItem, CompletionParams, CompletionResponse, Message, ServerRequestId, ServerResponse,
    Url,
};
use crate::type_path::{TypePath, TypePathItem};
use crate::utils::extract_executable_document_from_text;
use common::Span;
use crossbeam::Sender;
use graphql_ir::Program;
use graphql_syntax::{
    Directive, DirectiveLocation, ExecutableDefinition, FragmentSpread, InlineFragment,
    LinkedField, List, OperationDefinition, OperationKind, ScalarField, Selection,
};
use graphql_syntax::{ExecutableDocument, GraphQLSource};
use interner::StringKey;
use log::info;
use relay_compiler::FileCategorizer;
use schema::{Directive as SchemaDirective, Schema, Type, TypeReference, TypeWithFields};
use std::{
    collections::HashMap,
    path::PathBuf,
    sync::{Arc, RwLock},
};

#[derive(Debug, Copy, Clone)]
pub enum CompletionKind {
    FieldName,
    FragmentSpread,
    DirectiveName { location: DirectiveLocation },
}
#[derive(Debug)]
pub struct CompletionRequest {
    /// The type of the completion request we're responding to
    kind: CompletionKind,
    /// A list of type metadata that we can use to resolve the leaf
    /// type the request is being made against
    type_path: TypePath,
    /// The project the request belongs to
    pub project_name: StringKey,
}

impl CompletionRequest {
    fn new(project_name: StringKey) -> Self {
        Self {
            kind: CompletionKind::FieldName,
            type_path: Default::default(),
            project_name,
        }
    }
}

pub fn create_completion_request(
    document: ExecutableDocument,
    position_span: Span,
    project_name: StringKey,
) -> Option<CompletionRequest> {
    info!("Building completion path for {:#?}", document);
    let mut completion_request = CompletionRequest::new(project_name);

    for definition in document.definitions {
        match &definition {
            ExecutableDefinition::Operation(operation) => {
                if operation.location.contains(position_span) {
                    let (_, kind) = operation.operation.clone()?;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::Operation(kind));

                    info!(
                        "Completion request is within operation: {:?}",
                        operation.name
                    );
                    let OperationDefinition {
                        selections,
                        directives,
                        ..
                    } = operation;

                    let directive_location = match kind {
                        OperationKind::Query => DirectiveLocation::Query,
                        OperationKind::Mutation => DirectiveLocation::Mutation,
                        OperationKind::Subscription => DirectiveLocation::Subscription,
                    };

                    build_request_from_selection_or_directives(
                        selections,
                        directives,
                        directive_location,
                        position_span,
                        &mut completion_request,
                    );
                }
                // Check if the position span is within this operation's span
            }
            ExecutableDefinition::Fragment(fragment) => {
                if fragment.location.contains(position_span) {
                    let type_name = fragment.type_condition.type_.value;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::FragmentDefinition { type_name });
                    build_request_from_selection_or_directives(
                        &fragment.selections,
                        &fragment.directives,
                        DirectiveLocation::FragmentDefinition,
                        position_span,
                        &mut completion_request,
                    );
                }
            }
        }
    }
    Some(completion_request)
}

fn resolve_completion_items_from_fields<T: TypeWithFields>(
    type_: &T,
    schema: &Schema,
) -> Vec<CompletionItem> {
    type_
        .fields()
        .iter()
        .map(|field_id| {
            let field = schema.field(*field_id);
            let name = field.name.to_string();
            CompletionItem::new_simple(name, String::from(""))
        })
        .collect()
}

fn resolve_completion_items_for_fragment_spread(
    type_: Type,
    source_program: &Program,
    schema: &Schema,
) -> Vec<CompletionItem> {
    let mut valid_fragments = vec![];
    for fragment in source_program.fragments() {
        if fragment.type_condition == type_ && !fragment.variable_definitions.is_empty() {<commit_msg>Add `{}` when auto complet linked fields<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

//! Utilities for providing the completion language feature
use crate::lsp::{
    CompletionItem, CompletionParams, CompletionResponse, Message, ServerRequestId, ServerResponse,
    Url,
};
use crate::type_path::{TypePath, TypePathItem};
use crate::utils::extract_executable_document_from_text;
use common::Span;
use crossbeam::Sender;
use graphql_ir::Program;
use graphql_syntax::{
    Directive, DirectiveLocation, ExecutableDefinition, FragmentSpread, InlineFragment,
    LinkedField, List, OperationDefinition, OperationKind, ScalarField, Selection,
};
use graphql_syntax::{ExecutableDocument, GraphQLSource};
use interner::StringKey;
use log::info;
use relay_compiler::FileCategorizer;
use schema::{Directive as SchemaDirective, Schema, Type, TypeReference, TypeWithFields};
use std::{
    collections::HashMap,
    path::PathBuf,
    sync::{Arc, RwLock},
};

#[derive(Debug, Copy, Clone)]
pub enum CompletionKind {
    FieldName,
    FragmentSpread,
    DirectiveName { location: DirectiveLocation },
}
#[derive(Debug)]
pub struct CompletionRequest {
    /// The type of the completion request we're responding to
    kind: CompletionKind,
    /// A list of type metadata that we can use to resolve the leaf
    /// type the request is being made against
    type_path: TypePath,
    /// The project the request belongs to
    pub project_name: StringKey,
}

impl CompletionRequest {
    fn new(project_name: StringKey) -> Self {
        Self {
            kind: CompletionKind::FieldName,
            type_path: Default::default(),
            project_name,
        }
    }
}

pub fn create_completion_request(
    document: ExecutableDocument,
    position_span: Span,
    project_name: StringKey,
) -> Option<CompletionRequest> {
    info!("Building completion path for {:#?}", document);
    let mut completion_request = CompletionRequest::new(project_name);

    for definition in document.definitions {
        match &definition {
            ExecutableDefinition::Operation(operation) => {
                if operation.location.contains(position_span) {
                    let (_, kind) = operation.operation.clone()?;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::Operation(kind));

                    info!(
                        "Completion request is within operation: {:?}",
                        operation.name
                    );
                    let OperationDefinition {
                        selections,
                        directives,
                        ..
                    } = operation;

                    let directive_location = match kind {
                        OperationKind::Query => DirectiveLocation::Query,
                        OperationKind::Mutation => DirectiveLocation::Mutation,
                        OperationKind::Subscription => DirectiveLocation::Subscription,
                    };

                    build_request_from_selection_or_directives(
                        selections,
                        directives,
                        directive_location,
                        position_span,
                        &mut completion_request,
                    );
                }
                // Check if the position span is within this operation's span
            }
            ExecutableDefinition::Fragment(fragment) => {
                if fragment.location.contains(position_span) {
                    let type_name = fragment.type_condition.type_.value;
                    completion_request
                        .type_path
                        .add_type(TypePathItem::FragmentDefinition { type_name });
                    build_request_from_selection_or_directives(
                        &fragment.selections,
                        &fragment.directives,
                        DirectiveLocation::FragmentDefinition,
                        position_span,
                        &mut completion_request,
                    );
                }
            }
        }
    }
    Some(completion_request)
}

fn resolve_completion_items_from_fields<T: TypeWithFields>(
    type_: &T,
    schema: &Schema,
) -> Vec<CompletionItem> {
    type_
        .fields()
        .iter()
        .map(|field_id| {
            let field = schema.field(*field_id);
            let name = field.name.to_string();
            if field.type_.inner().is_scalar() {
                CompletionItem::new_simple(name, "".into())
            } else {
                let insert_text = format!("{} {{\n\t$1\n}}", name);
                CompletionItem {
                    label: name,
                    kind: None,
                    detail: None,
                    documentation: None,
                    deprecated: None,
                    preselect: None,
                    sort_text: None,
                    filter_text: None,
                    insert_text: Some(insert_text),
                    insert_text_format: Some(lsp_types::InsertTextFormat::Snippet),
                    text_edit: None,
                    additional_text_edits: None,
                    command: None,
                    data: None,
                    tags: None,
                }
            }
        })
        .collect()
}

fn resolve_completion_items_for_fragment_spread(
    type_: Type,
    source_program: &Program,
    schema: &Schema,
) -> Vec<CompletionItem> {
    let mut valid_fragments = vec![];
    for fragment in source_program.fragments() {
        if fragment.type_condition == type_ && !fragment.variable_definitions.is_empty() {
{'fraction_non_alphanumeric': 0.08762744557729402, 'fraction_numerical': 9.18526683200147e-05, 'mean_word_length': 1.868282402528978, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6122823', 'n_tokens_mistral': 2710, 'n_tokens_neox': 2593, 'n_words': 634}