editor: Revert to use ropey. (#1284)
For the License reason. - The performance is slow down, before version can handle 200K lines, now 150K lines is good.
This commit is contained in:
parent
ca8d595936
commit
1c3eca93b8
19 changed files with 369 additions and 192 deletions
32
Cargo.lock
generated
32
Cargo.lock
generated
|
|
@ -757,7 +757,7 @@ dependencies = [
|
|||
"bitflags 2.9.1",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.1",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
|
|
@ -780,7 +780,7 @@ dependencies = [
|
|||
"bitflags 2.9.1",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.13.0",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
|
|
@ -3225,7 +3225,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"paste",
|
||||
"regex",
|
||||
"rope",
|
||||
"ropey",
|
||||
"rust-i18n",
|
||||
"rust_decimal",
|
||||
"schemars",
|
||||
|
|
@ -3690,7 +3690,7 @@ dependencies = [
|
|||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.58.0",
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4980,7 +4980,7 @@ version = "0.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
|
||||
dependencies = [
|
||||
"proc-macro-crate 1.3.1",
|
||||
"proc-macro-crate 3.3.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.105",
|
||||
|
|
@ -6754,18 +6754,12 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "rope"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed.git#4532765ae845b8c98c73c88cc916f7d771b429d5"
|
||||
name = "ropey"
|
||||
version = "2.0.0-beta.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4045a00dc327d084a2bbf126976e14125b54f23bd30511d45b842eba76c52d74"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"log",
|
||||
"rayon",
|
||||
"smallvec",
|
||||
"sum_tree",
|
||||
"unicode-segmentation",
|
||||
"util",
|
||||
"workspace-hack",
|
||||
"str_indices",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -7802,6 +7796,12 @@ dependencies = [
|
|||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "str_indices"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6"
|
||||
|
||||
[[package]]
|
||||
name = "streaming-iterator"
|
||||
version = "0.1.9"
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ resolver = "2"
|
|||
[workspace.dependencies]
|
||||
gpui = { git = "https://github.com/zed-industries/zed.git" }
|
||||
gpui_macros = { git = "https://github.com/zed-industries/zed.git" }
|
||||
rope = { git = "https://github.com/zed-industries/zed.git" }
|
||||
reqwest_client = { git = "https://github.com/zed-industries/zed.git" }
|
||||
sum_tree = { git = "https://github.com/zed-industries/zed.git" }
|
||||
gpui-component = { path = "crates/ui" }
|
||||
gpui-component-macros = { path = "crates/macros" }
|
||||
story = { path = "crates/story" }
|
||||
wef = { path = "crates/wef" }
|
||||
ropey = { version = "=2.0.0-beta.1", features = ["metric_utf16"] }
|
||||
|
||||
anyhow = "1"
|
||||
log = "0.4"
|
||||
|
|
|
|||
|
|
@ -912,7 +912,9 @@ impl Render for Example {
|
|||
.xsmall()
|
||||
.label(format!(
|
||||
"{}:{} ({} byte)",
|
||||
position.line, position.character, cursor
|
||||
position.line + 1,
|
||||
position.character + 1,
|
||||
cursor
|
||||
))
|
||||
.on_click(cx.listener(Self::go_to_line))
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ gpui = { workspace = true }
|
|||
gpui-component-macros.workspace = true
|
||||
gpui_macros.workspace = true
|
||||
notify.workspace = true
|
||||
rope.workspace = true
|
||||
ropey.workspace = true
|
||||
rust-i18n.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
};
|
||||
|
||||
use gpui::{px, App, HighlightStyle, Hsla, SharedString, UnderlineStyle};
|
||||
use rope::Rope;
|
||||
use ropey::Rope;
|
||||
use sum_tree::{Bias, SeekTarget, SumTree};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -343,7 +343,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_diagnostic() {
|
||||
use rope::Rope;
|
||||
use ropey::Rope;
|
||||
|
||||
use super::{Diagnostic, DiagnosticSet, DiagnosticSeverity};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::{highlighter::LanguageRegistry, input::RopeExt as _, ActiveTheme};
|
||||
use crate::{highlighter::LanguageRegistry, ActiveTheme};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use gpui::{App, HighlightStyle, SharedString};
|
||||
|
||||
use rope::Rope;
|
||||
use ropey::{ChunkCursor, Rope};
|
||||
use std::{
|
||||
collections::{BTreeSet, HashMap},
|
||||
ops::Range,
|
||||
|
|
@ -40,12 +40,21 @@ pub struct SyntaxHighlighter {
|
|||
}
|
||||
|
||||
struct TextProvider<'a>(&'a Rope);
|
||||
struct ByteChunks<'a>(rope::Chunks<'a>);
|
||||
struct ByteChunks<'a> {
|
||||
cursor: ChunkCursor<'a>,
|
||||
end: usize,
|
||||
}
|
||||
impl<'a> tree_sitter::TextProvider<&'a [u8]> for TextProvider<'a> {
|
||||
type I = ByteChunks<'a>;
|
||||
|
||||
fn text(&mut self, node: tree_sitter::Node) -> Self::I {
|
||||
ByteChunks(self.0.chunks_in_range(node.byte_range()))
|
||||
let range = node.byte_range();
|
||||
let cursor = self.0.chunk_cursor_at(range.start);
|
||||
|
||||
ByteChunks {
|
||||
cursor,
|
||||
end: range.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +62,14 @@ impl<'a> Iterator for ByteChunks<'a> {
|
|||
type Item = &'a [u8];
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next().map(str::as_bytes)
|
||||
let cursor = &mut self.cursor;
|
||||
let end = self.end;
|
||||
|
||||
if cursor.next() && cursor.byte_offset() < end {
|
||||
Some(cursor.chunk().as_bytes())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -315,11 +331,14 @@ impl SyntaxHighlighter {
|
|||
.unwrap_or(self.parser.parse("", None).unwrap());
|
||||
old_tree.edit(&edit);
|
||||
|
||||
let mut chunks = text.chunks();
|
||||
let new_tree = self.parser.parse_with_options(
|
||||
&mut move |offset, _| {
|
||||
chunks.seek(offset);
|
||||
chunks.next().unwrap_or("").as_bytes()
|
||||
if offset >= text.len() {
|
||||
""
|
||||
} else {
|
||||
let (chunk, chunk_byte_ix) = text.chunk(offset);
|
||||
&chunk[offset - chunk_byte_ix..]
|
||||
}
|
||||
},
|
||||
Some(&old_tree),
|
||||
None,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::ops::Range;
|
||||
use std::ops::{Range, RangeBounds};
|
||||
|
||||
/// A selection in the text, represented by start and end byte indices.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
|
||||
|
|
@ -42,6 +42,15 @@ impl From<Selection> for Range<usize> {
|
|||
value.start..value.end
|
||||
}
|
||||
}
|
||||
impl RangeBounds<usize> for Selection {
|
||||
fn start_bound(&self) -> std::ops::Bound<&usize> {
|
||||
std::ops::Bound::Included(&self.start)
|
||||
}
|
||||
|
||||
fn end_bound(&self) -> std::ops::Bound<&usize> {
|
||||
std::ops::Bound::Excluded(&self.end)
|
||||
}
|
||||
}
|
||||
|
||||
pub type Position = lsp_types::Position;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use gpui::{
|
|||
MouseMoveEvent, Path, Pixels, Point, ShapedLine, SharedString, Size, Style, TextAlign, TextRun,
|
||||
UnderlineStyle, Window,
|
||||
};
|
||||
use rope::Rope;
|
||||
use ropey::Rope;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -508,7 +508,7 @@ impl TextElement {
|
|||
let mut styles = vec![];
|
||||
|
||||
for line in text
|
||||
.lines()
|
||||
.rows()
|
||||
.skip(visible_range.start)
|
||||
.take(visible_range.len())
|
||||
{
|
||||
|
|
@ -781,7 +781,7 @@ impl Element for TextElement {
|
|||
// NOTE: Here 50 lines about 150µs
|
||||
// let measure = crate::Measure::new("shape_text");
|
||||
let visible_text = display_text
|
||||
.slice_rows(visible_range.start as u32..visible_range.end as u32)
|
||||
.slice_rows(visible_range.start..visible_range.end)
|
||||
.to_string();
|
||||
|
||||
let lines = window
|
||||
|
|
@ -792,11 +792,8 @@ impl Element for TextElement {
|
|||
|
||||
let mut longest_line_width = wrap_width.unwrap_or(px(0.));
|
||||
if state.mode.is_multi_line() && !state.soft_wrap && lines.len() > 1 {
|
||||
let longtest_line: SharedString = state
|
||||
.text
|
||||
.line(state.text.summary().longest_row as usize)
|
||||
.to_string()
|
||||
.into();
|
||||
let longest_row = state.text_wrapper.longest_row.row;
|
||||
let longtest_line: SharedString = state.text.slice_row(longest_row).to_string().into();
|
||||
longest_line_width = window
|
||||
.text_system()
|
||||
.shape_line(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::Result;
|
||||
use gpui::{Context, EntityInputHandler, Task, Window};
|
||||
use lsp_types::{request::Completion, CompletionContext, CompletionItem, CompletionResponse};
|
||||
use rope::Rope;
|
||||
use ropey::Rope;
|
||||
use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||
|
||||
use crate::input::{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use gpui::{
|
||||
px, App, Context, HighlightStyle, Hitbox, MouseDownEvent, Task, UnderlineStyle, Window,
|
||||
};
|
||||
use rope::Rope;
|
||||
use ropey::Rope;
|
||||
use std::{ops::Range, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use anyhow::Result;
|
||||
use gpui::{App, Context, Task, Window};
|
||||
use rope::Rope;
|
||||
use ropey::Rope;
|
||||
|
||||
use crate::input::{popovers::HoverPopover, InputState, RopeExt};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub use mask_pattern::MaskPattern;
|
|||
pub use mode::TabSize;
|
||||
pub use number_input::{NumberInput, NumberInputEvent, StepAction};
|
||||
pub use otp_input::*;
|
||||
pub use rope::Rope;
|
||||
pub use rope_ext::*;
|
||||
pub use ropey::Rope;
|
||||
pub use state::*;
|
||||
pub use text_input::*;
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ use std::rc::Rc;
|
|||
use std::{cell::RefCell, ops::Range};
|
||||
|
||||
use gpui::{App, SharedString};
|
||||
use rope::Rope;
|
||||
use tree_sitter::{InputEdit, Point};
|
||||
use ropey::Rope;
|
||||
use tree_sitter::InputEdit;
|
||||
|
||||
use super::text_wrapper::TextWrapper;
|
||||
use crate::highlighter::DiagnosticSet;
|
||||
use crate::highlighter::SyntaxHighlighter;
|
||||
use crate::input::RopeExt as _;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct TabSize {
|
||||
|
|
@ -210,15 +211,9 @@ impl InputMode {
|
|||
start_byte: selected_range.start,
|
||||
old_end_byte: selected_range.end,
|
||||
new_end_byte: new_end,
|
||||
start_position: Point::new(start_pos.row as usize, start_pos.column as usize),
|
||||
old_end_position: Point::new(
|
||||
old_end_pos.row as usize,
|
||||
old_end_pos.column as usize,
|
||||
),
|
||||
new_end_position: Point::new(
|
||||
new_end_pos.row as usize,
|
||||
new_end_pos.column as usize,
|
||||
),
|
||||
start_position: start_pos,
|
||||
old_end_position: old_end_pos,
|
||||
new_end_position: new_end_pos,
|
||||
};
|
||||
|
||||
highlighter.update(Some(edit), text);
|
||||
|
|
|
|||
|
|
@ -1,19 +1,25 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use rope::{Point, Rope};
|
||||
use ropey::{LineType, Rope, RopeSlice};
|
||||
use sum_tree::Bias;
|
||||
use tree_sitter::Point;
|
||||
|
||||
use crate::input::Position;
|
||||
|
||||
/// An extension trait for `Rope` to provide additional utility methods.
|
||||
pub trait RopeExt {
|
||||
/// Get the line at the given row (0-based) index, including the `\r` at the end, but not `\n`.
|
||||
///
|
||||
/// Return empty rope if the row (0-based) is out of bounds.
|
||||
fn line(&self, row: usize) -> Rope;
|
||||
|
||||
/// Start offset of the line at the given row (0-based) index.
|
||||
fn line_start_offset(&self, row: usize) -> usize;
|
||||
|
||||
/// Return a line slice at the given row (0-based) index. including `\r` if present, but not `\n`.
|
||||
fn slice_row(&self, row: usize) -> RopeSlice<'_>;
|
||||
|
||||
fn slice_rows(&self, rows_range: Range<usize>) -> RopeSlice<'_>;
|
||||
|
||||
fn rows(&self) -> impl Iterator<Item = RopeSlice<'_>> + '_ {
|
||||
(0..self.lines_len()).map(|row| self.slice_row(row))
|
||||
}
|
||||
|
||||
/// Line the end offset (including `\n`) of the line at the given row (0-based) index.
|
||||
///
|
||||
/// Return the end of the rope if the row is out of bounds.
|
||||
|
|
@ -22,22 +28,19 @@ pub trait RopeExt {
|
|||
/// Return the number of lines in the rope.
|
||||
fn lines_len(&self) -> usize;
|
||||
|
||||
/// Return the lines iterator.
|
||||
///
|
||||
/// Each line is including the `\r` at the end, but not `\n`.
|
||||
fn lines(&self) -> RopeLines;
|
||||
|
||||
/// Check is equal to another rope.
|
||||
fn eq(&self, other: &Rope) -> bool;
|
||||
/// Return the length of the row (0-based) in characters, including `\r` if present, but not `\n`.
|
||||
fn line_len(&self, row: usize) -> usize;
|
||||
|
||||
/// Total number of characters in the rope.
|
||||
fn chars_count(&self) -> usize;
|
||||
|
||||
/// Replace the text in the given byte range with new text.
|
||||
fn replace(&mut self, range: Range<usize>, new_text: &str);
|
||||
|
||||
/// Get char at the given offset (byte).
|
||||
///
|
||||
/// If the offset is in the middle of a multi-byte character will panic.
|
||||
///
|
||||
/// If the offset is out of bounds, return None.
|
||||
/// - If the offset is in the middle of a multi-byte character will panic.
|
||||
/// - If the offset is out of bounds, return None.
|
||||
fn char_at(&self, offset: usize) -> Option<char>;
|
||||
|
||||
/// Get the byte offset from the given line, column [`Position`] (0-based).
|
||||
|
|
@ -46,76 +49,76 @@ pub trait RopeExt {
|
|||
/// Get the line, column [`Position`] (0-based) from the given byte offset.
|
||||
fn offset_to_position(&self, offset: usize) -> Position;
|
||||
|
||||
fn offset_to_point(&self, offset: usize) -> Point;
|
||||
fn point_to_offset(&self, point: Point) -> usize;
|
||||
|
||||
/// Get the word byte range at the given offset (byte).
|
||||
fn word_range(&self, offset: usize) -> Option<Range<usize>>;
|
||||
|
||||
/// Get word at the given offset (byte).
|
||||
fn word_at(&self, offset: usize) -> String;
|
||||
|
||||
/// Convert offset_utf16 to offset (byte).
|
||||
fn offset_utf16_to_offset(&self, offset_utf16: usize) -> usize;
|
||||
|
||||
/// Convert offset (byte) to offset_utf16.
|
||||
fn offset_to_offset_utf16(&self, offset: usize) -> usize;
|
||||
|
||||
/// Get a clipped offset (avoid in a char boundary).
|
||||
///
|
||||
/// - If Bias::Left and inside the char boundary, return the ix - 1;
|
||||
/// - If Bias::Right and in inside char boundary, return the ix + 1;
|
||||
/// - Otherwise return the ix.
|
||||
fn clip_offset(&self, offset: usize, bias: Bias) -> usize;
|
||||
}
|
||||
|
||||
/// An iterator over the lines of a `Rope`.
|
||||
pub struct RopeLines {
|
||||
row: usize,
|
||||
end_row: usize,
|
||||
rope: Rope,
|
||||
}
|
||||
|
||||
impl RopeLines {
|
||||
/// Create a new `RopeLines` iterator.
|
||||
pub fn new(rope: Rope) -> Self {
|
||||
let end_row = rope.lines_len();
|
||||
Self {
|
||||
row: 0,
|
||||
end_row,
|
||||
rope,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for RopeLines {
|
||||
type Item = Rope;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.row >= self.end_row {
|
||||
return None;
|
||||
}
|
||||
|
||||
let line = self.rope.line(self.row);
|
||||
self.row += 1;
|
||||
Some(line)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
||||
self.row = self.row.saturating_add(n);
|
||||
self.next()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let len = self.end_row - self.row;
|
||||
(len, Some(len))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::ExactSizeIterator for RopeLines {}
|
||||
impl std::iter::FusedIterator for RopeLines {}
|
||||
|
||||
impl RopeExt for Rope {
|
||||
fn line(&self, row: usize) -> Rope {
|
||||
let start = self.line_start_offset(row);
|
||||
let end = start + self.line_len(row as u32) as usize;
|
||||
fn slice_row(&self, row: usize) -> RopeSlice<'_> {
|
||||
if row >= self.lines_len() {
|
||||
return self.slice(0..0);
|
||||
}
|
||||
|
||||
let line = self.line(row, LineType::LF_CR);
|
||||
if line.len() > 0 && line.chars().last() == Some('\n') {
|
||||
line.slice(..line.len().saturating_sub(1))
|
||||
} else {
|
||||
line
|
||||
}
|
||||
}
|
||||
|
||||
fn slice_rows(&self, rows_range: Range<usize>) -> RopeSlice<'_> {
|
||||
let start = self.line_start_offset(rows_range.start);
|
||||
let end = self.line_end_offset(rows_range.end.saturating_sub(1));
|
||||
self.slice(start..end)
|
||||
}
|
||||
|
||||
fn line_len(&self, row: usize) -> usize {
|
||||
self.slice_row(row).len()
|
||||
}
|
||||
|
||||
fn line_start_offset(&self, row: usize) -> usize {
|
||||
let row = row as u32;
|
||||
self.point_to_offset(Point::new(row, 0))
|
||||
}
|
||||
|
||||
fn offset_to_point(&self, offset: usize) -> Point {
|
||||
let offset = self.clip_offset(offset, Bias::Left);
|
||||
let row = self.byte_to_line_idx(offset, LineType::LF_CR);
|
||||
let line_start = self.line_to_byte_idx(row, LineType::LF_CR);
|
||||
let column = offset.saturating_sub(line_start);
|
||||
Point::new(row, column)
|
||||
}
|
||||
|
||||
fn point_to_offset(&self, point: Point) -> usize {
|
||||
if point.row >= self.lines_len() {
|
||||
return self.len();
|
||||
}
|
||||
|
||||
let line_start = self.line_to_byte_idx(point.row, LineType::LF_CR);
|
||||
line_start + point.column
|
||||
}
|
||||
|
||||
fn position_to_offset(&self, pos: &Position) -> usize {
|
||||
let line = self.line(pos.line as usize);
|
||||
let line = self.slice_row(pos.line as usize);
|
||||
self.line_start_offset(pos.line as usize)
|
||||
+ line
|
||||
.chars()
|
||||
|
|
@ -126,30 +129,22 @@ impl RopeExt for Rope {
|
|||
|
||||
fn offset_to_position(&self, offset: usize) -> Position {
|
||||
let point = self.offset_to_point(offset);
|
||||
let line = self.line(point.row as usize);
|
||||
let column = line.clip_offset(point.column as usize, sum_tree::Bias::Left);
|
||||
let character = line.slice(0..column).chars().count();
|
||||
Position::new(point.row, character as u32)
|
||||
let line = self.slice_row(point.row);
|
||||
let offset = line.utf16_to_byte_idx(line.byte_to_utf16_idx(point.column));
|
||||
let character = line.slice(..offset).chars().count();
|
||||
Position::new(point.row as u32, character as u32)
|
||||
}
|
||||
|
||||
fn line_end_offset(&self, row: usize) -> usize {
|
||||
if row > self.max_point().row as usize {
|
||||
if row > self.lines_len() {
|
||||
return self.len();
|
||||
}
|
||||
|
||||
self.line_start_offset(row) + self.line_len(row as u32) as usize
|
||||
self.line_start_offset(row) + self.line_len(row)
|
||||
}
|
||||
|
||||
fn lines_len(&self) -> usize {
|
||||
self.max_point().row as usize + 1
|
||||
}
|
||||
|
||||
fn lines(&self) -> RopeLines {
|
||||
RopeLines::new(self.clone())
|
||||
}
|
||||
|
||||
fn eq(&self, other: &Rope) -> bool {
|
||||
self.summary() == other.summary()
|
||||
self.len_lines(LineType::LF_CR)
|
||||
}
|
||||
|
||||
fn chars_count(&self) -> usize {
|
||||
|
|
@ -161,8 +156,7 @@ impl RopeExt for Rope {
|
|||
return None;
|
||||
}
|
||||
|
||||
let offset = self.clip_offset(offset, sum_tree::Bias::Left);
|
||||
self.slice(offset..self.len()).chars().next()
|
||||
self.get_char(offset).ok()
|
||||
}
|
||||
|
||||
fn word_range(&self, offset: usize) -> Option<Range<usize>> {
|
||||
|
|
@ -170,10 +164,9 @@ impl RopeExt for Rope {
|
|||
return None;
|
||||
}
|
||||
|
||||
let offset = self.clip_offset(offset, sum_tree::Bias::Left);
|
||||
|
||||
let mut left = String::new();
|
||||
for c in self.reversed_chars_at(offset) {
|
||||
let offset = self.clip_offset(offset, Bias::Left);
|
||||
for c in self.chars_at(offset).reversed() {
|
||||
if c.is_alphanumeric() || c == '_' {
|
||||
left.insert(0, c);
|
||||
} else {
|
||||
|
|
@ -203,24 +196,65 @@ impl RopeExt for Rope {
|
|||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn offset_utf16_to_offset(&self, offset_utf16: usize) -> usize {
|
||||
if offset_utf16 > self.len_utf16() {
|
||||
return self.len();
|
||||
}
|
||||
|
||||
self.utf16_to_byte_idx(offset_utf16)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn offset_to_offset_utf16(&self, offset: usize) -> usize {
|
||||
if offset > self.len() {
|
||||
return self.len_utf16();
|
||||
}
|
||||
|
||||
self.byte_to_utf16_idx(offset)
|
||||
}
|
||||
|
||||
fn replace(&mut self, range: Range<usize>, new_text: &str) {
|
||||
self.remove(range.clone());
|
||||
self.insert(range.start, new_text);
|
||||
}
|
||||
|
||||
fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
|
||||
if offset > self.len() {
|
||||
return self.len();
|
||||
}
|
||||
|
||||
if self.is_char_boundary(offset) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
if bias == Bias::Left {
|
||||
self.floor_char_boundary(offset)
|
||||
} else {
|
||||
self.ceil_char_boundary(offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rope::Rope;
|
||||
use ropey::Rope;
|
||||
use sum_tree::Bias;
|
||||
use tree_sitter::Point;
|
||||
|
||||
use crate::input::{Position, RopeExt as _};
|
||||
use crate::input::{Position, RopeExt};
|
||||
|
||||
#[test]
|
||||
fn test_line() {
|
||||
let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
|
||||
assert_eq!(rope.line(0).to_string(), "Hello");
|
||||
assert_eq!(rope.line(1).to_string(), "World\r");
|
||||
assert_eq!(rope.line(2).to_string(), "This is a test 中文");
|
||||
assert_eq!(rope.line(3).to_string(), "Rope");
|
||||
assert_eq!(rope.slice_row(0).to_string(), "Hello");
|
||||
assert_eq!(rope.slice_row(1).to_string(), "World\r");
|
||||
assert_eq!(rope.slice_row(2).to_string(), "This is a test 中文");
|
||||
assert_eq!(rope.slice_row(3).to_string(), "Rope");
|
||||
|
||||
// over bounds
|
||||
assert_eq!(rope.line(6).to_string(), "");
|
||||
assert_eq!(rope.slice_row(6).to_string(), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -244,9 +278,9 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_lines() {
|
||||
fn test_rows() {
|
||||
let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
|
||||
let lines: Vec<_> = rope.lines().map(|r| r.to_string()).collect();
|
||||
let lines: Vec<_> = rope.rows().map(|r| r.to_string()).collect();
|
||||
assert_eq!(
|
||||
lines,
|
||||
vec!["Hello", "World\r", "This is a test 中文", "Rope"]
|
||||
|
|
@ -305,6 +339,32 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_offset_to_point() {
|
||||
let rope = Rope::from("a 中文🎉 test\nRope");
|
||||
assert_eq!(rope.offset_to_point(0), Point::new(0, 0));
|
||||
assert_eq!(rope.offset_to_point(1), Point::new(0, 1));
|
||||
assert_eq!(rope.offset_to_point("a 中".len()), Point::new(0, 5));
|
||||
assert_eq!(rope.offset_to_point("a 中文🎉".len()), Point::new(0, 12));
|
||||
assert_eq!(
|
||||
rope.offset_to_point("a 中文🎉 test\nR".len()),
|
||||
Point::new(1, 1)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_point_to_offset() {
|
||||
let rope = Rope::from("a 中文🎉 test\nRope");
|
||||
assert_eq!(rope.point_to_offset(Point::new(0, 0)), 0);
|
||||
assert_eq!(rope.point_to_offset(Point::new(0, 1)), 1);
|
||||
assert_eq!(rope.point_to_offset(Point::new(0, 5)), "a 中".len());
|
||||
assert_eq!(rope.point_to_offset(Point::new(0, 12)), "a 中文🎉".len());
|
||||
assert_eq!(
|
||||
rope.point_to_offset(Point::new(1, 1)),
|
||||
"a 中文🎉 test\nR".len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char_at() {
|
||||
let rope = Rope::from("Hello\nWorld\r\nThis is a test 中文🎉\nRope");
|
||||
|
|
@ -336,4 +396,65 @@ mod tests {
|
|||
assert_eq!(rope.word_range(44), Some(42..46));
|
||||
assert_eq!(rope.word_at(45), "Rope");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_offset_utf16_conversion() {
|
||||
let rope = Rope::from("hello 中文🎉 test\nRope");
|
||||
assert_eq!(rope.offset_to_offset_utf16("hello".len()), 5);
|
||||
assert_eq!(rope.offset_to_offset_utf16("hello 中".len()), 7);
|
||||
assert_eq!(rope.offset_to_offset_utf16("hello 中文".len()), 8);
|
||||
assert_eq!(rope.offset_to_offset_utf16("hello 中文🎉".len()), 10);
|
||||
assert_eq!(rope.offset_to_offset_utf16(100), 20);
|
||||
|
||||
assert_eq!(rope.offset_utf16_to_offset(5), "hello".len());
|
||||
assert_eq!(rope.offset_utf16_to_offset(7), "hello 中".len());
|
||||
assert_eq!(rope.offset_utf16_to_offset(8), "hello 中文".len());
|
||||
assert_eq!(rope.offset_utf16_to_offset(10), "hello 中文🎉".len());
|
||||
assert_eq!(rope.offset_utf16_to_offset(100), rope.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace() {
|
||||
let mut rope = Rope::from("Hello\nWorld\r\nThis is a test 中文\nRope");
|
||||
rope.replace(6..11, "Universe");
|
||||
assert_eq!(
|
||||
rope.to_string(),
|
||||
"Hello\nUniverse\r\nThis is a test 中文\nRope"
|
||||
);
|
||||
|
||||
rope.replace(0..5, "Hi");
|
||||
assert_eq!(
|
||||
rope.to_string(),
|
||||
"Hi\nUniverse\r\nThis is a test 中文\nRope"
|
||||
);
|
||||
|
||||
rope.replace(rope.len() - 4..rope.len(), "String");
|
||||
assert_eq!(
|
||||
rope.to_string(),
|
||||
"Hi\nUniverse\r\nThis is a test 中文\nString"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clip_offset() {
|
||||
let rope = Rope::from("Hello 中文🎉 test\nRope");
|
||||
// Inside multi-byte character '中' (3 bytes)
|
||||
assert_eq!(rope.clip_offset(5, Bias::Left), 5);
|
||||
assert_eq!(rope.clip_offset(7, Bias::Left), 6);
|
||||
assert_eq!(rope.clip_offset(7, Bias::Right), 9);
|
||||
assert_eq!(rope.clip_offset(9, Bias::Left), 9);
|
||||
|
||||
// Inside multi-byte character '🎉' (4 bytes)
|
||||
assert_eq!(rope.clip_offset(13, Bias::Left), 12);
|
||||
assert_eq!(rope.clip_offset(13, Bias::Right), 16);
|
||||
assert_eq!(rope.clip_offset(16, Bias::Left), 16);
|
||||
|
||||
// At character boundary
|
||||
assert_eq!(rope.clip_offset(5, Bias::Left), 5);
|
||||
assert_eq!(rope.clip_offset(5, Bias::Right), 5);
|
||||
|
||||
// Out of bounds
|
||||
assert_eq!(rope.clip_offset(26, Bias::Left), 26);
|
||||
assert_eq!(rope.clip_offset(100, Bias::Left), 26);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ use gpui::{
|
|||
Entity, FocusHandle, Focusable, Half, InteractiveElement as _, IntoElement, KeyBinding,
|
||||
ParentElement as _, Pixels, Render, Styled, Subscription, Window,
|
||||
};
|
||||
use rope::Rope;
|
||||
use ropey::Rope;
|
||||
|
||||
use crate::{
|
||||
actions::SelectPrev,
|
||||
button::{Button, ButtonVariants},
|
||||
h_flex,
|
||||
input::{Enter, Escape, IndentInline, InputEvent, InputState, RopeExt, Search, TextInput},
|
||||
input::{Enter, Escape, IndentInline, InputEvent, InputState, RopeExt as _, Search, TextInput},
|
||||
label::Label,
|
||||
v_flex, ActiveTheme, Disableable, IconName, Selectable, Sizable,
|
||||
};
|
||||
|
|
@ -65,7 +65,9 @@ impl SearchMatcher {
|
|||
fn update_matches(&mut self) {
|
||||
let mut new_ranges = Vec::new();
|
||||
if let Some(query) = &self.query {
|
||||
let matches = query.stream_find_iter(self.text.bytes_in_range(0..self.text.len()));
|
||||
let text = self.text.to_string();
|
||||
// FIXME: Use stream find
|
||||
let matches = query.stream_find_iter(text.as_bytes());
|
||||
|
||||
for query_match in matches.into_iter() {
|
||||
let query_match = query_match.expect("query match for select all action");
|
||||
|
|
@ -201,7 +203,7 @@ impl InputState {
|
|||
|
||||
let text = self.text.clone();
|
||||
let editor = cx.entity();
|
||||
let selected_text = self.selected_text();
|
||||
let selected_text = Rope::from(self.selected_text());
|
||||
search_panel.update(cx, |this, cx| {
|
||||
this.editor = editor;
|
||||
this.matcher.update(&text);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use gpui::{
|
|||
ScrollWheelEvent, SharedString, Styled as _, Subscription, Task, UTF16Selection, Window,
|
||||
WrappedLine,
|
||||
};
|
||||
use rope::{OffsetUtf16, Rope};
|
||||
use ropey::{Rope, RopeSlice};
|
||||
use serde::Deserialize;
|
||||
use smallvec::SmallVec;
|
||||
use std::cell::RefCell;
|
||||
|
|
@ -581,19 +581,18 @@ impl InputState {
|
|||
};
|
||||
|
||||
let point = self.text.offset_to_point(self.cursor());
|
||||
let row = (point.row as usize).saturating_sub(last_layout.visible_range.start);
|
||||
let row = point.row.saturating_sub(last_layout.visible_range.start);
|
||||
let Some(line) = last_layout.lines.get(row) else {
|
||||
self.preferred_column = None;
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(pos) = line.position_for_index(point.column as usize, last_layout.line_height)
|
||||
else {
|
||||
let Some(pos) = line.position_for_index(point.column, last_layout.line_height) else {
|
||||
self.preferred_column = None;
|
||||
return;
|
||||
};
|
||||
|
||||
self.preferred_column = Some((pos.x, point.column as usize));
|
||||
self.preferred_column = Some((pos.x, point.column));
|
||||
}
|
||||
|
||||
/// Find which line and sub-line the given offset belongs to, along with the position within that sub-line.
|
||||
|
|
@ -644,17 +643,18 @@ impl InputState {
|
|||
let was_preferred_column = self.preferred_column;
|
||||
|
||||
let row = self.text.offset_to_point(offset).row;
|
||||
let new_row = row.saturating_add_signed(move_lines as i32);
|
||||
let line_start_offset = self.text.point_to_offset(rope::Point::new(new_row, 0));
|
||||
let new_row = row.saturating_add_signed(move_lines);
|
||||
let line_start_offset = self
|
||||
.text
|
||||
.point_to_offset(tree_sitter::Point::new(new_row, 0));
|
||||
|
||||
let mut new_offset = line_start_offset;
|
||||
|
||||
if let Some((preferred_x, column)) = was_preferred_column {
|
||||
let new_column = column.min(self.text.line(new_row as usize).len());
|
||||
let new_column = column.min(self.text.slice_row(new_row).len());
|
||||
new_offset = line_start_offset + new_column;
|
||||
|
||||
// If in visible range, prefer to use position to get column.
|
||||
let new_row = new_row as usize;
|
||||
if new_row >= last_layout.visible_range.start {
|
||||
let visible_row = new_row.saturating_sub(last_layout.visible_range.start);
|
||||
if let Some(line) = last_layout.lines.get(visible_row) {
|
||||
|
|
@ -889,13 +889,7 @@ impl InputState {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let position: Position = position.into();
|
||||
let max_point = self.text.max_point();
|
||||
let row = position.line.min(max_point.row);
|
||||
let col = position.character.min(self.text.line_len(row));
|
||||
|
||||
let offset = self
|
||||
.text
|
||||
.point_to_offset(rope::Point::new(row as u32, col as u32));
|
||||
let offset = self.text.position_to_offset(&position);
|
||||
|
||||
self.move_to(offset, cx);
|
||||
self.update_preferred_column();
|
||||
|
|
@ -1164,7 +1158,7 @@ impl InputState {
|
|||
}
|
||||
|
||||
let row = self.text.offset_to_point(self.cursor()).row;
|
||||
self.text.line_start_offset(row as usize)
|
||||
self.text.line_start_offset(row)
|
||||
}
|
||||
|
||||
/// Get end of line byte offset of cursor
|
||||
|
|
@ -1174,7 +1168,7 @@ impl InputState {
|
|||
}
|
||||
|
||||
let row = self.text.offset_to_point(self.cursor()).row;
|
||||
self.text.line_end_offset(row as usize)
|
||||
self.text.line_end_offset(row)
|
||||
}
|
||||
|
||||
/// Get start line of selection start or end (The min value).
|
||||
|
|
@ -1668,7 +1662,7 @@ impl InputState {
|
|||
let line_height = last_layout.line_height;
|
||||
|
||||
let point = self.text.offset_to_point(offset);
|
||||
let row = point.row as usize;
|
||||
let row = point.row;
|
||||
|
||||
let mut row_offset_y = px(0.);
|
||||
for (ix, wrap_line) in self.text_wrapper.lines.iter().enumerate() {
|
||||
|
|
@ -1710,7 +1704,7 @@ impl InputState {
|
|||
return;
|
||||
}
|
||||
|
||||
let selected_text = self.text.slice(self.selected_range.into()).to_string();
|
||||
let selected_text = self.text.slice(self.selected_range).to_string();
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(selected_text));
|
||||
}
|
||||
|
||||
|
|
@ -1719,7 +1713,7 @@ impl InputState {
|
|||
return;
|
||||
}
|
||||
|
||||
let selected_text = self.text.slice(self.selected_range.into()).to_string();
|
||||
let selected_text = self.text.slice(self.selected_range).to_string();
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(selected_text));
|
||||
|
||||
self.replace_text_in_range_silent(None, "", window, cx);
|
||||
|
|
@ -2014,12 +2008,12 @@ impl InputState {
|
|||
|
||||
#[inline]
|
||||
pub(super) fn offset_from_utf16(&self, offset: usize) -> usize {
|
||||
self.text.offset_utf16_to_offset(OffsetUtf16(offset))
|
||||
self.text.offset_utf16_to_offset(offset)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn offset_to_utf16(&self, offset: usize) -> usize {
|
||||
self.text.offset_to_offset_utf16(offset).0
|
||||
self.text.offset_to_offset_utf16(offset)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -2191,7 +2185,7 @@ impl InputState {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn selected_text(&self) -> Rope {
|
||||
pub(super) fn selected_text(&self) -> RopeSlice<'_> {
|
||||
let range_utf16 = self.range_to_utf16(&self.selected_range.into());
|
||||
let range = self.range_from_utf16(&range_utf16);
|
||||
self.text.slice(range)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use gpui::{App, Font, LineFragment, Pixels};
|
||||
use rope::Rope;
|
||||
use ropey::{LineType, Rope};
|
||||
|
||||
use crate::input::RopeExt;
|
||||
|
||||
|
|
@ -37,6 +37,14 @@ impl LineItem {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct LongestRow {
|
||||
/// The 0-based row index.
|
||||
pub row: usize,
|
||||
/// The bytes length of the longest line.
|
||||
pub len: usize,
|
||||
}
|
||||
|
||||
/// Used to prepare the text with soft wrap to be get lines to displayed in the Editor.
|
||||
///
|
||||
/// After use lines to calculate the scroll size of the Editor.
|
||||
|
|
@ -48,6 +56,8 @@ pub(super) struct TextWrapper {
|
|||
font_size: Pixels,
|
||||
/// If is none, it means the text is not wrapped
|
||||
wrap_width: Option<Pixels>,
|
||||
/// The longest (row, bytes len) in characters, used to calculate the horizontal scroll width.
|
||||
pub(super) longest_row: LongestRow,
|
||||
/// The lines by split \n
|
||||
pub(super) lines: Vec<LineItem>,
|
||||
}
|
||||
|
|
@ -61,6 +71,7 @@ impl TextWrapper {
|
|||
font_size,
|
||||
wrap_width,
|
||||
soft_lines: 0,
|
||||
longest_row: LongestRow::default(),
|
||||
lines: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
|
@ -149,18 +160,25 @@ impl TextWrapper {
|
|||
}
|
||||
|
||||
// Remove the old changed lines.
|
||||
let start_row = self.text.offset_to_point(range.start).row as usize;
|
||||
let start_row = self.text.offset_to_point(range.start).row;
|
||||
let start_row = start_row.min(self.lines.len().saturating_sub(1));
|
||||
let end_row = self.text.offset_to_point(range.end).row as usize;
|
||||
let end_row = self.text.offset_to_point(range.end).row;
|
||||
let end_row = end_row.min(self.lines.len().saturating_sub(1));
|
||||
let rows_range = start_row..=end_row;
|
||||
|
||||
if rows_range.contains(&self.longest_row.row) {
|
||||
self.longest_row = LongestRow::default();
|
||||
}
|
||||
|
||||
let mut longest_row_ix = self.longest_row.row;
|
||||
let mut longest_row_len = self.longest_row.len;
|
||||
|
||||
// To add the new lines.
|
||||
let new_start_row = changed_text.offset_to_point(range.start).row as usize;
|
||||
let new_start_row = changed_text.offset_to_point(range.start).row;
|
||||
let new_start_offset = changed_text.line_start_offset(new_start_row);
|
||||
let new_end_row = changed_text
|
||||
.offset_to_point(range.start + new_text.len())
|
||||
.row as usize;
|
||||
.row;
|
||||
let new_end_offset = changed_text.line_end_offset(new_end_row);
|
||||
let new_range = new_start_offset..new_end_offset;
|
||||
|
||||
|
|
@ -168,11 +186,27 @@ impl TextWrapper {
|
|||
|
||||
let wrap_width = self.wrap_width;
|
||||
|
||||
for line in changed_text.slice(new_range).lines() {
|
||||
for (ix, line) in changed_text
|
||||
.slice(new_range)
|
||||
.lines(LineType::LF_CR)
|
||||
.enumerate()
|
||||
{
|
||||
// Remove the last `\n`
|
||||
let line = if line.len() > 0 && line.chars().last() == Some('\n') {
|
||||
line.slice(..line.len().saturating_sub(1))
|
||||
} else {
|
||||
line
|
||||
};
|
||||
|
||||
let line_str = line.to_string();
|
||||
let mut wrapped_lines = vec![];
|
||||
let mut prev_boundary_ix = 0;
|
||||
|
||||
if line_str.len() > longest_row_len {
|
||||
longest_row_ix = new_start_row + ix;
|
||||
longest_row_len = line_str.len();
|
||||
}
|
||||
|
||||
// If wrap_width is Pixels::MAX, skip wrapping to disable word wrap
|
||||
if let Some(wrap_width) = wrap_width {
|
||||
// Here only have wrapped line, if there is no wrap meet, the `line_wraps` result will empty.
|
||||
|
|
@ -188,7 +222,7 @@ impl TextWrapper {
|
|||
}
|
||||
|
||||
new_lines.push(LineItem {
|
||||
line: line.clone(),
|
||||
line: Rope::from(line),
|
||||
wrapped_lines,
|
||||
});
|
||||
}
|
||||
|
|
@ -204,6 +238,10 @@ impl TextWrapper {
|
|||
// dbg!(self.lines.len());
|
||||
self.text = changed_text.clone();
|
||||
self.soft_lines = self.lines.iter().map(|l| l.lines_len()).sum();
|
||||
self.longest_row = LongestRow {
|
||||
row: longest_row_ix,
|
||||
len: longest_row_len,
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the text wrapper and recalculate the wrapped lines.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use lsp_types::{
|
|||
CompletionItem, CompletionItemKind, CompletionResponse, CompletionTextEdit, Diagnostic,
|
||||
DiagnosticSeverity, Position, TextEdit,
|
||||
};
|
||||
use rope::Rope;
|
||||
use ropey::Rope;
|
||||
|
||||
use crate::{
|
||||
alert::Alert,
|
||||
|
|
@ -348,7 +348,7 @@ fn rust_to_style(mut style: StyleRefinement, source: &str) -> (StyleRefinement,
|
|||
let mut offset = 0;
|
||||
let mut method_offset = 0;
|
||||
let mut method = String::new();
|
||||
for line in rope.lines() {
|
||||
for line in rope.rows() {
|
||||
if line.to_string().trim().starts_with("//") {
|
||||
offset += line.len() + 1;
|
||||
continue;
|
||||
|
|
@ -564,7 +564,7 @@ struct LspProvider {}
|
|||
impl CompletionProvider for LspProvider {
|
||||
fn completions(
|
||||
&self,
|
||||
rope: &rope::Rope,
|
||||
rope: &ropey::Rope,
|
||||
offset: usize,
|
||||
_: lsp_types::CompletionContext,
|
||||
_: &mut Window,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use gpui::{
|
|||
StatefulInteractiveElement, Styled, StyledImage as _, Window,
|
||||
};
|
||||
use markdown::mdast;
|
||||
use rope::Rope;
|
||||
use ropey::Rope;
|
||||
|
||||
use crate::{
|
||||
h_flex,
|
||||
|
|
@ -291,7 +291,7 @@ impl CodeBlock {
|
|||
let mut styles = vec![];
|
||||
if let Some(lang) = &lang {
|
||||
let mut highlighter = SyntaxHighlighter::new(&lang, cx);
|
||||
highlighter.update(None, &Rope::from(code.as_str()));
|
||||
highlighter.update(None, &Rope::from_str(code.as_str()));
|
||||
styles = highlighter.styles(&(0..code.len()), cx);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue