Skip to content

Commit

Permalink
feat(grit): implement GritQL log() function (#4003)
Browse files Browse the repository at this point in the history
  • Loading branch information
arendjr committed Sep 19, 2024
1 parent 33d0958 commit 76a4f1a
Show file tree
Hide file tree
Showing 15 changed files with 131 additions and 55 deletions.
11 changes: 10 additions & 1 deletion crates/biome_grit_patterns/src/grit_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,16 @@ impl<'a> Binding<'a, GritQueryContext> for GritBinding<'a> {
}

fn get_sexp(&self) -> Option<String> {
None
Some(match self {
Self::File(path) => format!("({})", path.display()),
Self::Node(grit_target_node) => format!("({grit_target_node:?})"),
Self::Range(text_range, source) => format!(
"({})",
&source[text_range.start().into()..text_range.end().into()]
),
Self::Empty(_, _) => "(empty)".to_owned(),
Self::Constant(constant) => format!("({constant})"),
})
}

fn position(&self, _language: &GritTargetLanguage) -> Option<Range> {
Expand Down
6 changes: 3 additions & 3 deletions crates/biome_grit_patterns/src/grit_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use grit_pattern_matcher::file_owners::{FileOwner, FileOwners};
use grit_pattern_matcher::pattern::{
FilePtr, FileRegistry, Matcher, Pattern, ResolvedPattern, State, VariableSourceLocations,
};
use grit_util::{Ast, ByteRange, InputRanges, Range, VariableMatch};
use grit_util::{AnalysisLogs, Ast, ByteRange, InputRanges, Range, VariableMatch};
use im::Vector;
use std::collections::{BTreeMap, BTreeSet};
use std::ffi::OsStr;
Expand Down Expand Up @@ -64,7 +64,7 @@ pub struct GritQuery {
}

impl GritQuery {
pub fn execute(&self, file: GritTargetFile) -> Result<Vec<GritQueryResult>> {
pub fn execute(&self, file: GritTargetFile) -> Result<(Vec<GritQueryResult>, AnalysisLogs)> {
let file_owners = FileOwners::new();
let files = vec![file];
let file_ptr = FilePtr::new(0, 0);
Expand Down Expand Up @@ -100,7 +100,7 @@ impl GritQuery {
}
}

Ok(results)
Ok((results, logs))
}

pub fn from_node(
Expand Down
12 changes: 10 additions & 2 deletions crates/biome_grit_patterns/src/grit_target_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::util::TextRangeGritExt;
use biome_js_syntax::{JsSyntaxKind, JsSyntaxNode, JsSyntaxToken};
use biome_rowan::{NodeOrToken, SyntaxKind, SyntaxSlot, TextRange};
use grit_util::{AstCursor, AstNode as GritAstNode, ByteRange, CodeRange};
use std::{borrow::Cow, ops::Deref, str::Utf8Error};
use std::{borrow::Cow, fmt::Debug, ops::Deref, str::Utf8Error};

use NodeOrToken::*;

Expand Down Expand Up @@ -187,7 +187,7 @@ generate_target_node! {
[JsLanguage, JsSyntaxNode, JsSyntaxToken, JsSyntaxKind]
}

#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, PartialEq)]
pub struct GritTargetNode<'a> {
node: GritTargetLanguageNode,
tree: &'a GritTargetTree,
Expand Down Expand Up @@ -263,6 +263,14 @@ impl<'a> GritTargetNode<'a> {
}
}

impl<'a> Debug for GritTargetNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("GritTargetNode")
.field("node", &self.node)
.finish()
}
}

impl<'a> Deref for GritTargetNode<'a> {
type Target = GritTargetLanguageNode;

Expand Down
1 change: 1 addition & 0 deletions crates/biome_grit_patterns/src/pattern_compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ mod limit_compiler;
mod list_compiler;
mod list_index_compiler;
mod literal_compiler;
mod log_compiler;
mod map_accessor_compiler;
mod map_compiler;
mod match_compiler;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use super::{compilation_context::NodeCompilationContext, PatternCompiler};
use super::{
compilation_context::NodeCompilationContext, log_compiler::LogCompiler, PatternCompiler,
};
use crate::{grit_context::GritQueryContext, CompileError, NodeLikeArgumentError};
use biome_grit_syntax::{
AnyGritMaybeNamedArg, AnyGritPattern, GritNamedArgList, GritNodeLike, GritSyntaxKind,
Expand All @@ -18,6 +20,11 @@ pub(super) fn call_pattern_from_node_with_name(
context: &mut NodeCompilationContext,
is_rhs: bool,
) -> Result<Pattern<GritQueryContext>, CompileError> {
if name == "log" {
return LogCompiler::from_named_args(node.named_args(), context)
.map(|log| Pattern::Log(Box::new(log)));
}

let named_args = named_args_from_node(node, &name, context)?;
let mut args = named_args_to_map(named_args, context)?;
let named_args_count = node.named_args().into_iter().count();
Expand Down
33 changes: 33 additions & 0 deletions crates/biome_grit_patterns/src/pattern_compiler/log_compiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use super::call_compiler::*;
use super::compilation_context::NodeCompilationContext;
use crate::{grit_context::GritQueryContext, CompileError};
use biome_grit_syntax::GritNamedArgList;
use grit_pattern_matcher::pattern::{Log, Pattern, VariableInfo};

pub(crate) struct LogCompiler;

impl LogCompiler {
pub(crate) fn from_named_args(
named_args: GritNamedArgList,
context: &mut NodeCompilationContext,
) -> Result<Log<GritQueryContext>, CompileError> {
let named_args = node_to_args_pairs(
"log",
named_args,
&context.compilation.lang,
&Some(vec!["message".to_owned(), "variable".to_owned()]),
)?;
let mut args = named_args_to_map(named_args, context)?;
let message = args.remove("$message");
let variable = args.remove("$variable");
let variable = variable.and_then(|pattern| match pattern {
Pattern::Variable(variable) => {
let source_location = &context.vars_array[variable.scope][variable.index];
Some(VariableInfo::new(source_location.name.clone(), variable))
}
_ => None,
});

Ok(Log::new(variable, message))
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
use super::call_compiler::*;
use super::compilation_context::NodeCompilationContext;
use super::log_compiler::LogCompiler;
use crate::NodeLikeArgumentError;
use crate::{grit_context::GritQueryContext, CompileError};
use biome_grit_syntax::GritPredicateCall;
use biome_rowan::AstNode;
use grit_pattern_matcher::pattern::PrCall;
use grit_pattern_matcher::pattern::{PrCall, Predicate};

pub(crate) struct PrCallCompiler;

impl PrCallCompiler {
pub(crate) fn from_node(
node: &GritPredicateCall,
context: &mut NodeCompilationContext,
) -> Result<PrCall<GritQueryContext>, CompileError> {
) -> Result<Predicate<GritQueryContext>, CompileError> {
let name = node.name()?;
let name = name.text();

if name == "log" {
return LogCompiler::from_named_args(node.named_args(), context).map(Predicate::Log);
}

let info = if let Some(info) = context.compilation.predicate_definition_info.get(&name) {
info
} else if let Some(info) = context.compilation.function_definition_info.get(&name) {
Expand All @@ -37,6 +42,6 @@ impl PrCallCompiler {
}

let args = match_args_to_params(&name, args, &params, &context.compilation.lang)?;
Ok(PrCall::new(info.index, args))
Ok(Predicate::Call(Box::new(PrCall::new(info.index, args))))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ impl PredicateCompiler {
AnyGritPredicate::GritPredicateAssignment(node) => Ok(Predicate::Assignment(Box::new(
PrAssignmentCompiler::from_node(node, context)?,
))),
AnyGritPredicate::GritPredicateCall(node) => Ok(Predicate::Call(Box::new(
PrCallCompiler::from_node(node, context)?,
))),
AnyGritPredicate::GritPredicateCall(node) => PrCallCompiler::from_node(node, context),
AnyGritPredicate::GritPredicateEqual(node) => Ok(Predicate::Equal(Box::new(
PrEqualCompiler::from_node(node, context)?,
))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -523,38 +523,6 @@ mod tests {
,
),
),
tree: GritTargetTree {
root: JsLanguage(
Node(
0: JS_MODULE@0..20
0: (empty)
1: (empty)
2: JS_DIRECTIVE_LIST@0..0
3: JS_MODULE_ITEM_LIST@0..20
0: JS_EXPRESSION_STATEMENT@0..20
0: JS_CALL_EXPRESSION@0..20
0: JS_STATIC_MEMBER_EXPRESSION@0..11
0: JS_IDENTIFIER_EXPRESSION@0..7
0: JS_REFERENCE_IDENTIFIER@0..7
0: IDENT@0..7 "console" [] []
1: DOT@7..8 "." [] []
2: JS_NAME@8..11
0: IDENT@8..11 "log" [] []
1: (empty)
2: (empty)
3: JS_CALL_ARGUMENTS@11..20
0: L_PAREN@11..12 "(" [] []
1: JS_CALL_ARGUMENT_LIST@12..19
0: JS_STRING_LITERAL_EXPRESSION@12..19
0: JS_STRING_LITERAL@12..19 "'hello'" [] []
2: R_PAREN@19..20 ")" [] []
1: (empty)
4: EOF@20..20 "" [] []
,
),
),
source: "console.log('hello')",
},
}
"###);
}
Expand Down
25 changes: 18 additions & 7 deletions crates/biome_grit_patterns/tests/quick_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use biome_js_syntax::JsFileSource;
#[test]
fn test_query() {
let parse_grit_result = parse_grit(
"`console.log($args)` where {
$args <: contains `world`
"`console.log($arg)` => . where {
log(message=\"This is a debug log\", variable=$arg),
}
",
);
Expand All @@ -28,16 +28,27 @@ fn test_query() {
println!("Diagnostics from compiling query:\n{:?}", query.diagnostics);
}

let body = r#"console.log("hello, world");
console.log("hello", world);
console.log(`hello ${world}`);
"#;
let body = r#"console.log("grape");"#;

let file = GritTargetFile {
path: "test.js".into(),
parse: parse(body, JsFileSource::tsx(), JsParserOptions::default()).into(),
};
let results = query.execute(file).expect("could not execute query");
let (results, logs) = query.execute(file).expect("could not execute query");

println!("Results: {results:#?}");

if !logs.is_empty() {
println!(
"\n## Logs\n\n{}",
logs.iter()
.map(|log| format!(
"Message: {}Syntax: {}",
log.message,
log.syntax_tree.as_deref().unwrap_or_default()
))
.collect::<Vec<_>>()
.join("\n")
);
}
}
19 changes: 17 additions & 2 deletions crates/biome_grit_patterns/tests/spec_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,27 @@ fn run_test(input: &'static str, _: &str, _: &str, _: &str) {
}
};

let results = query
let (results, logs) = query
.execute(target_file)
.unwrap_or_else(|err| panic!("cannot execute query from {query_path:?}: {err:?}"));
let snapshot_result = SnapshotResult::from_query_results(results);

let snapshot = format!("{snapshot_result:#?}");
let snapshot = if logs.is_empty() {
format!("{snapshot_result:#?}")
} else {
let logs = logs
.iter()
.map(|log| {
format!(
"Message: {}Syntax: {}",
log.message,
log.syntax_tree.as_deref().unwrap_or_default()
)
})
.collect::<Vec<_>>()
.join("\n");
format!("{snapshot_result:#?}\n\n## Logs\n\n{logs}")
};

insta::with_settings!({
prepend_module_to_snapshot => false,
Expand Down
3 changes: 3 additions & 0 deletions crates/biome_grit_patterns/tests/specs/ts/log.grit
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`console.log($arg)` => . where {
log(message="This is a debug log", variable=$arg),
}
17 changes: 17 additions & 0 deletions crates/biome_grit_patterns/tests/specs/ts/log.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
source: crates/biome_grit_patterns/tests/spec_tests.rs
expression: log
---
SnapshotResult {
messages: [],
matched_ranges: [
"1:1-1:21",
],
rewritten_files: [],
created_files: [],
}

## Logs

Message: This is a debug log
Syntax: (GritTargetNode { node: JsLanguage(Node(JS_CALL_ARGUMENT_LIST@12..19)) })
1 change: 1 addition & 0 deletions crates/biome_grit_patterns/tests/specs/ts/log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log("grape");
2 changes: 1 addition & 1 deletion crates/biome_service/src/file_handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ pub(crate) fn search(
query: &GritQuery,
_settings: WorkspaceSettingsHandle,
) -> Result<Vec<TextRange>, WorkspaceError> {
let query_result = query
let (query_result, _logs) = query
.execute(GritTargetFile {
path: path.to_path_buf(),
parse,
Expand Down

0 comments on commit 76a4f1a

Please sign in to comment.