summaryrefslogtreecommitdiff
path: root/src/ide
diff options
context:
space:
mode:
Diffstat (limited to 'src/ide')
-rw-r--r--src/ide/jump.rs93
-rw-r--r--src/ide/mod.rs2
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;