Update marked text helpers to use more distinctive characters for markers
This commit is contained in:
parent
9c3b287a61
commit
eabd9c02e5
9 changed files with 540 additions and 609 deletions
|
@ -8050,33 +8050,33 @@ mod tests {
|
||||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||||
|
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
const a: |A = (
|
const a: ˇA = (
|
||||||
(|
|
(ˇ
|
||||||
[const_function}(|),
|
«const_functionˇ»(ˇ),
|
||||||
so{m]et[h}ing_|else,|
|
so«mˇ»et«hˇ»ing_ˇelse,ˇ
|
||||||
)|
|
)ˇ
|
||||||
|);|
|
ˇ);ˇ
|
||||||
"});
|
"});
|
||||||
cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
|
cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
const a: A = (
|
const a: A = (
|
||||||
|
|
ˇ
|
||||||
(
|
(
|
||||||
|
|
ˇ
|
||||||
const_function(),
|
const_function(),
|
||||||
|
|
ˇ
|
||||||
|
|
ˇ
|
||||||
something_else,
|
something_else,
|
||||||
|
|
ˇ
|
||||||
|
|
ˇ
|
||||||
|
|
ˇ
|
||||||
|
|
ˇ
|
||||||
)
|
)
|
||||||
|
|
ˇ
|
||||||
);
|
);
|
||||||
|
|
ˇ
|
||||||
|
|
ˇ
|
||||||
"});
|
"});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -8115,25 +8115,25 @@ mod tests {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
|ab|c
|
ˇabˇc
|
||||||
|🏀|🏀|efg
|
ˇ🏀ˇ🏀ˇefg
|
||||||
d|
|
dˇ
|
||||||
"});
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
|ab |c
|
ˇab ˇc
|
||||||
|🏀 |🏀 |efg
|
ˇ🏀 ˇ🏀 ˇefg
|
||||||
d |
|
d ˇ
|
||||||
"});
|
"});
|
||||||
|
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
a
|
a
|
||||||
[🏀}🏀[🏀}🏀[🏀}
|
«🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
|
||||||
"});
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
a
|
a
|
||||||
[🏀}🏀[🏀}🏀[🏀}
|
«🏀ˇ»🏀«🏀ˇ»🏀«🏀ˇ»
|
||||||
"});
|
"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8154,26 +8154,26 @@ mod tests {
|
||||||
// a soft tab. cursors that are to the left of the suggested indent
|
// a soft tab. cursors that are to the left of the suggested indent
|
||||||
// auto-indent their line.
|
// auto-indent their line.
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
|
|
ˇ
|
||||||
const a: B = (
|
const a: B = (
|
||||||
c(
|
c(
|
||||||
d(
|
d(
|
||||||
|
|
ˇ
|
||||||
)
|
)
|
||||||
|
|
ˇ
|
||||||
| )
|
ˇ )
|
||||||
);
|
);
|
||||||
"});
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
|
|
ˇ
|
||||||
const a: B = (
|
const a: B = (
|
||||||
c(
|
c(
|
||||||
d(
|
d(
|
||||||
|
|
ˇ
|
||||||
)
|
)
|
||||||
|
|
ˇ
|
||||||
|)
|
ˇ)
|
||||||
);
|
);
|
||||||
"});
|
"});
|
||||||
|
|
||||||
|
@ -8181,16 +8181,16 @@ mod tests {
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
const a: B = (
|
const a: B = (
|
||||||
c(
|
c(
|
||||||
| |
|
ˇ ˇ
|
||||||
| )
|
ˇ )
|
||||||
);
|
);
|
||||||
"});
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
const a: B = (
|
const a: B = (
|
||||||
c(
|
c(
|
||||||
|
|
ˇ
|
||||||
|)
|
ˇ)
|
||||||
);
|
);
|
||||||
"});
|
"});
|
||||||
}
|
}
|
||||||
|
@ -8200,58 +8200,68 @@ mod tests {
|
||||||
let mut cx = EditorTestContext::new(cx).await;
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
|
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
[one} [two}
|
«oneˇ» «twoˇ»
|
||||||
three
|
three
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
[one} [two}
|
«oneˇ» «twoˇ»
|
||||||
three
|
three
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
|
|
||||||
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
[one} [two}
|
«oneˇ» «twoˇ»
|
||||||
three
|
three
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
|
|
||||||
// select across line ending
|
// select across line ending
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
t[hree
|
t«hree
|
||||||
} four"});
|
ˇ» four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
t[hree
|
t«hree
|
||||||
} four"});
|
ˇ» four
|
||||||
|
"});
|
||||||
|
|
||||||
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
t[hree
|
t«hree
|
||||||
} four"});
|
ˇ» four
|
||||||
|
"});
|
||||||
|
|
||||||
// Ensure that indenting/outdenting works when the cursor is at column 0.
|
// Ensure that indenting/outdenting works when the cursor is at column 0.
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
|three
|
ˇthree
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
|three
|
ˇthree
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
|
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
| three
|
ˇ three
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
|three
|
ˇthree
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -8265,75 +8275,90 @@ mod tests {
|
||||||
|
|
||||||
// select two ranges on one line
|
// select two ranges on one line
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
[one} [two}
|
«oneˇ» «twoˇ»
|
||||||
three
|
three
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
\t[one} [two}
|
\t«oneˇ» «twoˇ»
|
||||||
three
|
three
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
\t\t[one} [two}
|
\t\t«oneˇ» «twoˇ»
|
||||||
three
|
three
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
\t[one} [two}
|
\t«oneˇ» «twoˇ»
|
||||||
three
|
three
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
[one} [two}
|
«oneˇ» «twoˇ»
|
||||||
three
|
three
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
|
|
||||||
// select across a line ending
|
// select across a line ending
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
t[hree
|
t«hree
|
||||||
}four"});
|
ˇ»four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
\tt[hree
|
\tt«hree
|
||||||
}four"});
|
ˇ»four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
\t\tt[hree
|
\t\tt«hree
|
||||||
}four"});
|
ˇ»four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
\tt[hree
|
\tt«hree
|
||||||
}four"});
|
ˇ»four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
t[hree
|
t«hree
|
||||||
}four"});
|
ˇ»four
|
||||||
|
"});
|
||||||
|
|
||||||
// Ensure that indenting/outdenting works when the cursor is at column 0.
|
// Ensure that indenting/outdenting works when the cursor is at column 0.
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
|three
|
ˇthree
|
||||||
four"});
|
four
|
||||||
cx.assert_editor_state(indoc! {"
|
"});
|
||||||
one two
|
|
||||||
|three
|
|
||||||
four"});
|
|
||||||
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
|
||||||
cx.assert_editor_state(indoc! {"
|
|
||||||
one two
|
|
||||||
\t|three
|
|
||||||
four"});
|
|
||||||
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one two
|
one two
|
||||||
|three
|
ˇthree
|
||||||
four"});
|
four
|
||||||
|
"});
|
||||||
|
cx.update_editor(|e, cx| e.tab(&Tab, cx));
|
||||||
|
cx.assert_editor_state(indoc! {"
|
||||||
|
one two
|
||||||
|
\tˇthree
|
||||||
|
four
|
||||||
|
"});
|
||||||
|
cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
|
||||||
|
cx.assert_editor_state(indoc! {"
|
||||||
|
one two
|
||||||
|
ˇthree
|
||||||
|
four
|
||||||
|
"});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -8412,10 +8437,10 @@ mod tests {
|
||||||
select_ranges(
|
select_ranges(
|
||||||
&mut editor,
|
&mut editor,
|
||||||
indoc! {"
|
indoc! {"
|
||||||
[a] = 1
|
«aˇ» = 1
|
||||||
b = 2
|
b = 2
|
||||||
|
|
||||||
[const c:] usize = 3;
|
«const c:ˇ» usize = 3;
|
||||||
"},
|
"},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -8424,10 +8449,10 @@ mod tests {
|
||||||
assert_text_with_selections(
|
assert_text_with_selections(
|
||||||
&mut editor,
|
&mut editor,
|
||||||
indoc! {"
|
indoc! {"
|
||||||
[a] = 1
|
«aˇ» = 1
|
||||||
b = 2
|
b = 2
|
||||||
|
|
||||||
[const c:] usize = 3;
|
«const c:ˇ» usize = 3;
|
||||||
"},
|
"},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -8435,10 +8460,10 @@ mod tests {
|
||||||
assert_text_with_selections(
|
assert_text_with_selections(
|
||||||
&mut editor,
|
&mut editor,
|
||||||
indoc! {"
|
indoc! {"
|
||||||
[a] = 1
|
«aˇ» = 1
|
||||||
b = 2
|
b = 2
|
||||||
|
|
||||||
[const c:] usize = 3;
|
«const c:ˇ» usize = 3;
|
||||||
"},
|
"},
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -8450,43 +8475,48 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_backspace(cx: &mut gpui::TestAppContext) {
|
async fn test_backspace(cx: &mut gpui::TestAppContext) {
|
||||||
let mut cx = EditorTestContext::new(cx).await;
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
|
|
||||||
// Basic backspace
|
// Basic backspace
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
on|e two three
|
onˇe two three
|
||||||
fou[r} five six
|
fou«rˇ» five six
|
||||||
seven {eight nine
|
seven «ˇeight nine
|
||||||
]ten"});
|
»ten
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
|
cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
o|e two three
|
oˇe two three
|
||||||
fou| five six
|
fouˇ five six
|
||||||
seven |ten"});
|
seven ˇten
|
||||||
|
"});
|
||||||
|
|
||||||
// Test backspace inside and around indents
|
// Test backspace inside and around indents
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
zero
|
zero
|
||||||
|one
|
ˇone
|
||||||
|two
|
ˇtwo
|
||||||
| | | three
|
ˇ ˇ ˇ three
|
||||||
| | four"});
|
ˇ ˇ four
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
|
cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
zero
|
zero
|
||||||
|one
|
ˇone
|
||||||
|two
|
ˇtwo
|
||||||
| three| four"});
|
ˇ threeˇ four
|
||||||
|
"});
|
||||||
|
|
||||||
// Test backspace with line_mode set to true
|
// Test backspace with line_mode set to true
|
||||||
cx.update_editor(|e, _| e.selections.line_mode = true);
|
cx.update_editor(|e, _| e.selections.line_mode = true);
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
The |quick |brown
|
The ˇquick ˇbrown
|
||||||
fox jumps over
|
fox jumps over
|
||||||
the lazy dog
|
the lazy dog
|
||||||
|The qu[ick b}rown"});
|
ˇThe qu«ick bˇ»rown"});
|
||||||
cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
|
cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
|fox jumps over
|
ˇfox jumps over
|
||||||
the lazy dog|"});
|
the lazy dogˇ"});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -8494,25 +8524,27 @@ mod tests {
|
||||||
let mut cx = EditorTestContext::new(cx).await;
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
|
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
on|e two three
|
onˇe two three
|
||||||
fou[r} five six
|
fou«rˇ» five six
|
||||||
seven {eight nine
|
seven «ˇeight nine
|
||||||
]ten"});
|
»ten
|
||||||
|
"});
|
||||||
cx.update_editor(|e, cx| e.delete(&Delete, cx));
|
cx.update_editor(|e, cx| e.delete(&Delete, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
on| two three
|
onˇ two three
|
||||||
fou| five six
|
fouˇ five six
|
||||||
seven |ten"});
|
seven ˇten
|
||||||
|
"});
|
||||||
|
|
||||||
// Test backspace with line_mode set to true
|
// Test backspace with line_mode set to true
|
||||||
cx.update_editor(|e, _| e.selections.line_mode = true);
|
cx.update_editor(|e, _| e.selections.line_mode = true);
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
The |quick |brown
|
The ˇquick ˇbrown
|
||||||
fox {jum]ps over
|
fox «ˇjum»ps over
|
||||||
the lazy dog
|
the lazy dog
|
||||||
|The qu[ick b}rown"});
|
ˇThe qu«ick bˇ»rown"});
|
||||||
cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
|
cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
|
||||||
cx.assert_editor_state("|the lazy dog|");
|
cx.assert_editor_state("ˇthe lazy dogˇ");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -8824,19 +8856,19 @@ mod tests {
|
||||||
async fn test_clipboard(cx: &mut gpui::TestAppContext) {
|
async fn test_clipboard(cx: &mut gpui::TestAppContext) {
|
||||||
let mut cx = EditorTestContext::new(cx).await;
|
let mut cx = EditorTestContext::new(cx).await;
|
||||||
|
|
||||||
cx.set_state("[one✅ }two [three }four [five }six ");
|
cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
|
||||||
cx.update_editor(|e, cx| e.cut(&Cut, cx));
|
cx.update_editor(|e, cx| e.cut(&Cut, cx));
|
||||||
cx.assert_editor_state("|two |four |six ");
|
cx.assert_editor_state("ˇtwo ˇfour ˇsix ");
|
||||||
|
|
||||||
// Paste with three cursors. Each cursor pastes one slice of the clipboard text.
|
// Paste with three cursors. Each cursor pastes one slice of the clipboard text.
|
||||||
cx.set_state("two |four |six |");
|
cx.set_state("two ˇfour ˇsix ˇ");
|
||||||
cx.update_editor(|e, cx| e.paste(&Paste, cx));
|
cx.update_editor(|e, cx| e.paste(&Paste, cx));
|
||||||
cx.assert_editor_state("two one✅ |four three |six five |");
|
cx.assert_editor_state("two one✅ ˇfour three ˇsix five ˇ");
|
||||||
|
|
||||||
// Paste again but with only two cursors. Since the number of cursors doesn't
|
// Paste again but with only two cursors. Since the number of cursors doesn't
|
||||||
// match the number of slices in the clipboard, the entire clipboard text
|
// match the number of slices in the clipboard, the entire clipboard text
|
||||||
// is pasted at each cursor.
|
// is pasted at each cursor.
|
||||||
cx.set_state("|two one✅ four three six five |");
|
cx.set_state("ˇtwo one✅ four three six five ˇ");
|
||||||
cx.update_editor(|e, cx| {
|
cx.update_editor(|e, cx| {
|
||||||
e.handle_input("( ", cx);
|
e.handle_input("( ", cx);
|
||||||
e.paste(&Paste, cx);
|
e.paste(&Paste, cx);
|
||||||
|
@ -8845,37 +8877,37 @@ mod tests {
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
( one✅
|
( one✅
|
||||||
three
|
three
|
||||||
five ) |two one✅ four three six five ( one✅
|
five ) ˇtwo one✅ four three six five ( one✅
|
||||||
three
|
three
|
||||||
five ) |"});
|
five ) ˇ"});
|
||||||
|
|
||||||
// Cut with three selections, one of which is full-line.
|
// Cut with three selections, one of which is full-line.
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
1[2}3
|
1«2ˇ»3
|
||||||
4|567
|
4ˇ567
|
||||||
[8}9"});
|
«8ˇ»9"});
|
||||||
cx.update_editor(|e, cx| e.cut(&Cut, cx));
|
cx.update_editor(|e, cx| e.cut(&Cut, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
1|3
|
1ˇ3
|
||||||
|9"});
|
ˇ9"});
|
||||||
|
|
||||||
// Paste with three selections, noticing how the copied selection that was full-line
|
// Paste with three selections, noticing how the copied selection that was full-line
|
||||||
// gets inserted before the second cursor.
|
// gets inserted before the second cursor.
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
1|3
|
1ˇ3
|
||||||
9|
|
9ˇ
|
||||||
[o}ne"});
|
«oˇ»ne"});
|
||||||
cx.update_editor(|e, cx| e.paste(&Paste, cx));
|
cx.update_editor(|e, cx| e.paste(&Paste, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
12|3
|
12ˇ3
|
||||||
4567
|
4567
|
||||||
9|
|
9ˇ
|
||||||
8|ne"});
|
8ˇne"});
|
||||||
|
|
||||||
// Copy with a single cursor only, which writes the whole line into the clipboard.
|
// Copy with a single cursor only, which writes the whole line into the clipboard.
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
The quick brown
|
The quick brown
|
||||||
fox ju|mps over
|
fox juˇmps over
|
||||||
the lazy dog"});
|
the lazy dog"});
|
||||||
cx.update_editor(|e, cx| e.copy(&Copy, cx));
|
cx.update_editor(|e, cx| e.copy(&Copy, cx));
|
||||||
cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
|
cx.cx.assert_clipboard_content(Some("fox jumps over\n"));
|
||||||
|
@ -8883,17 +8915,17 @@ mod tests {
|
||||||
// Paste with three selections, noticing how the copied full-line selection is inserted
|
// Paste with three selections, noticing how the copied full-line selection is inserted
|
||||||
// before the empty selections but replaces the selection that is non-empty.
|
// before the empty selections but replaces the selection that is non-empty.
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
T|he quick brown
|
Tˇhe quick brown
|
||||||
[fo}x jumps over
|
«foˇ»x jumps over
|
||||||
t|he lazy dog"});
|
tˇhe lazy dog"});
|
||||||
cx.update_editor(|e, cx| e.paste(&Paste, cx));
|
cx.update_editor(|e, cx| e.paste(&Paste, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
fox jumps over
|
fox jumps over
|
||||||
T|he quick brown
|
Tˇhe quick brown
|
||||||
fox jumps over
|
fox jumps over
|
||||||
|x jumps over
|
ˇx jumps over
|
||||||
fox jumps over
|
fox jumps over
|
||||||
t|he lazy dog"});
|
tˇhe lazy dog"});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
@ -8909,17 +8941,17 @@ mod tests {
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
const a: B = (
|
const a: B = (
|
||||||
c(),
|
c(),
|
||||||
[d(
|
«d(
|
||||||
e,
|
e,
|
||||||
f
|
f
|
||||||
)}
|
)ˇ»
|
||||||
);
|
);
|
||||||
"});
|
"});
|
||||||
cx.update_editor(|e, cx| e.cut(&Cut, cx));
|
cx.update_editor(|e, cx| e.cut(&Cut, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
const a: B = (
|
const a: B = (
|
||||||
c(),
|
c(),
|
||||||
|
|
ˇ
|
||||||
);
|
);
|
||||||
"});
|
"});
|
||||||
|
|
||||||
|
@ -8931,13 +8963,13 @@ mod tests {
|
||||||
d(
|
d(
|
||||||
e,
|
e,
|
||||||
f
|
f
|
||||||
)|
|
)ˇ
|
||||||
);
|
);
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Paste it at a line with a lower indent level.
|
// Paste it at a line with a lower indent level.
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
|
|
ˇ
|
||||||
const a: B = (
|
const a: B = (
|
||||||
c(),
|
c(),
|
||||||
);
|
);
|
||||||
|
@ -8947,7 +8979,7 @@ mod tests {
|
||||||
d(
|
d(
|
||||||
e,
|
e,
|
||||||
f
|
f
|
||||||
)|
|
)ˇ
|
||||||
const a: B = (
|
const a: B = (
|
||||||
c(),
|
c(),
|
||||||
);
|
);
|
||||||
|
@ -8957,17 +8989,17 @@ mod tests {
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
const a: B = (
|
const a: B = (
|
||||||
c(),
|
c(),
|
||||||
[ d(
|
« d(
|
||||||
e,
|
e,
|
||||||
f
|
f
|
||||||
)
|
)
|
||||||
});
|
ˇ»);
|
||||||
"});
|
"});
|
||||||
cx.update_editor(|e, cx| e.cut(&Cut, cx));
|
cx.update_editor(|e, cx| e.cut(&Cut, cx));
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
const a: B = (
|
const a: B = (
|
||||||
c(),
|
c(),
|
||||||
|);
|
ˇ);
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Paste it at the same position.
|
// Paste it at the same position.
|
||||||
|
@ -8979,7 +9011,7 @@ mod tests {
|
||||||
e,
|
e,
|
||||||
f
|
f
|
||||||
)
|
)
|
||||||
|);
|
ˇ);
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Paste it at a line with a higher indent level.
|
// Paste it at a line with a higher indent level.
|
||||||
|
@ -8988,7 +9020,7 @@ mod tests {
|
||||||
c(),
|
c(),
|
||||||
d(
|
d(
|
||||||
e,
|
e,
|
||||||
f|
|
fˇ
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
"});
|
"});
|
||||||
|
@ -9002,7 +9034,7 @@ mod tests {
|
||||||
e,
|
e,
|
||||||
f
|
f
|
||||||
)
|
)
|
||||||
|
|
ˇ
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
"});
|
"});
|
||||||
|
@ -10293,16 +10325,18 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
one|
|
oneˇ
|
||||||
two
|
two
|
||||||
three"});
|
three
|
||||||
|
"});
|
||||||
cx.simulate_keystroke(".");
|
cx.simulate_keystroke(".");
|
||||||
handle_completion_request(
|
handle_completion_request(
|
||||||
&mut cx,
|
&mut cx,
|
||||||
indoc! {"
|
indoc! {"
|
||||||
one.|<>
|
one.|<>
|
||||||
two
|
two
|
||||||
three"},
|
three
|
||||||
|
"},
|
||||||
vec!["first_completion", "second_completion"],
|
vec!["first_completion", "second_completion"],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -10315,9 +10349,10 @@ mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one.second_completion|
|
one.second_completionˇ
|
||||||
two
|
two
|
||||||
three"});
|
three
|
||||||
|
"});
|
||||||
|
|
||||||
handle_resolve_completion_request(
|
handle_resolve_completion_request(
|
||||||
&mut cx,
|
&mut cx,
|
||||||
|
@ -10325,23 +10360,26 @@ mod tests {
|
||||||
indoc! {"
|
indoc! {"
|
||||||
one.second_completion
|
one.second_completion
|
||||||
two
|
two
|
||||||
three<>"},
|
three<>
|
||||||
|
"},
|
||||||
"\nadditional edit",
|
"\nadditional edit",
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
apply_additional_edits.await.unwrap();
|
apply_additional_edits.await.unwrap();
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one.second_completion|
|
one.second_completionˇ
|
||||||
two
|
two
|
||||||
three
|
three
|
||||||
additional edit"});
|
additional edit
|
||||||
|
"});
|
||||||
|
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
one.second_completion
|
one.second_completion
|
||||||
two|
|
twoˇ
|
||||||
three|
|
threeˇ
|
||||||
additional edit"});
|
additional edit
|
||||||
|
"});
|
||||||
cx.simulate_keystroke(" ");
|
cx.simulate_keystroke(" ");
|
||||||
assert!(cx.editor(|e, _| e.context_menu.is_none()));
|
assert!(cx.editor(|e, _| e.context_menu.is_none()));
|
||||||
cx.simulate_keystroke("s");
|
cx.simulate_keystroke("s");
|
||||||
|
@ -10349,16 +10387,18 @@ mod tests {
|
||||||
|
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one.second_completion
|
one.second_completion
|
||||||
two s|
|
two sˇ
|
||||||
three s|
|
three sˇ
|
||||||
additional edit"});
|
additional edit
|
||||||
|
"});
|
||||||
handle_completion_request(
|
handle_completion_request(
|
||||||
&mut cx,
|
&mut cx,
|
||||||
indoc! {"
|
indoc! {"
|
||||||
one.second_completion
|
one.second_completion
|
||||||
two s
|
two s
|
||||||
three <s|>
|
three <s|>
|
||||||
additional edit"},
|
additional edit
|
||||||
|
"},
|
||||||
vec!["fourth_completion", "fifth_completion", "sixth_completion"],
|
vec!["fourth_completion", "fifth_completion", "sixth_completion"],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -10373,7 +10413,8 @@ mod tests {
|
||||||
one.second_completion
|
one.second_completion
|
||||||
two si
|
two si
|
||||||
three <si|>
|
three <si|>
|
||||||
additional edit"},
|
additional edit
|
||||||
|
"},
|
||||||
vec!["fourth_completion", "fifth_completion", "sixth_completion"],
|
vec!["fourth_completion", "fifth_completion", "sixth_completion"],
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -10387,9 +10428,10 @@ mod tests {
|
||||||
});
|
});
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
one.second_completion
|
one.second_completion
|
||||||
two sixth_completion|
|
two sixth_completionˇ
|
||||||
three sixth_completion|
|
three sixth_completionˇ
|
||||||
additional edit"});
|
additional edit
|
||||||
|
"});
|
||||||
|
|
||||||
handle_resolve_completion_request(&mut cx, None).await;
|
handle_resolve_completion_request(&mut cx, None).await;
|
||||||
apply_additional_edits.await.unwrap();
|
apply_additional_edits.await.unwrap();
|
||||||
|
@ -10399,13 +10441,13 @@ mod tests {
|
||||||
settings.show_completions_on_input = false;
|
settings.show_completions_on_input = false;
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
cx.set_state("editor|");
|
cx.set_state("editorˇ");
|
||||||
cx.simulate_keystroke(".");
|
cx.simulate_keystroke(".");
|
||||||
assert!(cx.editor(|e, _| e.context_menu.is_none()));
|
assert!(cx.editor(|e, _| e.context_menu.is_none()));
|
||||||
cx.simulate_keystroke("c");
|
cx.simulate_keystroke("c");
|
||||||
cx.simulate_keystroke("l");
|
cx.simulate_keystroke("l");
|
||||||
cx.simulate_keystroke("o");
|
cx.simulate_keystroke("o");
|
||||||
cx.assert_editor_state("editor.clo|");
|
cx.assert_editor_state("editor.cloˇ");
|
||||||
assert!(cx.editor(|e, _| e.context_menu.is_none()));
|
assert!(cx.editor(|e, _| e.context_menu.is_none()));
|
||||||
cx.update_editor(|editor, cx| {
|
cx.update_editor(|editor, cx| {
|
||||||
editor.show_completions(&ShowCompletions, cx);
|
editor.show_completions(&ShowCompletions, cx);
|
||||||
|
@ -10418,7 +10460,7 @@ mod tests {
|
||||||
.confirm_completion(&ConfirmCompletion::default(), cx)
|
.confirm_completion(&ConfirmCompletion::default(), cx)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
cx.assert_editor_state("editor.close|");
|
cx.assert_editor_state("editor.closeˇ");
|
||||||
handle_resolve_completion_request(&mut cx, None).await;
|
handle_resolve_completion_request(&mut cx, None).await;
|
||||||
apply_additional_edits.await.unwrap();
|
apply_additional_edits.await.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -32,13 +32,10 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use indoc::indoc;
|
|
||||||
|
|
||||||
use language::{BracketPair, Language, LanguageConfig};
|
|
||||||
|
|
||||||
use crate::test::EditorLspTestContext;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::test::EditorLspTestContext;
|
||||||
|
use indoc::indoc;
|
||||||
|
use language::{BracketPair, Language, LanguageConfig};
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
|
async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
|
||||||
|
@ -76,67 +73,61 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// positioning cursor inside bracket highlights both
|
// positioning cursor inside bracket highlights both
|
||||||
cx.set_state_by(
|
cx.set_state(indoc! {r#"
|
||||||
vec!['|'.into()],
|
pub fn test("Test ˇargument") {
|
||||||
indoc! {r#"
|
another_test(1, 2, 3);
|
||||||
pub fn test("Test |argument") {
|
}
|
||||||
another_test(1, 2, 3);
|
"#});
|
||||||
}"#},
|
|
||||||
);
|
|
||||||
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
|
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
|
||||||
pub fn test[(]"Test argument"[)] {
|
pub fn test«(»"Test argument"«)» {
|
||||||
another_test(1, 2, 3);
|
another_test(1, 2, 3);
|
||||||
}"#});
|
}
|
||||||
|
"#});
|
||||||
|
|
||||||
cx.set_state_by(
|
cx.set_state(indoc! {r#"
|
||||||
vec!['|'.into()],
|
pub fn test("Test argument") {
|
||||||
indoc! {r#"
|
another_test(1, ˇ2, 3);
|
||||||
pub fn test("Test argument") {
|
}
|
||||||
another_test(1, |2, 3);
|
"#});
|
||||||
}"#},
|
|
||||||
);
|
|
||||||
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
|
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
|
||||||
pub fn test("Test argument") {
|
pub fn test("Test argument") {
|
||||||
another_test[(]1, 2, 3[)];
|
another_test«(»1, 2, 3«)»;
|
||||||
}"#});
|
}
|
||||||
|
"#});
|
||||||
|
|
||||||
cx.set_state_by(
|
cx.set_state(indoc! {r#"
|
||||||
vec!['|'.into()],
|
pub fn test("Test argument") {
|
||||||
indoc! {r#"
|
anotherˇ_test(1, 2, 3);
|
||||||
pub fn test("Test argument") {
|
}
|
||||||
another|_test(1, 2, 3);
|
"#});
|
||||||
}"#},
|
|
||||||
);
|
|
||||||
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
|
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
|
||||||
pub fn test("Test argument") [{]
|
pub fn test("Test argument") «{»
|
||||||
another_test(1, 2, 3);
|
another_test(1, 2, 3);
|
||||||
[}]"#});
|
«}»
|
||||||
|
"#});
|
||||||
|
|
||||||
// positioning outside of brackets removes highlight
|
// positioning outside of brackets removes highlight
|
||||||
cx.set_state_by(
|
cx.set_state(indoc! {r#"
|
||||||
vec!['|'.into()],
|
pub fˇn test("Test argument") {
|
||||||
indoc! {r#"
|
another_test(1, 2, 3);
|
||||||
pub f|n test("Test argument") {
|
}
|
||||||
another_test(1, 2, 3);
|
"#});
|
||||||
}"#},
|
|
||||||
);
|
|
||||||
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
|
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
|
||||||
pub fn test("Test argument") {
|
pub fn test("Test argument") {
|
||||||
another_test(1, 2, 3);
|
another_test(1, 2, 3);
|
||||||
}"#});
|
}
|
||||||
|
"#});
|
||||||
|
|
||||||
// non empty selection dismisses highlight
|
// non empty selection dismisses highlight
|
||||||
// positioning outside of brackets removes highlight
|
cx.set_state(indoc! {r#"
|
||||||
cx.set_state_by(
|
pub fn test("Te«st argˇ»ument") {
|
||||||
vec![('<', '>').into()],
|
another_test(1, 2, 3);
|
||||||
indoc! {r#"
|
}
|
||||||
pub fn test("Te<st arg>ument") {
|
"#});
|
||||||
another_test(1, 2, 3);
|
|
||||||
}"#},
|
|
||||||
);
|
|
||||||
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
|
cx.assert_editor_background_highlights::<MatchingBracketHighlight>(indoc! {r#"
|
||||||
pub fn test("Test argument") {
|
pub fn test("Test argument") {
|
||||||
another_test(1, 2, 3);
|
another_test(1, 2, 3);
|
||||||
}"#});
|
}
|
||||||
|
"#});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -439,11 +439,11 @@ mod tests {
|
||||||
|
|
||||||
// Basic hover delays and then pops without moving the mouse
|
// Basic hover delays and then pops without moving the mouse
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
fn |test()
|
fn ˇtest() { println!(); }
|
||||||
println!();"});
|
"});
|
||||||
let hover_point = cx.display_point(indoc! {"
|
let hover_point = cx.display_point(indoc! {"
|
||||||
fn test()
|
fn test() { printˇln!(); }
|
||||||
print|ln!();"});
|
"});
|
||||||
|
|
||||||
cx.update_editor(|editor, cx| {
|
cx.update_editor(|editor, cx| {
|
||||||
hover_at(
|
hover_at(
|
||||||
|
@ -458,16 +458,16 @@ mod tests {
|
||||||
|
|
||||||
// After delay, hover should be visible.
|
// After delay, hover should be visible.
|
||||||
let symbol_range = cx.lsp_range(indoc! {"
|
let symbol_range = cx.lsp_range(indoc! {"
|
||||||
fn test()
|
fn test() { «println!»(); }
|
||||||
[println!]();"});
|
"});
|
||||||
let mut requests =
|
let mut requests =
|
||||||
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
|
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
|
||||||
Ok(Some(lsp::Hover {
|
Ok(Some(lsp::Hover {
|
||||||
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
|
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
|
||||||
kind: lsp::MarkupKind::Markdown,
|
kind: lsp::MarkupKind::Markdown,
|
||||||
value: indoc! {"
|
value: indoc! {"
|
||||||
# Some basic docs
|
# Some basic docs
|
||||||
Some test documentation"}
|
Some test documentation"}
|
||||||
.to_string(),
|
.to_string(),
|
||||||
}),
|
}),
|
||||||
range: Some(symbol_range),
|
range: Some(symbol_range),
|
||||||
|
@ -496,8 +496,8 @@ mod tests {
|
||||||
|
|
||||||
// Mouse moved with no hover response dismisses
|
// Mouse moved with no hover response dismisses
|
||||||
let hover_point = cx.display_point(indoc! {"
|
let hover_point = cx.display_point(indoc! {"
|
||||||
fn te|st()
|
fn teˇst() { println!(); }
|
||||||
println!();"});
|
"});
|
||||||
let mut request = cx
|
let mut request = cx
|
||||||
.lsp
|
.lsp
|
||||||
.handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) });
|
.handle_request::<lsp::request::HoverRequest, _, _>(|_, _| async move { Ok(None) });
|
||||||
|
@ -531,12 +531,12 @@ mod tests {
|
||||||
|
|
||||||
// Hover with keyboard has no delay
|
// Hover with keyboard has no delay
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
f|n test()
|
fˇn test() { println!(); }
|
||||||
println!();"});
|
"});
|
||||||
cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
|
cx.update_editor(|editor, cx| hover(editor, &Hover, cx));
|
||||||
let symbol_range = cx.lsp_range(indoc! {"
|
let symbol_range = cx.lsp_range(indoc! {"
|
||||||
[fn] test()
|
«fn» test() { println!(); }
|
||||||
println!();"});
|
"});
|
||||||
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
|
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
|
||||||
Ok(Some(lsp::Hover {
|
Ok(Some(lsp::Hover {
|
||||||
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
|
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
|
||||||
|
@ -584,13 +584,13 @@ mod tests {
|
||||||
// Hover with just diagnostic, pops DiagnosticPopover immediately and then
|
// Hover with just diagnostic, pops DiagnosticPopover immediately and then
|
||||||
// info popover once request completes
|
// info popover once request completes
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
fn te|st()
|
fn teˇst() { println!(); }
|
||||||
println!();"});
|
"});
|
||||||
|
|
||||||
// Send diagnostic to client
|
// Send diagnostic to client
|
||||||
let range = cx.text_anchor_range(indoc! {"
|
let range = cx.text_anchor_range(indoc! {"
|
||||||
fn [test]()
|
fn «test»() { println!(); }
|
||||||
println!();"});
|
"});
|
||||||
cx.update_buffer(|buffer, cx| {
|
cx.update_buffer(|buffer, cx| {
|
||||||
let snapshot = buffer.text_snapshot();
|
let snapshot = buffer.text_snapshot();
|
||||||
let set = DiagnosticSet::from_sorted_entries(
|
let set = DiagnosticSet::from_sorted_entries(
|
||||||
|
@ -616,15 +616,15 @@ mod tests {
|
||||||
|
|
||||||
// Info Popover shows after request responded to
|
// Info Popover shows after request responded to
|
||||||
let range = cx.lsp_range(indoc! {"
|
let range = cx.lsp_range(indoc! {"
|
||||||
fn [test]()
|
fn «test»() { println!(); }
|
||||||
println!();"});
|
"});
|
||||||
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
|
cx.handle_request::<lsp::request::HoverRequest, _, _>(move |_, _, _| async move {
|
||||||
Ok(Some(lsp::Hover {
|
Ok(Some(lsp::Hover {
|
||||||
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
|
contents: lsp::HoverContents::Markup(lsp::MarkupContent {
|
||||||
kind: lsp::MarkupKind::Markdown,
|
kind: lsp::MarkupKind::Markdown,
|
||||||
value: indoc! {"
|
value: indoc! {"
|
||||||
# Some other basic docs
|
# Some other basic docs
|
||||||
Some other test documentation"}
|
Some other test documentation"}
|
||||||
.to_string(),
|
.to_string(),
|
||||||
}),
|
}),
|
||||||
range: Some(range),
|
range: Some(range),
|
||||||
|
|
|
@ -405,20 +405,20 @@ mod tests {
|
||||||
|
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
struct A;
|
struct A;
|
||||||
let v|ariable = A;
|
let vˇariable = A;
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Basic hold cmd+shift, expect highlight in region if response contains type definition
|
// Basic hold cmd+shift, expect highlight in region if response contains type definition
|
||||||
let hover_point = cx.display_point(indoc! {"
|
let hover_point = cx.display_point(indoc! {"
|
||||||
struct A;
|
struct A;
|
||||||
let v|ariable = A;
|
let vˇariable = A;
|
||||||
"});
|
"});
|
||||||
let symbol_range = cx.lsp_range(indoc! {"
|
let symbol_range = cx.lsp_range(indoc! {"
|
||||||
struct A;
|
struct A;
|
||||||
let [variable] = A;
|
let «variable» = A;
|
||||||
"});
|
"});
|
||||||
let target_range = cx.lsp_range(indoc! {"
|
let target_range = cx.lsp_range(indoc! {"
|
||||||
struct [A];
|
struct «A»;
|
||||||
let variable = A;
|
let variable = A;
|
||||||
"});
|
"});
|
||||||
|
|
||||||
|
@ -450,7 +450,7 @@ mod tests {
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
||||||
struct A;
|
struct A;
|
||||||
let [variable] = A;
|
let «variable» = A;
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Unpress shift causes highlight to go away (normal goto-definition is not valid here)
|
// Unpress shift causes highlight to go away (normal goto-definition is not valid here)
|
||||||
|
@ -473,10 +473,10 @@ mod tests {
|
||||||
// Cmd+shift click without existing definition requests and jumps
|
// Cmd+shift click without existing definition requests and jumps
|
||||||
let hover_point = cx.display_point(indoc! {"
|
let hover_point = cx.display_point(indoc! {"
|
||||||
struct A;
|
struct A;
|
||||||
let v|ariable = A;
|
let vˇariable = A;
|
||||||
"});
|
"});
|
||||||
let target_range = cx.lsp_range(indoc! {"
|
let target_range = cx.lsp_range(indoc! {"
|
||||||
struct [A];
|
struct «A»;
|
||||||
let variable = A;
|
let variable = A;
|
||||||
"});
|
"});
|
||||||
|
|
||||||
|
@ -503,7 +503,7 @@ mod tests {
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
|
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
struct [A};
|
struct «Aˇ»;
|
||||||
let variable = A;
|
let variable = A;
|
||||||
"});
|
"});
|
||||||
}
|
}
|
||||||
|
@ -520,34 +520,22 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
fn |test()
|
fn ˇtest() { do_work(); }
|
||||||
do_work();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Basic hold cmd, expect highlight in region if response contains definition
|
// Basic hold cmd, expect highlight in region if response contains definition
|
||||||
let hover_point = cx.display_point(indoc! {"
|
let hover_point = cx.display_point(indoc! {"
|
||||||
fn test()
|
fn test() { do_wˇork(); }
|
||||||
do_w|ork();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
let symbol_range = cx.lsp_range(indoc! {"
|
let symbol_range = cx.lsp_range(indoc! {"
|
||||||
fn test()
|
fn test() { «do_work»(); }
|
||||||
[do_work]();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
let target_range = cx.lsp_range(indoc! {"
|
let target_range = cx.lsp_range(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn «do_work»() { test(); }
|
||||||
|
|
||||||
fn [do_work]()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
|
let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
|
||||||
|
@ -575,11 +563,8 @@ mod tests {
|
||||||
requests.next().await;
|
requests.next().await;
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
||||||
fn test()
|
fn test() { «do_work»(); }
|
||||||
[do_work]();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Unpress cmd causes highlight to go away
|
// Unpress cmd causes highlight to go away
|
||||||
|
@ -593,13 +578,11 @@ mod tests {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assert no link highlights
|
// Assert no link highlights
|
||||||
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Response without source range still highlights word
|
// Response without source range still highlights word
|
||||||
|
@ -630,20 +613,14 @@ mod tests {
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
|
|
||||||
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
||||||
fn test()
|
fn test() { «do_work»(); }
|
||||||
[do_work]();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Moving mouse to location with no response dismisses highlight
|
// Moving mouse to location with no response dismisses highlight
|
||||||
let hover_point = cx.display_point(indoc! {"
|
let hover_point = cx.display_point(indoc! {"
|
||||||
f|n test()
|
fˇn test() { do_work(); }
|
||||||
do_work();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
let mut requests = cx
|
let mut requests = cx
|
||||||
.lsp
|
.lsp
|
||||||
|
@ -667,20 +644,14 @@ mod tests {
|
||||||
|
|
||||||
// Assert no link highlights
|
// Assert no link highlights
|
||||||
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Move mouse without cmd and then pressing cmd triggers highlight
|
// Move mouse without cmd and then pressing cmd triggers highlight
|
||||||
let hover_point = cx.display_point(indoc! {"
|
let hover_point = cx.display_point(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn do_work() { teˇst(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
te|st();
|
|
||||||
"});
|
"});
|
||||||
cx.update_editor(|editor, cx| {
|
cx.update_editor(|editor, cx| {
|
||||||
update_go_to_definition_link(
|
update_go_to_definition_link(
|
||||||
|
@ -697,26 +668,17 @@ mod tests {
|
||||||
|
|
||||||
// Assert no link highlights
|
// Assert no link highlights
|
||||||
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
let symbol_range = cx.lsp_range(indoc! {"
|
let symbol_range = cx.lsp_range(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn do_work() { «test»(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
[test]();
|
|
||||||
"});
|
"});
|
||||||
let target_range = cx.lsp_range(indoc! {"
|
let target_range = cx.lsp_range(indoc! {"
|
||||||
fn [test]()
|
fn «test»() { do_work(); }
|
||||||
do_work();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
|
let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
|
||||||
|
@ -743,20 +705,14 @@ mod tests {
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
|
|
||||||
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn do_work() { «test»(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
[test]();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Moving within symbol range doesn't re-request
|
// Moving within symbol range doesn't re-request
|
||||||
let hover_point = cx.display_point(indoc! {"
|
let hover_point = cx.display_point(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn do_work() { tesˇt(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
tes|t();
|
|
||||||
"});
|
"});
|
||||||
cx.update_editor(|editor, cx| {
|
cx.update_editor(|editor, cx| {
|
||||||
update_go_to_definition_link(
|
update_go_to_definition_link(
|
||||||
|
@ -771,11 +727,8 @@ mod tests {
|
||||||
});
|
});
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn do_work() { «test»(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
[test]();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Cmd click with existing definition doesn't re-request and dismisses highlight
|
// Cmd click with existing definition doesn't re-request and dismisses highlight
|
||||||
|
@ -790,35 +743,24 @@ mod tests {
|
||||||
Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
|
Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
|
||||||
});
|
});
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
fn [test}()
|
fn «testˇ»() { do_work(); }
|
||||||
do_work();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Assert no link highlights after jump
|
// Assert no link highlights after jump
|
||||||
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
// Cmd click without existing definition requests and jumps
|
// Cmd click without existing definition requests and jumps
|
||||||
let hover_point = cx.display_point(indoc! {"
|
let hover_point = cx.display_point(indoc! {"
|
||||||
fn test()
|
fn test() { do_wˇork(); }
|
||||||
do_w|ork();
|
fn do_work() { test(); }
|
||||||
|
|
||||||
fn do_work()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
let target_range = cx.lsp_range(indoc! {"
|
let target_range = cx.lsp_range(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn «do_work»() { test(); }
|
||||||
|
|
||||||
fn [do_work]()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
|
|
||||||
let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
|
let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
|
||||||
|
@ -836,13 +778,9 @@ mod tests {
|
||||||
});
|
});
|
||||||
requests.next().await;
|
requests.next().await;
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
|
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
fn test()
|
fn test() { do_work(); }
|
||||||
do_work();
|
fn «do_workˇ»() { test(); }
|
||||||
|
|
||||||
fn [do_work}()
|
|
||||||
test();
|
|
||||||
"});
|
"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,11 +67,9 @@ pub fn deploy_context_menu(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use indoc::indoc;
|
|
||||||
|
|
||||||
use crate::test::EditorLspTestContext;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::test::EditorLspTestContext;
|
||||||
|
use indoc::indoc;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
|
async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
|
||||||
|
@ -85,11 +83,15 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
cx.set_state(indoc! {"
|
cx.set_state(indoc! {"
|
||||||
fn te|st()
|
fn teˇst() {
|
||||||
do_work();"});
|
do_work();
|
||||||
|
}
|
||||||
|
"});
|
||||||
let point = cx.display_point(indoc! {"
|
let point = cx.display_point(indoc! {"
|
||||||
fn test()
|
fn test() {
|
||||||
do_w|ork();"});
|
do_wˇork();
|
||||||
|
}
|
||||||
|
"});
|
||||||
cx.update_editor(|editor, cx| {
|
cx.update_editor(|editor, cx| {
|
||||||
deploy_context_menu(
|
deploy_context_menu(
|
||||||
editor,
|
editor,
|
||||||
|
@ -102,8 +104,10 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.assert_editor_state(indoc! {"
|
cx.assert_editor_state(indoc! {"
|
||||||
fn test()
|
fn test() {
|
||||||
do_w|ork();"});
|
do_wˇork();
|
||||||
|
}
|
||||||
|
"});
|
||||||
cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
|
cx.editor(|editor, app| assert!(editor.mouse_context_menu.read(app).visible()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ use anyhow::Result;
|
||||||
use futures::{Future, StreamExt};
|
use futures::{Future, StreamExt};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
|
|
||||||
use collections::BTreeMap;
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
json, keymap::Keystroke, AppContext, ModelContext, ModelHandle, ViewContext, ViewHandle,
|
json, keymap::Keystroke, AppContext, ModelContext, ModelHandle, ViewContext, ViewHandle,
|
||||||
};
|
};
|
||||||
|
@ -20,7 +19,7 @@ use project::Project;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use util::{
|
use util::{
|
||||||
assert_set_eq, set_eq,
|
assert_set_eq, set_eq,
|
||||||
test::{marked_text, marked_text_ranges, marked_text_ranges_by, SetEqError, TextRangeMarker},
|
test::{generate_marked_text, marked_text, parse_marked_text},
|
||||||
};
|
};
|
||||||
use workspace::{pane, AppState, Workspace, WorkspaceHandle};
|
use workspace::{pane, AppState, Workspace, WorkspaceHandle};
|
||||||
|
|
||||||
|
@ -65,7 +64,7 @@ pub fn marked_display_snapshot(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
|
pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
|
||||||
let (umarked_text, text_ranges) = marked_text_ranges(marked_text);
|
let (umarked_text, text_ranges) = parse_marked_text(marked_text, true).unwrap();
|
||||||
assert_eq!(editor.text(cx), umarked_text);
|
assert_eq!(editor.text(cx), umarked_text);
|
||||||
editor.change_selections(None, cx, |s| s.select_ranges(text_ranges));
|
editor.change_selections(None, cx, |s| s.select_ranges(text_ranges));
|
||||||
}
|
}
|
||||||
|
@ -75,8 +74,7 @@ pub fn assert_text_with_selections(
|
||||||
marked_text: &str,
|
marked_text: &str,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
let (unmarked_text, text_ranges) = marked_text_ranges(marked_text);
|
let (unmarked_text, text_ranges) = parse_marked_text(marked_text, true).unwrap();
|
||||||
|
|
||||||
assert_eq!(editor.text(cx), unmarked_text);
|
assert_eq!(editor.text(cx), unmarked_text);
|
||||||
assert_eq!(editor.selections.ranges(cx), text_ranges);
|
assert_eq!(editor.selections.ranges(cx), text_ranges);
|
||||||
}
|
}
|
||||||
|
@ -190,94 +188,49 @@ impl<'a> EditorTestContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_point(&mut self, cursor_location: &str) -> DisplayPoint {
|
pub fn ranges(&self, marked_text: &str) -> Vec<Range<usize>> {
|
||||||
let (_, locations) = marked_text(cursor_location);
|
let (unmarked_text, ranges) = parse_marked_text(marked_text, false).unwrap();
|
||||||
|
assert_eq!(self.buffer_text(), unmarked_text);
|
||||||
|
ranges
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint {
|
||||||
|
let ranges = self.ranges(marked_text);
|
||||||
let snapshot = self
|
let snapshot = self
|
||||||
.editor
|
.editor
|
||||||
.update(self.cx, |editor, cx| editor.snapshot(cx));
|
.update(self.cx, |editor, cx| editor.snapshot(cx));
|
||||||
locations[0].to_display_point(&snapshot.display_snapshot)
|
ranges[0].start.to_display_point(&snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns anchors for the current buffer using `[`..`]`
|
// Returns anchors for the current buffer using `«` and `»`
|
||||||
pub fn text_anchor_range(&self, marked_text: &str) -> Range<language::Anchor> {
|
pub fn text_anchor_range(&self, marked_text: &str) -> Range<language::Anchor> {
|
||||||
let range_marker: TextRangeMarker = ('[', ']').into();
|
let ranges = self.ranges(marked_text);
|
||||||
let (unmarked_text, mut ranges) =
|
|
||||||
marked_text_ranges_by(&marked_text, vec![range_marker.clone()]);
|
|
||||||
assert_eq!(self.buffer_text(), unmarked_text);
|
|
||||||
let offset_range = ranges.remove(&range_marker).unwrap()[0].clone();
|
|
||||||
let snapshot = self.buffer_snapshot();
|
let snapshot = self.buffer_snapshot();
|
||||||
|
snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
|
||||||
snapshot.anchor_before(offset_range.start)..snapshot.anchor_after(offset_range.end)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the editor state via a marked string.
|
pub fn set_state(&mut self, marked_text: &str) {
|
||||||
// `|` characters represent empty selections
|
let (unmarked_text, selection_ranges) = parse_marked_text(marked_text, true).unwrap();
|
||||||
// `[` to `}` represents a non empty selection with the head at `}`
|
|
||||||
// `{` to `]` represents a non empty selection with the head at `{`
|
|
||||||
pub fn set_state(&mut self, text: &str) {
|
|
||||||
self.set_state_by(
|
|
||||||
vec![
|
|
||||||
'|'.into(),
|
|
||||||
('[', '}').into(),
|
|
||||||
TextRangeMarker::ReverseRange('{', ']'),
|
|
||||||
],
|
|
||||||
text,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_state_by(&mut self, range_markers: Vec<TextRangeMarker>, text: &str) {
|
|
||||||
self.editor.update(self.cx, |editor, cx| {
|
self.editor.update(self.cx, |editor, cx| {
|
||||||
let (unmarked_text, selection_ranges) = marked_text_ranges_by(&text, range_markers);
|
|
||||||
editor.set_text(unmarked_text, cx);
|
editor.set_text(unmarked_text, cx);
|
||||||
|
|
||||||
let selection_ranges: Vec<Range<usize>> = selection_ranges
|
|
||||||
.values()
|
|
||||||
.into_iter()
|
|
||||||
.flatten()
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
editor.change_selections(Some(Autoscroll::Fit), cx, |s| {
|
||||||
s.select_ranges(selection_ranges)
|
s.select_ranges(selection_ranges)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asserts the editor state via a marked string.
|
pub fn assert_editor_state(&mut self, marked_text: &str) {
|
||||||
// `|` characters represent empty selections
|
let (unmarked_text, expected_selections) = parse_marked_text(marked_text, true).unwrap();
|
||||||
// `[` to `}` represents a non empty selection with the head at `}`
|
|
||||||
// `{` to `]` represents a non empty selection with the head at `{`
|
|
||||||
pub fn assert_editor_state(&mut self, text: &str) {
|
|
||||||
let (unmarked_text, mut selection_ranges) = marked_text_ranges_by(
|
|
||||||
&text,
|
|
||||||
vec!['|'.into(), ('[', '}').into(), ('{', ']').into()],
|
|
||||||
);
|
|
||||||
let buffer_text = self.buffer_text();
|
let buffer_text = self.buffer_text();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
buffer_text, unmarked_text,
|
buffer_text, unmarked_text,
|
||||||
"Unmarked text doesn't match buffer text"
|
"Unmarked text doesn't match buffer text"
|
||||||
);
|
);
|
||||||
|
self.assert_selections(expected_selections, marked_text.to_string())
|
||||||
let expected_empty_selections = selection_ranges.remove(&'|'.into()).unwrap_or_default();
|
|
||||||
let expected_reverse_selections = selection_ranges
|
|
||||||
.remove(&('{', ']').into())
|
|
||||||
.unwrap_or_default();
|
|
||||||
let expected_forward_selections = selection_ranges
|
|
||||||
.remove(&('[', '}').into())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
self.assert_selections(
|
|
||||||
expected_empty_selections,
|
|
||||||
expected_reverse_selections,
|
|
||||||
expected_forward_selections,
|
|
||||||
Some(text.to_string()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
|
pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
|
||||||
let (unmarked, mut ranges) = marked_text_ranges_by(marked_text, vec![('[', ']').into()]);
|
let expected_ranges = self.ranges(marked_text);
|
||||||
assert_eq!(unmarked, self.buffer_text());
|
|
||||||
|
|
||||||
let asserted_ranges = ranges.remove(&('[', ']').into()).unwrap();
|
|
||||||
let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
|
let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
|
||||||
let snapshot = editor.snapshot(cx);
|
let snapshot = editor.snapshot(cx);
|
||||||
editor
|
editor
|
||||||
|
@ -289,176 +242,58 @@ impl<'a> EditorTestContext<'a> {
|
||||||
.map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
.map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
||||||
.collect()
|
.collect()
|
||||||
});
|
});
|
||||||
|
assert_set_eq!(actual_ranges, expected_ranges);
|
||||||
assert_set_eq!(asserted_ranges, actual_ranges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
|
pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
|
||||||
let (unmarked, mut ranges) = marked_text_ranges_by(marked_text, vec![('[', ']').into()]);
|
let expected_ranges = self.ranges(marked_text);
|
||||||
assert_eq!(unmarked, self.buffer_text());
|
|
||||||
|
|
||||||
let asserted_ranges = ranges.remove(&('[', ']').into()).unwrap();
|
|
||||||
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
||||||
let actual_ranges: Vec<Range<usize>> = snapshot
|
let actual_ranges: Vec<Range<usize>> = snapshot
|
||||||
.display_snapshot
|
|
||||||
.highlight_ranges::<Tag>()
|
.highlight_ranges::<Tag>()
|
||||||
.map(|ranges| ranges.as_ref().clone().1)
|
.map(|ranges| ranges.as_ref().clone().1)
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
.map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
||||||
.collect();
|
.collect();
|
||||||
|
assert_set_eq!(actual_ranges, expected_ranges);
|
||||||
assert_set_eq!(asserted_ranges, actual_ranges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_editor_selections(&mut self, expected_selections: Vec<Selection<usize>>) {
|
pub fn assert_editor_selections(&mut self, expected_selections: Vec<Selection<usize>>) {
|
||||||
let mut empty_selections = Vec::new();
|
let expected_selections = expected_selections
|
||||||
let mut reverse_selections = Vec::new();
|
.into_iter()
|
||||||
let mut forward_selections = Vec::new();
|
.map(|s| s.range())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
for selection in expected_selections {
|
let expected_marked_text =
|
||||||
let range = selection.range();
|
generate_marked_text(&self.buffer_text(), &expected_selections, true);
|
||||||
if selection.is_empty() {
|
self.assert_selections(expected_selections, expected_marked_text)
|
||||||
empty_selections.push(range);
|
|
||||||
} else if selection.reversed {
|
|
||||||
reverse_selections.push(range);
|
|
||||||
} else {
|
|
||||||
forward_selections.push(range)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.assert_selections(
|
|
||||||
empty_selections,
|
|
||||||
reverse_selections,
|
|
||||||
forward_selections,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_selections(
|
fn assert_selections(
|
||||||
&mut self,
|
&mut self,
|
||||||
expected_empty_selections: Vec<Range<usize>>,
|
expected_selections: Vec<Range<usize>>,
|
||||||
expected_reverse_selections: Vec<Range<usize>>,
|
expected_marked_text: String,
|
||||||
expected_forward_selections: Vec<Range<usize>>,
|
|
||||||
asserted_text: Option<String>,
|
|
||||||
) {
|
) {
|
||||||
let (empty_selections, reverse_selections, forward_selections) =
|
let actual_selections = self
|
||||||
self.editor.read_with(self.cx, |editor, cx| {
|
.editor
|
||||||
let mut empty_selections = Vec::new();
|
.read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
|
||||||
let mut reverse_selections = Vec::new();
|
.into_iter()
|
||||||
let mut forward_selections = Vec::new();
|
.map(|s| s.range())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
for selection in editor.selections.all::<usize>(cx) {
|
let actual_marked_text =
|
||||||
let range = selection.range();
|
generate_marked_text(&self.buffer_text(), &actual_selections, true);
|
||||||
if selection.is_empty() {
|
if expected_selections != actual_selections {
|
||||||
empty_selections.push(range);
|
panic!(
|
||||||
} else if selection.reversed {
|
indoc! {"
|
||||||
reverse_selections.push(range);
|
Editor has unexpected selections.
|
||||||
} else {
|
Expected selections:
|
||||||
forward_selections.push(range)
|
{}
|
||||||
}
|
Actual selections:
|
||||||
}
|
{}",
|
||||||
|
},
|
||||||
(empty_selections, reverse_selections, forward_selections)
|
expected_marked_text, actual_marked_text,
|
||||||
});
|
);
|
||||||
|
|
||||||
let asserted_selections = asserted_text.unwrap_or_else(|| {
|
|
||||||
self.insert_markers(
|
|
||||||
&expected_empty_selections,
|
|
||||||
&expected_reverse_selections,
|
|
||||||
&expected_forward_selections,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let actual_selections =
|
|
||||||
self.insert_markers(&empty_selections, &reverse_selections, &forward_selections);
|
|
||||||
|
|
||||||
let unmarked_text = self.buffer_text();
|
|
||||||
let all_eq: Result<(), SetEqError<String>> =
|
|
||||||
set_eq!(expected_empty_selections, empty_selections)
|
|
||||||
.map_err(|err| {
|
|
||||||
err.map(|missing| {
|
|
||||||
let mut error_text = unmarked_text.clone();
|
|
||||||
error_text.insert(missing.start, '|');
|
|
||||||
error_text
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.and_then(|_| {
|
|
||||||
set_eq!(expected_reverse_selections, reverse_selections).map_err(|err| {
|
|
||||||
err.map(|missing| {
|
|
||||||
let mut error_text = unmarked_text.clone();
|
|
||||||
error_text.insert(missing.start, '{');
|
|
||||||
error_text.insert(missing.end, ']');
|
|
||||||
error_text
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.and_then(|_| {
|
|
||||||
set_eq!(expected_forward_selections, forward_selections).map_err(|err| {
|
|
||||||
err.map(|missing| {
|
|
||||||
let mut error_text = unmarked_text.clone();
|
|
||||||
error_text.insert(missing.start, '[');
|
|
||||||
error_text.insert(missing.end, '}');
|
|
||||||
error_text
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
match all_eq {
|
|
||||||
Err(SetEqError::LeftMissing(location_text)) => {
|
|
||||||
panic!(
|
|
||||||
indoc! {"
|
|
||||||
Editor has extra selection
|
|
||||||
Extra Selection Location:
|
|
||||||
{}
|
|
||||||
Asserted selections:
|
|
||||||
{}
|
|
||||||
Actual selections:
|
|
||||||
{}"},
|
|
||||||
location_text, asserted_selections, actual_selections,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(SetEqError::RightMissing(location_text)) => {
|
|
||||||
panic!(
|
|
||||||
indoc! {"
|
|
||||||
Editor is missing empty selection
|
|
||||||
Missing Selection Location:
|
|
||||||
{}
|
|
||||||
Asserted selections:
|
|
||||||
{}
|
|
||||||
Actual selections:
|
|
||||||
{}"},
|
|
||||||
location_text, asserted_selections, actual_selections,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_markers(
|
|
||||||
&mut self,
|
|
||||||
empty_selections: &Vec<Range<usize>>,
|
|
||||||
reverse_selections: &Vec<Range<usize>>,
|
|
||||||
forward_selections: &Vec<Range<usize>>,
|
|
||||||
) -> String {
|
|
||||||
let mut editor_text_with_selections = self.buffer_text();
|
|
||||||
let mut selection_marks = BTreeMap::new();
|
|
||||||
for range in empty_selections {
|
|
||||||
selection_marks.insert(&range.start, '|');
|
|
||||||
}
|
|
||||||
for range in reverse_selections {
|
|
||||||
selection_marks.insert(&range.start, '{');
|
|
||||||
selection_marks.insert(&range.end, ']');
|
|
||||||
}
|
|
||||||
for range in forward_selections {
|
|
||||||
selection_marks.insert(&range.start, '[');
|
|
||||||
selection_marks.insert(&range.end, '}');
|
|
||||||
}
|
|
||||||
for (offset, mark) in selection_marks.into_iter().rev() {
|
|
||||||
editor_text_with_selections.insert(*offset, mark);
|
|
||||||
}
|
|
||||||
|
|
||||||
editor_text_with_selections
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Deref for EditorTestContext<'a> {
|
impl<'a> Deref for EditorTestContext<'a> {
|
||||||
|
@ -575,10 +410,8 @@ impl<'a> EditorLspTestContext<'a> {
|
||||||
|
|
||||||
// Constructs lsp range using a marked string with '[', ']' range delimiters
|
// Constructs lsp range using a marked string with '[', ']' range delimiters
|
||||||
pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
|
pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
|
||||||
let (unmarked, mut ranges) = marked_text_ranges_by(marked_text, vec![('[', ']').into()]);
|
let ranges = self.ranges(marked_text);
|
||||||
assert_eq!(unmarked, self.buffer_text());
|
self.to_lsp_range(ranges[0].clone())
|
||||||
let offset_range = ranges.remove(&('[', ']').into()).unwrap()[0].clone();
|
|
||||||
self.to_lsp_range(offset_range)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {
|
pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {
|
||||||
|
|
|
@ -15,6 +15,9 @@ futures = "0.3"
|
||||||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||||
rand = { version = "0.8", optional = true }
|
rand = { version = "0.8", optional = true }
|
||||||
tempdir = { version = "0.3.7", optional = true }
|
tempdir = { version = "0.3.7", optional = true }
|
||||||
serde_json = { version = "1.0", features = [
|
serde_json = { version = "1.0", features = ["preserve_order"], optional = true }
|
||||||
"preserve_order",
|
|
||||||
], optional = true }
|
[dev-dependencies]
|
||||||
|
rand = { version = "0.8" }
|
||||||
|
tempdir = { version = "0.3.7" }
|
||||||
|
serde_json = { version = "1.0", features = ["preserve_order"] }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#[cfg(feature = "test-support")]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::{collections::HashMap, ops::Range};
|
use anyhow::{anyhow, Result};
|
||||||
|
use std::{cmp::Ordering, collections::HashMap, ops::Range};
|
||||||
|
|
||||||
pub fn marked_text_by(
|
pub fn marked_text_by(
|
||||||
marked_text: &str,
|
marked_text: &str,
|
||||||
|
@ -125,3 +126,122 @@ pub fn marked_text_ranges(full_marked_text: &str) -> (String, Vec<Range<usize>>)
|
||||||
combined_ranges.sort_by_key(|range| range.start);
|
combined_ranges.sort_by_key(|range| range.start);
|
||||||
(unmarked, combined_ranges)
|
(unmarked, combined_ranges)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn parse_marked_text(
|
||||||
|
input_text: &str,
|
||||||
|
indicate_cursors: bool,
|
||||||
|
) -> Result<(String, Vec<Range<usize>>)> {
|
||||||
|
let mut output_text = String::with_capacity(input_text.len());
|
||||||
|
let mut ranges = Vec::new();
|
||||||
|
let mut prev_input_ix = 0;
|
||||||
|
let mut current_range_start = None;
|
||||||
|
let mut current_range_cursor = None;
|
||||||
|
|
||||||
|
for (input_ix, marker) in input_text.match_indices(&['«', '»', 'ˇ']) {
|
||||||
|
output_text.push_str(&input_text[prev_input_ix..input_ix]);
|
||||||
|
let output_len = output_text.len();
|
||||||
|
let len = marker.len();
|
||||||
|
prev_input_ix = input_ix + len;
|
||||||
|
|
||||||
|
match marker {
|
||||||
|
"ˇ" => {
|
||||||
|
if current_range_start.is_some() {
|
||||||
|
if current_range_cursor.is_some() {
|
||||||
|
Err(anyhow!("duplicate point marker 'ˇ' at index {input_ix}"))?;
|
||||||
|
} else {
|
||||||
|
current_range_cursor = Some(output_len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ranges.push(output_len..output_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"«" => {
|
||||||
|
if current_range_start.is_some() {
|
||||||
|
Err(anyhow!(
|
||||||
|
"unexpected range start marker '«' at index {input_ix}"
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
current_range_start = Some(output_len);
|
||||||
|
}
|
||||||
|
"»" => {
|
||||||
|
let current_range_start = current_range_start.take().ok_or_else(|| {
|
||||||
|
anyhow!("unexpected range end marker '»' at index {input_ix}")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut reversed = false;
|
||||||
|
if let Some(current_range_cursor) = current_range_cursor.take() {
|
||||||
|
if current_range_cursor == current_range_start {
|
||||||
|
reversed = true;
|
||||||
|
} else if current_range_cursor != output_len {
|
||||||
|
Err(anyhow!("unexpected 'ˇ' marker in the middle of a range"))?;
|
||||||
|
}
|
||||||
|
} else if indicate_cursors {
|
||||||
|
Err(anyhow!("missing 'ˇ' marker to indicate range direction"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ranges.push(if reversed {
|
||||||
|
output_len..current_range_start
|
||||||
|
} else {
|
||||||
|
current_range_start..output_len
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output_text.push_str(&input_text[prev_input_ix..]);
|
||||||
|
Ok((output_text, ranges))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_marked_text(
|
||||||
|
output_text: &str,
|
||||||
|
ranges: &[Range<usize>],
|
||||||
|
indicate_cursors: bool,
|
||||||
|
) -> String {
|
||||||
|
let mut marked_text = output_text.to_string();
|
||||||
|
for range in ranges.iter().rev() {
|
||||||
|
if indicate_cursors {
|
||||||
|
match range.start.cmp(&range.end) {
|
||||||
|
Ordering::Less => {
|
||||||
|
marked_text.insert_str(range.end, "ˇ»");
|
||||||
|
marked_text.insert_str(range.start, "«");
|
||||||
|
}
|
||||||
|
Ordering::Equal => {
|
||||||
|
marked_text.insert_str(range.start, "ˇ");
|
||||||
|
}
|
||||||
|
Ordering::Greater => {
|
||||||
|
marked_text.insert_str(range.start, "»");
|
||||||
|
marked_text.insert_str(range.end, "«ˇ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
marked_text.insert_str(range.end, "»");
|
||||||
|
marked_text.insert_str(range.start, "«");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
marked_text
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{generate_marked_text, parse_marked_text};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_marked_text() {
|
||||||
|
let (text, ranges) =
|
||||||
|
parse_marked_text("one «ˇtwo» «threeˇ» «ˇfour» fiveˇ six", true).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(text, "one two three four five six");
|
||||||
|
assert_eq!(ranges.len(), 4);
|
||||||
|
assert_eq!(ranges[0], 7..4);
|
||||||
|
assert_eq!(ranges[1], 8..13);
|
||||||
|
assert_eq!(ranges[2], 18..14);
|
||||||
|
assert_eq!(ranges[3], 23..23);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
generate_marked_text(&text, &ranges, true),
|
||||||
|
"one «ˇtwo» «threeˇ» «ˇfour» fiveˇ six"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue