using ropey

This commit is contained in:
2026-05-24 16:24:22 +02:00
parent 5a6adbc5e4
commit fbf60944a8
3 changed files with 49 additions and 24 deletions
Generated
+17
View File
@@ -448,6 +448,7 @@ dependencies = [
"dashmap 6.1.0",
"mist-codegen",
"mist-parser",
"ropey",
"serde",
"serde_json",
"tokio",
@@ -623,6 +624,16 @@ dependencies = [
"bitflags 2.11.1",
]
[[package]]
name = "ropey"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5"
dependencies = [
"smallvec",
"str_indices",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -742,6 +753,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "str_indices"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6"
[[package]]
name = "syn"
version = "2.0.117"
+1
View File
@@ -19,3 +19,4 @@ cargo_metadata = "0.23.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
ropey = "1"
+31 -24
View File
@@ -8,6 +8,7 @@ use std::path::{Component, PathBuf};
use std::sync::Arc;
use mist_parser::rev_mapper;
use ropey::Rope;
use tokio::sync::Mutex;
use tower_lsp::jsonrpc::Result;
use tower_lsp::lsp_types::notification::Notification;
@@ -539,44 +540,50 @@ pub fn from_rust_to_mist(mut path: PathBuf) -> PathBuf {
}
fn insert_at_position(s: &str, line: usize, col: usize, insert: &str) -> String {
let mut lines: Vec<String> = s.lines().map(|l| l.to_string()).collect();
let mut rope = Rope::from_str(s);
// Convert to 0-index
// Convert to 0-indexed
let line_idx = line.saturating_sub(1);
let col_idx = col.saturating_sub(1);
// Ensure line exists (optional behavior: extend with empty lines)
if line_idx >= lines.len() {
lines.resize(line_idx + 1, String::new());
}
// Clamp line
let line_idx = line_idx.min(rope.len_lines().saturating_sub(1));
let target = &mut lines[line_idx];
// Get start char index of the line
let line_start = rope.line_to_char(line_idx);
// Clamp column to valid char boundary
let char_idx = target
.char_indices()
.nth(col_idx)
.map(|(i, _)| i)
.unwrap_or(target.len()); // if past end, append
// Get line length in chars
let line = rope.line(line_idx);
let line_len = line.len_chars();
target.insert_str(char_idx, insert);
// Clamp column
let col_idx = col_idx.min(line_len);
lines.join("\n")
// Final insertion char index
let insert_idx = line_start + col_idx;
rope.insert(insert_idx, insert);
rope.to_string()
}
fn find_row_col(output: &str, inject: &str) -> Option<(usize, usize)> {
// 1. Find the flat byte index just like your original code
let rope = Rope::from_str(output);
let byte_idx = output.find(inject)?;
// 2. Slice the string up to the match point
let prefix = &output[..byte_idx];
// Convert byte index -> char index
let char_idx = output[..byte_idx].chars().count();
// 3. Row = number of newlines found before the match + 1 (1-indexed)
let row = prefix.lines().count();
// Ropey line lookup
let line_idx = rope.char_to_line(char_idx);
// 4. Column = character count of the remaining text on the current line + 1
// (Using .chars().count() ensures it works with multi-byte UTF-8 symbols)
let col = prefix.lines().last().unwrap_or("").chars().count() + 1;
// Line start char index
let line_start = rope.line_to_char(line_idx);
Some((row, col))
// Column within line
let col_idx = char_idx - line_start;
// Return 1-indexed
Some((line_idx + 1, col_idx + 1))
}