diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 3870323048..592323fb0d 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -8,7 +8,7 @@ use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task}; pub use language::Completion; use language::{ Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language, Outline, - OutlineItem, Selection, ToOffset as _, ToPoint as _, TransactionId, + OutlineItem, Selection, ToOffset as _, ToPoint as _, ToPointUtf16 as _, TransactionId, }; use std::{ cell::{Ref, RefCell}, @@ -73,6 +73,10 @@ pub trait ToPoint: 'static + fmt::Debug { fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point; } +pub trait ToPointUtf16: 'static + fmt::Debug { + fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16; +} + struct BufferState { buffer: ModelHandle, last_version: clock::Global, @@ -1360,6 +1364,48 @@ impl MultiBufferSnapshot { } } + pub fn offset_to_point_utf16(&self, offset: usize) -> PointUtf16 { + if let Some(excerpt) = self.as_singleton() { + return excerpt.buffer.offset_to_point_utf16(offset); + } + + let mut cursor = self.excerpts.cursor::<(usize, PointUtf16)>(); + cursor.seek(&offset, Bias::Right, &()); + if let Some(excerpt) = cursor.item() { + let (start_offset, start_point) = cursor.start(); + let overshoot = offset - start_offset; + let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer); + let excerpt_start_point = excerpt.range.start.to_point_utf16(&excerpt.buffer); + let buffer_point = excerpt + .buffer + .offset_to_point_utf16(excerpt_start_offset + overshoot); + *start_point + (buffer_point - excerpt_start_point) + } else { + self.excerpts.summary().text.lines_utf16 + } + } + + pub fn point_to_point_utf16(&self, point: Point) -> PointUtf16 { + if let Some(excerpt) = self.as_singleton() { + return excerpt.buffer.point_to_point_utf16(point); + } + + let mut cursor = self.excerpts.cursor::<(Point, PointUtf16)>(); + cursor.seek(&point, Bias::Right, &()); + if let Some(excerpt) = cursor.item() { + let (start_offset, start_point) = cursor.start(); + let overshoot = point - start_offset; + let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer); + let excerpt_start_point_utf16 = excerpt.range.start.to_point_utf16(&excerpt.buffer); + let buffer_point = excerpt + .buffer + .point_to_point_utf16(excerpt_start_point + overshoot); + *start_point + (buffer_point - excerpt_start_point_utf16) + } else { + self.excerpts.summary().text.lines_utf16 + } + } + pub fn point_to_offset(&self, point: Point) -> usize { if let Some(excerpt) = self.as_singleton() { return excerpt.buffer.point_to_offset(point); @@ -2487,6 +2533,24 @@ impl ToPoint for Point { } } +impl ToPointUtf16 for usize { + fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 { + snapshot.offset_to_point_utf16(*self) + } +} + +impl ToPointUtf16 for Point { + fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 { + snapshot.point_to_point_utf16(*self) + } +} + +impl ToPointUtf16 for PointUtf16 { + fn to_point_utf16<'a>(&self, _: &MultiBufferSnapshot) -> PointUtf16 { + *self + } +} + pub fn char_kind(c: char) -> CharKind { if c == '\n' { CharKind::Newline diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index e4d22346bf..3c6d6f0f42 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -7,19 +7,20 @@ pub mod proto; mod tests; use anyhow::{anyhow, Result}; -pub use buffer::Operation; -pub use buffer::*; use collections::HashSet; -pub use diagnostic_set::DiagnosticEntry; use gpui::AppContext; use highlight_map::HighlightMap; use lazy_static::lazy_static; -pub use outline::{Outline, OutlineItem}; use parking_lot::Mutex; use serde::Deserialize; use std::{cell::RefCell, ops::Range, path::Path, str, sync::Arc}; use theme::SyntaxTheme; use tree_sitter::{self, Query}; + +pub use buffer::Operation; +pub use buffer::*; +pub use diagnostic_set::DiagnosticEntry; +pub use outline::{Outline, OutlineItem}; pub use tree_sitter::{Parser, Tree}; thread_local! { @@ -39,10 +40,6 @@ lazy_static! { )); } -pub trait ToPointUtf16 { - fn to_point_utf16(self) -> PointUtf16; -} - pub trait ToLspPosition { fn to_lsp_position(self) -> lsp::Position; } @@ -386,18 +383,16 @@ impl LanguageServerConfig { } } -impl ToPointUtf16 for lsp::Position { - fn to_point_utf16(self) -> PointUtf16 { - PointUtf16::new(self.line, self.character) - } -} - impl ToLspPosition for PointUtf16 { fn to_lsp_position(self) -> lsp::Position { lsp::Position::new(self.row, self.column) } } +pub fn point_from_lsp(point: lsp::Position) -> PointUtf16 { + PointUtf16::new(point.line, point.character) +} + pub fn range_from_lsp(range: lsp::Range) -> Range { let start = PointUtf16::new(range.start.line, range.start.character); let end = PointUtf16::new(range.end.line, range.end.character); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index aced7b4f09..55a3583f6e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -13,9 +13,10 @@ use gpui::{ WeakModelHandle, }; use language::{ + point_from_lsp, proto::{deserialize_anchor, serialize_anchor}, range_from_lsp, Bias, Buffer, Diagnostic, DiagnosticEntry, File as _, Language, - LanguageRegistry, PointUtf16, ToOffset, ToPointUtf16, + LanguageRegistry, PointUtf16, ToOffset, }; use lsp::{DiagnosticSeverity, LanguageServer}; use postage::{prelude::Stream, watch}; @@ -1098,9 +1099,9 @@ impl Project { cx.read(|cx| { let target_buffer = target_buffer_handle.read(cx); let target_start = target_buffer - .clip_point_utf16(target_range.start.to_point_utf16(), Bias::Left); + .clip_point_utf16(point_from_lsp(target_range.start), Bias::Left); let target_end = target_buffer - .clip_point_utf16(target_range.end.to_point_utf16(), Bias::Left); + .clip_point_utf16(point_from_lsp(target_range.end), Bias::Left); definitions.push(Definition { target_buffer: target_buffer_handle, target_range: target_buffer.anchor_after(target_start) diff --git a/crates/text/src/rope.rs b/crates/text/src/rope.rs index d9c900d8bc..cd474cc4da 100644 --- a/crates/text/src/rope.rs +++ b/crates/text/src/rope.rs @@ -179,6 +179,19 @@ impl Rope { }) } + pub fn point_to_point_utf16(&self, point: Point) -> PointUtf16 { + if point >= self.summary().lines { + return self.summary().lines_utf16; + } + let mut cursor = self.chunks.cursor::<(Point, PointUtf16)>(); + cursor.seek(&point, Bias::Left, &()); + let overshoot = point - cursor.start().0; + cursor.start().1 + + cursor.item().map_or(PointUtf16::zero(), |chunk| { + chunk.point_to_point_utf16(overshoot) + }) + } + pub fn point_to_offset(&self, point: Point) -> usize { if point >= self.summary().lines { return self.summary().bytes; @@ -580,6 +593,27 @@ impl Chunk { offset } + fn point_to_point_utf16(&self, target: Point) -> PointUtf16 { + let mut point = Point::zero(); + let mut point_utf16 = PointUtf16::new(0, 0); + for ch in self.0.chars() { + if point >= target { + break; + } + + if ch == '\n' { + point_utf16.row += 1; + point_utf16.column = 0; + point.row += 1; + point.column = 0; + } else { + point_utf16.column += ch.len_utf16() as u32; + point.column += ch.len_utf8() as u32; + } + } + point_utf16 + } + fn point_utf16_to_offset(&self, target: PointUtf16) -> usize { let mut offset = 0; let mut point = PointUtf16::new(0, 0); diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 088cd51cbe..1bbd75867e 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1512,6 +1512,10 @@ impl BufferSnapshot { self.visible_text.offset_to_point_utf16(offset) } + pub fn point_to_point_utf16(&self, point: Point) -> PointUtf16 { + self.visible_text.point_to_point_utf16(point) + } + pub fn version(&self) -> &clock::Global { &self.version } @@ -2260,6 +2264,34 @@ impl ToPoint for Point { } } +pub trait ToPointUtf16 { + fn to_point_utf16<'a>(&self, snapshot: &BufferSnapshot) -> PointUtf16; +} + +impl ToPointUtf16 for Anchor { + fn to_point_utf16<'a>(&self, snapshot: &BufferSnapshot) -> PointUtf16 { + snapshot.summary_for_anchor(self) + } +} + +impl ToPointUtf16 for usize { + fn to_point_utf16<'a>(&self, snapshot: &BufferSnapshot) -> PointUtf16 { + snapshot.offset_to_point_utf16(*self) + } +} + +impl ToPointUtf16 for PointUtf16 { + fn to_point_utf16<'a>(&self, _: &BufferSnapshot) -> PointUtf16 { + *self + } +} + +impl ToPointUtf16 for Point { + fn to_point_utf16<'a>(&self, snapshot: &BufferSnapshot) -> PointUtf16 { + snapshot.point_to_point_utf16(*self) + } +} + pub trait Clip { fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self; }