-//! FIXME: write short doc here
+//! Code actions can specify desirable final position of the cursor.
+//!
+//! The position is specified as a `TextUnit` in the final file. We need to send
+//! it in `(Line, Column)` coordinate though. However, we only have a LineIndex
+//! for a file pre-edit!
+//!
+//! Code in this module applies this "to (Line, Column) after edit"
+//! transformation.
use ra_syntax::{TextRange, TextUnit};
use ra_text_edit::{AtomTextEdit, TextEdit};
use crate::line_index::{LineCol, LineIndex, Utf16Char};
+pub fn translate_offset_with_edit(
+ line_index: &LineIndex,
+ offset: TextUnit,
+ text_edit: &TextEdit,
+) -> LineCol {
+ let mut state = Edits::from_text_edit(&text_edit);
+
+ let mut res = RunningLineCol::new();
+
+ macro_rules! test_step {
+ ($x:ident) => {
+ match &$x {
+ Step::Newline(n) => {
+ if offset < *n {
+ return res.to_line_col(offset);
+ } else {
+ res.add_line(*n);
+ }
+ }
+ Step::Utf16Char(x) => {
+ if offset < x.end() {
+ // if the offset is inside a multibyte char it's invalid
+ // clamp it to the start of the char
+ let clamp = offset.min(x.start());
+ return res.to_line_col(clamp);
+ } else {
+ res.adjust_col(*x);
+ }
+ }
+ }
+ };
+ }
+
+ for orig_step in LineIndexStepIter::from(line_index) {
+ loop {
+ let translated_step = state.translate_step(&orig_step);
+ match state.next_steps(&translated_step) {
+ NextSteps::Use => {
+ test_step!(translated_step);
+ break;
+ }
+ NextSteps::ReplaceMany(ns) => {
+ for n in ns {
+ test_step!(n);
+ }
+ break;
+ }
+ NextSteps::AddMany(ns) => {
+ for n in ns {
+ test_step!(n);
+ }
+ }
+ }
+ }
+ }
+
+ loop {
+ match state.next_inserted_steps() {
+ None => break,
+ Some(ns) => {
+ for n in ns {
+ test_step!(n);
+ }
+ }
+ }
+ }
+
+ res.to_line_col(offset)
+}
+
#[derive(Debug, Clone)]
enum Step {
Newline(TextUnit),
offset: TextUnit,
}
-impl<'a> Iterator for OffsetStepIter<'a> {
+impl Iterator for OffsetStepIter<'_> {
type Item = Step;
fn next(&mut self) -> Option<Step> {
let (next, next_offset) = self
}
}
-pub fn translate_offset_with_edit(
- line_index: &LineIndex,
- offset: TextUnit,
- text_edit: &TextEdit,
-) -> LineCol {
- let mut state = Edits::from_text_edit(&text_edit);
-
- let mut res = RunningLineCol::new();
-
- macro_rules! test_step {
- ($x:ident) => {
- match &$x {
- Step::Newline(n) => {
- if offset < *n {
- return res.to_line_col(offset);
- } else {
- res.add_line(*n);
- }
- }
- Step::Utf16Char(x) => {
- if offset < x.end() {
- // if the offset is inside a multibyte char it's invalid
- // clamp it to the start of the char
- let clamp = offset.min(x.start());
- return res.to_line_col(clamp);
- } else {
- res.adjust_col(*x);
- }
- }
- }
- };
- }
-
- for orig_step in LineIndexStepIter::from(line_index) {
- loop {
- let translated_step = state.translate_step(&orig_step);
- match state.next_steps(&translated_step) {
- NextSteps::Use => {
- test_step!(translated_step);
- break;
- }
- NextSteps::ReplaceMany(ns) => {
- for n in ns {
- test_step!(n);
- }
- break;
- }
- NextSteps::AddMany(ns) => {
- for n in ns {
- test_step!(n);
- }
- }
- }
- }
- }
-
- loop {
- match state.next_inserted_steps() {
- None => break,
- Some(ns) => {
- for n in ns {
- test_step!(n);
- }
- }
- }
- }
-
- res.to_line_col(offset)
-}
-
#[cfg(test)]
mod test {
use proptest::{prelude::*, proptest};