Dynamically resize line number gutter width (#3469)
* dynamically resize line number gutter width * removing digits lower-bound, permitting spacer * removing max line num char limit; adding notes; qualified successors; notes * updating tests to use new line number width when testing views * linenr width based on document line count * using min width of 2 so line numbers relative is useful * lint rolling; removing unnecessary type parameter lifetime * merge change resolution * reformat code * rename row_styler to style; add int_log resource * adding spacer to gutters default; updating book config entry * adding view.inner_height(), swap for loop for iterator * reverting change of current! to view! now that doc is not needed
This commit is contained in:
parent
c94feed83d
commit
7ed9e9cf25
8 changed files with 143 additions and 109 deletions
|
@ -46,7 +46,7 @@ on unix operating systems.
|
|||
| `line-number` | Line number display: `absolute` simply shows each line's number, while `relative` shows the distance from the current line. When unfocused or in insert mode, `relative` will still show absolute line numbers. | `absolute` |
|
||||
| `cursorline` | Highlight all lines with a cursor. | `false` |
|
||||
| `cursorcolumn` | Highlight all columns with a cursor. | `false` |
|
||||
| `gutters` | Gutters to display: Available are `diagnostics` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "line-numbers"]` |
|
||||
| `gutters` | Gutters to display: Available are `diagnostics` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "spacer", "line-numbers"]` |
|
||||
| `auto-completion` | Enable automatic pop up of auto-completion. | `true` |
|
||||
| `auto-format` | Enable automatic formatting on save. | `true` |
|
||||
| `auto-save` | Enable automatic saving on focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal. | `false` |
|
||||
|
|
|
@ -870,7 +870,7 @@ fn goto_window(cx: &mut Context, align: Align) {
|
|||
let config = cx.editor.config();
|
||||
let (view, doc) = current!(cx.editor);
|
||||
|
||||
let height = view.inner_area().height as usize;
|
||||
let height = view.inner_height();
|
||||
|
||||
// respect user given count if any
|
||||
// - 1 so we have at least one gap in the middle.
|
||||
|
@ -1372,9 +1372,9 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
|
|||
return;
|
||||
}
|
||||
|
||||
let height = view.inner_area().height;
|
||||
let height = view.inner_height();
|
||||
|
||||
let scrolloff = config.scrolloff.min(height as usize / 2);
|
||||
let scrolloff = config.scrolloff.min(height / 2);
|
||||
|
||||
view.offset.row = match direction {
|
||||
Forward => view.offset.row + offset,
|
||||
|
@ -1412,25 +1412,25 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
|
|||
|
||||
fn page_up(cx: &mut Context) {
|
||||
let view = view!(cx.editor);
|
||||
let offset = view.inner_area().height as usize;
|
||||
let offset = view.inner_height();
|
||||
scroll(cx, offset, Direction::Backward);
|
||||
}
|
||||
|
||||
fn page_down(cx: &mut Context) {
|
||||
let view = view!(cx.editor);
|
||||
let offset = view.inner_area().height as usize;
|
||||
let offset = view.inner_height();
|
||||
scroll(cx, offset, Direction::Forward);
|
||||
}
|
||||
|
||||
fn half_page_up(cx: &mut Context) {
|
||||
let view = view!(cx.editor);
|
||||
let offset = view.inner_area().height as usize / 2;
|
||||
let offset = view.inner_height() / 2;
|
||||
scroll(cx, offset, Direction::Backward);
|
||||
}
|
||||
|
||||
fn half_page_down(cx: &mut Context) {
|
||||
let view = view!(cx.editor);
|
||||
let offset = view.inner_area().height as usize / 2;
|
||||
let offset = view.inner_height() / 2;
|
||||
scroll(cx, offset, Direction::Forward);
|
||||
}
|
||||
|
||||
|
@ -4342,7 +4342,7 @@ fn align_view_middle(cx: &mut Context) {
|
|||
|
||||
view.offset.col = pos
|
||||
.col
|
||||
.saturating_sub((view.inner_area().width as usize) / 2);
|
||||
.saturating_sub((view.inner_area(doc).width as usize) / 2);
|
||||
}
|
||||
|
||||
fn scroll_up(cx: &mut Context) {
|
||||
|
|
|
@ -79,7 +79,7 @@ impl EditorView {
|
|||
surface: &mut Surface,
|
||||
is_focused: bool,
|
||||
) {
|
||||
let inner = view.inner_area();
|
||||
let inner = view.inner_area(doc);
|
||||
let area = view.area;
|
||||
let theme = &editor.theme;
|
||||
|
||||
|
@ -736,9 +736,10 @@ impl EditorView {
|
|||
// avoid lots of small allocations by reusing a text buffer for each line
|
||||
let mut text = String::with_capacity(8);
|
||||
|
||||
for (constructor, width) in view.gutters() {
|
||||
let gutter = constructor(editor, doc, view, theme, is_focused, *width);
|
||||
text.reserve(*width); // ensure there's enough space for the gutter
|
||||
for gutter_type in view.gutters() {
|
||||
let gutter = gutter_type.style(editor, doc, view, theme, is_focused);
|
||||
let width = gutter_type.width(view, doc);
|
||||
text.reserve(width); // ensure there's enough space for the gutter
|
||||
for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
|
||||
let selected = cursors.contains(&line);
|
||||
let x = viewport.x + offset;
|
||||
|
@ -751,13 +752,13 @@ impl EditorView {
|
|||
};
|
||||
|
||||
if let Some(style) = gutter(line, selected, &mut text) {
|
||||
surface.set_stringn(x, y, &text, *width, gutter_style.patch(style));
|
||||
surface.set_stringn(x, y, &text, width, gutter_style.patch(style));
|
||||
} else {
|
||||
surface.set_style(
|
||||
Rect {
|
||||
x,
|
||||
y,
|
||||
width: *width as u16,
|
||||
width: width as u16,
|
||||
height: 1,
|
||||
},
|
||||
gutter_style,
|
||||
|
@ -766,7 +767,7 @@ impl EditorView {
|
|||
text.clear();
|
||||
}
|
||||
|
||||
offset += *width as u16;
|
||||
offset += width as u16;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -882,7 +883,7 @@ impl EditorView {
|
|||
.or_else(|| theme.try_get_exact("ui.cursorcolumn"))
|
||||
.unwrap_or_else(|| theme.get("ui.cursorline.secondary"));
|
||||
|
||||
let inner_area = view.inner_area();
|
||||
let inner_area = view.inner_area(doc);
|
||||
let offset = view.offset.col;
|
||||
|
||||
let selection = doc.selection(view.id);
|
||||
|
|
|
@ -456,6 +456,7 @@ impl std::str::FromStr for GutterType {
|
|||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"diagnostics" => Ok(Self::Diagnostics),
|
||||
"spacer" => Ok(Self::Spacer),
|
||||
"line-numbers" => Ok(Self::LineNumbers),
|
||||
_ => anyhow::bail!("Gutter type can only be `diagnostics` or `line-numbers`."),
|
||||
}
|
||||
|
@ -589,7 +590,11 @@ impl Default for Config {
|
|||
line_number: LineNumber::Absolute,
|
||||
cursorline: false,
|
||||
cursorcolumn: false,
|
||||
gutters: vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
gutters: vec![
|
||||
GutterType::Diagnostics,
|
||||
GutterType::Spacer,
|
||||
GutterType::LineNumbers,
|
||||
],
|
||||
middle_click_paste: true,
|
||||
auto_pairs: AutoPairConfig::default(),
|
||||
auto_completion: true,
|
||||
|
@ -1308,7 +1313,7 @@ impl Editor {
|
|||
.primary()
|
||||
.cursor(doc.text().slice(..));
|
||||
if let Some(mut pos) = view.screen_coords_at_pos(doc, doc.text().slice(..), cursor) {
|
||||
let inner = view.inner_area();
|
||||
let inner = view.inner_area(doc);
|
||||
pos.col += inner.x as usize;
|
||||
pos.row += inner.y as usize;
|
||||
let cursorkind = config.cursor_shape.from_mode(self.mode);
|
||||
|
|
|
@ -1,21 +1,54 @@
|
|||
use std::fmt::Write;
|
||||
|
||||
use crate::{
|
||||
editor::GutterType,
|
||||
graphics::{Color, Style, UnderlineStyle},
|
||||
Document, Editor, Theme, View,
|
||||
};
|
||||
|
||||
fn count_digits(n: usize) -> usize {
|
||||
// NOTE: if int_log gets standardized in stdlib, can use checked_log10
|
||||
// (https://github.com/rust-lang/rust/issues/70887#issue)
|
||||
std::iter::successors(Some(n), |&n| (n >= 10).then(|| n / 10)).count()
|
||||
}
|
||||
|
||||
pub type GutterFn<'doc> = Box<dyn Fn(usize, bool, &mut String) -> Option<Style> + 'doc>;
|
||||
pub type Gutter =
|
||||
for<'doc> fn(&'doc Editor, &'doc Document, &View, &Theme, bool, usize) -> GutterFn<'doc>;
|
||||
|
||||
impl GutterType {
|
||||
pub fn style<'doc>(
|
||||
self,
|
||||
editor: &'doc Editor,
|
||||
doc: &'doc Document,
|
||||
view: &View,
|
||||
theme: &Theme,
|
||||
is_focused: bool,
|
||||
) -> GutterFn<'doc> {
|
||||
match self {
|
||||
GutterType::Diagnostics => {
|
||||
diagnostics_or_breakpoints(editor, doc, view, theme, is_focused)
|
||||
}
|
||||
GutterType::LineNumbers => line_numbers(editor, doc, view, theme, is_focused),
|
||||
GutterType::Spacer => padding(editor, doc, view, theme, is_focused),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(self, _view: &View, doc: &Document) -> usize {
|
||||
match self {
|
||||
GutterType::Diagnostics => 1,
|
||||
GutterType::LineNumbers => line_numbers_width(_view, doc),
|
||||
GutterType::Spacer => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn diagnostic<'doc>(
|
||||
_editor: &'doc Editor,
|
||||
doc: &'doc Document,
|
||||
_view: &View,
|
||||
theme: &Theme,
|
||||
_is_focused: bool,
|
||||
_width: usize,
|
||||
) -> GutterFn<'doc> {
|
||||
let warning = theme.get("warning");
|
||||
let error = theme.get("error");
|
||||
|
@ -56,10 +89,11 @@ pub fn line_numbers<'doc>(
|
|||
view: &View,
|
||||
theme: &Theme,
|
||||
is_focused: bool,
|
||||
width: usize,
|
||||
) -> GutterFn<'doc> {
|
||||
let text = doc.text().slice(..);
|
||||
let last_line = view.last_line(doc);
|
||||
let width = GutterType::LineNumbers.width(view, doc);
|
||||
|
||||
// Whether to draw the line number for the last line of the
|
||||
// document or not. We only draw it if it's not an empty line.
|
||||
let draw_last = text.line_to_byte(last_line) < text.len_bytes();
|
||||
|
@ -91,24 +125,35 @@ pub fn line_numbers<'doc>(
|
|||
} else {
|
||||
line + 1
|
||||
};
|
||||
|
||||
let style = if selected && is_focused {
|
||||
linenr_select
|
||||
} else {
|
||||
linenr
|
||||
};
|
||||
|
||||
write!(out, "{:>1$}", display_num, width).unwrap();
|
||||
Some(style)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn line_numbers_width(_view: &View, doc: &Document) -> usize {
|
||||
let text = doc.text();
|
||||
let last_line = text.len_lines().saturating_sub(1);
|
||||
let draw_last = text.line_to_byte(last_line) < text.len_bytes();
|
||||
let last_drawn = if draw_last { last_line + 1 } else { last_line };
|
||||
|
||||
// set a lower bound to 2-chars to minimize ambiguous relative line numbers
|
||||
std::cmp::max(count_digits(last_drawn), 2)
|
||||
}
|
||||
|
||||
pub fn padding<'doc>(
|
||||
_editor: &'doc Editor,
|
||||
_doc: &'doc Document,
|
||||
_view: &View,
|
||||
_theme: &Theme,
|
||||
_is_focused: bool,
|
||||
_width: usize,
|
||||
) -> GutterFn<'doc> {
|
||||
Box::new(|_line: usize, _selected: bool, _out: &mut String| None)
|
||||
}
|
||||
|
@ -128,7 +173,6 @@ pub fn breakpoints<'doc>(
|
|||
_view: &View,
|
||||
theme: &Theme,
|
||||
_is_focused: bool,
|
||||
_width: usize,
|
||||
) -> GutterFn<'doc> {
|
||||
let warning = theme.get("warning");
|
||||
let error = theme.get("error");
|
||||
|
@ -181,10 +225,9 @@ pub fn diagnostics_or_breakpoints<'doc>(
|
|||
view: &View,
|
||||
theme: &Theme,
|
||||
is_focused: bool,
|
||||
width: usize,
|
||||
) -> GutterFn<'doc> {
|
||||
let diagnostics = diagnostic(editor, doc, view, theme, is_focused, width);
|
||||
let breakpoints = breakpoints(editor, doc, view, theme, is_focused, width);
|
||||
let diagnostics = diagnostic(editor, doc, view, theme, is_focused);
|
||||
let breakpoints = breakpoints(editor, doc, view, theme, is_focused);
|
||||
|
||||
Box::new(move |line, selected, out| {
|
||||
breakpoints(line, selected, out).or_else(|| diagnostics(line, selected, out))
|
||||
|
|
|
@ -55,7 +55,7 @@ pub fn align_view(doc: &Document, view: &mut View, align: Align) {
|
|||
.cursor(doc.text().slice(..));
|
||||
let line = doc.text().char_to_line(pos);
|
||||
|
||||
let last_line_height = view.inner_area().height.saturating_sub(1) as usize;
|
||||
let last_line_height = view.inner_height().saturating_sub(1);
|
||||
|
||||
let relative = match align {
|
||||
Align::Center => last_line_height / 2,
|
||||
|
|
|
@ -499,7 +499,7 @@ impl Tree {
|
|||
// in a vertical container (and already correct based on previous search)
|
||||
child_id = *container.children.iter().min_by_key(|id| {
|
||||
let x = match &self.nodes[**id].content {
|
||||
Content::View(view) => view.inner_area().left(),
|
||||
Content::View(view) => view.area.left(),
|
||||
Content::Container(container) => container.area.left(),
|
||||
};
|
||||
(current_x as i16 - x as i16).abs()
|
||||
|
@ -510,7 +510,7 @@ impl Tree {
|
|||
// in a horizontal container (and already correct based on previous search)
|
||||
child_id = *container.children.iter().min_by_key(|id| {
|
||||
let y = match &self.nodes[**id].content {
|
||||
Content::View(view) => view.inner_area().top(),
|
||||
Content::View(view) => view.area.top(),
|
||||
Content::Container(container) => container.area.top(),
|
||||
};
|
||||
(current_y as i16 - y as i16).abs()
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
use crate::{
|
||||
graphics::Rect,
|
||||
gutter::{self, Gutter},
|
||||
Document, DocumentId, ViewId,
|
||||
};
|
||||
use crate::{editor::GutterType, graphics::Rect, Document, DocumentId, ViewId};
|
||||
use helix_core::{
|
||||
pos_at_visual_coords, visual_coords_at_pos, Position, RopeSlice, Selection, Transaction,
|
||||
};
|
||||
|
@ -98,11 +94,8 @@ pub struct View {
|
|||
pub last_modified_docs: [Option<DocumentId>; 2],
|
||||
/// used to store previous selections of tree-sitter objects
|
||||
pub object_selections: Vec<Selection>,
|
||||
/// Gutter (constructor) and width of gutter, used to calculate
|
||||
/// `gutter_offset`
|
||||
gutters: Vec<(Gutter, usize)>,
|
||||
/// cached total width of gutter
|
||||
gutter_offset: u16,
|
||||
/// GutterTypes used to fetch Gutter (constructor) and width for rendering
|
||||
gutters: Vec<GutterType>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for View {
|
||||
|
@ -117,28 +110,6 @@ impl fmt::Debug for View {
|
|||
|
||||
impl View {
|
||||
pub fn new(doc: DocumentId, gutter_types: Vec<crate::editor::GutterType>) -> Self {
|
||||
let mut gutters: Vec<(Gutter, usize)> = vec![];
|
||||
let mut gutter_offset = 0;
|
||||
use crate::editor::GutterType;
|
||||
for gutter_type in &gutter_types {
|
||||
let width = match gutter_type {
|
||||
GutterType::Diagnostics => 1,
|
||||
GutterType::LineNumbers => 5,
|
||||
GutterType::Spacer => 1,
|
||||
};
|
||||
gutter_offset += width;
|
||||
gutters.push((
|
||||
match gutter_type {
|
||||
GutterType::Diagnostics => gutter::diagnostics_or_breakpoints,
|
||||
GutterType::LineNumbers => gutter::line_numbers,
|
||||
GutterType::Spacer => gutter::padding,
|
||||
},
|
||||
width as usize,
|
||||
));
|
||||
}
|
||||
if !gutter_types.is_empty() {
|
||||
gutter_offset += 1;
|
||||
}
|
||||
Self {
|
||||
id: ViewId::default(),
|
||||
doc,
|
||||
|
@ -148,8 +119,7 @@ impl View {
|
|||
docs_access_history: Vec::new(),
|
||||
last_modified_docs: [None, None],
|
||||
object_selections: Vec::new(),
|
||||
gutters,
|
||||
gutter_offset,
|
||||
gutters: gutter_types,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,15 +130,32 @@ impl View {
|
|||
self.docs_access_history.push(id);
|
||||
}
|
||||
|
||||
pub fn inner_area(&self) -> Rect {
|
||||
// TODO add abilty to not use cached offset for runtime configurable gutter
|
||||
self.area.clip_left(self.gutter_offset).clip_bottom(1) // -1 for statusline
|
||||
pub fn inner_area(&self, doc: &Document) -> Rect {
|
||||
self.area.clip_left(self.gutter_offset(doc)).clip_bottom(1) // -1 for statusline
|
||||
}
|
||||
|
||||
pub fn gutters(&self) -> &[(Gutter, usize)] {
|
||||
pub fn inner_height(&self) -> usize {
|
||||
self.area.clip_bottom(1).height.into() // -1 for statusline
|
||||
}
|
||||
|
||||
pub fn gutters(&self) -> &[GutterType] {
|
||||
&self.gutters
|
||||
}
|
||||
|
||||
pub fn gutter_offset(&self, doc: &Document) -> u16 {
|
||||
let mut offset = self
|
||||
.gutters
|
||||
.iter()
|
||||
.map(|gutter| gutter.width(self, doc) as u16)
|
||||
.sum();
|
||||
|
||||
if offset > 0 {
|
||||
offset += 1
|
||||
}
|
||||
|
||||
offset
|
||||
}
|
||||
|
||||
//
|
||||
pub fn offset_coords_to_in_view(
|
||||
&self,
|
||||
|
@ -183,7 +170,7 @@ impl View {
|
|||
let Position { col, row: line } =
|
||||
visual_coords_at_pos(doc.text().slice(..), cursor, doc.tab_width());
|
||||
|
||||
let inner_area = self.inner_area();
|
||||
let inner_area = self.inner_area(doc);
|
||||
let last_line = (self.offset.row + inner_area.height as usize).saturating_sub(1);
|
||||
|
||||
// - 1 so we have at least one gap in the middle.
|
||||
|
@ -233,10 +220,9 @@ impl View {
|
|||
/// Calculates the last visible line on screen
|
||||
#[inline]
|
||||
pub fn last_line(&self, doc: &Document) -> usize {
|
||||
let height = self.inner_area().height;
|
||||
std::cmp::min(
|
||||
// Saturating subs to make it inclusive zero indexing.
|
||||
(self.offset.row + height as usize).saturating_sub(1),
|
||||
(self.offset.row + self.inner_height()).saturating_sub(1),
|
||||
doc.text().len_lines().saturating_sub(1),
|
||||
)
|
||||
}
|
||||
|
@ -270,12 +256,13 @@ impl View {
|
|||
|
||||
pub fn text_pos_at_screen_coords(
|
||||
&self,
|
||||
text: &RopeSlice,
|
||||
doc: &Document,
|
||||
row: u16,
|
||||
column: u16,
|
||||
tab_width: usize,
|
||||
) -> Option<usize> {
|
||||
let inner = self.inner_area();
|
||||
let text = doc.text().slice(..);
|
||||
let inner = self.inner_area(doc);
|
||||
// 1 for status
|
||||
if row < inner.top() || row >= inner.bottom() {
|
||||
return None;
|
||||
|
@ -293,7 +280,7 @@ impl View {
|
|||
let text_col = (column - inner.x) as usize + self.offset.col;
|
||||
|
||||
Some(pos_at_visual_coords(
|
||||
*text,
|
||||
text,
|
||||
Position {
|
||||
row: text_row,
|
||||
col: text_col,
|
||||
|
@ -305,7 +292,7 @@ impl View {
|
|||
/// Translates a screen position to position in the text document.
|
||||
/// Returns a usize typed position in bounds of the text if found in this view, None if out of view.
|
||||
pub fn pos_at_screen_coords(&self, doc: &Document, row: u16, column: u16) -> Option<usize> {
|
||||
self.text_pos_at_screen_coords(&doc.text().slice(..), row, column, doc.tab_width())
|
||||
self.text_pos_at_screen_coords(doc, row, column, doc.tab_width())
|
||||
}
|
||||
|
||||
/// Translates screen coordinates into coordinates on the gutter of the view.
|
||||
|
@ -366,9 +353,10 @@ impl View {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use helix_core::Rope;
|
||||
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
|
||||
const OFFSET: u16 = 4; // 1 diagnostic + 2 linenr (< 100 lines) + 1 gutter
|
||||
const OFFSET_WITHOUT_LINE_NUMBERS: u16 = 2; // 1 diagnostic + 1 gutter
|
||||
// const OFFSET: u16 = GUTTERS.iter().map(|(_, width)| *width as u16).sum();
|
||||
use crate::document::Document;
|
||||
use crate::editor::GutterType;
|
||||
|
||||
#[test]
|
||||
|
@ -379,45 +367,45 @@ mod tests {
|
|||
);
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
let rope = Rope::from_str("abc\n\tdef");
|
||||
let text = rope.slice(..);
|
||||
let doc = Document::from(rope, None);
|
||||
|
||||
assert_eq!(view.text_pos_at_screen_coords(&text, 40, 2, 4), None);
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 2, 4), None);
|
||||
|
||||
assert_eq!(view.text_pos_at_screen_coords(&text, 40, 41, 4), None);
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 41, 4), None);
|
||||
|
||||
assert_eq!(view.text_pos_at_screen_coords(&text, 0, 2, 4), None);
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 0, 2, 4), None);
|
||||
|
||||
assert_eq!(view.text_pos_at_screen_coords(&text, 0, 49, 4), None);
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 0, 49, 4), None);
|
||||
|
||||
assert_eq!(view.text_pos_at_screen_coords(&text, 0, 41, 4), None);
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 0, 41, 4), None);
|
||||
|
||||
assert_eq!(view.text_pos_at_screen_coords(&text, 40, 81, 4), None);
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 81, 4), None);
|
||||
|
||||
assert_eq!(view.text_pos_at_screen_coords(&text, 78, 41, 4), None);
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 78, 41, 4), None);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 3, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 3, 4),
|
||||
Some(3)
|
||||
);
|
||||
|
||||
assert_eq!(view.text_pos_at_screen_coords(&text, 40, 80, 4), Some(3));
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 80, 4), Some(3));
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 41, 40 + OFFSET + 1, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 1, 4),
|
||||
Some(4)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 41, 40 + OFFSET + 4, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 4, 4),
|
||||
Some(5)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 41, 40 + OFFSET + 7, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 7, 4),
|
||||
Some(8)
|
||||
);
|
||||
|
||||
assert_eq!(view.text_pos_at_screen_coords(&text, 41, 80, 4), Some(8));
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 41, 80, 4), Some(8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -425,9 +413,9 @@ mod tests {
|
|||
let mut view = View::new(DocumentId::default(), vec![GutterType::Diagnostics]);
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
let rope = Rope::from_str("abc\n\tdef");
|
||||
let text = rope.slice(..);
|
||||
let doc = Document::from(rope, None);
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 41, 40 + OFFSET_WITHOUT_LINE_NUMBERS + 1, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET_WITHOUT_LINE_NUMBERS + 1, 4),
|
||||
Some(4)
|
||||
);
|
||||
}
|
||||
|
@ -437,11 +425,8 @@ mod tests {
|
|||
let mut view = View::new(DocumentId::default(), vec![]);
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
let rope = Rope::from_str("abc\n\tdef");
|
||||
let text = rope.slice(..);
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 41, 40 + 1, 4),
|
||||
Some(4)
|
||||
);
|
||||
let doc = Document::from(rope, None);
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 41, 40 + 1, 4), Some(4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -452,34 +437,34 @@ mod tests {
|
|||
);
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
let rope = Rope::from_str("Hi! こんにちは皆さん");
|
||||
let text = rope.slice(..);
|
||||
let doc = Document::from(rope, None);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET, 4),
|
||||
Some(0)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 4, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 4, 4),
|
||||
Some(4)
|
||||
);
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 5, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 5, 4),
|
||||
Some(4)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 6, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 6, 4),
|
||||
Some(5)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 7, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 7, 4),
|
||||
Some(5)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 8, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 8, 4),
|
||||
Some(6)
|
||||
);
|
||||
}
|
||||
|
@ -492,30 +477,30 @@ mod tests {
|
|||
);
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
let rope = Rope::from_str("Hèl̀l̀ò world!");
|
||||
let text = rope.slice(..);
|
||||
let doc = Document::from(rope, None);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET, 4),
|
||||
Some(0)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 1, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 1, 4),
|
||||
Some(1)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 2, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 2, 4),
|
||||
Some(3)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 3, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 3, 4),
|
||||
Some(5)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&text, 40, 40 + OFFSET + 4, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 4, 4),
|
||||
Some(7)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue