Skip to content

Commit 9d8ddf1

Browse files
committed
feat(workspace): add debug_type_info
1 parent 1715ec6 commit 9d8ddf1

17 files changed

+198
-67
lines changed

crates/biome_js_type_info/src/format_type_info.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -261,15 +261,15 @@ impl Format<FormatTypeContext> for FunctionParameter {
261261
});
262262
write!(
263263
f,
264-
[&group(&format_args![
264+
[&group(&block_indent(&format_args![
265265
optional,
266266
space(),
267267
self.name.as_ref().unwrap_or(&Text::Static("(unnamed)")),
268268
text(":"),
269269
space(),
270270
&self.ty,
271271
bindings
272-
])]
272+
]))]
273273
)
274274
}
275275
}
@@ -653,7 +653,7 @@ impl Format<FormatTypeContext> for FmtFunctionParameters<'_> {
653653
}
654654

655655
let function_parameters = format_with(|f| {
656-
let separator = format_with(|f| write!(f, [&format_args![text(","), space()]]));
656+
let separator = format_with(|f| write!(f, [&format_args![soft_line_break_or_space()]]));
657657
let mut joiner = f.join_with(separator);
658658
for part in self.0 {
659659
joiner.entry(&format_args![part]);
@@ -679,19 +679,15 @@ impl Format<FormatTypeContext> for FmtFunctionParameterBindings<'_> {
679679
}
680680

681681
let function_parameters = format_with(|f| {
682-
let separator = format_with(|f| write!(f, [&format_args![text(","), space()]]));
682+
let separator =
683+
format_with(|f| write!(f, [&format_args![text(","), soft_line_break_or_space()]]));
683684
let mut joiner = f.join_with(separator);
684685
for part in self.0 {
685686
joiner.entry(&format_args![&part.name, text(":"), &part.ty]);
686687
}
687688
joiner.finish()
688689
});
689-
write!(
690-
f,
691-
[&format_args![&group(&soft_block_indent(
692-
&function_parameters
693-
)),]]
694-
)
690+
write!(f, [&function_parameters])
695691
}
696692
}
697693

crates/biome_js_type_info/tests/snapshots/infer_type_of_function_with_destructured_arguments.snap

+24-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
source: crates/biome_js_type_info/tests/utils/mod.rs
33
expression: content
4+
snapshot_kind: text
45
---
56
```ts
67
function destruct(
@@ -14,20 +15,7 @@ function destruct(
1415
sync Function "destruct" {
1516
accepts: {
1617
params: [
17-
required (unnamed): Object {
18-
prototype: No prototype
19-
members: {TypeMembers(
20-
Property(
21-
[a, required]
22-
Type(number)
23-
)
24-
Property(
25-
[b, required]
26-
Type(string)
27-
)
28-
)}
29-
} (bindings:
30-
a:Object {
18+
required (unnamed): Object {
3119
prototype: No prototype
3220
members: {TypeMembers(
3321
Property(
@@ -39,7 +27,7 @@ sync Function "destruct" {
3927
Type(string)
4028
)
4129
)}
42-
}.a, b:Object {
30+
} (bindings: a:Object {
4331
prototype: No prototype
4432
members: {TypeMembers(
4533
Property(
@@ -51,19 +39,31 @@ sync Function "destruct" {
5139
Type(string)
5240
)
5341
)}
54-
}.b
55-
), required (unnamed): "Array" {
56-
resolved: unknown
57-
type_args: boolean
58-
} (bindings:
59-
first:"Array" {
42+
}.a,
43+
b:Object {
44+
prototype: No prototype
45+
members: {TypeMembers(
46+
Property(
47+
[a, required]
48+
Type(number)
49+
)
50+
Property(
51+
[b, required]
52+
Type(string)
53+
)
54+
)}
55+
}.b)
56+
required (unnamed): "Array" {
57+
resolved: unknown
58+
type_args: boolean
59+
} (bindings: first:"Array" {
6060
resolved: unknown
6161
type_args: boolean
62-
}[0], rest:[(1 others)..."Array" {
62+
}[0],
63+
rest:[(1 others)..."Array" {
6364
resolved: unknown
6465
type_args: boolean
65-
}]
66-
)
66+
}])
6767
]
6868
type_args: []
6969
}

crates/biome_service/src/file_handlers/astro.rs

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ impl ExtensionHandler for AstroFileHandler {
8080
debug_syntax_tree: None,
8181
debug_control_flow: None,
8282
debug_formatter_ir: None,
83+
debug_type_info: None,
8384
},
8485
analyzer: AnalyzerCapabilities {
8586
lint: Some(lint),

crates/biome_service/src/file_handlers/css.rs

+1
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ impl ExtensionHandler for CssFileHandler {
328328
debug_syntax_tree: Some(debug_syntax_tree),
329329
debug_control_flow: None,
330330
debug_formatter_ir: Some(debug_formatter_ir),
331+
debug_type_info: None,
331332
},
332333
analyzer: AnalyzerCapabilities {
333334
lint: Some(lint),

crates/biome_service/src/file_handlers/graphql.rs

+1
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ impl ExtensionHandler for GraphqlFileHandler {
275275
debug_syntax_tree: Some(debug_syntax_tree),
276276
debug_control_flow: None,
277277
debug_formatter_ir: Some(debug_formatter_ir),
278+
debug_type_info: None,
278279
},
279280
analyzer: AnalyzerCapabilities {
280281
lint: Some(lint),

crates/biome_service/src/file_handlers/grit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ impl ExtensionHandler for GritFileHandler {
247247
debug_syntax_tree: Some(debug_syntax_tree),
248248
debug_control_flow: None,
249249
debug_formatter_ir: Some(debug_formatter_ir),
250+
debug_type_info: None,
250251
},
251252
analyzer: AnalyzerCapabilities {
252253
lint: Some(lint),

crates/biome_service/src/file_handlers/html.rs

+1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ impl ExtensionHandler for HtmlFileHandler {
206206
debug_syntax_tree: Some(debug_syntax_tree),
207207
debug_control_flow: None,
208208
debug_formatter_ir: Some(debug_formatter_ir),
209+
debug_type_info: None,
209210
},
210211
analyzer: AnalyzerCapabilities {
211212
lint: Some(lint),

crates/biome_service/src/file_handlers/javascript.rs

+36-2
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,13 @@ use biome_js_formatter::format_node;
4747
use biome_js_parser::JsParserOptions;
4848
use biome_js_semantic::{SemanticModelOptions, semantic_model};
4949
use biome_js_syntax::{
50-
AnyJsRoot, JsFileSource, JsLanguage, JsSyntaxNode, LanguageVariant, TextRange, TextSize,
50+
AnyJsRoot, JsClassDeclaration, JsClassExpression, JsFileSource, JsFunctionDeclaration,
51+
JsLanguage, JsSyntaxNode, JsVariableDeclarator, LanguageVariant, TextRange, TextSize,
5152
TokenAtOffset,
5253
};
54+
use biome_js_type_info::Type;
5355
use biome_parser::AnyParse;
54-
use biome_rowan::{AstNode, BatchMutationExt, Direction, NodeCache};
56+
use biome_rowan::{AstNode, BatchMutationExt, Direction, NodeCache, WalkEvent};
5557
use camino::Utf8Path;
5658
use serde::{Deserialize, Serialize};
5759
use std::borrow::Cow;
@@ -481,6 +483,7 @@ impl ExtensionHandler for JsFileHandler {
481483
debug_syntax_tree: Some(debug_syntax_tree),
482484
debug_control_flow: Some(debug_control_flow),
483485
debug_formatter_ir: Some(debug_formatter_ir),
486+
debug_type_info: Some(debug_type_info),
484487
},
485488
analyzer: AnalyzerCapabilities {
486489
lint: Some(lint),
@@ -630,6 +633,37 @@ fn debug_formatter_ir(
630633
Ok(root_element.to_string())
631634
}
632635

636+
fn debug_type_info(_path: &BiomePath, parse: AnyParse) -> Result<String, WorkspaceError> {
637+
let tree: AnyJsRoot = parse.tree();
638+
let mut result = String::new();
639+
let preorder = tree.syntax().preorder();
640+
641+
for event in preorder {
642+
match event {
643+
WalkEvent::Enter(node) => {
644+
if let Some(node) = JsVariableDeclarator::cast_ref(&node) {
645+
if let Some(ty) = Type::from_js_variable_declarator(&node) {
646+
result.push_str(&ty.to_string());
647+
result.push('\n');
648+
}
649+
} else if let Some(function) = JsFunctionDeclaration::cast_ref(&node) {
650+
result.push_str(&Type::from_js_function_declaration(&function).to_string());
651+
result.push('\n');
652+
} else if let Some(class) = JsClassDeclaration::cast_ref(&node) {
653+
result.push_str(&Type::from_js_class_declaration(&class).to_string());
654+
result.push('\n');
655+
} else if let Some(expression) = JsClassExpression::cast_ref(&node) {
656+
result.push_str(&Type::from_js_class_expression(&expression).to_string());
657+
result.push('\n');
658+
}
659+
}
660+
WalkEvent::Leave(_) => {}
661+
}
662+
}
663+
664+
Ok(result)
665+
}
666+
633667
pub(crate) fn lint(params: LintParams) -> LintResults {
634668
let _ =
635669
debug_span!("Linting JavaScript file", path =? params.path, language =? params.language)

crates/biome_service/src/file_handlers/json.rs

+1
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ impl ExtensionHandler for JsonFileHandler {
327327
debug_syntax_tree: Some(debug_syntax_tree),
328328
debug_control_flow: None,
329329
debug_formatter_ir: Some(debug_formatter_ir),
330+
debug_type_info: None,
330331
},
331332
analyzer: AnalyzerCapabilities {
332333
lint: Some(lint),

crates/biome_service/src/file_handlers/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ type DebugFormatterIR = fn(
450450
AnyParse,
451451
WorkspaceSettingsHandle,
452452
) -> Result<String, WorkspaceError>;
453+
type DebugTypeInfo = fn(&BiomePath, AnyParse) -> Result<String, WorkspaceError>;
453454

454455
#[derive(Default)]
455456
pub struct DebugCapabilities {
@@ -459,6 +460,8 @@ pub struct DebugCapabilities {
459460
pub(crate) debug_control_flow: Option<DebugControlFlow>,
460461
/// Prints the formatter IR
461462
pub(crate) debug_formatter_ir: Option<DebugFormatterIR>,
463+
/// Prints the type info
464+
pub(crate) debug_type_info: Option<DebugTypeInfo>,
462465
}
463466

464467
#[derive(Debug)]

crates/biome_service/src/file_handlers/svelte.rs

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ impl ExtensionHandler for SvelteFileHandler {
9393
debug_syntax_tree: None,
9494
debug_control_flow: None,
9595
debug_formatter_ir: None,
96+
debug_type_info: None,
9697
},
9798
analyzer: AnalyzerCapabilities {
9899
lint: Some(lint),

crates/biome_service/src/file_handlers/vue.rs

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ impl ExtensionHandler for VueFileHandler {
9393
debug_syntax_tree: None,
9494
debug_control_flow: None,
9595
debug_formatter_ir: None,
96+
debug_type_info: None,
9697
},
9798
analyzer: AnalyzerCapabilities {
9899
lint: Some(lint),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
source: crates/biome_service/src/workspace.tests.rs
3+
expression: result.unwrap()
4+
snapshot_kind: text
5+
---
6+
sync Function "foo" {
7+
accepts: {
8+
params: [
9+
required name: string (bindings: name:string)
10+
required age: number (bindings: age:number)
11+
]
12+
type_args: []
13+
}
14+
returns: "Person" {
15+
resolved: unknown
16+
type_args: No types
17+
}
18+
}
19+
"Person" {
20+
extends: none
21+
type_args: []
22+
}

crates/biome_service/src/workspace.rs

+19
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,14 @@ pub struct GetControlFlowGraphParams {
634634
pub cursor: TextSize,
635635
}
636636

637+
#[derive(Debug, serde::Serialize, serde::Deserialize)]
638+
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
639+
#[serde(rename_all = "camelCase")]
640+
pub struct GetTypeInfoParams {
641+
pub project_key: ProjectKey,
642+
pub path: BiomePath,
643+
}
644+
637645
#[derive(Debug, serde::Serialize, serde::Deserialize)]
638646
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
639647
#[serde(rename_all = "camelCase")]
@@ -1152,6 +1160,10 @@ pub trait Workspace: Send + Sync + RefUnwindSafe {
11521160
/// document.
11531161
fn get_formatter_ir(&self, params: GetFormatterIRParams) -> Result<String, WorkspaceError>;
11541162

1163+
/// Returns a textual, debug representation of the control flow graph at a
1164+
/// given position in the document.
1165+
fn get_type_info(&self, params: GetTypeInfoParams) -> Result<String, WorkspaceError>;
1166+
11551167
/// Returns the content of a given file.
11561168
fn get_file_content(&self, params: GetFileContentParams) -> Result<String, WorkspaceError>;
11571169

@@ -1298,6 +1310,13 @@ impl<'app, W: Workspace + ?Sized> FileGuard<'app, W> {
12981310
})
12991311
}
13001312

1313+
pub fn get_type_info(&self) -> Result<String, WorkspaceError> {
1314+
self.workspace.get_type_info(GetTypeInfoParams {
1315+
project_key: self.project_key,
1316+
path: self.path.clone(),
1317+
})
1318+
}
1319+
13011320
pub fn change_file(&self, version: i32, content: String) -> Result<(), WorkspaceError> {
13021321
self.workspace.change_file(ChangeFileParams {
13031322
project_key: self.project_key,

crates/biome_service/src/workspace.tests.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use biome_configuration::{Configuration, FilesConfiguration};
77
use biome_fs::{BiomePath, MemoryFileSystem};
88
use biome_js_syntax::{JsFileSource, TextSize};
99
use camino::Utf8PathBuf;
10-
use insta::assert_debug_snapshot;
10+
use insta::{assert_debug_snapshot, assert_snapshot};
1111

1212
use crate::file_handlers::DocumentFileSource;
1313
use crate::projects::ProjectKey;
@@ -630,3 +630,37 @@ fn test_order() {
630630
assert!(items[0] < items[1], "{} < {}", items[0], items[1]);
631631
}
632632
}
633+
634+
#[test]
635+
fn debug_type_info() {
636+
let (workspace, project_key) = create_server();
637+
638+
let file = FileGuard::open(
639+
workspace.as_ref(),
640+
OpenFileParams {
641+
project_key,
642+
path: BiomePath::new("file.ts"),
643+
content: FileContent::from_client(
644+
r#"
645+
function foo(name: string, age: number): Person {
646+
return new Person(string, age)
647+
}
648+
class Person {
649+
#name: string
650+
#age: number
651+
constructor(name: string, age: number) {
652+
this.#name = name;
653+
this.#age = age;
654+
}
655+
}
656+
"#,
657+
),
658+
document_file_source: None,
659+
persist_node_cache: false,
660+
},
661+
)
662+
.unwrap();
663+
let result = file.get_type_info();
664+
assert!(result.is_ok());
665+
assert_snapshot!(result.unwrap());
666+
}

0 commit comments

Comments
 (0)