diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-03-11 23:28:35 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-03-11 23:29:32 +0100 |
| commit | ca6edf5283c258d8410134d678347977cb273cdd (patch) | |
| tree | b2b46ba70c054b0cbdefa06edbc5fd999cddb1fa /src/ide | |
| parent | 1a390deaea040191cf0e5937bd8e1427b49db71b (diff) | |
Jump to source and preview
Diffstat (limited to 'src/ide')
| -rw-r--r-- | src/ide/jump.rs | 93 | ||||
| -rw-r--r-- | src/ide/mod.rs | 2 |
2 files changed, 95 insertions, 0 deletions
diff --git a/src/ide/jump.rs b/src/ide/jump.rs new file mode 100644 index 00000000..1a96fbbe --- /dev/null +++ b/src/ide/jump.rs @@ -0,0 +1,93 @@ +use std::num::NonZeroUsize; + +use crate::doc::{Element, Frame, Location}; +use crate::geom::Point; +use crate::syntax::{LinkedNode, Source, Span, SyntaxKind}; +use crate::World; + +/// Find the source file and byte offset for a click position. +pub fn jump_to_source<'a>( + world: &'a dyn World, + frame: &Frame, + click: Point, +) -> Option<(&'a Source, usize)> { + for (mut pos, element) in frame.elements() { + if let Element::Text(text) = element { + for glyph in &text.glyphs { + if glyph.span.is_detached() { + continue; + } + + let width = glyph.x_advance.at(text.size); + if pos.x <= click.x + && pos.x + width >= click.x + && pos.y >= click.y + && pos.y - text.size <= click.y + { + let source = world.source(glyph.span.source()); + let node = source.find(glyph.span); + let pos = if node.kind() == SyntaxKind::Text { + let range = node.range(); + (range.start + usize::from(glyph.offset)).min(range.end) + } else { + node.offset() + }; + return Some((source, pos)); + } + + pos.x += width; + } + } + + if let Element::Group(group) = element { + if let Some(span) = jump_to_source(world, &group.frame, click - pos) { + return Some(span); + } + } + } + + None +} + +/// Find the output location for a cursor position. +pub fn jump_to_preview( + frames: &[Frame], + source: &Source, + cursor: usize, +) -> Option<Location> { + let node = LinkedNode::new(source.root()).leaf_at(cursor)?; + if node.kind() != SyntaxKind::Text { + return None; + } + + let span = node.span(); + for (i, frame) in frames.iter().enumerate() { + if let Some(pos) = find_in_frame(frame, span) { + return Some(Location { page: NonZeroUsize::new(i + 1).unwrap(), pos }); + } + } + + None +} + +/// Find the position of a span in a frame. +fn find_in_frame(frame: &Frame, span: Span) -> Option<Point> { + for (mut pos, element) in frame.elements() { + if let Element::Text(text) = element { + for glyph in &text.glyphs { + if glyph.span == span { + return Some(pos); + } + pos.x += glyph.x_advance.at(text.size); + } + } + + if let Element::Group(group) = element { + if let Some(point) = find_in_frame(&group.frame, span) { + return Some(point + pos); + } + } + } + + None +} diff --git a/src/ide/mod.rs b/src/ide/mod.rs index 4999da52..bee959cd 100644 --- a/src/ide/mod.rs +++ b/src/ide/mod.rs @@ -3,10 +3,12 @@ mod analyze; mod complete; mod highlight; +mod jump; mod tooltip; pub use self::complete::*; pub use self::highlight::*; +pub use self::jump::*; pub use self::tooltip::*; use std::fmt::Write; |
