mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Add side parameter to leaf_at (#3767)
This commit is contained in:
parent
b6ec5a88f9
commit
b0d6cb900c
@ -10,7 +10,7 @@ use typst::foundations::{
|
|||||||
};
|
};
|
||||||
use typst::model::Document;
|
use typst::model::Document;
|
||||||
use typst::syntax::{
|
use typst::syntax::{
|
||||||
ast, is_id_continue, is_id_start, is_ident, LinkedNode, Source, SyntaxKind,
|
ast, is_id_continue, is_id_start, is_ident, LinkedNode, Side, Source, SyntaxKind,
|
||||||
};
|
};
|
||||||
use typst::text::RawElem;
|
use typst::text::RawElem;
|
||||||
use typst::visualize::Color;
|
use typst::visualize::Color;
|
||||||
@ -1033,7 +1033,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let text = source.text();
|
let text = source.text();
|
||||||
let library = world.library();
|
let library = world.library();
|
||||||
let leaf = LinkedNode::new(source.root()).leaf_at(cursor)?;
|
let leaf = LinkedNode::new(source.root()).leaf_at(cursor, Side::Before)?;
|
||||||
Some(Self {
|
Some(Self {
|
||||||
world,
|
world,
|
||||||
document,
|
document,
|
||||||
|
@ -4,7 +4,7 @@ use ecow::EcoString;
|
|||||||
use typst::introspection::Meta;
|
use typst::introspection::Meta;
|
||||||
use typst::layout::{Frame, FrameItem, Point, Position, Size};
|
use typst::layout::{Frame, FrameItem, Point, Position, Size};
|
||||||
use typst::model::{Destination, Document};
|
use typst::model::{Destination, Document};
|
||||||
use typst::syntax::{FileId, LinkedNode, Source, Span, SyntaxKind};
|
use typst::syntax::{FileId, LinkedNode, Side, Source, Span, SyntaxKind};
|
||||||
use typst::visualize::Geometry;
|
use typst::visualize::Geometry;
|
||||||
use typst::World;
|
use typst::World;
|
||||||
|
|
||||||
@ -115,11 +115,16 @@ pub fn jump_from_cursor(
|
|||||||
source: &Source,
|
source: &Source,
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
) -> Option<Position> {
|
) -> Option<Position> {
|
||||||
let node = LinkedNode::new(source.root()).leaf_at(cursor)?;
|
fn is_text(node: &LinkedNode) -> bool {
|
||||||
if node.kind() != SyntaxKind::Text {
|
node.get().kind() == SyntaxKind::Text
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let root = LinkedNode::new(source.root());
|
||||||
|
let node = root
|
||||||
|
.leaf_at(cursor, Side::Before)
|
||||||
|
.filter(is_text)
|
||||||
|
.or_else(|| root.leaf_at(cursor, Side::After).filter(is_text))?;
|
||||||
|
|
||||||
let span = node.span();
|
let span = node.span();
|
||||||
for (i, page) in document.pages.iter().enumerate() {
|
for (i, page) in document.pages.iter().enumerate() {
|
||||||
if let Some(pos) = find_in_frame(&page.frame, span) {
|
if let Some(pos) = find_in_frame(&page.frame, span) {
|
||||||
|
@ -6,7 +6,7 @@ use typst::eval::{CapturesVisitor, Tracer};
|
|||||||
use typst::foundations::{repr, Capturer, CastInfo, Repr, Value};
|
use typst::foundations::{repr, Capturer, CastInfo, Repr, Value};
|
||||||
use typst::layout::Length;
|
use typst::layout::Length;
|
||||||
use typst::model::Document;
|
use typst::model::Document;
|
||||||
use typst::syntax::{ast, LinkedNode, Source, SyntaxKind};
|
use typst::syntax::{ast, LinkedNode, Side, Source, SyntaxKind};
|
||||||
use typst::util::{round_2, Numeric};
|
use typst::util::{round_2, Numeric};
|
||||||
use typst::World;
|
use typst::World;
|
||||||
|
|
||||||
@ -23,8 +23,9 @@ pub fn tooltip(
|
|||||||
document: Option<&Document>,
|
document: Option<&Document>,
|
||||||
source: &Source,
|
source: &Source,
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
|
side: Side,
|
||||||
) -> Option<Tooltip> {
|
) -> Option<Tooltip> {
|
||||||
let leaf = LinkedNode::new(source.root()).leaf_at(cursor)?;
|
let leaf = LinkedNode::new(source.root()).leaf_at(cursor, side)?;
|
||||||
if leaf.kind().is_trivia() {
|
if leaf.kind().is_trivia() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ pub use self::kind::SyntaxKind;
|
|||||||
pub use self::lexer::{
|
pub use self::lexer::{
|
||||||
is_id_continue, is_id_start, is_ident, is_newline, link_prefix, split_newlines,
|
is_id_continue, is_id_start, is_ident, is_newline, link_prefix, split_newlines,
|
||||||
};
|
};
|
||||||
pub use self::node::{LinkedChildren, LinkedNode, SyntaxError, SyntaxNode};
|
pub use self::node::{LinkedChildren, LinkedNode, Side, SyntaxError, SyntaxNode};
|
||||||
pub use self::parser::{parse, parse_code, parse_math};
|
pub use self::parser::{parse, parse_code, parse_math};
|
||||||
pub use self::path::VirtualPath;
|
pub use self::path::VirtualPath;
|
||||||
pub use self::source::Source;
|
pub use self::source::Source;
|
||||||
|
@ -811,6 +811,13 @@ impl<'a> LinkedNode<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Indicates whether the cursor is before the related byte index, or after.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Side {
|
||||||
|
Before,
|
||||||
|
After,
|
||||||
|
}
|
||||||
|
|
||||||
/// Access to leafs.
|
/// Access to leafs.
|
||||||
impl<'a> LinkedNode<'a> {
|
impl<'a> LinkedNode<'a> {
|
||||||
/// Get the rightmost non-trivia leaf before this node.
|
/// Get the rightmost non-trivia leaf before this node.
|
||||||
@ -840,8 +847,8 @@ impl<'a> LinkedNode<'a> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the leaf at the specified byte offset.
|
/// Get the leaf immediately before the specified byte offset.
|
||||||
pub fn leaf_at(&self, cursor: usize) -> Option<Self> {
|
fn leaf_before(&self, cursor: usize) -> Option<Self> {
|
||||||
if self.node.children().len() == 0 && cursor <= self.offset + self.len() {
|
if self.node.children().len() == 0 && cursor <= self.offset + self.len() {
|
||||||
return Some(self.clone());
|
return Some(self.clone());
|
||||||
}
|
}
|
||||||
@ -853,7 +860,7 @@ impl<'a> LinkedNode<'a> {
|
|||||||
if (offset < cursor && cursor <= offset + len)
|
if (offset < cursor && cursor <= offset + len)
|
||||||
|| (offset == cursor && i + 1 == count)
|
|| (offset == cursor && i + 1 == count)
|
||||||
{
|
{
|
||||||
return child.leaf_at(cursor);
|
return child.leaf_before(cursor);
|
||||||
}
|
}
|
||||||
offset += len;
|
offset += len;
|
||||||
}
|
}
|
||||||
@ -861,6 +868,32 @@ impl<'a> LinkedNode<'a> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the leaf after the specified byte offset.
|
||||||
|
fn leaf_after(&self, cursor: usize) -> Option<Self> {
|
||||||
|
if self.node.children().len() == 0 && cursor < self.offset + self.len() {
|
||||||
|
return Some(self.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut offset = self.offset;
|
||||||
|
for child in self.children() {
|
||||||
|
let len = child.len();
|
||||||
|
if offset <= cursor && cursor < offset + len {
|
||||||
|
return child.leaf_after(cursor);
|
||||||
|
}
|
||||||
|
offset += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the leaf at the specified byte offset.
|
||||||
|
pub fn leaf_at(&self, cursor: usize, side: Side) -> Option<Self> {
|
||||||
|
match side {
|
||||||
|
Side::Before => self.leaf_before(cursor),
|
||||||
|
Side::After => self.leaf_after(cursor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Find the rightmost contained non-trivia leaf.
|
/// Find the rightmost contained non-trivia leaf.
|
||||||
pub fn rightmost_leaf(&self) -> Option<Self> {
|
pub fn rightmost_leaf(&self) -> Option<Self> {
|
||||||
if self.is_leaf() && !self.kind().is_trivia() {
|
if self.is_leaf() && !self.kind().is_trivia() {
|
||||||
@ -974,8 +1007,13 @@ mod tests {
|
|||||||
fn test_linked_node() {
|
fn test_linked_node() {
|
||||||
let source = Source::detached("#set text(12pt, red)");
|
let source = Source::detached("#set text(12pt, red)");
|
||||||
|
|
||||||
// Find "text".
|
// Find "text" with Before.
|
||||||
let node = LinkedNode::new(source.root()).leaf_at(7).unwrap();
|
let node = LinkedNode::new(source.root()).leaf_at(7, Side::Before).unwrap();
|
||||||
|
assert_eq!(node.offset(), 5);
|
||||||
|
assert_eq!(node.text(), "text");
|
||||||
|
|
||||||
|
// Find "text" with After.
|
||||||
|
let node = LinkedNode::new(source.root()).leaf_at(7, Side::After).unwrap();
|
||||||
assert_eq!(node.offset(), 5);
|
assert_eq!(node.offset(), 5);
|
||||||
assert_eq!(node.text(), "text");
|
assert_eq!(node.text(), "text");
|
||||||
|
|
||||||
@ -988,17 +1026,26 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_linked_node_non_trivia_leaf() {
|
fn test_linked_node_non_trivia_leaf() {
|
||||||
let source = Source::detached("#set fun(12pt, red)");
|
let source = Source::detached("#set fun(12pt, red)");
|
||||||
let leaf = LinkedNode::new(source.root()).leaf_at(6).unwrap();
|
let leaf = LinkedNode::new(source.root()).leaf_at(6, Side::Before).unwrap();
|
||||||
let prev = leaf.prev_leaf().unwrap();
|
let prev = leaf.prev_leaf().unwrap();
|
||||||
assert_eq!(leaf.text(), "fun");
|
assert_eq!(leaf.text(), "fun");
|
||||||
assert_eq!(prev.text(), "set");
|
assert_eq!(prev.text(), "set");
|
||||||
|
|
||||||
|
// Check position 9 with Before.
|
||||||
let source = Source::detached("#let x = 10");
|
let source = Source::detached("#let x = 10");
|
||||||
let leaf = LinkedNode::new(source.root()).leaf_at(9).unwrap();
|
let leaf = LinkedNode::new(source.root()).leaf_at(9, Side::Before).unwrap();
|
||||||
let prev = leaf.prev_leaf().unwrap();
|
let prev = leaf.prev_leaf().unwrap();
|
||||||
let next = leaf.next_leaf().unwrap();
|
let next = leaf.next_leaf().unwrap();
|
||||||
assert_eq!(prev.text(), "=");
|
assert_eq!(prev.text(), "=");
|
||||||
assert_eq!(leaf.text(), " ");
|
assert_eq!(leaf.text(), " ");
|
||||||
assert_eq!(next.text(), "10");
|
assert_eq!(next.text(), "10");
|
||||||
|
|
||||||
|
// Check position 9 with After.
|
||||||
|
let source = Source::detached("#let x = 10");
|
||||||
|
let leaf = LinkedNode::new(source.root()).leaf_at(9, Side::After).unwrap();
|
||||||
|
let prev = leaf.prev_leaf().unwrap();
|
||||||
|
assert!(leaf.next_leaf().is_none());
|
||||||
|
assert_eq!(prev.text(), "=");
|
||||||
|
assert_eq!(leaf.text(), "10");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user