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

feat(workspace): adds GraphQL parsing capabilities via feature #3238

Merged
merged 5 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion crates/biome_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ mod verbatim;
use crate::formatter::Formatter;
use crate::group_id::UniqueGroupIdBuilder;
use crate::prelude::TagKind;
use std::fmt;
use std::fmt::{Debug, Display};

use crate::builders::syntax_token_cow_slice;
Expand Down Expand Up @@ -673,7 +674,7 @@ impl FormatContext for SimpleFormatContext {
}
}

#[derive(Debug, Default, Eq, PartialEq)]
#[derive(Debug, Default, Eq, PartialEq, Copy, Clone)]
pub struct SimpleFormatOptions {
pub indent_style: IndentStyle,
pub indent_width: IndentWidth,
Expand Down Expand Up @@ -713,6 +714,12 @@ impl FormatOptions for SimpleFormatOptions {
}
}

impl Display for SimpleFormatOptions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt::Debug::fmt(self, f)
}
}

/// Lightweight sourcemap marker between source and output tokens
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(
Expand Down
4 changes: 2 additions & 2 deletions crates/biome_graphql_syntax/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ version = "0.1.0"
[dependencies]
biome_rowan = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"], optional = true }
serde = { workspace = true, features = ["derive"] }

[features]
serde = ["dep:serde", "schemars", "biome_rowan/serde"]
schema = ["schemars", "biome_rowan/serde"]

[lints]
workspace = true
68 changes: 68 additions & 0 deletions crates/biome_graphql_syntax/src/file_source.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::ffi::OsStr;
use std::path::Path;
use biome_rowan::FileSourceError;

#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(
Debug, Clone, Default, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct GraphqlFileSource {}

impl GraphqlFileSource {

/// Try to return the CSS file source corresponding to this file name from well-known files
ematipico marked this conversation as resolved.
Show resolved Hide resolved
pub fn try_from_well_known(file_name: &str) -> Result<Self, FileSourceError> {
// TODO: to be implemented
Err(FileSourceError::UnknownFileName(file_name.into()))
}

/// Try to return the CSS file source corresponding to this file extension
ematipico marked this conversation as resolved.
Show resolved Hide resolved
pub fn try_from_extension(extension: &str) -> Result<Self, FileSourceError> {
match extension {
"graphql" => Ok(Self::default()),
ematipico marked this conversation as resolved.
Show resolved Hide resolved
_ => Err(FileSourceError::UnknownExtension(
Default::default(),
extension.into(),
)),
}
}

/// Try to return the CSS file source corresponding to this language ID
ematipico marked this conversation as resolved.
Show resolved Hide resolved
///
/// See the [LSP spec] and [VS Code spec] for a list of language identifiers
///
/// [LSP spec]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem
/// [VS Code spec]: https://code.visualstudio.com/docs/languages/identifiers
pub fn try_from_language_id(language_id: &str) -> Result<Self, FileSourceError> {
match language_id {
"graphql" => Ok(Self::default()),
_ => Err(FileSourceError::UnknownLanguageId(language_id.into())),
}
}
}


impl TryFrom<&Path> for GraphqlFileSource {
type Error = FileSourceError;

fn try_from(path: &Path) -> Result<Self, Self::Error> {
let file_name = path
.file_name()
.and_then(OsStr::to_str)
.ok_or_else(|| FileSourceError::MissingFileName(path.into()))?;

if let Ok(file_source) = Self::try_from_well_known(file_name) {
return Ok(file_source);
}

// We assume the file extensions are case-insensitive
// and we use the lowercase form of them for pattern matching
let extension = &path
.extension()
.and_then(OsStr::to_str)
.map(str::to_lowercase)
.ok_or_else(|| FileSourceError::MissingFileExtension(path.into()))?;

Self::try_from_extension(extension)
}
}
2 changes: 2 additions & 0 deletions crates/biome_graphql_syntax/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

#[macro_use]
mod generated;
mod file_source;
mod syntax_node;

use biome_rowan::{AstNode, RawSyntaxKind, SyntaxKind};
pub use biome_rowan::{TextLen, TextRange, TextSize, TokenAtOffset, TriviaPieceKind, WalkEvent};
pub use file_source::GraphqlFileSource;
pub use generated::*;
pub use syntax_node::*;

Expand Down
4 changes: 1 addition & 3 deletions crates/biome_grit_parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ quickcheck_macros = { workspace = true }
tests_macros = { workspace = true }

[features]
schemars = ["dep:schemars"]
serde = ["biome_grit_syntax/serde"]
tests = []
schemars = ["dep:schemars", "biome_grit_syntax/schema"]

# cargo-workspaces metadata
[package.metadata.workspaces]
Expand Down
4 changes: 2 additions & 2 deletions crates/biome_grit_syntax/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ version = "0.5.7"
[dependencies]
biome_rowan = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"], optional = true }
serde = { workspace = true, features = ["derive"] }

[features]
serde = ["dep:serde", "schemars", "biome_rowan/serde"]
schema = ["schemars", "biome_rowan/serde"]

[lints]
workspace = true
5 changes: 5 additions & 0 deletions crates/biome_service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ biome_diagnostics = { workspace = true }
biome_flags = { workspace = true }
biome_formatter = { workspace = true, features = ["serde"] }
biome_fs = { workspace = true, features = ["serde"] }
biome_graphql_parser = { workspace = true }
biome_graphql_syntax = { workspace = true }
biome_grit_patterns = { workspace = true }
biome_js_analyze = { workspace = true }
biome_js_factory = { workspace = true, optional = true }
Expand Down Expand Up @@ -65,8 +67,11 @@ schema = [
"biome_text_edit/schemars",
"biome_json_syntax/schema",
"biome_css_syntax/schema",
"biome_graphql_syntax/schema",
]

graphql = []

[dev-dependencies]
insta = { workspace = true }
tests_macros = { workspace = true }
Expand Down
125 changes: 125 additions & 0 deletions crates/biome_service/src/file_handlers/graphql.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use super::{DocumentFileSource, ExtensionHandler, ParseResult};
use crate::file_handlers::DebugCapabilities;
use crate::file_handlers::{
AnalyzerCapabilities, Capabilities, FormatterCapabilities, ParserCapabilities,
};
use crate::settings::{
FormatSettings, LanguageListSettings, LanguageSettings, LinterSettings, OverrideSettings,
ServiceLanguage, Settings,
};
use crate::workspace::GetSyntaxTreeResult;
use biome_analyze::{AnalyzerConfiguration, AnalyzerOptions};
use biome_formatter::SimpleFormatOptions;
use biome_fs::BiomePath;
use biome_graphql_parser::parse_graphql_with_cache;
use biome_graphql_syntax::{GraphqlLanguage, GraphqlRoot, GraphqlSyntaxNode};
use biome_parser::AnyParse;
use biome_rowan::NodeCache;

#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct JsonParserSettings {
ematipico marked this conversation as resolved.
Show resolved Hide resolved
pub allow_comments: bool,
pub allow_trailing_commas: bool,
}

#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct JsonLinterSettings {
ematipico marked this conversation as resolved.
Show resolved Hide resolved
pub enabled: Option<bool>,
}

impl ServiceLanguage for GraphqlLanguage {
type FormatterSettings = ();
type LinterSettings = ();
type OrganizeImportsSettings = ();
type FormatOptions = SimpleFormatOptions;
type ParserSettings = ();
type EnvironmentSettings = ();

fn lookup_settings(language: &LanguageListSettings) -> &LanguageSettings<Self> {
&language.graphql
}

fn resolve_format_options(
_global: Option<&FormatSettings>,
_overrides: Option<&OverrideSettings>,
_language: Option<&()>,
_path: &BiomePath,
_document_file_source: &DocumentFileSource,
) -> Self::FormatOptions {
SimpleFormatOptions::default()
}

fn resolve_analyzer_options(
_global: Option<&Settings>,
_linter: Option<&LinterSettings>,
_overrides: Option<&OverrideSettings>,
_language: Option<&Self::LinterSettings>,
path: &BiomePath,
_file_source: &DocumentFileSource,
) -> AnalyzerOptions {
AnalyzerOptions {
configuration: AnalyzerConfiguration::default(),
file_path: path.to_path_buf(),
}
}
}

#[derive(Debug, Default, PartialEq, Eq)]
pub(crate) struct GraphqlFileHandler;

impl ExtensionHandler for GraphqlFileHandler {
fn capabilities(&self) -> Capabilities {
Capabilities {
parser: ParserCapabilities { parse: Some(parse) },
debug: DebugCapabilities {
debug_syntax_tree: Some(debug_syntax_tree),
debug_control_flow: None,
debug_formatter_ir: None,
},
analyzer: AnalyzerCapabilities {
lint: None,
code_actions: None,
rename: None,
fix_all: None,
organize_imports: None,
},
formatter: FormatterCapabilities {
format: None,
format_range: None,
format_on_type: None,
},
}
}
}

fn parse(
_biome_path: &BiomePath,
file_source: DocumentFileSource,
text: &str,
_settings: Option<&Settings>,
cache: &mut NodeCache,
) -> ParseResult {
let parse = parse_graphql_with_cache(text, cache);
let root = parse.syntax();
let diagnostics = parse.into_diagnostics();

ParseResult {
any_parse: AnyParse::new(
// SAFETY: the parser should always return a root node
root.as_send().unwrap(),
diagnostics,
),
language: Some(file_source),
}
}

fn debug_syntax_tree(_rome_path: &BiomePath, parse: AnyParse) -> GetSyntaxTreeResult {
let syntax: GraphqlSyntaxNode = parse.syntax();
let tree: GraphqlRoot = parse.tree();
GetSyntaxTreeResult {
cst: format!("{syntax:#?}"),
ast: format!("{tree:#?}"),
}
}
Loading
Loading