use std::fmt::{self, Debug, Display, Formatter};
use std::hash::Hash;
use bitflags::bitflags;
use regex::Regex;
use crate::application::ApplicationWindowId;
use crate::context::EditContext;
use crate::util::{
is_filename_char,
is_filepath_char,
is_horizontal_space,
is_keyword,
is_newline,
is_space_char,
is_word_char,
sort2,
};
use crate::*;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Case {
Upper,
Lower,
Title,
Toggle,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum JoinStyle {
NoChange,
OneSpace,
NewSpace,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum PasteStyle {
Cursor,
Side(MoveDir1D),
Replace,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CompletionScope {
Buffer,
Global,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CompletionStyle {
List(MoveDir1D),
None,
Prefix,
Single,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CompletionType {
Auto,
File,
Line(CompletionScope),
Word(CompletionScope),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CompletionDisplay {
None,
Bar,
List,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum EditTarget {
Boundary(RangeType, bool, MoveTerminus, Count),
CurrentPosition,
CharJump(Specifier<Mark>),
LineJump(Specifier<Mark>),
Motion(MoveType, Count),
Range(RangeType, bool, Count),
Search(SearchType, MoveDirMod, Count),
Selection,
}
impl EditTarget {
pub fn is_jumping(&self) -> bool {
match self {
EditTarget::Boundary(..) => true,
EditTarget::CurrentPosition => false,
EditTarget::CharJump(_) => true,
EditTarget::LineJump(_) => true,
EditTarget::Motion(mt, _) => mt.is_jumping(),
EditTarget::Range(..) => true,
EditTarget::Search(st, ..) => st.is_jumping(),
EditTarget::Selection => false,
}
}
}
impl From<MoveType> for EditTarget {
fn from(mt: MoveType) -> Self {
EditTarget::Motion(mt, Count::Contextual)
}
}
impl From<RangeType> for EditTarget {
fn from(mt: RangeType) -> Self {
EditTarget::Range(mt, true, Count::Contextual)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CursorEnd {
Keep,
Start,
End,
Selection,
Auto,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct EditRange<Cursor> {
pub start: Cursor,
pub end: Cursor,
pub shape: TargetShape,
pub inclusive: bool,
}
impl<Cursor: Ord> EditRange<Cursor> {
pub fn new(a: Cursor, b: Cursor, shape: TargetShape, inclusive: bool) -> Self {
let (start, end) = sort2(a, b);
EditRange { start, end, shape, inclusive }
}
pub fn inclusive(a: Cursor, b: Cursor, shape: TargetShape) -> Self {
Self::new(a, b, shape, true)
}
pub fn exclusive(a: Cursor, b: Cursor, shape: TargetShape) -> Self {
Self::new(a, b, shape, false)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum RepeatType {
EditSequence,
LastAction,
LastSelection,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SearchType {
Char(bool),
Regex,
Word(WordStyle, bool),
}
impl SearchType {
pub fn is_inclusive_motion(&self) -> bool {
match self {
SearchType::Char(..) => true,
SearchType::Regex => false,
SearchType::Word(..) => false,
}
}
fn is_jumping(&self) -> bool {
match self {
SearchType::Char(..) => false,
SearchType::Regex => true,
SearchType::Word(..) => true,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum WordStyle {
AlphaNum,
Big,
CharSet(fn(char) -> bool),
FileName,
FilePath,
Little,
NonAlphaNum,
Number(Radix),
Whitespace(bool),
}
impl WordStyle {
pub fn contains(&self, c: char) -> bool {
match self {
WordStyle::AlphaNum => is_word_char(c),
WordStyle::NonAlphaNum => !is_word_char(c),
WordStyle::Big => !is_space_char(c),
WordStyle::CharSet(f) => f(c),
WordStyle::FileName => is_filename_char(c),
WordStyle::FilePath => is_filepath_char(c),
WordStyle::Little => is_word_char(c) || is_keyword(c),
WordStyle::Number(radix) => radix.contains(c),
WordStyle::Whitespace(true) => is_space_char(c),
WordStyle::Whitespace(false) => is_horizontal_space(c),
}
}
}
impl BoundaryTest for WordStyle {
fn is_boundary_begin(&self, ctx: &BoundaryTestContext) -> bool {
match self {
WordStyle::AlphaNum => {
if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
return true;
} else if let Some(before) = ctx.before {
let befwc = is_word_char(before);
let curwc = is_word_char(ctx.current);
return !befwc && curwc;
} else {
return true;
}
},
WordStyle::NonAlphaNum => {
if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
return true;
} else if let Some(before) = ctx.before {
let befwc = is_word_char(before);
let curwc = is_word_char(ctx.current);
return befwc && !curwc;
} else {
return true;
}
},
WordStyle::Big => {
if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
return true;
} else if let Some(before) = ctx.before {
let befws = is_space_char(before);
let curws = is_space_char(ctx.current);
let curnl = is_newline(ctx.current);
let last = !ctx.motion && ctx.count == 1;
return (last && curnl) || (befws && !curws);
} else {
return true;
}
},
WordStyle::CharSet(f) => {
if let Some(before) = ctx.before {
f(ctx.current) && !f(before)
} else {
f(ctx.current)
}
},
WordStyle::FileName => {
if let Some(before) = ctx.before {
is_filename_char(ctx.current) && !is_filename_char(before)
} else {
is_filename_char(ctx.current)
}
},
WordStyle::FilePath => {
if let Some(before) = ctx.before {
is_filepath_char(ctx.current) && !is_filepath_char(before)
} else {
is_filepath_char(ctx.current)
}
},
WordStyle::Little => {
if ctx.after.is_none() && ctx.dir == MoveDir1D::Next {
return true;
} else if let Some(before) = ctx.before {
let befwc = is_word_char(before);
let befkw = is_keyword(before);
let curwc = is_word_char(ctx.current);
let curkw = is_keyword(ctx.current);
let curnl = is_newline(ctx.current);
let last = !ctx.motion && ctx.count == 1;
return (last && curnl) ||
(befwc && curkw) ||
(befkw && curwc) ||
(!befwc && curwc) ||
(!befkw && curkw);
} else {
return true;
}
},
WordStyle::Number(radix) => {
let cn = radix.contains(ctx.current);
if ctx.current == '-' {
matches!(ctx.after, Some(c) if radix.contains(c))
} else if let Some(before) = ctx.before {
cn && before != '-' && !radix.contains(before)
} else {
cn
}
},
WordStyle::Whitespace(multiline) => {
let f = if *multiline {
is_space_char
} else {
is_horizontal_space
};
return f(ctx.current) && matches!(ctx.before, Some(c) if !f(c));
},
}
}
fn is_boundary_end(&self, ctx: &BoundaryTestContext) -> bool {
match self {
WordStyle::AlphaNum => {
if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
return true;
} else if let Some(after) = ctx.after {
let curwc = is_word_char(ctx.current);
let aftwc = is_word_char(after);
return curwc && !aftwc;
} else {
return true;
}
},
WordStyle::NonAlphaNum => {
if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
return true;
} else if let Some(after) = ctx.after {
let curwc = is_word_char(ctx.current);
let aftwc = is_word_char(after);
return !curwc && aftwc;
} else {
return true;
}
},
WordStyle::Big => {
if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
return true;
} else if let Some(after) = ctx.after {
!is_space_char(ctx.current) && is_space_char(after)
} else {
return true;
}
},
WordStyle::CharSet(f) => {
if let Some(after) = ctx.after {
f(ctx.current) && !f(after)
} else {
f(ctx.current)
}
},
WordStyle::FileName => {
if let Some(after) = ctx.after {
is_filename_char(ctx.current) && !is_filename_char(after)
} else {
is_filename_char(ctx.current)
}
},
WordStyle::FilePath => {
if let Some(after) = ctx.after {
is_filepath_char(ctx.current) && !is_filepath_char(after)
} else {
is_filepath_char(ctx.current)
}
},
WordStyle::Little => {
if ctx.before.is_none() && ctx.dir == MoveDir1D::Previous {
return true;
} else if let Some(after) = ctx.after {
let curwc = is_word_char(ctx.current);
let curkw = is_keyword(ctx.current);
let aftwc = is_word_char(after);
let aftkw = is_keyword(after);
return (curwc && aftkw) ||
(curkw && aftwc) ||
(curwc && !aftwc) ||
(curkw && !aftkw);
} else {
return true;
}
},
WordStyle::Number(radix) => {
if let Some(after) = ctx.after {
return radix.contains(ctx.current) && !radix.contains(after);
} else {
return radix.contains(ctx.current);
}
},
WordStyle::Whitespace(multiline) => {
let f = if *multiline {
is_space_char
} else {
is_horizontal_space
};
return f(ctx.current) && matches!(ctx.after, Some(c) if !f(c));
},
}
}
}
impl From<Radix> for WordStyle {
fn from(radix: Radix) -> Self {
WordStyle::Number(radix)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Radix {
Binary,
Octal,
Decimal,
Hexadecimal,
}
impl Radix {
pub fn contains(&self, c: char) -> bool {
match self {
Radix::Binary => c == '0' || c == '1',
Radix::Octal => c >= '0' && c <= '7',
Radix::Decimal => c.is_ascii_digit(),
Radix::Hexadecimal => c.is_ascii_hexdigit(),
}
}
}
pub struct BoundaryTestContext {
pub current: char,
pub before: Option<char>,
pub after: Option<char>,
pub dir: MoveDir1D,
pub motion: bool,
pub count: usize,
}
pub trait BoundaryTest {
fn is_boundary_begin(&self, ctx: &BoundaryTestContext) -> bool;
fn is_boundary_end(&self, ctx: &BoundaryTestContext) -> bool;
fn is_boundary(&self, terminus: MoveTerminus, ctx: &BoundaryTestContext) -> bool {
match terminus {
MoveTerminus::Beginning => self.is_boundary_begin(ctx),
MoveTerminus::End => self.is_boundary_end(ctx),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum RangeType {
Word(WordStyle),
Buffer,
Paragraph,
Sentence,
Line,
Bracketed(char, char),
Item,
Quote(char),
XmlTag,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum MoveType {
BufferPos(MovePosition),
BufferByteOffset,
BufferLineOffset,
BufferLinePercent,
Column(MoveDir1D, bool),
FinalNonBlank(MoveDir1D),
FirstWord(MoveDir1D),
ItemMatch,
Line(MoveDir1D),
LineColumnOffset,
LinePercent,
LinePos(MovePosition),
WordBegin(WordStyle, MoveDir1D),
WordEnd(WordStyle, MoveDir1D),
ParagraphBegin(MoveDir1D),
SentenceBegin(MoveDir1D),
SectionBegin(MoveDir1D),
SectionEnd(MoveDir1D),
ScreenFirstWord(MoveDir1D),
ScreenLine(MoveDir1D),
ScreenLinePos(MovePosition),
ViewportPos(MovePosition),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MoveDir1D {
Previous,
Next,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MoveDir2D {
Left,
Right,
Up,
Down,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MoveTerminus {
Beginning,
End,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MovePosition {
Beginning,
Middle,
End,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MoveDirMod {
Same,
Flip,
Exact(MoveDir1D),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Axis {
Horizontal,
Vertical,
}
impl Axis {
pub fn rotate(&self) -> Axis {
match self {
Axis::Horizontal => Axis::Vertical,
Axis::Vertical => Axis::Horizontal,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ScrollSize {
Cell,
HalfPage,
Page,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ScrollStyle {
Direction2D(MoveDir2D, ScrollSize, Count),
CursorPos(MovePosition, Axis),
LinePos(MovePosition, Count),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SelectionCursorChange {
Beginning,
End,
SwapAnchor,
SwapSide,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum FocusChange {
Current,
Offset(Count, bool),
Position(MovePosition),
PreviouslyFocused,
Direction1D(MoveDir1D, Count, bool),
Direction2D(MoveDir2D, Count),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SizeChange<I = Count> {
Equal,
Exact(I),
Decrease(I),
Increase(I),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum IndentChange<I = Count> {
Auto,
Decrease(I),
Increase(I),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum NumberChange {
Decrease(Count),
Increase(Count),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum KeywordTarget {
Word(WordStyle),
Selection,
}
impl From<WordStyle> for KeywordTarget {
fn from(style: WordStyle) -> KeywordTarget {
KeywordTarget::Word(style)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OpenTarget<W: ApplicationWindowId> {
Alternate,
Application(W),
Current,
Cursor(WordStyle),
List(Count),
Name(String),
Offset(MoveDir1D, Count),
Selection,
Unnamed,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TabTarget {
Single(FocusChange),
AllBut(FocusChange),
All,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum WindowTarget {
Single(FocusChange),
AllBut(FocusChange),
All,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CursorCloseTarget {
Leader,
Followers,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CursorGroupCombineStyle {
Append,
Merge(CursorMergeStyle),
Replace,
}
impl From<CursorMergeStyle> for CursorGroupCombineStyle {
fn from(style: CursorMergeStyle) -> Self {
CursorGroupCombineStyle::Merge(style)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum CursorMergeStyle {
Union,
Intersect,
SelectCursor(MoveDir1D),
SelectShort,
SelectLong,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Count {
Contextual,
MinusOne,
Exact(usize),
}
impl From<usize> for Count {
fn from(n: usize) -> Self {
Count::Exact(n)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Mark {
BufferLastExited,
BufferNamed(char),
GlobalLastExited(usize),
GlobalNamed(char),
LastChanged,
LastInserted,
LastJump,
VisualBegin,
VisualEnd,
LastYankedBegin,
LastYankedEnd,
}
impl Mark {
pub fn is_global(&self) -> bool {
match self {
Mark::GlobalNamed(_) => true,
Mark::GlobalLastExited(_) => true,
Mark::BufferLastExited => false,
Mark::BufferNamed(_) => false,
Mark::LastChanged => false,
Mark::LastInserted => false,
Mark::LastJump => false,
Mark::VisualBegin => false,
Mark::VisualEnd => false,
Mark::LastYankedBegin => false,
Mark::LastYankedEnd => false,
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub enum Specifier<T> {
#[default]
Contextual,
Exact(T),
}
impl<T> From<T> for Specifier<T> {
fn from(v: T) -> Self {
Specifier::Exact(v)
}
}
bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct WriteFlags: u32 {
const NONE = 0b00000000;
const FORCE = 0b00000001;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct OpenFlags: u32 {
const NONE = 0b00000000;
const FORCE = 0b00000001;
const CREATE = 0b00000010;
}
}
bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct CloseFlags: u32 {
const NONE = 0b00000000;
const FORCE = 0b00000001;
const WRITE = 0b00000010;
const QUIT = 0b00000100;
const WQ = CloseFlags::WRITE.bits() | CloseFlags::QUIT.bits();
const FQ = CloseFlags::FORCE.bits() | CloseFlags::QUIT.bits();
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum SelectionBoundary {
Line,
NonWhitespace,
}
impl BoundaryTest for SelectionBoundary {
fn is_boundary_begin(&self, ctx: &BoundaryTestContext) -> bool {
match self {
SelectionBoundary::Line => {
if let Some(before) = ctx.before {
return before == '\n';
} else {
return true;
}
},
SelectionBoundary::NonWhitespace => {
return !is_space_char(ctx.current);
},
}
}
fn is_boundary_end(&self, ctx: &BoundaryTestContext) -> bool {
match self {
SelectionBoundary::Line => ctx.current == '\n' || ctx.after.is_none(),
SelectionBoundary::NonWhitespace => {
return !is_space_char(ctx.current);
},
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum SelectionSplitStyle {
Anchor,
Lines,
Regex(MatchAction),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum SelectionResizeStyle {
Extend,
Object,
Restart,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum CommandType {
Command,
Search,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RecallFilter {
All,
PrefixMatch,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PositionList {
ChangeList,
JumpList,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum InsertStyle {
Insert,
Replace,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Char {
Single(char),
Digraph(char, char),
CtrlSeq(String),
CopyLine(MoveDir1D),
}
impl From<char> for Char {
fn from(c: char) -> Self {
Char::Single(c)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Register {
Unnamed,
UnnamedMacro,
UnnamedCursorGroup,
RecentlyDeleted(usize),
SmallDelete,
LastInserted,
LastCommand(CommandType),
LastYanked,
Named(char),
AltBufName,
CurBufName,
Blackhole,
SelectionPrimary,
SelectionClipboard,
}
impl Register {
pub fn is_cursor_storage(&self) -> bool {
match self {
Register::Named(_) => true,
Register::Blackhole => true,
Register::UnnamedCursorGroup => true,
Register::Unnamed => false,
Register::UnnamedMacro => false,
Register::RecentlyDeleted(_) => false,
Register::SmallDelete => false,
Register::LastCommand(_) => false,
Register::LastInserted => false,
Register::LastYanked => false,
Register::AltBufName => false,
Register::CurBufName => false,
Register::SelectionPrimary => false,
Register::SelectionClipboard => false,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TargetShape {
CharWise,
LineWise,
BlockWise,
}
bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct TargetShapeFilter: u32 {
const NONE = 0b00000000;
const ALL = 0b00000111;
const CHAR = 0b00000001;
const LINE = 0b00000010;
const BLOCK = 0b00000100;
}
}
impl TargetShapeFilter {
pub fn matches(&self, shape: &TargetShape) -> bool {
match shape {
TargetShape::CharWise => self.contains(TargetShapeFilter::CHAR),
TargetShape::LineWise => self.contains(TargetShapeFilter::LINE),
TargetShape::BlockWise => self.contains(TargetShapeFilter::BLOCK),
}
}
}
impl From<TargetShape> for TargetShapeFilter {
fn from(shape: TargetShape) -> Self {
match shape {
TargetShape::CharWise => TargetShapeFilter::CHAR,
TargetShape::LineWise => TargetShapeFilter::LINE,
TargetShape::BlockWise => TargetShapeFilter::BLOCK,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum MatchAction {
Keep,
Drop,
}
impl MatchAction {
pub fn is_keep(&self) -> bool {
matches!(self, MatchAction::Keep)
}
pub fn is_drop(&self) -> bool {
matches!(self, MatchAction::Drop)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RangeEndingType {
Absolute(Count),
All,
Current,
Last,
Mark(Specifier<Mark>),
Search(MoveDir1D),
SubPatSearch(MoveDir1D),
Unspecified,
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RangeEndingModifier {
Offset(MoveDir1D, Count),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RangeEnding(pub RangeEndingType, pub Vec<RangeEndingModifier>);
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RangeSearchInit {
Cursor,
Start,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RangeSpec {
Single(RangeEnding),
Double(RangeEnding, RangeEnding, RangeSearchInit),
}
pub trait Wrappable {
fn set_wrap(&mut self, wrap: bool);
}
pub struct ViewportContext<Cursor> {
pub corner: Cursor,
pub dimensions: (usize, usize),
pub wrap: bool,
}
impl<Cursor: Default> ViewportContext<Cursor> {
pub fn new() -> Self {
ViewportContext {
corner: Cursor::default(),
dimensions: (0, 0),
wrap: false,
}
}
pub fn get_height(&self) -> usize {
self.dimensions.1
}
pub fn get_width(&self) -> usize {
self.dimensions.0
}
}
impl<Cursor: Default> Default for ViewportContext<Cursor> {
fn default() -> Self {
ViewportContext::new()
}
}
impl<Cursor: Clone> Clone for ViewportContext<Cursor> {
fn clone(&self) -> Self {
ViewportContext {
corner: self.corner.clone(),
dimensions: self.dimensions,
wrap: self.wrap,
}
}
}
impl<Cursor: Wrappable> Wrappable for ViewportContext<Cursor> {
fn set_wrap(&mut self, wrap: bool) {
self.wrap = wrap;
self.corner.set_wrap(wrap);
}
}
pub struct CursorMovementsContext<'a, Cursor> {
pub action: &'a EditAction,
pub view: &'a ViewportContext<Cursor>,
pub context: &'a EditContext,
}
pub trait CursorMovements<Cursor> {
fn first_word(&self, cursor: &Cursor, ctx: &CursorMovementsContext<'_, Cursor>) -> Cursor;
fn movement(
&self,
cursor: &Cursor,
movement: &MoveType,
count: &Count,
ctx: &CursorMovementsContext<'_, Cursor>,
) -> Option<Cursor>;
fn range_of_movement(
&self,
cursor: &Cursor,
movement: &MoveType,
count: &Count,
ctx: &CursorMovementsContext<'_, Cursor>,
) -> Option<EditRange<Cursor>>;
fn range(
&self,
cursor: &Cursor,
range: &RangeType,
inclusive: bool,
count: &Count,
ctx: &CursorMovementsContext<'_, Cursor>,
) -> Option<EditRange<Cursor>>;
}
pub trait CursorSearch<Cursor> {
fn find_char(
&self,
cursor: &Cursor,
inclusive: bool,
dir: MoveDir1D,
multiline: bool,
needle: char,
count: usize,
) -> Option<Cursor>;
fn find_matches(&self, start: &Cursor, end: &Cursor, needle: &Regex) -> Vec<EditRange<Cursor>>;
fn find_regex(
&self,
cursor: &Cursor,
dir: MoveDir1D,
needle: &Regex,
count: usize,
) -> Option<EditRange<Cursor>>;
}
pub trait Flip {
fn flip(&self) -> Self;
}
impl Flip for MoveDir1D {
fn flip(&self) -> MoveDir1D {
match self {
MoveDir1D::Previous => MoveDir1D::Next,
MoveDir1D::Next => MoveDir1D::Previous,
}
}
}
impl Flip for MoveDir2D {
fn flip(&self) -> MoveDir2D {
match self {
MoveDir2D::Left => MoveDir2D::Right,
MoveDir2D::Right => MoveDir2D::Left,
MoveDir2D::Up => MoveDir2D::Down,
MoveDir2D::Down => MoveDir2D::Up,
}
}
}
impl MoveDir2D {
pub fn axis(&self) -> Axis {
match self {
MoveDir2D::Left => Axis::Horizontal,
MoveDir2D::Right => Axis::Horizontal,
MoveDir2D::Up => Axis::Vertical,
MoveDir2D::Down => Axis::Vertical,
}
}
}
impl std::ops::Not for InsertStyle {
type Output = Self;
fn not(self) -> Self::Output {
match self {
InsertStyle::Insert => InsertStyle::Replace,
InsertStyle::Replace => InsertStyle::Insert,
}
}
}
impl MoveType {
pub fn is_inclusive_motion(&self) -> bool {
match self {
MoveType::BufferPos(_) => true,
MoveType::FinalNonBlank(_) => true,
MoveType::ItemMatch => true,
MoveType::LineColumnOffset => true,
MoveType::WordEnd(_, _) => true,
MoveType::BufferByteOffset => false,
MoveType::BufferLineOffset => false,
MoveType::BufferLinePercent => false,
MoveType::Column(_, _) => false,
MoveType::FirstWord(_) => false,
MoveType::Line(_) => false,
MoveType::LinePercent => false,
MoveType::LinePos(_) => false,
MoveType::ParagraphBegin(_) => false,
MoveType::ScreenFirstWord(_) => false,
MoveType::ScreenLine(_) => false,
MoveType::ScreenLinePos(_) => false,
MoveType::ViewportPos(_) => false,
MoveType::SectionBegin(_) => false,
MoveType::SectionEnd(_) => false,
MoveType::SentenceBegin(_) => false,
MoveType::WordBegin(_, _) => false,
}
}
pub fn is_jumping(&self) -> bool {
match self {
MoveType::BufferByteOffset => true,
MoveType::BufferLineOffset => true,
MoveType::BufferLinePercent => true,
MoveType::BufferPos(_) => true,
MoveType::ItemMatch => true,
MoveType::ParagraphBegin(_) => true,
MoveType::ViewportPos(_) => true,
MoveType::SectionBegin(_) => true,
MoveType::SentenceBegin(_) => true,
MoveType::Column(_, _) => false,
MoveType::FinalNonBlank(_) => false,
MoveType::FirstWord(_) => false,
MoveType::LineColumnOffset => false,
MoveType::Line(_) => false,
MoveType::LinePercent => false,
MoveType::LinePos(_) => false,
MoveType::ScreenFirstWord(_) => false,
MoveType::ScreenLine(_) => false,
MoveType::ScreenLinePos(_) => false,
MoveType::SectionEnd(_) => false,
MoveType::WordBegin(_, _) => false,
MoveType::WordEnd(_, _) => false,
}
}
pub fn shape(&self) -> TargetShape {
match self {
MoveType::BufferLineOffset => TargetShape::LineWise,
MoveType::BufferLinePercent => TargetShape::LineWise,
MoveType::BufferPos(_) => TargetShape::LineWise,
MoveType::FirstWord(_) => TargetShape::LineWise,
MoveType::Line(_) => TargetShape::LineWise,
MoveType::ViewportPos(_) => TargetShape::LineWise,
MoveType::SectionBegin(_) => TargetShape::LineWise,
MoveType::SectionEnd(_) => TargetShape::LineWise,
MoveType::BufferByteOffset => TargetShape::CharWise,
MoveType::Column(_, _) => TargetShape::CharWise,
MoveType::FinalNonBlank(_) => TargetShape::CharWise,
MoveType::ItemMatch => TargetShape::CharWise,
MoveType::LineColumnOffset => TargetShape::CharWise,
MoveType::LinePercent => TargetShape::CharWise,
MoveType::LinePos(_) => TargetShape::CharWise,
MoveType::ParagraphBegin(_) => TargetShape::CharWise,
MoveType::ScreenFirstWord(_) => TargetShape::CharWise,
MoveType::ScreenLinePos(_) => TargetShape::CharWise,
MoveType::ScreenLine(_) => TargetShape::CharWise,
MoveType::SentenceBegin(_) => TargetShape::CharWise,
MoveType::WordBegin(_, _) => TargetShape::CharWise,
MoveType::WordEnd(_, _) => TargetShape::CharWise,
}
}
}
impl MoveDirMod {
pub fn resolve(&self, dir: &MoveDir1D) -> MoveDir1D {
match self {
MoveDirMod::Same => *dir,
MoveDirMod::Flip => dir.flip(),
MoveDirMod::Exact(exact) => *exact,
}
}
}
impl From<MoveDir1D> for MoveDirMod {
fn from(dir: MoveDir1D) -> Self {
MoveDirMod::Exact(dir)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum InfoMessage {
Message(String),
Pager(String),
}
impl Display for InfoMessage {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
InfoMessage::Message(s) | InfoMessage::Pager(s) => write!(f, "{}", s),
}
}
}
impl From<&str> for InfoMessage {
fn from(msg: &str) -> Self {
InfoMessage::from(msg.to_string())
}
}
impl From<String> for InfoMessage {
fn from(msg: String) -> Self {
InfoMessage::Message(msg)
}
}
pub type EditInfo = Option<InfoMessage>;