diff --git a/stack-graphs/src/serde/graph.rs b/stack-graphs/src/serde/graph.rs index 6cb06744a..00a453ab0 100644 --- a/stack-graphs/src/serde/graph.rs +++ b/stack-graphs/src/serde/graph.rs @@ -143,6 +143,7 @@ impl StackGraph { .as_ref() .map(|st| graph.add_string(&st)) .into(), + definiens_span: source_info.definiens_span.clone(), ..Default::default() }; } @@ -330,8 +331,17 @@ impl Node { )] #[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))] pub struct SourceInfo { + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "span_is_empty") + )] pub span: lsp_positions::Span, pub syntax_type: Option, + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "span_is_empty") + )] + pub definiens_span: lsp_positions::Span, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -473,6 +483,7 @@ impl crate::graph::StackGraph { self.source_info(handle).map(|info| SourceInfo { span: info.span.clone(), syntax_type: info.syntax_type.into_option().map(|ty| self[ty].to_owned()), + definiens_span: info.definiens_span.clone(), }) } @@ -597,3 +608,8 @@ impl crate::graph::StackGraph { }) } } + +#[cfg(feature = "serde")] +fn span_is_empty(span: &lsp_positions::Span) -> bool { + *span == lsp_positions::Span::default() +} diff --git a/stack-graphs/src/visualization/visualization.js b/stack-graphs/src/visualization/visualization.js index 116850791..042e14699 100644 --- a/stack-graphs/src/visualization/visualization.js +++ b/stack-graphs/src/visualization/visualization.js @@ -742,7 +742,15 @@ class StackGraph { tooltip.add_row("exported?", node.is_exported ? "yes" : "no"); } if (this.node_has_source_info(node)) { - tooltip.add_row("location", this.source_info_to_str(node.source_info)); + if (!this.span_is_empty(node.source_info.span)) { + tooltip.add_row("location", this.location_to_str(node.source_info.span.start)); + } + if (node.source_info.syntax_type) { + tooltip.add_row("syntax type", node.source_info.syntax_type); + } + if (!this.span_is_empty(node.source_info.definiens_span)) { + tooltip.add_row("definiens span", this.span_to_str(node.source_info.definiens_span)); + } } if (node.paths.length > 0) { tooltip.add_row("outgoing paths", `${node.paths.length}`); @@ -964,20 +972,23 @@ class StackGraph { node_has_source_info(node) { return node.hasOwnProperty("source_info") - && !this.source_info_is_empty(node.source_info); } - source_info_to_str(source_info) { - const line = source_info.span.start.line; - const column = source_info.span.start.column.grapheme_offset; - return `line ${line + 1} column ${column + 1}`; + span_to_str(span) { + return `${this.location_to_str(span.start)}–${this.location_to_str(span.end)}`; + } + + location_to_str(loc) { + return `${loc.line + 1}:${loc.column.grapheme_offset + 1}`; } - source_info_is_empty(source_info) { - return source_info.span.start.line === 0 - && source_info.span.start.column.utf8_offset === 0 - && source_info.span.end.line === 0 - && source_info.span.end.column.utf8_offset === 0; + span_is_empty(span) { + return !span + || ( span.start.line === 0 + && span.start.column.utf8_offset === 0 + && span.end.line === 0 + && span.end.column.utf8_offset === 0 + ); } // ------------------------------------------------------------------------------------------------ diff --git a/stack-graphs/tests/it/serde.rs b/stack-graphs/tests/it/serde.rs index 3ecc1c48f..ff142bc4e 100644 --- a/stack-graphs/tests/it/serde.rs +++ b/stack-graphs/tests/it/serde.rs @@ -6,6 +6,8 @@ // ------------------------------------------------------------------------------------------------ use assert_json_diff::assert_json_eq; +use pretty_assertions::assert_eq; + use serde_json; use serde_json::json; use stack_graphs::graph; @@ -44,15 +46,16 @@ fn serde_json_stack_graph() { end: lsp_positions::Position { line: 0, column: lsp_positions::Offset { - utf8_offset: 0, + utf8_offset: 5, utf16_offset: 0, - grapheme_offset: 0, + grapheme_offset: 5, }, containing_line: 0..0, trimmed_line: 0..0, }, }, - syntax_type: None, + syntax_type: Some("function".to_string()), + definiens_span: lsp_positions::Span::default(), }), debug_info: Some(serde::DebugInfo { data: vec![] }), }], @@ -102,18 +105,18 @@ fn serde_json_stack_graph() { "span" : { "end" : { "column" : { - "grapheme_offset" : 0, + "grapheme_offset" : 5, "utf16_offset" : 0, - "utf8_offset" : 0 + "utf8_offset" : 5 }, + "line" : 0, "containing_line" : { - "end" : 0, - "start" : 0 + "start" : 0, + "end" : 0 }, - "line" : 0, "trimmed_line" : { - "end" : 0, - "start" : 0 + "start" : 0, + "end" : 0 } }, "start" : { @@ -122,17 +125,18 @@ fn serde_json_stack_graph() { "utf16_offset" : 0, "utf8_offset" : 0 }, + "line" : 0, "containing_line" : { - "end" : 0, - "start" : 0 + "start" : 0, + "end" : 0 }, - "line" : 0, "trimmed_line" : { - "end" : 0, - "start" : 0 + "start" : 0, + "end" : 0 } } - } + }, + "syntax_type" : "function" }, "type" : "root" } @@ -171,42 +175,6 @@ fn can_load_serialized_stack_graph() { "id" : { "local_id" : 1 }, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "containing_line" : { - "end" : 0, - "start" : 0 - }, - "line" : 0, - "trimmed_line" : { - "end" : 0, - "start" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "containing_line" : { - "end" : 0, - "start" : 0 - }, - "line" : 0, - "trimmed_line" : { - "end" : 0, - "start" : 0 - } - } - } - }, "type" : "root" }, { @@ -214,42 +182,6 @@ fn can_load_serialized_stack_graph() { "id" : { "local_id" : 2 }, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "containing_line" : { - "end" : 0, - "start" : 0 - }, - "line" : 0, - "trimmed_line" : { - "end" : 0, - "start" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "containing_line" : { - "end" : 0, - "start" : 0 - }, - "line" : 0, - "trimmed_line" : { - "end" : 0, - "start" : 0 - } - } - } - }, "type" : "jump_to_scope" }, { @@ -268,42 +200,6 @@ fn can_load_serialized_stack_graph() { "local_id" : 0 }, "is_exported" : false, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "containing_line" : { - "end" : 0, - "start" : 0 - }, - "line" : 0, - "trimmed_line" : { - "end" : 0, - "start" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "containing_line" : { - "end" : 0, - "start" : 0 - }, - "line" : 0, - "trimmed_line" : { - "end" : 0, - "start" : 0 - } - } - } - }, "type" : "scope" } ] @@ -321,7 +217,7 @@ fn can_load_serialized_stack_graph() { .iter_nodes() .find(|handle| matches!(sg[*handle], graph::Node::Scope(..))) .unwrap(); - assert!(sg.source_info(handle).is_some()); + assert!(!sg.source_info(handle).is_some()); assert!(sg.node_debug_info(handle).is_some()); } @@ -457,42 +353,7 @@ fn can_serialize_graph() { "id" : { "local_id" : 1 }, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - } - } - }, + "source_info": {}, "type" : "root" }, { @@ -500,42 +361,7 @@ fn can_serialize_graph() { "id" : { "local_id" : 2 }, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - } - } - }, + "source_info": {}, "type" : "jump_to_scope" }, { @@ -592,42 +418,7 @@ fn can_serialize_graph() { "local_id" : 2 }, "is_reference" : false, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - } - } - }, + "source_info": {}, "symbol" : ".", "type" : "push_symbol" }, @@ -647,42 +438,7 @@ fn can_serialize_graph() { "local_id" : 3 }, "is_exported" : true, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - } - } - }, + "source_info": {}, "type" : "scope" }, { @@ -696,42 +452,7 @@ fn can_serialize_graph() { "file" : "test.py", "local_id" : 3 }, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - } - } - }, + "source_info": {}, "symbol" : "()", "type" : "push_scoped_symbol" }, @@ -751,42 +472,7 @@ fn can_serialize_graph() { "local_id" : 5 }, "is_exported" : false, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - } - } - }, + "source_info": {}, "type" : "scope" }, { @@ -795,42 +481,7 @@ fn can_serialize_graph() { "local_id" : 6 }, "is_definition" : false, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - } - } - }, + "source_info": {}, "symbol" : "()", "type" : "pop_scoped_symbol" }, @@ -839,42 +490,7 @@ fn can_serialize_graph() { "file" : "test.py", "local_id" : 7 }, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - } - } - }, + "source_info": {}, "type" : "drop_scopes" }, { @@ -883,42 +499,7 @@ fn can_serialize_graph() { "local_id" : 8 }, "is_definition" : false, - "source_info" : { - "span" : { - "end" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - }, - "start" : { - "column" : { - "grapheme_offset" : 0, - "utf16_offset" : 0, - "utf8_offset" : 0 - }, - "line" : 0, - "containing_line" : { - "start" : 0, - "end" : 0 - }, - "trimmed_line" : { - "start" : 0, - "end" : 0 - } - } - } - }, + "source_info": {}, "symbol" : ".", "type" : "pop_symbol" },