mirror of
https://github.com/joshuadavidthomas/django-language-server.git
synced 2025-12-23 08:47:53 +00:00
reorganize semantic crate a bit (#243)
Some checks failed
lint / pre-commit (push) Has been cancelled
lint / rustfmt (push) Has been cancelled
lint / clippy (push) Has been cancelled
lint / cargo-check (push) Has been cancelled
release / build (push) Has been cancelled
release / test (push) Has been cancelled
test / generate-matrix (push) Has been cancelled
zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
release / release (push) Has been cancelled
test / Python , Django () (push) Has been cancelled
test / tests (push) Has been cancelled
Some checks failed
lint / pre-commit (push) Has been cancelled
lint / rustfmt (push) Has been cancelled
lint / clippy (push) Has been cancelled
lint / cargo-check (push) Has been cancelled
release / build (push) Has been cancelled
release / test (push) Has been cancelled
test / generate-matrix (push) Has been cancelled
zizmor 🌈 / zizmor latest via PyPI (push) Has been cancelled
release / release (push) Has been cancelled
test / Python , Django () (push) Has been cancelled
test / tests (push) Has been cancelled
This commit is contained in:
parent
eff5b7bace
commit
eec734ca44
5 changed files with 193 additions and 198 deletions
|
|
@ -1,5 +1,4 @@
|
|||
mod builder;
|
||||
mod grammar;
|
||||
mod nodes;
|
||||
mod snapshot;
|
||||
mod tree;
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ use djls_templates::Node;
|
|||
use super::grammar::CloseValidation;
|
||||
use super::grammar::TagClass;
|
||||
use super::grammar::TagIndex;
|
||||
use super::nodes::BlockId;
|
||||
use super::nodes::BlockNode;
|
||||
use super::nodes::BranchKind;
|
||||
use super::tree::BlockId;
|
||||
use super::tree::BlockNode;
|
||||
use super::tree::BlockTree;
|
||||
use super::tree::BranchKind;
|
||||
use crate::traits::SemanticModel;
|
||||
use crate::Db;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum BlockSemantics {
|
||||
enum BlockSemanticOp {
|
||||
AddRoot {
|
||||
id: BlockId,
|
||||
},
|
||||
|
|
@ -51,7 +51,7 @@ pub struct BlockTreeBuilder<'db> {
|
|||
index: &'db TagIndex,
|
||||
stack: Vec<TreeFrame<'db>>,
|
||||
block_allocs: Vec<(Span, Option<BlockId>)>,
|
||||
semantic_ops: Vec<BlockSemantics>,
|
||||
semantic_ops: Vec<BlockSemanticOp>,
|
||||
}
|
||||
|
||||
impl<'db> BlockTreeBuilder<'db> {
|
||||
|
|
@ -88,10 +88,10 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
|
||||
for op in self.semantic_ops {
|
||||
match op {
|
||||
BlockSemantics::AddRoot { id } => {
|
||||
BlockSemanticOp::AddRoot { id } => {
|
||||
tree.roots_mut().push(id);
|
||||
}
|
||||
BlockSemantics::AddBranchNode {
|
||||
BlockSemanticOp::AddBranchNode {
|
||||
target,
|
||||
tag,
|
||||
marker_span,
|
||||
|
|
@ -108,7 +108,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
},
|
||||
);
|
||||
}
|
||||
BlockSemantics::AddLeafNode {
|
||||
BlockSemanticOp::AddLeafNode {
|
||||
target,
|
||||
label,
|
||||
span,
|
||||
|
|
@ -116,7 +116,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
tree.blocks_mut()
|
||||
.push_node(target, BlockNode::Leaf { label, span });
|
||||
}
|
||||
BlockSemantics::AddErrorNode {
|
||||
BlockSemanticOp::AddErrorNode {
|
||||
target,
|
||||
message,
|
||||
span,
|
||||
|
|
@ -124,10 +124,10 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
tree.blocks_mut()
|
||||
.push_node(target, BlockNode::Error { message, span });
|
||||
}
|
||||
BlockSemantics::ExtendBlockSpan { id, span } => {
|
||||
BlockSemanticOp::ExtendBlockSpan { id, span } => {
|
||||
tree.blocks_mut().extend_block(id, span);
|
||||
}
|
||||
BlockSemantics::FinalizeSpanTo { id, end } => {
|
||||
BlockSemanticOp::FinalizeSpanTo { id, end } => {
|
||||
tree.blocks_mut().finalize_block_span(id, end);
|
||||
}
|
||||
}
|
||||
|
|
@ -150,14 +150,14 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
|
||||
if let Some(parent_id) = parent {
|
||||
// Nested block
|
||||
self.semantic_ops.push(BlockSemantics::AddBranchNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddBranchNode {
|
||||
target: parent_id,
|
||||
tag: tag_name.clone(),
|
||||
marker_span: span,
|
||||
body: container,
|
||||
kind: BranchKind::Opener,
|
||||
});
|
||||
self.semantic_ops.push(BlockSemantics::AddBranchNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddBranchNode {
|
||||
target: container,
|
||||
tag: tag_name.clone(),
|
||||
marker_span: span,
|
||||
|
|
@ -167,8 +167,8 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
} else {
|
||||
// Root block
|
||||
self.semantic_ops
|
||||
.push(BlockSemantics::AddRoot { id: container });
|
||||
self.semantic_ops.push(BlockSemantics::AddBranchNode {
|
||||
.push(BlockSemanticOp::AddRoot { id: container });
|
||||
self.semantic_ops.push(BlockSemanticOp::AddBranchNode {
|
||||
target: container,
|
||||
tag: tag_name.clone(),
|
||||
marker_span: span,
|
||||
|
|
@ -194,7 +194,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
}
|
||||
TagClass::Unknown => {
|
||||
if let Some(segment) = get_active_segment(&self.stack) {
|
||||
self.semantic_ops.push(BlockSemantics::AddLeafNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddLeafNode {
|
||||
target: segment,
|
||||
label: tag_name,
|
||||
span,
|
||||
|
|
@ -210,7 +210,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
while self.stack.len() > frame_idx + 1 {
|
||||
if let Some(unclosed) = self.stack.pop() {
|
||||
if let Some(parent) = unclosed.parent_body {
|
||||
self.semantic_ops.push(BlockSemantics::AddErrorNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddErrorNode {
|
||||
target: parent,
|
||||
message: format!("Unclosed block '{}'", unclosed.opener_name),
|
||||
span: unclosed.opener_span,
|
||||
|
|
@ -229,18 +229,18 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
CloseValidation::Valid => {
|
||||
// Finalize the last segment body to end just before the closer marker
|
||||
let content_end = span.start().saturating_sub(TagDelimiter::LENGTH_U32);
|
||||
self.semantic_ops.push(BlockSemantics::FinalizeSpanTo {
|
||||
self.semantic_ops.push(BlockSemanticOp::FinalizeSpanTo {
|
||||
id: frame.segment_body,
|
||||
end: content_end,
|
||||
});
|
||||
// Extend to include closer
|
||||
self.semantic_ops.push(BlockSemantics::ExtendBlockSpan {
|
||||
self.semantic_ops.push(BlockSemanticOp::ExtendBlockSpan {
|
||||
id: frame.container_body,
|
||||
span,
|
||||
});
|
||||
}
|
||||
CloseValidation::ArgumentMismatch { arg, expected, got } => {
|
||||
self.semantic_ops.push(BlockSemantics::AddErrorNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddErrorNode {
|
||||
target: frame.segment_body,
|
||||
message: format!(
|
||||
"Argument '{arg}' mismatch: expected '{expected}', got '{got}'"
|
||||
|
|
@ -250,7 +250,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
self.stack.push(frame); // Restore frame
|
||||
}
|
||||
CloseValidation::MissingRequiredArg { arg, expected } => {
|
||||
self.semantic_ops.push(BlockSemantics::AddErrorNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddErrorNode {
|
||||
target: frame.segment_body,
|
||||
message: format!(
|
||||
"Missing required argument '{arg}': expected '{expected}'"
|
||||
|
|
@ -260,7 +260,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
self.stack.push(frame);
|
||||
}
|
||||
CloseValidation::UnexpectedArg { arg, got } => {
|
||||
self.semantic_ops.push(BlockSemantics::AddErrorNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddErrorNode {
|
||||
target: frame.segment_body,
|
||||
message: format!("Unexpected argument '{arg}' with value '{got}'"),
|
||||
span,
|
||||
|
|
@ -270,7 +270,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
CloseValidation::NotABlock => {
|
||||
// Should not happen as we already classified it
|
||||
if let Some(segment) = get_active_segment(&self.stack) {
|
||||
self.semantic_ops.push(BlockSemantics::AddErrorNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddErrorNode {
|
||||
target: segment,
|
||||
message: format!("Internal error: {opener_name} is not a block"),
|
||||
span,
|
||||
|
|
@ -279,7 +279,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
}
|
||||
}
|
||||
} else if let Some(segment) = get_active_segment(&self.stack) {
|
||||
self.semantic_ops.push(BlockSemantics::AddErrorNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddErrorNode {
|
||||
target: segment,
|
||||
message: format!("Unexpected closing tag '{opener_name}'"),
|
||||
span,
|
||||
|
|
@ -295,7 +295,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
let segment_to_finalize = frame.segment_body;
|
||||
let container = frame.container_body;
|
||||
|
||||
self.semantic_ops.push(BlockSemantics::FinalizeSpanTo {
|
||||
self.semantic_ops.push(BlockSemanticOp::FinalizeSpanTo {
|
||||
id: segment_to_finalize,
|
||||
end: content_end,
|
||||
});
|
||||
|
|
@ -304,7 +304,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
let new_segment_id = self.alloc_block_id(Span::new(body_start, 0), Some(container));
|
||||
|
||||
// Add the branch node for the new segment
|
||||
self.semantic_ops.push(BlockSemantics::AddBranchNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddBranchNode {
|
||||
target: container,
|
||||
tag: tag_name.to_string(),
|
||||
marker_span: span,
|
||||
|
|
@ -317,7 +317,7 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
let segment = frame.segment_body;
|
||||
let opener_name = frame.opener_name.clone();
|
||||
|
||||
self.semantic_ops.push(BlockSemantics::AddErrorNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddErrorNode {
|
||||
target: segment,
|
||||
message: format!("'{tag_name}' is not valid in '{opener_name}'"),
|
||||
span,
|
||||
|
|
@ -334,12 +334,12 @@ impl<'db> BlockTreeBuilder<'db> {
|
|||
if self.index.is_end_optional(&frame.opener_name) {
|
||||
// No explicit closer: finalize last segment to end of input (best-effort)
|
||||
// We do not know the real end; leave as-is and extend container by opener span only.
|
||||
self.semantic_ops.push(BlockSemantics::ExtendBlockSpan {
|
||||
self.semantic_ops.push(BlockSemanticOp::ExtendBlockSpan {
|
||||
id: frame.container_body,
|
||||
span: frame.opener_span,
|
||||
});
|
||||
} else if let Some(parent) = frame.parent_body {
|
||||
self.semantic_ops.push(BlockSemantics::AddErrorNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddErrorNode {
|
||||
target: parent,
|
||||
message: format!("Unclosed block '{}'", frame.opener_name),
|
||||
span: frame.opener_span,
|
||||
|
|
@ -380,7 +380,7 @@ impl<'db> SemanticModel<'db> for BlockTreeBuilder<'db> {
|
|||
}
|
||||
Node::Comment { span, .. } => {
|
||||
if let Some(parent) = get_active_segment(&self.stack) {
|
||||
self.semantic_ops.push(BlockSemantics::AddLeafNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddLeafNode {
|
||||
target: parent,
|
||||
label: "<comment>".into(),
|
||||
span,
|
||||
|
|
@ -389,7 +389,7 @@ impl<'db> SemanticModel<'db> for BlockTreeBuilder<'db> {
|
|||
}
|
||||
Node::Variable { span, .. } => {
|
||||
if let Some(parent) = get_active_segment(&self.stack) {
|
||||
self.semantic_ops.push(BlockSemantics::AddLeafNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddLeafNode {
|
||||
target: parent,
|
||||
label: "<var>".into(),
|
||||
span,
|
||||
|
|
@ -400,7 +400,7 @@ impl<'db> SemanticModel<'db> for BlockTreeBuilder<'db> {
|
|||
full_span, error, ..
|
||||
} => {
|
||||
if let Some(parent) = get_active_segment(&self.stack) {
|
||||
self.semantic_ops.push(BlockSemantics::AddLeafNode {
|
||||
self.semantic_ops.push(BlockSemanticOp::AddLeafNode {
|
||||
target: parent,
|
||||
label: error.to_string(),
|
||||
span: full_span,
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
use djls_source::Span;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
|
||||
pub struct BlockId(u32);
|
||||
|
||||
impl BlockId {
|
||||
pub fn new(id: u32) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
|
||||
pub fn id(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn index(self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Blocks(Vec<Region>);
|
||||
|
||||
impl Blocks {
|
||||
pub fn get(&self, id: usize) -> &Region {
|
||||
&self.0[id]
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Blocks {
|
||||
type Item = Region;
|
||||
type IntoIter = std::vec::IntoIter<Region>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Blocks {
|
||||
type Item = &'a Region;
|
||||
type IntoIter = std::slice::Iter<'a, Region>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a mut Blocks {
|
||||
type Item = &'a mut Region;
|
||||
type IntoIter = std::slice::IterMut<'a, Region>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl Blocks {
|
||||
pub fn alloc(&mut self, span: Span, parent: Option<BlockId>) -> BlockId {
|
||||
let id = BlockId(u32::try_from(self.0.len()).unwrap_or_default());
|
||||
self.0.push(Region::new(span, parent));
|
||||
id
|
||||
}
|
||||
|
||||
pub fn extend_block(&mut self, id: BlockId, span: Span) {
|
||||
self.block_mut(id).extend_span(span);
|
||||
}
|
||||
|
||||
pub fn set_block_span(&mut self, id: BlockId, span: Span) {
|
||||
self.block_mut(id).set_span(span);
|
||||
}
|
||||
|
||||
pub fn finalize_block_span(&mut self, id: BlockId, end: u32) {
|
||||
let block = self.block_mut(id);
|
||||
let start = block.span().start();
|
||||
block.set_span(Span::saturating_from_bounds_usize(
|
||||
start as usize,
|
||||
end as usize,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn push_node(&mut self, target: BlockId, node: BlockNode) {
|
||||
let span = node.span();
|
||||
self.extend_block(target, span);
|
||||
self.block_mut(target).nodes.push(node);
|
||||
}
|
||||
|
||||
fn block_mut(&mut self, id: BlockId) -> &mut Region {
|
||||
let idx = id.index();
|
||||
&mut self.0[idx]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Region {
|
||||
span: Span,
|
||||
nodes: Vec<BlockNode>,
|
||||
parent: Option<BlockId>,
|
||||
}
|
||||
|
||||
impl Region {
|
||||
fn new(span: Span, parent: Option<BlockId>) -> Self {
|
||||
Self {
|
||||
span,
|
||||
nodes: Vec::new(),
|
||||
parent,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> &Span {
|
||||
&self.span
|
||||
}
|
||||
|
||||
pub fn set_span(&mut self, span: Span) {
|
||||
self.span = span;
|
||||
}
|
||||
|
||||
pub fn nodes(&self) -> &Vec<BlockNode> {
|
||||
&self.nodes
|
||||
}
|
||||
|
||||
fn extend_span(&mut self, span: Span) {
|
||||
let opening = self.span.start().saturating_sub(span.start());
|
||||
let closing = span.end().saturating_sub(self.span.end());
|
||||
self.span = self.span.expand(opening, closing);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub enum BranchKind {
|
||||
Opener,
|
||||
Segment,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub enum BlockNode {
|
||||
Leaf {
|
||||
label: String,
|
||||
span: Span,
|
||||
},
|
||||
Branch {
|
||||
tag: String,
|
||||
marker_span: Span,
|
||||
body: BlockId,
|
||||
kind: BranchKind,
|
||||
},
|
||||
Error {
|
||||
message: String,
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl BlockNode {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
BlockNode::Leaf { span, .. } | BlockNode::Error { span, .. } => *span,
|
||||
BlockNode::Branch { marker_span, .. } => *marker_span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,10 @@ use std::collections::HashSet;
|
|||
use djls_source::Span;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::nodes::BlockId;
|
||||
use super::nodes::BlockNode;
|
||||
use super::nodes::BranchKind;
|
||||
use super::tree::BlockId;
|
||||
use super::tree::BlockNode;
|
||||
use super::tree::BlockTree;
|
||||
use super::tree::BranchKind;
|
||||
|
||||
// TODO: centralize salsa struct snapshots so this mess can be shared
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
use djls_source::Span;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::nodes::BlockId;
|
||||
use super::nodes::Blocks;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct BlockTree {
|
||||
roots: Vec<BlockId>,
|
||||
|
|
@ -54,6 +52,163 @@ impl Default for BlockTree {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize)]
|
||||
pub struct BlockId(u32);
|
||||
|
||||
impl BlockId {
|
||||
pub fn new(id: u32) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
|
||||
pub fn id(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn index(self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize)]
|
||||
pub struct Blocks(Vec<Region>);
|
||||
|
||||
impl Blocks {
|
||||
pub fn get(&self, id: usize) -> &Region {
|
||||
&self.0[id]
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Blocks {
|
||||
type Item = Region;
|
||||
type IntoIter = std::vec::IntoIter<Region>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Blocks {
|
||||
type Item = &'a Region;
|
||||
type IntoIter = std::slice::Iter<'a, Region>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a mut Blocks {
|
||||
type Item = &'a mut Region;
|
||||
type IntoIter = std::slice::IterMut<'a, Region>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl Blocks {
|
||||
pub fn alloc(&mut self, span: Span, parent: Option<BlockId>) -> BlockId {
|
||||
let id = BlockId(u32::try_from(self.0.len()).unwrap_or_default());
|
||||
self.0.push(Region::new(span, parent));
|
||||
id
|
||||
}
|
||||
|
||||
pub fn extend_block(&mut self, id: BlockId, span: Span) {
|
||||
self.block_mut(id).extend_span(span);
|
||||
}
|
||||
|
||||
pub fn set_block_span(&mut self, id: BlockId, span: Span) {
|
||||
self.block_mut(id).set_span(span);
|
||||
}
|
||||
|
||||
pub fn finalize_block_span(&mut self, id: BlockId, end: u32) {
|
||||
let block = self.block_mut(id);
|
||||
let start = block.span().start();
|
||||
block.set_span(Span::saturating_from_bounds_usize(
|
||||
start as usize,
|
||||
end as usize,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn push_node(&mut self, target: BlockId, node: BlockNode) {
|
||||
let span = node.span();
|
||||
self.extend_block(target, span);
|
||||
self.block_mut(target).nodes.push(node);
|
||||
}
|
||||
|
||||
fn block_mut(&mut self, id: BlockId) -> &mut Region {
|
||||
let idx = id.index();
|
||||
&mut self.0[idx]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Region {
|
||||
span: Span,
|
||||
nodes: Vec<BlockNode>,
|
||||
parent: Option<BlockId>,
|
||||
}
|
||||
|
||||
impl Region {
|
||||
fn new(span: Span, parent: Option<BlockId>) -> Self {
|
||||
Self {
|
||||
span,
|
||||
nodes: Vec::new(),
|
||||
parent,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> &Span {
|
||||
&self.span
|
||||
}
|
||||
|
||||
pub fn set_span(&mut self, span: Span) {
|
||||
self.span = span;
|
||||
}
|
||||
|
||||
pub fn nodes(&self) -> &Vec<BlockNode> {
|
||||
&self.nodes
|
||||
}
|
||||
|
||||
fn extend_span(&mut self, span: Span) {
|
||||
let opening = self.span.start().saturating_sub(span.start());
|
||||
let closing = span.end().saturating_sub(self.span.end());
|
||||
self.span = self.span.expand(opening, closing);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub enum BranchKind {
|
||||
Opener,
|
||||
Segment,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub enum BlockNode {
|
||||
Leaf {
|
||||
label: String,
|
||||
span: Span,
|
||||
},
|
||||
Branch {
|
||||
tag: String,
|
||||
marker_span: Span,
|
||||
body: BlockId,
|
||||
kind: BranchKind,
|
||||
},
|
||||
Error {
|
||||
message: String,
|
||||
span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl BlockNode {
|
||||
fn span(&self) -> Span {
|
||||
match self {
|
||||
BlockNode::Leaf { span, .. } | BlockNode::Error { span, .. } => *span,
|
||||
BlockNode::Branch { marker_span, .. } => *marker_span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue