Adopt rustfmt.toml from rust-lang/rust

This commit is contained in:
Leonard Hecker 2025-05-01 14:39:44 +02:00
parent d82a1659b6
commit b7024f1bf9
27 changed files with 326 additions and 917 deletions

View file

@ -1,7 +1,6 @@
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use edit::helpers::*;
use edit::simd;
use edit::ucd;
use edit::{simd, ucd};
fn bench(c: &mut Criterion) {
let mut buffer1 = [0u8; 2048];
@ -23,9 +22,7 @@ fn bench(c: &mut Criterion) {
});
group.bench_function("word_wrap", |b| {
b.iter(|| {
ucd::MeasurementConfig::new(&bytes)
.with_word_wrap_column(50)
.goto_logical(Point::MAX)
ucd::MeasurementConfig::new(&bytes).with_word_wrap_column(50).goto_logical(Point::MAX)
})
});
group.finish();

View file

@ -4,10 +4,7 @@ fn main() {
winres::WindowsResource::new()
.set_manifest_file("src/bin/edit/edit.exe.manifest")
.set("FileDescription", "Microsoft Edit")
.set(
"LegalCopyright",
"© Microsoft Corporation. All rights reserved.",
)
.set("LegalCopyright", "© Microsoft Corporation. All rights reserved.")
.compile()
.unwrap();
}

5
rustfmt.toml Normal file
View file

@ -0,0 +1,5 @@
style_edition = "2024"
use_small_heuristics = "Max"
group_imports = "StdExternalCrate"
imports_granularity = "Module"
use_field_init_shorthand = true

View file

@ -1,6 +1,6 @@
use std::{io, result};
use crate::sys;
use std::io;
use std::result;
// Remember to add an entry to `Error::message()` for each new error.
pub const APP_ICU_MISSING: Error = Error::new_app(0);

View file

@ -1,17 +1,11 @@
use crate::apperr;
use crate::helpers;
use crate::sys;
use std::alloc::AllocError;
use std::alloc::Allocator;
use std::alloc::Layout;
use std::alloc::{AllocError, Allocator, Layout};
use std::cell::Cell;
use std::fmt;
use std::mem;
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::ops::DerefMut;
use std::ops::{Deref, DerefMut};
use std::ptr::{self, NonNull};
use std::slice;
use std::{fmt, mem, slice};
use crate::{apperr, helpers, sys};
const ALLOC_CHUNK_SIZE: usize = 64 * 1024;
@ -24,23 +18,13 @@ pub struct Arena {
impl Arena {
pub const fn empty() -> Self {
Self {
base: NonNull::dangling(),
capacity: 0,
commit: Cell::new(0),
offset: Cell::new(0),
}
Self { base: NonNull::dangling(), capacity: 0, commit: Cell::new(0), offset: Cell::new(0) }
}
pub fn new(capacity: usize) -> apperr::Result<Arena> {
let capacity = (capacity + ALLOC_CHUNK_SIZE - 1) & !(ALLOC_CHUNK_SIZE - 1);
let base = unsafe { sys::virtual_reserve(capacity)? };
Ok(Arena {
base,
capacity,
commit: Cell::new(0),
offset: Cell::new(0),
})
Ok(Arena { base, capacity, commit: Cell::new(0), offset: Cell::new(0) })
}
/// "Deallocates" the memory in the arena down to the given offset.
@ -340,9 +324,7 @@ pub struct ArenaString<'a> {
impl<'a> ArenaString<'a> {
#[must_use]
pub const fn new_in(arena: &'a Arena) -> Self {
Self {
vec: Vec::new_in(arena),
}
Self { vec: Vec::new_in(arena) }
}
#[inline]
@ -448,9 +430,7 @@ impl<'a> ArenaString<'a> {
pub fn push(&mut self, ch: char) {
match ch.len_utf8() {
1 => self.vec.push(ch as u8),
_ => self
.vec
.extend_from_slice(ch.encode_utf8(&mut [0; 4]).as_bytes()),
_ => self.vec.extend_from_slice(ch.encode_utf8(&mut [0; 4]).as_bytes()),
}
}

View file

@ -1,12 +1,12 @@
use edit::apperr;
use edit::buffer::{RcTextBuffer, TextBuffer};
use edit::helpers::{CoordType, Point};
use edit::simd::memrchr2;
use edit::sys;
use std::collections::LinkedList;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use edit::buffer::{RcTextBuffer, TextBuffer};
use edit::helpers::{CoordType, Point};
use edit::simd::memrchr2;
use edit::{apperr, sys};
pub enum DocumentPath {
None,
Preliminary(PathBuf),
@ -39,11 +39,7 @@ pub struct Document {
impl Document {
fn update_file_mode(&mut self) {
let mut tb = self.buffer.borrow_mut();
tb.set_ruler(if self.filename == "COMMIT_EDITMSG" {
72
} else {
0
});
tb.set_ruler(if self.filename == "COMMIT_EDITMSG" { 72 } else { 0 });
}
}
@ -164,15 +160,8 @@ impl DocumentManager {
// Path exists but is not a file (a directory?).
_ => DocumentPath::None,
};
let filename = path
.as_path()
.map_or(Default::default(), Self::get_filename_from_path);
let mut doc = Document {
buffer,
path,
filename,
new_file_counter: 0,
};
let filename = path.as_path().map_or(Default::default(), Self::get_filename_from_path);
let mut doc = Document { buffer, path, filename, new_file_counter: 0 };
if doc.filename.is_empty() {
self.gen_untitled_name(&mut doc);
@ -210,10 +199,7 @@ impl DocumentManager {
}
pub fn get_filename_from_path(path: &Path) -> String {
path.file_name()
.unwrap_or_default()
.to_string_lossy()
.into_owned()
path.file_name().unwrap_or_default().to_string_lossy().into_owned()
}
// Parse a filename in the form of "filename:line:char".
@ -288,10 +274,7 @@ mod tests {
assert_eq!(parse("45:123"), ("45", Some(Point { x: 0, y: 122 })));
assert_eq!(parse(":45:123"), (":45", Some(Point { x: 0, y: 122 })));
assert_eq!(parse("abc:45:123"), ("abc", Some(Point { x: 122, y: 44 })));
assert_eq!(
parse("abc:def:123"),
("abc:def", Some(Point { x: 0, y: 122 }))
);
assert_eq!(parse("abc:def:123"), ("abc:def", Some(Point { x: 0, y: 122 })));
assert_eq!(parse("1:2:3"), ("1", Some(Point { x: 2, y: 1 })));
assert_eq!(parse("::3"), (":", Some(Point { x: 0, y: 2 })));
assert_eq!(parse("1::3"), ("1:", Some(Point { x: 0, y: 2 })));
@ -300,13 +283,7 @@ mod tests {
assert_eq!(parse("::"), ("::", None));
assert_eq!(parse("a:1"), ("a", Some(Point { x: 0, y: 0 })));
assert_eq!(parse("1:a"), ("1:a", None));
assert_eq!(
parse("file.txt:10"),
("file.txt", Some(Point { x: 0, y: 9 }))
);
assert_eq!(
parse("file.txt:10:5"),
("file.txt", Some(Point { x: 4, y: 9 }))
);
assert_eq!(parse("file.txt:10"), ("file.txt", Some(Point { x: 0, y: 9 })));
assert_eq!(parse("file.txt:10:5"), ("file.txt", Some(Point { x: 4, y: 9 })));
}
}

View file

@ -1,17 +1,14 @@
use crate::loc::*;
use crate::state::*;
use edit::framebuffer::IndexedColor;
use edit::helpers::*;
use edit::icu;
use edit::input::kbmod;
use edit::input::vk;
use edit::input::{kbmod, vk};
use edit::tui::*;
use crate::loc::*;
use crate::state::*;
pub fn draw_editor(ctx: &mut Context, state: &mut State) {
if !matches!(
state.wants_search.kind,
StateSearchKind::Hidden | StateSearchKind::Disabled
) {
if !matches!(state.wants_search.kind, StateSearchKind::Hidden | StateSearchKind::Disabled) {
draw_search(ctx, state);
}
@ -31,10 +28,7 @@ pub fn draw_editor(ctx: &mut Context, state: &mut State) {
ctx.block_end();
}
ctx.attr_intrinsic_size(Size {
width: 0,
height: size.height - height_reduction,
});
ctx.attr_intrinsic_size(Size { width: 0, height: size.height - height_reduction });
}
fn draw_search(ctx: &mut Context, state: &mut State) {
@ -81,10 +75,7 @@ fn draw_search(ctx: &mut Context, state: &mut State) {
}
ctx.table_begin("needle");
ctx.table_set_cell_gap(Size {
width: 1,
height: 0,
});
ctx.table_set_cell_gap(Size { width: 1, height: 0 });
{
{
ctx.table_next_row();
@ -97,10 +88,7 @@ fn draw_search(ctx: &mut Context, state: &mut State) {
ctx.attr_background_rgba(ctx.indexed(IndexedColor::Red));
ctx.attr_foreground_rgba(ctx.indexed(IndexedColor::BrightWhite));
}
ctx.attr_intrinsic_size(Size {
width: COORD_TYPE_SAFE_MAX,
height: 1,
});
ctx.attr_intrinsic_size(Size { width: COORD_TYPE_SAFE_MAX, height: 1 });
if focus == StateSearchKind::Search {
ctx.steal_focus();
}
@ -114,10 +102,7 @@ fn draw_search(ctx: &mut Context, state: &mut State) {
ctx.label("label", Overflow::Clip, loc(LocId::SearchReplacementLabel));
ctx.editline("replacement", &mut state.search_replacement);
ctx.attr_intrinsic_size(Size {
width: COORD_TYPE_SAFE_MAX,
height: 1,
});
ctx.attr_intrinsic_size(Size { width: COORD_TYPE_SAFE_MAX, height: 1 });
if focus == StateSearchKind::Replace {
ctx.steal_focus();
}
@ -133,10 +118,7 @@ fn draw_search(ctx: &mut Context, state: &mut State) {
ctx.table_end();
ctx.table_begin("options");
ctx.table_set_cell_gap(Size {
width: 2,
height: 0,
});
ctx.table_set_cell_gap(Size { width: 2, height: 0 });
{
ctx.table_next_row();
@ -181,10 +163,9 @@ fn draw_search(ctx: &mut Context, state: &mut State) {
state.search_success = match action {
SearchAction::None => return,
SearchAction::Search => doc
.buffer
.borrow_mut()
.find_and_select(&state.search_needle, state.search_options),
SearchAction::Search => {
doc.buffer.borrow_mut().find_and_select(&state.search_needle, state.search_options)
}
SearchAction::Replace => doc.buffer.borrow_mut().find_and_replace(
&state.search_needle,
state.search_options,
@ -243,21 +224,14 @@ pub fn draw_handle_wants_close(ctx: &mut Context, state: &mut State) {
ctx.attr_background_rgba(ctx.indexed(IndexedColor::Red));
ctx.attr_foreground_rgba(ctx.indexed(IndexedColor::BrightWhite));
{
ctx.label(
"description",
Overflow::Clip,
loc(LocId::UnsavedChangesDialogDescription),
);
ctx.label("description", Overflow::Clip, loc(LocId::UnsavedChangesDialogDescription));
ctx.attr_padding(Rect::three(1, 2, 1));
ctx.table_begin("choices");
ctx.inherit_focus();
ctx.attr_padding(Rect::three(0, 2, 1));
ctx.attr_position(Position::Center);
ctx.table_set_cell_gap(Size {
width: 2,
height: 0,
});
ctx.table_set_cell_gap(Size { width: 2, height: 0 });
{
ctx.table_next_row();
ctx.inherit_focus();
@ -269,11 +243,7 @@ pub fn draw_handle_wants_close(ctx: &mut Context, state: &mut State) {
if ctx.button("no", Overflow::Clip, loc(LocId::UnsavedChangesDialogNo)) {
action = Action::Discard;
}
if ctx.button(
"cancel",
Overflow::Clip,
loc(LocId::UnsavedChangesDialogCancel),
) {
if ctx.button("cancel", Overflow::Clip, loc(LocId::UnsavedChangesDialogCancel)) {
action = Action::Cancel;
}

View file

@ -1,14 +1,15 @@
use crate::documents::*;
use crate::loc::*;
use crate::state::*;
use std::cmp::Ordering;
use std::path::{Component, PathBuf};
use edit::framebuffer::IndexedColor;
use edit::helpers::*;
use edit::icu;
use edit::input::vk;
use edit::tui::*;
use std::cmp::Ordering;
use std::path::Component;
use std::path::PathBuf;
use crate::documents::*;
use crate::loc::*;
use crate::state::*;
pub fn draw_file_picker(ctx: &mut Context, state: &mut State) {
let width = (ctx.size().width - 20).max(10);
@ -29,34 +30,19 @@ pub fn draw_file_picker(ctx: &mut Context, state: &mut State) {
ctx.table_begin("path");
ctx.table_set_columns(&[0, COORD_TYPE_SAFE_MAX]);
ctx.table_set_cell_gap(Size {
width: 1,
height: 0,
});
ctx.table_set_cell_gap(Size { width: 1, height: 0 });
ctx.attr_padding(Rect::two(1, 1));
ctx.inherit_focus();
{
ctx.table_next_row();
ctx.label(
"dir-label",
Overflow::Clip,
loc(LocId::SaveAsDialogPathLabel),
);
ctx.label(
"dir",
Overflow::TruncateMiddle,
state.file_picker_pending_dir.as_str(),
);
ctx.label("dir-label", Overflow::Clip, loc(LocId::SaveAsDialogPathLabel));
ctx.label("dir", Overflow::TruncateMiddle, state.file_picker_pending_dir.as_str());
ctx.table_next_row();
ctx.inherit_focus();
ctx.label(
"name-label",
Overflow::Clip,
loc(LocId::SaveAsDialogNameLabel),
);
ctx.label("name-label", Overflow::Clip, loc(LocId::SaveAsDialogNameLabel));
ctx.editline("name", &mut state.file_picker_pending_name);
ctx.inherit_focus();
if ctx.is_focused() && ctx.consume_shortcut(vk::RETURN) {
@ -139,10 +125,7 @@ pub fn draw_file_picker(ctx: &mut Context, state: &mut State) {
ctx.inherit_focus();
ctx.attr_padding(Rect::three(0, 2, 1));
ctx.attr_position(Position::Center);
ctx.table_set_cell_gap(Size {
width: 2,
height: 0,
});
ctx.table_set_cell_gap(Size { width: 2, height: 0 });
{
ctx.table_next_row();
ctx.inherit_focus();
@ -213,11 +196,7 @@ fn draw_file_picker_update_path(state: &mut State) -> Option<PathBuf> {
}
state.file_picker_pending_name = name;
if state.file_picker_pending_name.is_empty() {
None
} else {
Some(normalized)
}
if state.file_picker_pending_name.is_empty() { None } else { Some(normalized) }
}
fn draw_dialog_saveas_refresh_files(state: &mut State) {

View file

@ -1,10 +1,11 @@
use crate::loc::*;
use crate::state::*;
use edit::arena_format;
use edit::helpers::*;
use edit::input::{kbmod, vk};
use edit::tui::*;
use crate::loc::*;
use crate::state::*;
pub fn draw_menubar(ctx: &mut Context, state: &mut State) {
ctx.menubar_begin();
ctx.attr_background_rgba(state.menubar_color_bg);
@ -132,11 +133,7 @@ pub fn draw_dialog_about(ctx: &mut Context, state: &mut State) {
);
ctx.attr_position(Position::Center);
ctx.label(
"copyright",
Overflow::TruncateTail,
"Copyright (c) Microsoft Corp 2025",
);
ctx.label("copyright", Overflow::TruncateTail, "Copyright (c) Microsoft Corp 2025");
ctx.attr_position(Position::Center);
ctx.block_begin("choices");

View file

@ -1,27 +1,21 @@
use std::ptr;
use edit::framebuffer::IndexedColor;
use edit::helpers::*;
use edit::input::vk;
use edit::tui::*;
use edit::{arena_format, icu};
use crate::documents::*;
use crate::loc::*;
use crate::state::*;
use edit::arena_format;
use edit::framebuffer::IndexedColor;
use edit::helpers::*;
use edit::icu;
use edit::input::vk;
use edit::tui::*;
pub fn draw_statusbar(ctx: &mut Context, state: &mut State) {
ctx.table_begin("statusbar");
ctx.attr_background_rgba(state.menubar_color_bg);
ctx.attr_foreground_rgba(state.menubar_color_fg);
ctx.table_set_cell_gap(Size {
width: 2,
height: 0,
});
ctx.attr_intrinsic_size(Size {
width: COORD_TYPE_SAFE_MAX,
height: 1,
});
ctx.table_set_cell_gap(Size { width: 2, height: 0 });
ctx.attr_intrinsic_size(Size { width: COORD_TYPE_SAFE_MAX, height: 1 });
ctx.attr_padding(Rect::two(0, 1));
if let Some(doc) = state.documents.active() {
@ -29,11 +23,7 @@ pub fn draw_statusbar(ctx: &mut Context, state: &mut State) {
ctx.table_next_row();
if ctx.button(
"newline",
Overflow::Clip,
if tb.is_crlf() { "CRLF" } else { "LF" },
) {
if ctx.button("newline", Overflow::Clip, if tb.is_crlf() { "CRLF" } else { "LF" }) {
let is_crlf = tb.is_crlf();
tb.normalize_newlines(!is_crlf);
}
@ -109,10 +99,7 @@ pub fn draw_statusbar(ctx: &mut Context, state: &mut State) {
});
ctx.attr_border();
ctx.attr_padding(Rect::two(0, 1));
ctx.table_set_cell_gap(Size {
width: 1,
height: 0,
});
ctx.table_set_cell_gap(Size { width: 1, height: 0 });
{
if ctx.consume_shortcut(vk::RETURN) {
ctx.toss_focus_up();
@ -203,10 +190,7 @@ pub fn draw_statusbar(ctx: &mut Context, state: &mut State) {
}
ctx.block_begin("filename-container");
ctx.attr_intrinsic_size(Size {
width: COORD_TYPE_SAFE_MAX,
height: 1,
});
ctx.attr_intrinsic_size(Size { width: COORD_TYPE_SAFE_MAX, height: 1 });
{
let total = state.documents.len();
let mut filename = doc.filename.as_str();
@ -236,11 +220,7 @@ pub fn draw_dialog_encoding_change(ctx: &mut Context, state: &mut State) {
ctx.modal_begin(
"encode",
if reopen {
loc(LocId::EncodingReopen)
} else {
loc(LocId::EncodingConvert)
},
if reopen { loc(LocId::EncodingReopen) } else { loc(LocId::EncodingConvert) },
);
{
ctx.scrollarea_begin("scrollarea", Size { width, height });

View file

@ -8,30 +8,28 @@ mod draw_statusbar;
mod loc;
mod state;
use std::borrow::Cow;
#[cfg(feature = "debug-latency")]
use std::fmt::Write;
use std::path::PathBuf;
use std::process;
use documents::DocumentPath;
use draw_editor::*;
use draw_filepicker::*;
use draw_menubar::*;
use draw_statusbar::*;
use edit::apperr;
use edit::arena::{self, ArenaString, scratch_arena};
use edit::base64;
#[cfg(feature = "debug-latency")]
use edit::arena_format;
use edit::buffer::TextBuffer;
use edit::framebuffer::{self, IndexedColor, alpha_blend};
use edit::input::{self, kbmod, vk};
use edit::sys;
use edit::tui::*;
use edit::vt::{self, Token};
use edit::{apperr, base64, sys};
use loc::*;
use state::*;
use std::borrow::Cow;
use std::path::PathBuf;
use std::process;
#[cfg(feature = "debug-latency")]
use edit::arena_format;
#[cfg(feature = "debug-latency")]
use std::fmt::Write;
impl State {
fn new() -> apperr::Result<Self> {
@ -58,10 +56,7 @@ impl State {
file_picker_entries: None,
file_picker_overwrite_warning: None,
wants_search: StateSearch {
kind: StateSearchKind::Hidden,
focus: false,
},
wants_search: StateSearch { kind: StateSearchKind::Hidden, focus: false },
search_needle: Default::default(),
search_replacement: Default::default(),
search_options: Default::default(),
@ -200,10 +195,7 @@ fn run() -> apperr::Result<()> {
#[cfg(feature = "debug-layout")]
{
drop(ctx);
state
.buffer
.buffer
.debug_replace_everything(&tui.debug_layout());
state.buffer.buffer.debug_replace_everything(&tui.debug_layout());
}
#[cfg(feature = "debug-latency")]

View file

@ -1,16 +1,15 @@
use crate::documents::DocumentManager;
use crate::loc::*;
use edit::framebuffer::IndexedColor;
use edit::helpers::*;
use edit::icu;
use edit::sys;
use edit::tui::*;
use edit::{apperr, buffer};
use std::borrow::Cow;
use std::ffi::{OsStr, OsString};
use std::mem;
use std::path::Path;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use edit::framebuffer::IndexedColor;
use edit::helpers::*;
use edit::tui::*;
use edit::{apperr, buffer, icu, sys};
use crate::documents::DocumentManager;
use crate::loc::*;
#[repr(transparent)]
pub struct FormatApperr(apperr::Error);
@ -59,10 +58,7 @@ impl DisplayablePathBuf {
impl Default for DisplayablePathBuf {
fn default() -> Self {
Self {
value: PathBuf::default(),
str: Cow::Borrowed(""),
}
Self { value: PathBuf::default(), str: Cow::Borrowed("") }
}
}

View file

@ -13,29 +13,26 @@
//! The solution to the former is to keep line caches, which further complicates the architecture.
//! There's no solution for the latter. However, there's a chance that the performance will still be sufficient.
use crate::apperr;
use crate::arena::scratch_arena;
use crate::cell::SemiRefCell;
use crate::framebuffer::{Framebuffer, IndexedColor, alpha_blend};
use crate::helpers::{self, COORD_TYPE_SAFE_MAX, CoordType, Point, Rect};
use crate::icu;
use crate::simd::memchr2;
use crate::sys;
use crate::ucd::{self, Document};
use std::borrow::Cow;
use std::cell::UnsafeCell;
use std::collections::LinkedList;
use std::fmt::Write as _;
use std::fs::File;
use std::io::Read as _;
use std::io::Write as _;
use std::io::{Read as _, Write as _};
use std::mem::{self, MaybeUninit};
use std::ops::Range;
use std::path::Path;
use std::ptr::{self, NonNull};
use std::rc::Rc;
use std::slice;
use std::str;
use std::{slice, str};
use crate::arena::scratch_arena;
use crate::cell::SemiRefCell;
use crate::framebuffer::{Framebuffer, IndexedColor, alpha_blend};
use crate::helpers::{self, COORD_TYPE_SAFE_MAX, CoordType, Point, Rect};
use crate::simd::memchr2;
use crate::ucd::{self, Document};
use crate::{apperr, icu, sys};
/// The margin template is used for line numbers.
/// The max. line number we should ever expect is probably 64-bit,
@ -183,10 +180,7 @@ impl TextBuffer {
active_edit_depth: 0,
active_edit_off: 0,
stats: TextBufferStatistics {
logical_lines: 1,
visual_lines: 1,
},
stats: TextBufferStatistics { logical_lines: 1, visual_lines: 1 },
cursor: ucd::UcdCursor::default(),
cursor_for_rendering: None,
selection: TextBufferSelection::None,
@ -243,9 +237,8 @@ impl TextBuffer {
let mut off = 0;
let mut cursor_offset = self.cursor.offset;
let mut cursor_for_rendering_offset = self
.cursor_for_rendering
.map_or(cursor_offset, |c| c.offset);
let mut cursor_for_rendering_offset =
self.cursor_for_rendering.map_or(cursor_offset, |c| c.offset);
#[cfg(debug_assertions)]
let mut adjusted_newlines = 0;
@ -288,9 +281,7 @@ impl TextBuffer {
// Replace the newline.
off -= chunk_newline_len;
let gap = self
.buffer
.allocate_gap(off, newline.len(), chunk_newline_len);
let gap = self.buffer.allocate_gap(off, newline.len(), chunk_newline_len);
gap.copy_from_slice(newline);
self.buffer.commit_gap(newline.len());
off += newline.len();
@ -417,11 +408,8 @@ impl TextBuffer {
let text_width = self.get_text_width();
// 2 columns are required, because otherwise wide glyphs wouldn't ever fit.
let word_wrap_column = if self.word_wrap_enabled && text_width >= 2 {
text_width
} else {
0
};
let word_wrap_column =
if self.word_wrap_enabled && text_width >= 2 { text_width } else { 0 };
if force || self.word_wrap_column > word_wrap_column {
self.word_wrap_column = word_wrap_column;
@ -463,10 +451,7 @@ impl TextBuffer {
pub fn copy_from_str(&mut self, text: &str) {
if self.buffer.copy_from_str(text) {
self.recalc_after_content_swap();
self.cursor_move_to_logical(Point {
x: CoordType::MAX,
y: 0,
});
self.cursor_move_to_logical(Point { x: CoordType::MAX, y: 0 });
let delete = self.buffer.len() - self.cursor.offset;
if delete != 0 {
@ -481,10 +466,7 @@ impl TextBuffer {
let before = self.cursor.logical_pos;
let end = self.cursor_move_to_logical_internal(
ucd::UcdCursor::default(),
Point {
x: 0,
y: CoordType::MAX,
},
Point { x: 0, y: CoordType::MAX },
);
self.stats.logical_lines = end.logical_pos.y + 1;
self.stats.visual_lines = self.stats.logical_lines;
@ -586,11 +568,8 @@ impl TextBuffer {
} else {
// Otherwise, check how many spaces the line starts with. Searching for >8 spaces
// allows us to reject lines that have more than 1 level of indentation.
let space_indentation = chunk[offset..]
.iter()
.take(9)
.take_while(|&&c| c == b' ')
.count();
let space_indentation =
chunk[offset..].iter().take(9).take_while(|&&c| c == b' ').count();
// We'll also reject lines starting with 1 space, because that's too fickle as a heuristic.
if (2..=8).contains(&space_indentation) {
@ -917,18 +896,10 @@ impl TextBuffer {
pub fn select_line(&mut self) {
let beg = self.cursor_move_to_logical_internal(
self.cursor,
Point {
x: 0,
y: self.cursor.logical_pos.y,
},
);
let end = self.cursor_move_to_logical_internal(
beg,
Point {
x: 0,
y: self.cursor.logical_pos.y + 1,
},
Point { x: 0, y: self.cursor.logical_pos.y },
);
let end = self
.cursor_move_to_logical_internal(beg, Point { x: 0, y: self.cursor.logical_pos.y + 1 });
self.set_cursor_for_selection(end);
self.set_selection(TextBufferSelection::Done {
beg: beg.logical_pos,
@ -1000,8 +971,7 @@ impl TextBuffer {
if self.selection_generation == search.selection_generation {
search.next_search_offset
} else {
self.cursor_move_to_logical_internal(self.cursor, beg.min(end))
.offset
self.cursor_move_to_logical_internal(self.cursor, beg.min(end)).offset
}
}
_ => self.cursor.offset,
@ -1209,16 +1179,10 @@ impl TextBuffer {
if self.word_wrap_column > 0 {
let upward = result.offset < cursor.offset;
let (top, bottom) = if upward {
(result, cursor)
} else {
(cursor, result)
};
let (top, bottom) = if upward { (result, cursor) } else { (cursor, result) };
let mut bottom_remeasured = self
.measurement_config()
.with_cursor(top)
.goto_logical(bottom.logical_pos);
let mut bottom_remeasured =
self.measurement_config().with_cursor(top).goto_logical(bottom.logical_pos);
// The second problem is that visual positions can be ambiguous. A single logical position
// can map to two visual positions: One at the end of the preceeding line in front of
@ -1274,9 +1238,7 @@ impl TextBuffer {
cursor = self.goto_line_start(cursor, cursor.logical_pos.y - 1);
}
self.measurement_config()
.with_cursor(cursor)
.goto_offset(offset)
self.measurement_config().with_cursor(cursor).goto_offset(offset)
}
fn cursor_move_to_logical_internal(
@ -1284,10 +1246,7 @@ impl TextBuffer {
mut cursor: ucd::UcdCursor,
pos: Point,
) -> ucd::UcdCursor {
let pos = Point {
x: pos.x.max(0),
y: pos.y.max(0),
};
let pos = Point { x: pos.x.max(0), y: pos.y.max(0) };
if pos == cursor.logical_pos {
return cursor;
@ -1300,9 +1259,7 @@ impl TextBuffer {
cursor = self.goto_line_start(cursor, pos.y);
}
self.measurement_config()
.with_cursor(cursor)
.goto_logical(pos)
self.measurement_config().with_cursor(cursor).goto_logical(pos)
}
fn cursor_move_to_visual_internal(
@ -1310,10 +1267,7 @@ impl TextBuffer {
mut cursor: ucd::UcdCursor,
pos: Point,
) -> ucd::UcdCursor {
let pos = Point {
x: pos.x.max(0),
y: pos.y.max(0),
};
let pos = Point { x: pos.x.max(0), y: pos.y.max(0) };
if pos == cursor.visual_pos {
return cursor;
@ -1336,9 +1290,7 @@ impl TextBuffer {
}
}
self.measurement_config()
.with_cursor(cursor)
.goto_visual(pos)
self.measurement_config().with_cursor(cursor).goto_visual(pos)
}
fn cursor_move_delta_internal(
@ -1362,10 +1314,7 @@ impl TextBuffer {
cursor = self.cursor_move_to_logical_internal(
cursor,
Point {
x: target_x,
y: cursor.logical_pos.y,
},
Point { x: target_x, y: cursor.logical_pos.y },
);
// We can stop if we ran out of remaining delta
@ -1381,10 +1330,7 @@ impl TextBuffer {
cursor = self.cursor_move_to_logical_internal(
cursor,
Point {
x: start_x,
y: cursor.logical_pos.y + sign,
},
Point { x: start_x, y: cursor.logical_pos.y + sign },
);
// We crossed a newline which counts for 1 grapheme cluster.
@ -1511,19 +1457,11 @@ impl TextBuffer {
line.clear();
let visual_line = origin.y + y;
let mut cursor_beg = self.cursor_move_to_visual_internal(
cursor,
Point {
x: origin.x,
y: visual_line,
},
);
let mut cursor_beg =
self.cursor_move_to_visual_internal(cursor, Point { x: origin.x, y: visual_line });
let cursor_end = self.cursor_move_to_visual_internal(
cursor_beg,
Point {
x: origin.x + text_width,
y: visual_line,
},
Point { x: origin.x + text_width, y: visual_line },
);
// Accelerate the next render pass by remembering where we started off.
@ -1542,12 +1480,7 @@ impl TextBuffer {
line.push_str(&MARGIN_TEMPLATE[off..]);
} else if self.word_wrap_column <= 0 || cursor_beg.logical_pos.x == 0 {
// Regular line? Place "123 | " in the margin.
_ = write!(
line,
"{:1$} │ ",
cursor_beg.logical_pos.y + 1,
line_number_width
);
_ = write!(line, "{:1$} │ ", cursor_beg.logical_pos.y + 1, line_number_width);
} else {
// Wrapped line? Place " ... | " in the margin.
let number_width = (cursor_beg.logical_pos.y + 1).ilog10() as usize + 1;
@ -1562,12 +1495,7 @@ impl TextBuffer {
let left = destination.left;
let top = destination.top + y;
fb.blend_fg(
Rect {
left,
top,
right: left + line_number_width as i32,
bottom: top + 1,
},
Rect { left, top, right: left + line_number_width as i32, bottom: top + 1 },
fb.indexed(IndexedColor::Background),
);
}
@ -1581,10 +1509,7 @@ impl TextBuffer {
if cursor_beg.visual_pos.x < origin.x {
let cursor_next = self.cursor_move_to_logical_internal(
cursor_beg,
Point {
x: cursor_beg.logical_pos.x + 1,
y: cursor_beg.logical_pos.y,
},
Point { x: cursor_beg.logical_pos.x + 1, y: cursor_beg.logical_pos.y },
);
if cursor_next.visual_pos.x > origin.x {
@ -1662,12 +1587,7 @@ impl TextBuffer {
visual_pos_x_max = visual_pos_x_max.max(cursor_end.visual_pos.x);
}
fb.replace_text(
destination.top + y,
destination.left,
destination.right,
&line,
);
fb.replace_text(destination.top + y, destination.left, destination.right, &line);
// Draw the selection on this line, if any.
// FYI: `cursor_beg.visual_pos.y == visual_line` is necessary as the `visual_line`
@ -1702,12 +1622,7 @@ impl TextBuffer {
let left = destination.left + self.margin_width - origin.x;
let top = destination.top + y;
let rect = Rect {
left: left + beg,
top,
right: left + end,
bottom: top + 1,
};
let rect = Rect { left: left + beg, top, right: left + end, bottom: top + 1 };
let mut bg = alpha_blend(
fb.indexed(IndexedColor::Foreground),
@ -1740,12 +1655,7 @@ impl TextBuffer {
let right = destination.right;
if left < right {
fb.blend_bg(
Rect {
left,
top: destination.top,
right,
bottom: destination.bottom,
},
Rect { left, top: destination.top, right, bottom: destination.bottom },
fb.indexed_alpha(IndexedColor::BrightRed, 0x1f),
);
}
@ -1849,10 +1759,7 @@ impl TextBuffer {
let delete = self.cursor.logical_pos.x - column_before;
let end = self.cursor_move_to_logical_internal(
self.cursor,
Point {
x: self.cursor.logical_pos.x + delete,
y: self.cursor.logical_pos.y,
},
Point { x: self.cursor.logical_pos.x + delete, y: self.cursor.logical_pos.y },
);
self.edit_delete(end);
}
@ -1986,17 +1893,10 @@ impl TextBuffer {
let [beg, end] = helpers::minmax(selection_beg, selection_end);
let beg = self.cursor_move_to_logical_internal(self.cursor, Point { x: 0, y: beg.y });
let end = self.cursor_move_to_logical_internal(
beg,
Point {
x: CoordType::MAX,
y: end.y,
},
);
let end = self.cursor_move_to_logical_internal(beg, Point { x: CoordType::MAX, y: end.y });
let mut replacement = Vec::new();
self.buffer
.extract_raw(beg.offset, end.offset, &mut replacement, 0);
self.buffer.extract_raw(beg.offset, end.offset, &mut replacement, 0);
let initial_len = replacement.len();
let mut offset = 0;
@ -2099,14 +1999,8 @@ impl TextBuffer {
let [beg, end] = match self.selection {
TextBufferSelection::None if !line_fallback => return None,
TextBufferSelection::None => [
Point {
x: 0,
y: self.cursor.logical_pos.y,
},
Point {
x: 0,
y: self.cursor.logical_pos.y + 1,
},
Point { x: 0, y: self.cursor.logical_pos.y },
Point { x: 0, y: self.cursor.logical_pos.y + 1 },
],
TextBufferSelection::Active { beg, end } | TextBufferSelection::Done { beg, end } => {
helpers::minmax(beg, end)
@ -2116,11 +2010,7 @@ impl TextBuffer {
let beg = self.cursor_move_to_logical_internal(self.cursor, beg);
let end = self.cursor_move_to_logical_internal(beg, end);
if beg.offset < end.offset {
Some((beg, end))
} else {
None
}
if beg.offset < end.offset { Some((beg, end)) } else { None }
}
fn edit_begin(&mut self, history_type: HistoryType, cursor: ucd::UcdCursor) {
@ -2163,10 +2053,7 @@ impl TextBuffer {
let safe_start = self.goto_line_start(cursor, cursor.logical_pos.y);
let next_line = self.cursor_move_to_logical_internal(
cursor,
Point {
x: 0,
y: cursor.logical_pos.y + 1,
},
Point { x: 0, y: cursor.logical_pos.y + 1 },
);
self.active_edit_line_info = Some(ActiveEditLineInfo {
safe_start,
@ -2187,9 +2074,7 @@ impl TextBuffer {
// Write!
{
let gap = self
.buffer
.allocate_gap(self.active_edit_off, text.len(), 0);
let gap = self.buffer.allocate_gap(self.active_edit_off, text.len(), 0);
gap.copy_from_slice(text);
self.buffer.commit_gap(text.len());
}
@ -2240,13 +2125,7 @@ impl TextBuffer {
}
if let Some(info) = self.active_edit_line_info.take() {
let deleted_count = self
.undo_stack
.back_mut()
.unwrap()
.borrow_mut()
.deleted
.len();
let deleted_count = self.undo_stack.back_mut().unwrap().borrow_mut().deleted.len();
let target = self.cursor.logical_pos;
// From our safe position we can measure the actual visual position of the cursor.
@ -2261,13 +2140,8 @@ impl TextBuffer {
// the entire buffer contents until the end to compute `self.stats.visual_lines`.
if deleted_count < info.distance_next_line_start {
// Now we can measure how many more visual rows this logical line spans.
let next_line = self.cursor_move_to_logical_internal(
self.cursor,
Point {
x: 0,
y: target.y + 1,
},
);
let next_line = self
.cursor_move_to_logical_internal(self.cursor, Point { x: 0, y: target.y + 1 });
let lines_before = info.line_height_in_rows;
let lines_after = next_line.visual_pos.y - info.safe_start.visual_pos.y;
self.stats.visual_lines += lines_after - lines_before;
@ -2311,11 +2185,7 @@ impl TextBuffer {
}
let change = {
let to = if undo {
&self.redo_stack
} else {
&self.undo_stack
};
let to = if undo { &self.redo_stack } else { &self.undo_stack };
to.back().unwrap()
};
@ -2340,9 +2210,7 @@ impl TextBuffer {
// Delete the inserted portion and reinsert the deleted portion.
let deleted = change.deleted.len();
let added = &change.added[..];
let gap = self
.buffer
.allocate_gap(cursor.offset, added.len(), deleted);
let gap = self.buffer.allocate_gap(cursor.offset, added.len(), deleted);
gap.copy_from_slice(added);
self.buffer.commit_gap(added.len());

View file

@ -1,12 +1,13 @@
use crate::arena::{Arena, ArenaString};
use crate::helpers::{CoordType, Point, Rect, Size};
use crate::simd::{MemsetSafe, memset};
use crate::ucd;
use std::fmt::Write;
use std::ops::{BitOr, BitXor};
use std::ptr;
use std::slice::ChunksExact;
use crate::arena::{Arena, ArenaString};
use crate::helpers::{CoordType, Point, Rect, Size};
use crate::simd::{MemsetSafe, memset};
use crate::ucd;
#[derive(Clone, Copy)]
pub enum IndexedColor {
Black,
@ -201,21 +202,16 @@ impl Framebuffer {
let mut fract_buf = [0xE2, 0x96, 0x88];
if top_fract != 0 {
fract_buf[2] = (0x88 - top_fract) as u8;
self.replace_text(
thumb_top - 1,
track_clipped.left,
track_clipped.right,
unsafe { std::str::from_utf8_unchecked(&fract_buf) },
);
self.replace_text(thumb_top - 1, track_clipped.left, track_clipped.right, unsafe {
std::str::from_utf8_unchecked(&fract_buf)
});
}
if bottom_fract != 0 {
fract_buf[2] = (0x88 - bottom_fract) as u8;
let rect = self.replace_text(
thumb_bottom,
track_clipped.left,
track_clipped.right,
unsafe { std::str::from_utf8_unchecked(&fract_buf) },
);
let rect =
self.replace_text(thumb_bottom, track_clipped.left, track_clipped.right, unsafe {
std::str::from_utf8_unchecked(&fract_buf)
});
self.blend_bg(rect, self.indexed(IndexedColor::BrightWhite));
self.blend_fg(rect, self.indexed(IndexedColor::BrightBlack));
}
@ -400,12 +396,7 @@ impl Framebuffer {
}
let beg = cfg.cursor().offset;
let end = cfg
.goto_visual(Point {
x: chunk_end as CoordType,
y: 0,
})
.offset;
let end = cfg.goto_visual(Point { x: chunk_end as CoordType, y: 0 }).offset;
result.push_str(&back_line[beg..end]);
chunk_end < back_bg.len()
@ -458,10 +449,7 @@ struct LineBuffer {
impl LineBuffer {
fn new(size: Size) -> Self {
Self {
lines: vec![String::new(); size.height as usize],
size,
}
Self { lines: vec![String::new(); size.height as usize], size }
}
fn fill_whitespace(&mut self) {
@ -513,10 +501,7 @@ impl LineBuffer {
if left < 0 && cursor.offset < text.len() {
// `-left` must've intersected a wide glyph and since goto_visual stops _before_ reaching the target,
// we stoped before the wide glyph and thus must step forward to the next glyph.
let cursor = cfg.goto_logical(Point {
x: cursor.logical_pos.x + 1,
y: 0,
});
let cursor = cfg.goto_logical(Point { x: cursor.logical_pos.x + 1, y: 0 });
left += cursor.visual_pos.x;
}
}
@ -528,10 +513,7 @@ impl LineBuffer {
}
// Measure the width of the new text (= `res_new.visual_target.x`).
let res_new = cfg.goto_visual(Point {
x: layout_width,
y: 0,
});
let res_new = cfg.goto_visual(Point { x: layout_width, y: 0 });
// Figure out at which byte offset the new text gets inserted.
let right = left + res_new.visual_pos.x;
@ -543,10 +525,7 @@ impl LineBuffer {
// Since the goto functions will always stop short of the target position,
// we need to manually step beyond it if we intersect with a wide glyph.
if res_old_end.visual_pos.x < right {
res_old_end = cfg_old.goto_logical(Point {
x: res_old_end.logical_pos.x + 1,
y: 0,
});
res_old_end = cfg_old.goto_logical(Point { x: res_old_end.logical_pos.x + 1, y: 0 });
}
// If we intersect a wide glyph, we need to pad the new text with spaces.
@ -607,12 +586,7 @@ impl LineBuffer {
dst.set_len(dst_len - total_del + total_add);
}
Rect {
left,
top: y,
right,
bottom: y + 1,
}
Rect { left, top: y, right, bottom: y + 1 }
}
}
@ -624,10 +598,7 @@ struct Bitmap {
impl Bitmap {
fn new(size: Size) -> Self {
Self {
data: vec![0; (size.width * size.height) as usize],
size,
}
Self { data: vec![0; (size.width * size.height) as usize], size }
}
fn fill(&mut self, color: u32) {
@ -809,10 +780,7 @@ struct AttributeBuffer {
impl AttributeBuffer {
fn new(size: Size) -> Self {
Self {
data: vec![Default::default(); (size.width * size.height) as usize],
size,
}
Self { data: vec![Default::default(); (size.width * size.height) as usize], size }
}
fn reset(&mut self) {
@ -881,16 +849,10 @@ struct Cursor {
impl Cursor {
const fn new_invalid() -> Self {
Self {
pos: Point::MIN,
overtype: false,
}
Self { pos: Point::MIN, overtype: false }
}
const fn new_disabled() -> Self {
Self {
pos: Point { x: -1, y: -1 },
overtype: false,
}
Self { pos: Point { x: -1, y: -1 }, overtype: false }
}
}

View file

@ -1,11 +1,10 @@
use crate::apperr;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::io::Read;
use std::mem::{self, MaybeUninit};
use std::ptr;
use std::slice;
use std::str;
use std::{ptr, slice, str};
use crate::apperr;
pub type CoordType = i32;
@ -19,14 +18,8 @@ pub struct Point {
}
impl Point {
pub const MIN: Point = Point {
x: CoordType::MIN,
y: CoordType::MIN,
};
pub const MAX: Point = Point {
x: CoordType::MAX,
y: CoordType::MAX,
};
pub const MIN: Point = Point { x: CoordType::MIN, y: CoordType::MIN };
pub const MAX: Point = Point { x: CoordType::MAX, y: CoordType::MAX };
}
impl PartialOrd<Point> for Point {
@ -52,12 +45,7 @@ pub struct Size {
impl Size {
pub fn as_rect(&self) -> Rect {
Rect {
left: 0,
top: 0,
right: self.width,
bottom: self.height,
}
Rect { left: 0, top: 0, right: self.width, bottom: self.height }
}
}
@ -71,30 +59,15 @@ pub struct Rect {
impl Rect {
pub fn one(value: CoordType) -> Self {
Self {
left: value,
top: value,
right: value,
bottom: value,
}
Self { left: value, top: value, right: value, bottom: value }
}
pub fn two(top_bottom: CoordType, left_right: CoordType) -> Self {
Self {
left: left_right,
top: top_bottom,
right: left_right,
bottom: top_bottom,
}
Self { left: left_right, top: top_bottom, right: left_right, bottom: top_bottom }
}
pub fn three(top: CoordType, left_right: CoordType, bottom: CoordType) -> Self {
Self {
left: left_right,
top,
right: left_right,
bottom,
}
Self { left: left_right, top, right: left_right, bottom }
}
pub fn is_empty(&self) -> bool {
@ -124,12 +97,7 @@ impl Rect {
let r = l.max(r);
let b = t.max(b);
Rect {
left: l,
top: t,
right: r,
bottom: b,
}
Rect { left: l, top: t, right: r, bottom: b }
}
}

View file

@ -1,13 +1,14 @@
use crate::arena::{Arena, ArenaString, scratch_arena};
use crate::buffer::TextBuffer;
use crate::utf8::Utf8Chars;
use crate::{apperr, sys};
use std::ffi::CStr;
use std::mem::MaybeUninit;
use std::ops::Range;
use std::ptr::{null, null_mut};
use std::{cmp, mem};
use crate::arena::{Arena, ArenaString, scratch_arena};
use crate::buffer::TextBuffer;
use crate::utf8::Utf8Chars;
use crate::{apperr, sys};
static mut ENCODINGS: Vec<&'static str> = Vec::new();
pub fn get_available_encodings() -> &'static [&'static str] {
@ -104,14 +105,7 @@ impl<'pivot> Converter<'pivot> {
let pivot_source = pivot_buffer.as_mut_ptr() as *mut u16;
let pivot_target = unsafe { pivot_source.add(pivot_buffer.len()) };
Ok(Self {
source,
target,
pivot_buffer,
pivot_source,
pivot_target,
reset: true,
})
Ok(Self { source, target, pivot_buffer, pivot_source, pivot_target, reset: true })
}
fn append_nul<'a>(arena: &'a Arena, input: &str) -> ArenaString<'a> {
@ -425,10 +419,7 @@ fn utext_access_impl<'a>(
// When it comes to performance, and the search space is small (which it is here),
// it's always a good idea to keep the loops small and tight...
let len = haystack
.iter()
.position(|&c| c >= 0x80)
.unwrap_or(haystack.len());
let len = haystack.iter().position(|&c| c >= 0x80).unwrap_or(haystack.len());
// ...In this case it allows the compiler to vectorize this loop and double
// the performance. Luckily, llvm doesn't unroll the loop, which is great,
@ -844,10 +835,9 @@ fn init_if_needed() -> apperr::Result<&'static LibraryFunctions> {
#[cfg(unix)]
let suffix = sys::icu_proc_suffix(&scratch_outer, libicuuc);
for (handle, names) in [
(libicuuc, &LIBICUUC_PROC_NAMES[..]),
(libicui18n, &LIBICUI18N_PROC_NAMES[..]),
] {
for (handle, names) in
[(libicuuc, &LIBICUUC_PROC_NAMES[..]), (libicui18n, &LIBICUI18N_PROC_NAMES[..])]
{
for name in names {
#[cfg(unix)]
let scratch = scratch_arena(Some(&scratch_outer));
@ -895,9 +885,10 @@ fn assume_loaded() -> &'static LibraryFunctions {
mod icu_ffi {
#![allow(dead_code, non_camel_case_types)]
use crate::apperr;
use std::ffi::{c_char, c_int, c_void};
use crate::apperr;
#[derive(Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct UErrorCode(c_int);

View file

@ -260,10 +260,7 @@ impl Parser {
&'parser mut self,
stream: vt::Stream<'vt, 'input>,
) -> Stream<'parser, 'vt, 'input> {
Stream {
parser: self,
stream,
}
Stream { parser: self, stream }
}
}
@ -289,10 +286,7 @@ impl<'input> Stream<'_, '_, 'input> {
match self.stream.next()? {
vt::Token::Text(text) => {
return Some(Input::Text(InputText {
text,
bracketed: false,
}));
return Some(Input::Text(InputText { text, bracketed: false }));
}
vt::Token::Ctrl(ch) => match ch {
'\0' | '\t' | '\r' => return Some(Input::Keyboard(InputKey::new(ch as u32))),
@ -312,11 +306,8 @@ impl<'input> Stream<'_, '_, 'input> {
' '..='~' => {
let ch = ch as u32;
let key = ch & !0x20; // Shift a-z to A-Z
let modifiers = if (ch & 0x20) != 0 {
kbmod::ALT
} else {
kbmod::ALT_SHIFT
};
let modifiers =
if (ch & 0x20) != 0 { kbmod::ALT } else { kbmod::ALT_SHIFT };
return Some(Input::Keyboard(modifiers | InputKey::new(key)));
}
_ => {}
@ -426,21 +417,12 @@ impl<'input> Stream<'_, '_, 'input> {
}
mouse.modifiers = kbmod::NONE;
mouse.modifiers |= if (btn & 0x04) != 0 {
kbmod::SHIFT
} else {
kbmod::NONE
};
mouse.modifiers |= if (btn & 0x08) != 0 {
kbmod::ALT
} else {
kbmod::NONE
};
mouse.modifiers |= if (btn & 0x10f) != 0 {
kbmod::CTRL
} else {
kbmod::NONE
};
mouse.modifiers |=
if (btn & 0x04) != 0 { kbmod::SHIFT } else { kbmod::NONE };
mouse.modifiers |=
if (btn & 0x08) != 0 { kbmod::ALT } else { kbmod::NONE };
mouse.modifiers |=
if (btn & 0x10f) != 0 { kbmod::CTRL } else { kbmod::NONE };
mouse.position.x = csi.params[1] - 1;
mouse.position.y = csi.params[2] - 1;
@ -480,10 +462,7 @@ impl<'input> Stream<'_, '_, 'input> {
if end != beg {
let input = self.stream.input();
Some(Input::Text(InputText {
text: &input[beg..end],
bracketed: true,
}))
Some(Input::Text(InputText { text: &input[beg..end], bracketed: true }))
} else {
None
}
@ -498,9 +477,8 @@ impl<'input> Stream<'_, '_, 'input> {
/// This is so puzzling to me. The existence of this function makes me unhappy.
#[cold]
fn parse_x10_mouse_coordinates(&mut self) -> Option<Input<'input>> {
self.parser.x10_mouse_len += self
.stream
.read(&mut self.parser.x10_mouse_buf[self.parser.x10_mouse_len..]);
self.parser.x10_mouse_len +=
self.stream.read(&mut self.parser.x10_mouse_buf[self.parser.x10_mouse_len..]);
if self.parser.x10_mouse_len < 3 {
return None;
}

View file

@ -1,9 +1,10 @@
//! Rust has a very popular `memchr` crate. It's quite fast, so you may ask yourself
//! why we don't just use it: Simply put, this is optimized for short inputs.
use super::distance;
use std::ptr;
use super::distance;
/// memchr(), but with two needles.
/// Returns the index of the first occurrence of either needle in the `haystack`.
/// If no needle is found, `haystack.len()` is returned.
@ -58,11 +59,7 @@ static mut MEMCHR2_DISPATCH: unsafe fn(
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
unsafe fn memchr2_dispatch(needle1: u8, needle2: u8, beg: *const u8, end: *const u8) -> *const u8 {
let func = if is_x86_feature_detected!("avx2") {
memchr2_avx2
} else {
memchr2_fallback
};
let func = if is_x86_feature_detected!("avx2") { memchr2_avx2 } else { memchr2_fallback };
unsafe { MEMCHR2_DISPATCH = func };
unsafe { func(needle1, needle2, beg, end) }
}
@ -136,9 +133,10 @@ unsafe fn memchr2_neon(needle1: u8, needle2: u8, mut beg: *const u8, end: *const
#[cfg(test)]
mod tests {
use std::slice;
use super::*;
use crate::sys;
use std::slice;
#[test]
fn test_empty() {

View file

@ -1,9 +1,10 @@
//! Rust has a very popular `memchr` crate. It's quite fast, so you may ask yourself
//! why we don't just use it: Simply put, this is optimized for short inputs.
use super::distance;
use std::ptr;
use super::distance;
/// Same as `memchr2`, but searches from the end of the haystack.
/// If no needle is found, 0 is returned.
///
@ -15,11 +16,7 @@ pub fn memrchr2(needle1: u8, needle2: u8, haystack: &[u8], offset: usize) -> Opt
let beg = haystack.as_ptr();
let it = beg.add(offset.min(haystack.len()));
let it = memrchr2_raw(needle1, needle2, beg, it);
if it.is_null() {
None
} else {
Some(distance(it, beg))
}
if it.is_null() { None } else { Some(distance(it, beg)) }
}
}
@ -62,11 +59,7 @@ static mut MEMRCHR2_DISPATCH: unsafe fn(
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
unsafe fn memrchr2_dispatch(needle1: u8, needle2: u8, beg: *const u8, end: *const u8) -> *const u8 {
let func = if is_x86_feature_detected!("avx2") {
memrchr2_avx2
} else {
memrchr2_fallback
};
let func = if is_x86_feature_detected!("avx2") { memrchr2_avx2 } else { memrchr2_fallback };
unsafe { MEMRCHR2_DISPATCH = func };
unsafe { func(needle1, needle2, beg, end) }
}
@ -143,9 +136,10 @@ unsafe fn memrchr2_neon(needle1: u8, needle2: u8, beg: *const u8, mut end: *cons
#[cfg(test)]
mod tests {
use std::slice;
use super::*;
use crate::sys;
use std::slice;
#[test]
fn test_empty() {

View file

@ -6,9 +6,10 @@
//! types into a larger `u64` register we can treat all sizes as if they were `u64`. The only thing we need
//! to take care of then, is the tail end of the array, where we need to write 0-7 additional bytes.
use super::distance;
use std::mem;
use super::distance;
/// A trait to mark types that are safe to use with `memset`.
///
/// # Safety
@ -44,21 +45,13 @@ pub fn memset<T: MemsetSafe>(dst: &mut [T], val: T) {
let beg = dst.as_mut_ptr();
let end = beg.add(dst.len());
let val = mem::transmute_copy::<_, u16>(&val);
memset_raw(
beg as *mut u8,
end as *mut u8,
val as u64 * 0x0001000100010001,
);
memset_raw(beg as *mut u8, end as *mut u8, val as u64 * 0x0001000100010001);
}
4 => {
let beg = dst.as_mut_ptr();
let end = beg.add(dst.len());
let val = mem::transmute_copy::<_, u32>(&val);
memset_raw(
beg as *mut u8,
end as *mut u8,
val as u64 * 0x0000000100000001,
);
memset_raw(beg as *mut u8, end as *mut u8, val as u64 * 0x0000000100000001);
}
8 => {
let beg = dst.as_mut_ptr();
@ -85,11 +78,7 @@ static mut MEMSET_DISPATCH: unsafe fn(beg: *mut u8, end: *mut u8, val: u64) = me
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn memset_dispatch(beg: *mut u8, end: *mut u8, val: u64) {
let func = if is_x86_feature_detected!("avx2") {
memset_avx2
} else {
memset_sse2
};
let func = if is_x86_feature_detected!("avx2") { memset_avx2 } else { memset_sse2 };
unsafe { MEMSET_DISPATCH = func };
unsafe { func(beg, end, val) }
}
@ -253,10 +242,11 @@ unsafe fn memset_neon(mut beg: *mut u8, end: *mut u8, val: u64) {
#[cfg(test)]
mod tests {
use super::*;
use std::fmt;
use std::ops::Not;
use super::*;
fn check_memset<T>(val: T, len: usize)
where
T: MemsetSafe + Not<Output = T> + PartialEq + fmt::Debug,

View file

@ -3,10 +3,10 @@ mod unix;
#[cfg(windows)]
mod windows;
#[cfg(not(windows))]
pub use std::fs::canonicalize;
#[cfg(unix)]
pub use unix::*;
#[cfg(windows)]
pub use windows::*;
#[cfg(not(windows))]
pub use std::fs::canonicalize;

View file

@ -1,12 +1,12 @@
use crate::apperr;
use crate::arena::{Arena, ArenaString};
use std::ffi::{CStr, c_int, c_void};
use std::fs::{self, File};
use std::mem::{self, MaybeUninit};
use std::os::fd::FromRawFd;
use std::ptr::{self, NonNull, null, null_mut};
use std::thread;
use std::time;
use std::{thread, time};
use crate::apperr;
use crate::arena::{Arena, ArenaString};
struct State {
stdin: libc::c_int,
@ -68,11 +68,7 @@ pub fn switch_modes() -> apperr::Result<()> {
// Set STATE.inject_resize to true whenever we get a SIGWINCH.
let mut sigwinch_action: libc::sigaction = mem::zeroed();
sigwinch_action.sa_sigaction = sigwinch_handler as libc::sighandler_t;
check_int_return(libc::sigaction(
libc::SIGWINCH,
&sigwinch_action,
null_mut(),
))?;
check_int_return(libc::sigaction(libc::SIGWINCH, &sigwinch_action, null_mut()))?;
// Get the original terminal modes so we can disable raw mode on exit.
let mut termios = MaybeUninit::<libc::termios>::uninit();
@ -189,11 +185,7 @@ pub fn read_stdin(arena: &Arena, mut timeout: time::Duration) -> Option<ArenaStr
if timeout != time::Duration::MAX {
let beg = time::Instant::now();
let mut pollfd = libc::pollfd {
fd: STATE.stdin,
events: libc::POLLIN,
revents: 0,
};
let mut pollfd = libc::pollfd { fd: STATE.stdin, events: libc::POLLIN, revents: 0 };
let ts = libc::timespec {
tv_sec: timeout.as_secs() as libc::time_t,
tv_nsec: timeout.subsec_nanos() as libc::c_long,
@ -361,16 +353,8 @@ pub unsafe fn virtual_release(base: NonNull<u8>, size: usize) {
/// and to pass a size less than or equal to the size passed to `virtual_reserve`.
pub unsafe fn virtual_commit(base: NonNull<u8>, size: usize) -> apperr::Result<()> {
unsafe {
let status = libc::mprotect(
base.cast().as_ptr(),
size,
libc::PROT_READ | libc::PROT_WRITE,
);
if status != 0 {
Err(errno_to_apperr(libc::ENOMEM))
} else {
Ok(())
}
let status = libc::mprotect(base.cast().as_ptr(), size, libc::PROT_READ | libc::PROT_WRITE);
if status != 0 { Err(errno_to_apperr(libc::ENOMEM)) } else { Ok(()) }
}
}
@ -498,9 +482,7 @@ pub fn preferred_languages(arena: &Arena) -> Vec<ArenaString<'_>, &Arena> {
for key in ["LANGUAGE", "LC_ALL", "LANG"] {
if let Ok(val) = std::env::var(key) {
locales.extend(
val.split(':')
.filter(|s| !s.is_empty())
.map(|s| ArenaString::from_str(arena, s)),
val.split(':').filter(|s| !s.is_empty()).map(|s| ArenaString::from_str(arena, s)),
);
}
}
@ -536,9 +518,5 @@ const fn errno_to_apperr(no: c_int) -> apperr::Error {
}
fn check_int_return(ret: libc::c_int) -> apperr::Result<libc::c_int> {
if ret < 0 {
Err(errno_to_apperr(unsafe { *libc::__errno_location() }))
} else {
Ok(ret)
}
if ret < 0 { Err(errno_to_apperr(unsafe { *libc::__errno_location() })) } else { Ok(ret) }
}

View file

@ -1,6 +1,3 @@
use crate::arena::{Arena, ArenaString, scratch_arena};
use crate::helpers::{CoordType, Size};
use crate::{apperr, helpers};
use std::ffi::{CStr, OsString, c_void};
use std::fmt::Write as _;
use std::fs::{self, File};
@ -9,17 +6,17 @@ use std::os::windows::io::FromRawHandle;
use std::path::{Path, PathBuf};
use std::ptr::{self, NonNull, null, null_mut};
use std::{mem, time};
use windows_sys::Win32::Foundation;
use windows_sys::Win32::Globalization;
use windows_sys::Win32::Storage::FileSystem;
use windows_sys::Win32::System::Console;
use windows_sys::Win32::System::Diagnostics::Debug;
use windows_sys::Win32::System::IO;
use windows_sys::Win32::System::LibraryLoader;
use windows_sys::Win32::System::Memory;
use windows_sys::Win32::System::Threading;
use windows_sys::Win32::System::{Console, IO, LibraryLoader, Memory, Threading};
use windows_sys::Win32::{Foundation, Globalization};
use windows_sys::w;
use crate::arena::{Arena, ArenaString, scratch_arena};
use crate::helpers::{CoordType, Size};
use crate::{apperr, helpers};
type ReadConsoleInputExW = unsafe extern "system" fn(
h_console_input: Foundation::HANDLE,
lp_buffer: *mut Console::INPUT_RECORD,
@ -156,21 +153,12 @@ impl Drop for Deinit {
pub fn switch_modes() -> apperr::Result<()> {
unsafe {
check_bool_return(Console::SetConsoleCtrlHandler(
Some(console_ctrl_handler),
1,
))?;
check_bool_return(Console::SetConsoleCtrlHandler(Some(console_ctrl_handler), 1))?;
STATE.stdin_cp_old = Console::GetConsoleCP();
STATE.stdout_cp_old = Console::GetConsoleOutputCP();
check_bool_return(Console::GetConsoleMode(
STATE.stdin,
&raw mut STATE.stdin_mode_old,
))?;
check_bool_return(Console::GetConsoleMode(
STATE.stdout,
&raw mut STATE.stdout_mode_old,
))?;
check_bool_return(Console::GetConsoleMode(STATE.stdin, &raw mut STATE.stdin_mode_old))?;
check_bool_return(Console::GetConsoleMode(STATE.stdout, &raw mut STATE.stdout_mode_old))?;
check_bool_return(Console::SetConsoleCP(Globalization::CP_UTF8))?;
check_bool_return(Console::SetConsoleOutputCP(Globalization::CP_UTF8))?;
@ -208,10 +196,7 @@ fn get_console_size() -> Option<Size> {
let w = (info.srWindow.Right - info.srWindow.Left + 1).max(1) as CoordType;
let h = (info.srWindow.Bottom - info.srWindow.Top + 1).max(1) as CoordType;
Some(Size {
width: w,
height: h,
})
Some(Size { width: w, height: h })
}
}
@ -301,10 +286,7 @@ pub fn read_stdin(arena: &Arena, mut timeout: time::Duration) -> Option<ArenaStr
// Windows is prone to sending broken/useless `WINDOW_BUFFER_SIZE_EVENT`s.
// E.g. starting conhost will emit 3 in a row. Skip rendering in that case.
if w > 0 && h > 0 {
resize_event = Some(Size {
width: w,
height: h,
});
resize_event = Some(Size { width: w, height: h });
}
}
_ => {}
@ -317,11 +299,7 @@ pub fn read_stdin(arena: &Arena, mut timeout: time::Duration) -> Option<ArenaStr
}
const RESIZE_EVENT_FMT_MAX_LEN: usize = 16; // "\x1b[8;65535;65535t"
let resize_event_len = if resize_event.is_some() {
RESIZE_EVENT_FMT_MAX_LEN
} else {
0
};
let resize_event_len = if resize_event.is_some() { RESIZE_EVENT_FMT_MAX_LEN } else { 0 };
// +1 to account for a potential `STATE.leading_surrogate`.
let utf8_max_len = (utf16_buf_len + 1) * 3;
let mut text = arena.new_string();
@ -332,11 +310,7 @@ pub fn read_stdin(arena: &Arena, mut timeout: time::Duration) -> Option<ArenaStr
// If I read xterm's documentation correctly, CSI 18 t reports the window size in characters.
// CSI 8 ; height ; width t is the response. Of course, we didn't send the request,
// but we can use this fake response to trigger the editor to resize itself.
_ = write!(
text,
"\x1b[8;{};{}t",
resize_event.height, resize_event.width
);
_ = write!(text, "\x1b[8;{};{}t", resize_event.height, resize_event.width);
}
// If the input ends with a lone lead surrogate, we need to remember it for the next read.
@ -398,11 +372,7 @@ pub fn open_stdin_if_redirected() -> Option<File> {
unsafe {
let handle = Console::GetStdHandle(Console::STD_INPUT_HANDLE);
// Did we reopen stdin during `init()`?
if !std::ptr::eq(STATE.stdin, handle) {
Some(File::from_raw_handle(handle))
} else {
None
}
if !std::ptr::eq(STATE.stdin, handle) { Some(File::from_raw_handle(handle)) } else { None }
}
}
@ -506,11 +476,7 @@ unsafe fn load_library(name: *const u16) -> apperr::Result<NonNull<c_void>> {
pub unsafe fn get_proc_address<T>(handle: NonNull<c_void>, name: &CStr) -> apperr::Result<T> {
unsafe {
let ptr = LibraryLoader::GetProcAddress(handle.as_ptr(), name.as_ptr() as *const u8);
if let Some(ptr) = ptr {
Ok(mem::transmute_copy(&ptr))
} else {
Err(get_last_error())
}
if let Some(ptr) = ptr { Ok(mem::transmute_copy(&ptr)) } else { Err(get_last_error()) }
}
}
@ -601,11 +567,7 @@ fn get_last_error() -> apperr::Error {
#[inline]
const fn gle_to_apperr(gle: u32) -> apperr::Error {
apperr::Error::new_sys(if gle == 0 {
0x8000FFFF
} else {
0x80070000 | gle
})
apperr::Error::new_sys(if gle == 0 { 0x8000FFFF } else { 0x80070000 | gle })
}
#[inline]
@ -647,11 +609,7 @@ pub fn apperr_is_not_found(err: apperr::Error) -> bool {
}
fn check_bool_return(ret: Foundation::BOOL) -> apperr::Result<()> {
if ret == 0 {
Err(get_last_error())
} else {
Ok(())
}
if ret == 0 { Err(get_last_error()) } else { Ok(()) }
}
fn check_ptr_return<T>(ret: *mut T) -> apperr::Result<NonNull<T>> {

View file

@ -1,3 +1,9 @@
use std::arch::breakpoint;
#[cfg(debug_assertions)]
use std::collections::HashSet;
use std::fmt::Write as _;
use std::{iter, mem, ptr, time};
use crate::arena::{Arena, ArenaString, scratch_arena};
use crate::buffer::{CursorMovement, RcTextBuffer, TextBuffer, TextBufferCell};
use crate::cell::*;
@ -6,15 +12,6 @@ use crate::helpers::{CoordType, Point, Rect, Size, hash, hash_str, opt_ptr_eq, w
use crate::input::{InputKeyMod, kbmod, vk};
use crate::ucd::Document;
use crate::{apperr, helpers, input, ucd};
use std::arch::breakpoint;
use std::fmt::Write as _;
use std::iter;
use std::mem;
use std::ptr;
use std::time;
#[cfg(debug_assertions)]
use std::collections::HashSet;
const ROOT_ID: u64 = 0x14057B7EF767814F; // Knuth's MMIX constant
const SHIFT_TAB: InputKey = vk::TAB.with_modifiers(kbmod::SHIFT);
@ -105,10 +102,7 @@ impl Tui {
modal_default_bg: 0,
modal_default_fg: 0,
size: Size {
width: 0,
height: 0,
},
size: Size { width: 0, height: 0 },
mouse_position: Point::MIN,
mouse_down_position: Point::MIN,
left_mouse_down_target: 0,
@ -593,8 +587,7 @@ impl Tui {
}
}
self.framebuffer
.replace_attr(outer_clipped, Attributes::All, Attributes::None);
self.framebuffer.replace_attr(outer_clipped, Attributes::All, Attributes::None);
}
self.framebuffer.blend_bg(outer_clipped, node.attributes.bg);
@ -655,20 +648,15 @@ impl Tui {
let mut end = cfg.goto_visual(Point { x: mid_end_x, y: 0 });
if end.visual_pos.x < mid_end_x {
// If we intersected a wide glyph, we need to move past that.
end = cfg.goto_logical(Point {
x: end.logical_pos.x + 1,
y: 0,
});
end =
cfg.goto_logical(Point { x: end.logical_pos.x + 1, y: 0 });
}
modified.push_str(&text[..beg.offset]);
modified.push('…');
modified.push_str(&text[end.offset..]);
}
Overflow::TruncateTail => {
let end = cfg.goto_visual(Point {
x: restricted_width - 1,
y: 0,
});
let end = cfg.goto_visual(Point { x: restricted_width - 1, y: 0 });
modified.push_str(&text[..end.offset]);
modified.push('…');
}
@ -712,12 +700,9 @@ impl Tui {
destination.right -= 1;
}
if let Some(res) = tb.render(
tc.scroll_offset,
destination,
tc.has_focus,
&mut self.framebuffer,
) {
if let Some(res) =
tb.render(tc.scroll_offset, destination, tc.has_focus, &mut self.framebuffer)
{
tc.scroll_offset_x_max = res.visual_pos_x_max;
}
@ -836,11 +821,7 @@ impl Tui {
_ = write!(
result,
" text: \"{}\"\r\n",
content
.chunks
.iter()
.map(|c| c.text.as_str())
.collect::<String>()
content.chunks.iter().map(|c| c.text.as_str()).collect::<String>()
);
}
NodeContent::Textarea(content) => {
@ -894,9 +875,7 @@ impl Tui {
.iter()
.skip(pop_minimum)
.position(|&id| {
self.prev_node_map
.get(id)
.is_some_and(|node| node.borrow().attributes.focusable)
self.prev_node_map.get(id).is_some_and(|node| node.borrow().attributes.focusable)
})
.map(|idx| idx + pop_minimum)
.unwrap_or(path.len());
@ -983,17 +962,9 @@ impl Tui {
let row = row.borrow();
let cell = cell.borrow();
let mut next = if input == vk::LEFT {
cell.siblings.prev
} else {
cell.siblings.next
};
let mut next = if input == vk::LEFT { cell.siblings.prev } else { cell.siblings.next };
if next.is_none() {
next = if input == vk::LEFT {
row.children.last
} else {
row.children.first
};
next = if input == vk::LEFT { row.children.last } else { row.children.first };
}
if let Some(next) = next {
@ -1283,10 +1254,7 @@ impl<'a> Context<'a, '_> {
ln.attributes.float = Some(FloatAttributes {
gravity_x: spec.gravity_x.clamp(0.0, 1.0),
gravity_y: spec.gravity_y.clamp(0.0, 1.0),
offset: Point {
x: spec.offset_x,
y: spec.offset_y,
},
offset: Point { x: spec.offset_x, y: spec.offset_y },
});
ln.attributes.bg = self.tui.floater_default_bg;
ln.attributes.fg = self.tui.floater_default_fg;
@ -1367,14 +1335,8 @@ impl<'a> Context<'a, '_> {
pub fn modal_begin(&mut self, classname: &'static str, title: &str) {
self.block_begin(classname);
self.attr_float(FloatSpec {
anchor: Anchor::Root,
..Default::default()
});
self.attr_intrinsic_size(Size {
width: self.tui.size.width,
height: self.tui.size.height,
});
self.attr_float(FloatSpec { anchor: Anchor::Root, ..Default::default() });
self.attr_intrinsic_size(Size { width: self.tui.size.width, height: self.tui.size.height });
self.attr_background_rgba(self.indexed_alpha(IndexedColor::Background, 0xd6));
self.attr_foreground_rgba(self.indexed_alpha(IndexedColor::Background, 0xd6));
self.attr_focus_well();
@ -1482,10 +1444,8 @@ impl<'a> Context<'a, '_> {
pub fn styled_label_begin(&mut self, classname: &'static str, overflow: Overflow) {
self.block_begin(classname);
self.tree.last_node.borrow_mut().content = NodeContent::Text(TextContent {
chunks: Vec::new_in(self.arena()),
overflow,
});
self.tree.last_node.borrow_mut().content =
NodeContent::Text(TextContent { chunks: Vec::new_in(self.arena()), overflow });
}
pub fn styled_label_set_foreground(&mut self, color: u32) {
@ -1537,10 +1497,8 @@ impl<'a> Context<'a, '_> {
return;
};
let cursor = ucd::MeasurementConfig::new(&content.chunks).goto_visual(Point {
x: CoordType::MAX,
y: 0,
});
let cursor = ucd::MeasurementConfig::new(&content.chunks)
.goto_visual(Point { x: CoordType::MAX, y: 0 });
last_node.intrinsic_size.width = cursor.visual_pos.x;
last_node.intrinsic_size.height = 1;
last_node.intrinsic_size_set = true;
@ -1995,14 +1953,10 @@ impl<'a> Context<'a, '_> {
},
vk::HOME => match modifiers {
kbmod::CTRL => tb.cursor_move_to_logical(Point::default()),
kbmod::SHIFT => tb.selection_update_visual(Point {
x: 0,
y: tb.get_cursor_visual_pos().y,
}),
_ => tb.cursor_move_to_visual(Point {
x: 0,
y: tb.get_cursor_visual_pos().y,
}),
kbmod::SHIFT => {
tb.selection_update_visual(Point { x: 0, y: tb.get_cursor_visual_pos().y })
}
_ => tb.cursor_move_to_visual(Point { x: 0, y: tb.get_cursor_visual_pos().y }),
},
vk::LEFT => {
let granularity = if modifiers.contains(kbmod::CTRL) {
@ -2334,16 +2288,12 @@ impl<'a> Context<'a, '_> {
.prev_node_map
.get(last_node.id)
.and_then(|node| match &node.borrow().content {
NodeContent::List(content) => Some(ListContent {
selected: content.selected,
selected_node: None,
}),
NodeContent::List(content) => {
Some(ListContent { selected: content.selected, selected_node: None })
}
_ => None,
})
.unwrap_or(ListContent {
selected: 0,
selected_node: None,
});
.unwrap_or(ListContent { selected: 0, selected_node: None });
last_node.attributes.focus_void = true;
last_node.content = NodeContent::List(content);
@ -2432,17 +2382,10 @@ impl<'a> Context<'a, '_> {
{
let selected = selected_node.unwrap().borrow();
let forward = self.input_keyboard == Some(vk::DOWN);
let mut node = if forward {
selected.siblings.next
} else {
selected.siblings.prev
};
let mut node =
if forward { selected.siblings.next } else { selected.siblings.prev };
if node.is_none() {
node = if forward {
list.children.first
} else {
list.children.last
};
node = if forward { list.children.first } else { list.children.last };
}
selected_next = node;
} else {
@ -2656,12 +2599,7 @@ impl<'a> Context<'a, '_> {
}
self.styled_label_end();
self.attr_padding(Rect {
left: 0,
top: 0,
right: 2,
bottom: 0,
});
self.attr_padding(Rect { left: 0, top: 0, right: 2, bottom: 0 });
}
fn menubar_shortcut(&mut self, shortcut: InputKey) {
@ -2687,12 +2625,7 @@ impl<'a> Context<'a, '_> {
self.block_begin("shortcut");
self.block_end();
}
self.attr_padding(Rect {
left: 0,
top: 0,
right: 2,
bottom: 0,
});
self.attr_padding(Rect { left: 0, top: 0, right: 2, bottom: 0 });
}
}
@ -2828,16 +2761,8 @@ impl<'a> Tree<'a> {
mut cb: T,
) {
let mut node = start;
let children_idx = if forward {
NodeChildren::FIRST
} else {
NodeChildren::LAST
};
let siblings_idx = if forward {
NodeSiblings::NEXT
} else {
NodeSiblings::PREV
};
let children_idx = if forward { NodeChildren::FIRST } else { NodeChildren::LAST };
let siblings_idx = if forward { NodeSiblings::NEXT } else { NodeSiblings::PREV };
while {
'traverse: {
@ -2888,11 +2813,7 @@ struct NodeMap<'a> {
impl Default for NodeMap<'_> {
fn default() -> Self {
Self {
slots: vec![None; 1],
shift: 0,
mask: 0,
}
Self { slots: vec![None; 1], shift: 0, mask: 0 }
}
}

View file

@ -1,8 +1,9 @@
use std::ops::Range;
use crate::helpers::{self, CoordType, Point};
use crate::simd::{memchr2, memrchr2};
use crate::ucd_gen::*;
use crate::utf8::Utf8Chars;
use std::ops::Range;
/// An abstraction over potentially chunked text containers.
pub trait Document {
@ -74,12 +75,7 @@ pub struct MeasurementConfig<'doc> {
impl<'doc> MeasurementConfig<'doc> {
pub fn new(buffer: &'doc dyn Document) -> Self {
Self {
buffer,
tab_size: 8,
word_wrap_column: 0,
cursor: UcdCursor::default(),
}
Self { buffer, tab_size: 8, word_wrap_column: 0, cursor: UcdCursor::default() }
}
pub fn with_tab_size(mut self, tab_size: CoordType) -> Self {
@ -472,14 +468,8 @@ impl<'doc> MeasurementConfig<'doc> {
UcdCursor {
offset,
logical_pos: Point {
x: logical_pos_x,
y: logical_pos_y,
},
visual_pos: Point {
x: visual_pos_x,
y: visual_pos_y,
},
logical_pos: Point { x: logical_pos_x, y: logical_pos_y },
visual_pos: Point { x: visual_pos_x, y: visual_pos_y },
column,
wrap_opp,
}
@ -529,22 +519,12 @@ const WORD_CLASSIFIER: [CharClass; 256] =
/// Finds the next word boundary given a document cursor offset.
/// Returns the offset of the next word boundary.
pub fn word_forward(doc: &dyn Document, offset: usize) -> usize {
word_navigation(WordForward {
doc,
offset,
chunk: &[],
chunk_off: 0,
})
word_navigation(WordForward { doc, offset, chunk: &[], chunk_off: 0 })
}
/// The backward version of `word_forward`.
pub fn word_backward(doc: &dyn Document, offset: usize) -> usize {
word_navigation(WordBackward {
doc,
offset,
chunk: &[],
chunk_off: 0,
})
word_navigation(WordBackward { doc, offset, chunk: &[], chunk_off: 0 })
}
/// Word navigation implementation. Matches the behavior of VS Code.
@ -923,10 +903,7 @@ mod test {
// Does hitting the visual target within a word reset the hit back to the end of the visual line?
let mut cfg = MeasurementConfig::new(&text).with_word_wrap_column(6);
let cursor = cfg.goto_visual(Point {
x: CoordType::MAX,
y: 0,
});
let cursor = cfg.goto_visual(Point { x: CoordType::MAX, y: 0 });
assert_eq!(
cursor,
UcdCursor {
@ -939,9 +916,8 @@ mod test {
);
// Does hitting the same target but with a non-zero starting position result in the same outcome?
let mut cfg = MeasurementConfig::new(&text)
.with_word_wrap_column(6)
.with_cursor(UcdCursor {
let mut cfg =
MeasurementConfig::new(&text).with_word_wrap_column(6).with_cursor(UcdCursor {
offset: 1,
logical_pos: Point { x: 1, y: 0 },
visual_pos: Point { x: 1, y: 0 },
@ -1012,9 +988,8 @@ mod test {
#[test]
fn test_measure_forward_tabs() {
let text = "a\tb\tc".as_bytes();
let cursor = MeasurementConfig::new(&text)
.with_tab_size(4)
.goto_visual(Point { x: 4, y: 0 });
let cursor =
MeasurementConfig::new(&text).with_tab_size(4).goto_visual(Point { x: 4, y: 0 });
assert_eq!(
cursor,
UcdCursor {
@ -1045,12 +1020,7 @@ mod test {
// |foo_ |
// |bar. |
// |abc |
let chunks = [
"foo ".as_bytes(),
"bar".as_bytes(),
".\n".as_bytes(),
"abc".as_bytes(),
];
let chunks = ["foo ".as_bytes(), "bar".as_bytes(), ".\n".as_bytes(), "abc".as_bytes()];
let doc = ChunkedDoc(&chunks);
let mut cfg = MeasurementConfig::new(&doc).with_word_wrap_column(7);
let max = CoordType::MAX;
@ -1225,10 +1195,7 @@ mod test {
let mut cfg = MeasurementConfig::new(&bytes).with_word_wrap_column(8);
// At the end of "// " there should be a wrap.
let end0 = cfg.goto_visual(Point {
x: CoordType::MAX,
y: 0,
});
let end0 = cfg.goto_visual(Point { x: CoordType::MAX, y: 0 });
assert_eq!(
end0,
UcdCursor {
@ -1240,10 +1207,7 @@ mod test {
}
);
let mid1 = cfg.goto_visual(Point {
x: end0.visual_pos.x,
y: 1,
});
let mid1 = cfg.goto_visual(Point { x: end0.visual_pos.x, y: 1 });
assert_eq!(
mid1,
UcdCursor {
@ -1255,10 +1219,7 @@ mod test {
}
);
let mid2 = cfg.goto_visual(Point {
x: end0.visual_pos.x,
y: 2,
});
let mid2 = cfg.goto_visual(Point { x: end0.visual_pos.x, y: 2 });
assert_eq!(
mid2,
UcdCursor {
@ -1329,9 +1290,7 @@ mod test {
// |____b | <- 1 tab, 1 space
let text = "foo \t b";
let bytes = text.as_bytes();
let mut cfg = MeasurementConfig::new(&bytes)
.with_word_wrap_column(8)
.with_tab_size(4);
let mut cfg = MeasurementConfig::new(&bytes).with_word_wrap_column(8).with_tab_size(4);
let max = CoordType::MAX;
let end0 = cfg.goto_visual(Point { x: max, y: 0 });
@ -1374,10 +1333,7 @@ mod test {
#[test]
fn test_crlf() {
let text = "a\r\nbcd\r\ne".as_bytes();
let cursor = MeasurementConfig::new(&text).goto_visual(Point {
x: CoordType::MAX,
y: 1,
});
let cursor = MeasurementConfig::new(&text).goto_visual(Point { x: CoordType::MAX, y: 1 });
assert_eq!(
cursor,
UcdCursor {

View file

@ -1,5 +1,4 @@
use std::hint;
use std::iter;
use std::{hint, iter};
#[derive(Clone, Copy)]
pub struct Utf8Chars<'a> {

View file

@ -1,5 +1,4 @@
use std::mem;
use std::time;
use std::{mem, time};
use crate::simd::memchr2;
@ -43,12 +42,7 @@ impl Parser {
pub fn new() -> Self {
Self {
state: State::Ground,
csi: Csi {
params: [0; 32],
param_count: 0,
private_byte: '\0',
final_byte: '\0',
},
csi: Csi { params: [0; 32], param_count: 0, private_byte: '\0', final_byte: '\0' },
}
}
@ -75,11 +69,7 @@ impl Parser {
&'parser mut self,
input: &'input str,
) -> Stream<'parser, 'input> {
Stream {
parser: self,
input,
off: 0,
}
Stream { parser: self, input, off: 0 }
}
}
@ -294,14 +284,8 @@ impl<'parser, 'input> Stream<'parser, 'input> {
self.off += 1;
return match state {
State::OscEsc => Some(Token::Osc {
data: "",
partial: false,
}),
_ => Some(Token::Dcs {
data: "",
partial: false,
}),
State::OscEsc => Some(Token::Osc { data: "", partial: false }),
_ => Some(Token::Dcs { data: "", partial: false }),
};
} else {
// False alarm: Not a string terminator.
@ -312,14 +296,8 @@ impl<'parser, 'input> Stream<'parser, 'input> {
_ => State::Dcs,
};
return match parser.state {
State::Osc => Some(Token::Osc {
data: "\x1b",
partial: true,
}),
_ => Some(Token::Dcs {
data: "\x1b",
partial: true,
}),
State::Osc => Some(Token::Osc { data: "\x1b", partial: true }),
_ => Some(Token::Dcs { data: "\x1b", partial: true }),
};
}
}