Skip to main content

libghostty_vt/
selection.rs

1//! Selecting terminal content between two endpoints.
2//!
3//! The start and end values are [`GridRef`] values. They are therefore
4//! untracked grid references and inherit the same lifetime rules: they are
5//! only safe to use until the next mutating operation on the terminal that
6//! produced them, including dropping the terminal. To keep a selection valid
7//! across terminal mutations, callers must maintain tracked grid references
8//! for the endpoints and reconstruct a [`Selection`] from fresh snapshots
9//! when needed.
10//!
11//! Selections can be directly obtained by calling methods such as
12//! [`Terminal::select_all`], [`Terminal::select_word`], etc., but this can be
13//! quite cumbersome to use for terminal emulators designed for human users.
14//! For this use case, [selection gestures](self::gesture) serve as a convenient
15//! way of translating common UI actions (clicking, dragging, etc.) into selections,
16//! to be copied, formatted, or installed as the active selection.
17use std::{marker::PhantomData, ptr::NonNull};
18
19use crate::{
20    alloc::{Allocator, Bytes},
21    error::{Error, Result, from_optional_result, from_optional_result_with_len, from_result},
22    ffi,
23    fmt::Format,
24    screen::GridRef,
25    terminal::{Point, Terminal},
26};
27
28pub mod gesture;
29
30/// A snapshot selection range defined by two grid references.
31///
32/// # Preconditions
33///
34/// For every method that interacts with the terminal,
35/// the selection's start and end grid refs must both be valid untracked
36/// snapshots for the given terminal's currently active screen. In practice,
37/// they must come from that terminal and screen, and no mutating terminal call
38/// may have occurred since the refs were produced or reconstructed from
39/// tracked refs. Passing refs from another terminal, another screen, or stale
40/// refs violates this precondition.
41#[derive(Clone, Debug)]
42pub struct Selection<'t> {
43    pub(crate) inner: ffi::Selection,
44    _phan: PhantomData<&'t ffi::Terminal>,
45}
46impl<'t> Selection<'t> {
47    /// Create a new selection between two endpoints.
48    ///
49    /// Both endpoints are inclusive. The endpoints preserve selection direction
50    /// and may be reversed; callers must not assume that start is the top-left
51    /// endpoint or that end is the bottom-right endpoint.
52    ///
53    /// When `rectangle` is false, the endpoints describe a linear selection. When
54    /// `rectangle` is true, the same endpoints are interpreted as opposite corners
55    /// of a rectangular/block selection.
56    pub fn new(start: GridRef<'t>, end: GridRef<'t>, rectangle: bool) -> Self {
57        // SAFETY: provided by the type system
58        unsafe {
59            Self::from_raw(ffi::Selection {
60                start: start.inner,
61                end: end.inner,
62                rectangle,
63                ..ffi::sized!(ffi::Selection)
64            })
65        }
66    }
67
68    /// # Safety
69    ///
70    /// Caller must guarantee that the selection is bound by the lifetime `'t`.
71    pub(crate) unsafe fn from_raw(value: ffi::Selection) -> Self {
72        Self {
73            inner: value,
74            _phan: PhantomData,
75        }
76    }
77
78    /// Start of the selection range (inclusive).
79    ///
80    /// This may be before start in terminal order. It is an untracked
81    /// [`GridRef`] snapshot and follows untracked grid-ref lifetime rules.
82    pub fn start(&self) -> GridRef<'t> {
83        unsafe { GridRef::from_raw(self.inner.start) }
84    }
85    /// End of the selection range (inclusive).
86    ///
87    /// This may be before start in terminal order. It is an untracked
88    /// [`GridRef`] snapshot and follows untracked grid-ref lifetime rules.
89    pub fn end(&self) -> GridRef<'t> {
90        unsafe { GridRef::from_raw(self.inner.end) }
91    }
92    /// Whether the endpoints are interpreted as a rectangular/block
93    /// selection rather than a linear selection.
94    pub fn is_rectangle(&self) -> bool {
95        self.inner.rectangle
96    }
97
98    /// Adjust a selection snapshot using terminal selection semantics.
99    ///
100    /// The logical end endpoint is always moved, regardless of whether the
101    /// selection is forward or reversed visually. The input selection remains
102    /// a snapshot: after adjustment, call [`Terminal::set_selection`] to
103    /// install it as the terminal-owned selection if desired.
104    ///
105    /// See [#Preconditions](#preconditions) for the necessary preconditions.
106    pub fn adjust(&mut self, terminal: &'t Terminal<'_, '_>, adjustment: Adjustment) -> Result<()> {
107        let result = unsafe {
108            ffi::ghostty_terminal_selection_adjust(
109                terminal.inner.as_raw(),
110                &raw mut self.inner,
111                adjustment.into(),
112            )
113        };
114        from_result(result)
115    }
116
117    /// Test whether a terminal point is inside a selection snapshot.
118    ///
119    /// This uses the same selection semantics as the terminal, including
120    /// rectangular/block selections and linear selections spanning multiple rows.
121    ///
122    /// See [#Preconditions](#preconditions) for the necessary preconditions.
123    pub fn contains(&self, terminal: &'t Terminal<'_, '_>, point: Point) -> Result<bool> {
124        let mut contains = false;
125        let result = unsafe {
126            ffi::ghostty_terminal_selection_contains(
127                terminal.inner.as_raw(),
128                &self.inner,
129                point.into(),
130                &raw mut contains,
131            )
132        };
133        from_result(result)?;
134        Ok(contains)
135    }
136
137    /// Test whether two selection snapshots are equal.
138    ///
139    /// Equality uses the terminal's internal selection semantics: both endpoint
140    /// pins must match and both selections must have the same rectangular/block
141    /// state. This avoids requiring callers to compare raw [`GridRef`] internals.
142    ///
143    /// See [#Preconditions](#preconditions) for the necessary preconditions.
144    pub fn equals(&self, terminal: &'t Terminal<'_, '_>, other: &Self) -> Result<bool> {
145        let mut equal = false;
146        let result = unsafe {
147            ffi::ghostty_terminal_selection_equal(
148                terminal.inner.as_raw(),
149                &self.inner,
150                &other.inner,
151                &raw mut equal,
152            )
153        };
154        from_result(result)?;
155        Ok(equal)
156    }
157
158    /// Get the current endpoint ordering of a selection snapshot.
159    ///
160    /// See [#Preconditions](#preconditions) for the necessary preconditions.
161    pub fn order(&self, terminal: &'t Terminal<'_, '_>) -> Result<Order> {
162        let mut order = ffi::SelectionOrder::FORWARD;
163
164        let result = unsafe {
165            ffi::ghostty_terminal_selection_order(
166                terminal.inner.as_raw(),
167                &self.inner,
168                &raw mut order,
169            )
170        };
171        from_result(result)?;
172        Order::try_from(order).map_err(|_| Error::InvalidValue)
173    }
174
175    /// Return a selection snapshot with endpoints ordered as requested.
176    ///
177    /// Use [`Order::Forward`] to get top-left to bottom-right bounds,
178    /// and [`Order::Reverse`] to get bottom-right to top-left bounds.
179    /// Mirrored desired orders are accepted but normalized the same as forward.
180    /// The output selection is a fresh untracked snapshot and is not installed as
181    /// the terminal's current selection.
182    ///
183    /// See [#Preconditions](#preconditions) for the necessary preconditions.
184    pub fn to_ordered(&self, terminal: &'t Terminal<'_, '_>, desired: Order) -> Result<Self> {
185        let mut selection = ffi::sized!(ffi::Selection);
186        let result = unsafe {
187            ffi::ghostty_terminal_selection_ordered(
188                terminal.inner.as_raw(),
189                &self.inner,
190                desired.into(),
191                &raw mut selection,
192            )
193        };
194        from_result(result)?;
195        Ok(unsafe { Self::from_raw(selection) })
196    }
197}
198
199/// Methods related to [selections](crate::selection).
200impl Terminal<'_, '_> {
201    /// Set the active screen selection.
202    ///
203    /// The selection's grid references must be valid for this terminal's
204    /// active screen at the time of the call.
205    ///
206    /// Passing `None` clears the active screen selection.
207    ///
208    /// This function does not take `&mut self` since it does not invalidate
209    /// any state that relies on the terminal.
210    pub fn set_selection(&self, selection: Option<&Selection<'_>>) -> Result<&Self> {
211        self.set_optional(ffi::TerminalOption::SELECTION, selection.map(|v| &v.inner))?;
212        Ok(self)
213    }
214
215    /// Derive a selection snapshot covering all selectable terminal content.
216    ///
217    /// The returned selection is not installed as the terminal's current selection.
218    pub fn select_all(&self) -> Result<Option<Selection<'_>>> {
219        let mut value = ffi::sized!(ffi::Selection);
220        let result =
221            unsafe { ffi::ghostty_terminal_select_all(self.inner.as_raw(), &raw mut value) };
222
223        let sel = from_optional_result(result, value)?;
224        Ok(sel.map(|v| unsafe {
225            // SAFETY: Selection should be initialized and valid on success
226            Selection::from_raw(v)
227        }))
228    }
229    /// Derive a selection snapshot covering all selectable terminal content.
230    ///
231    /// The returned selection is not installed as the terminal's current selection.
232    pub fn select_line(&self, options: SelectLineOptions) -> Result<Option<Selection<'_>>> {
233        let mut value = ffi::sized!(ffi::Selection);
234
235        let result = unsafe {
236            ffi::ghostty_terminal_select_line(self.inner.as_raw(), &options.inner, &raw mut value)
237        };
238
239        let sel = from_optional_result(result, value)?;
240        Ok(sel.map(|v| unsafe {
241            // SAFETY: Selection should be initialized and valid on success
242            Selection::from_raw(v)
243        }))
244    }
245    /// Derive a command-output selection snapshot from a terminal grid reference.
246    ///
247    /// The returned selection is not installed as the terminal's current selection.
248    pub fn select_output(&self, grid_ref: GridRef<'_>) -> Result<Option<Selection<'_>>> {
249        let mut value = ffi::sized!(ffi::Selection);
250
251        let result = unsafe {
252            ffi::ghostty_terminal_select_output(self.inner.as_raw(), grid_ref.inner, &raw mut value)
253        };
254
255        let sel = from_optional_result(result, value)?;
256        Ok(sel.map(|v| unsafe {
257            // SAFETY: Selection should be initialized and valid on success
258            Selection::from_raw(v)
259        }))
260    }
261    /// Derive a word selection snapshot from a terminal grid reference.
262    ///
263    /// The returned selection is not installed as the terminal's current selection.
264    pub fn select_word(&self, options: SelectWordOptions) -> Result<Option<Selection<'_>>> {
265        let mut value = ffi::sized!(ffi::Selection);
266
267        let result = unsafe {
268            ffi::ghostty_terminal_select_word(self.inner.as_raw(), &options.inner, &raw mut value)
269        };
270
271        let sel = from_optional_result(result, value)?;
272        Ok(sel.map(|v| unsafe {
273            // SAFETY: Selection should be initialized and valid on success
274            Selection::from_raw(v)
275        }))
276    }
277
278    /// Derive the nearest word selection snapshot between two
279    /// terminal grid refs.
280    ///
281    /// Starting at `options.start`, this searches toward `options.end`
282    /// (inclusive) and returns the first selectable word found using
283    /// Ghostty's word-selection rules.
284    ///
285    /// This is useful for implementing double-click-and-drag selection in a UI.
286    /// If a user double-clicks one word and drags across spaces or punctuation
287    /// toward another word, selecting only the word directly under the current
288    /// pointer can flicker or collapse when the pointer is between words.
289    /// Instead, ask for the nearest word between the original click and the
290    /// drag point, ask again in the reverse direction, and combine the two word
291    /// bounds into the drag selection.
292    ///
293    /// The returned selection is not installed as the terminal's current selection.
294    ///
295    /// # Example
296    ///
297    /// ```no_run
298    /// use libghostty_vt::{
299    ///     Error,
300    ///     terminal::{Terminal, Point, PointCoordinate},
301    ///     screen::GridRef,
302    ///     selection::{Selection, SelectWordBetweenOptions},
303    /// };
304    /// # use libghostty_vt::TerminalOptions;
305    /// # fn main() -> libghostty_vt::error::Result<()> {
306    /// # let terminal = Terminal::new(TerminalOptions { cols: 80, rows: 24, max_scrollback: 0 }).unwrap();
307    ///
308    /// // Double-click-and-drag style selection. Suppose the user double-clicks
309    /// // "git" and drags to "status". The pointer may pass over whitespace, so
310    /// // select the nearest word between the original click and current drag point
311    /// // in both directions, then combine the outer word bounds.
312    /// fn ref_at<'t>(terminal: &'t Terminal<'_, '_>, x: u16, y: u32) -> Result<GridRef<'t>, Error> {
313    ///     terminal.grid_ref(Point::Active(PointCoordinate { x, y }))
314    /// }
315    ///
316    /// let click_ref = ref_at(&terminal, 2, 0)?; // the "git" in "git status"
317    /// let drag_ref = ref_at(&terminal, 6, 0)?;  // the "status" in "git status"
318    ///
319    /// let start_word = terminal.select_word_between(
320    ///     SelectWordBetweenOptions::new(click_ref.clone(), drag_ref.clone())
321    /// )?;
322    ///
323    /// let end_word = terminal.select_word_between(
324    ///     SelectWordBetweenOptions::new(drag_ref, click_ref)
325    /// )?;
326    ///
327    /// let drag_selection = Selection::new(
328    ///     start_word.unwrap().start(),
329    ///     end_word.unwrap().end(),
330    ///     false,
331    /// );
332    /// # Ok(())}
333    /// ```
334    pub fn select_word_between(
335        &self,
336        options: SelectWordBetweenOptions,
337    ) -> Result<Option<Selection<'_>>> {
338        let mut value = ffi::sized!(ffi::Selection);
339
340        let result = unsafe {
341            ffi::ghostty_terminal_select_word_between(
342                self.inner.as_raw(),
343                &options.inner,
344                &raw mut value,
345            )
346        };
347
348        let sel = from_optional_result(result, value)?;
349        Ok(sel.map(|v| unsafe {
350            // SAFETY: Selection should be initialized and valid on success
351            Selection::from_raw(v)
352        }))
353    }
354
355    /// Format a terminal selection into an allocated buffer.
356    ///
357    /// This is a one-shot convenience API for formatting either the terminal's
358    /// active selection or a caller-provided [`Selection`] without explicitly
359    /// creating a [`Formatter`](crate::fmt::Formatter).
360    ///
361    /// The returned buffer is allocated using allocator, or the default allocator
362    /// if `None` is passed. The returned bytes are not NUL-terminated.
363    /// This supports plain text, VT, and HTML uniformly as byte output.
364    ///
365    /// If `options.selection` is `None` and the terminal has no active selection,
366    /// the function returns `None`.
367    pub fn format_selection_alloc<'a, 'ctx: 'a>(
368        &self,
369        alloc: Option<&'a Allocator<'ctx>>,
370        options: FormatOptions,
371    ) -> Result<Option<Bytes<'a>>> {
372        let mut out = std::ptr::null_mut();
373        let mut out_len = 0usize;
374        let alloc = alloc.map_or(std::ptr::null(), |v| v.to_raw());
375
376        let result = unsafe {
377            ffi::ghostty_terminal_selection_format_alloc(
378                self.inner.as_raw(),
379                alloc,
380                options.inner,
381                &raw mut out,
382                &raw mut out_len,
383            )
384        };
385
386        let out = from_optional_result(result, out)?;
387        Ok(out
388            .and_then(NonNull::new)
389            .map(|ptr| unsafe { Bytes::from_raw_parts(ptr, out_len, alloc) }))
390    }
391
392    /// Format a terminal selection into a caller-provided buffer.
393    ///
394    /// This is a one-shot convenience API for formatting either the terminal's
395    /// active selection or a caller-provided [`Selection`] without explicitly
396    /// creating a [`Formatter`](crate::fmt::Formatter).
397    ///
398    /// If `buf` is too small, this returns `Err(Error::OutOfSpace { required })`
399    /// where `required` is the required size. The caller can then retry with a
400    /// larger buffer.
401    ///
402    /// If `options.selection` is `None` and the terminal has no active selection,
403    /// the function returns `None`.
404    pub fn format_selection_buf(
405        &self,
406        options: FormatOptions,
407        buf: &mut [u8],
408    ) -> Result<Option<usize>> {
409        let mut written = 0usize;
410
411        let result = unsafe {
412            ffi::ghostty_terminal_selection_format_buf(
413                self.inner.as_raw(),
414                options.inner,
415                buf.as_mut_ptr(),
416                buf.len(),
417                &raw mut written,
418            )
419        };
420
421        from_optional_result_with_len(result, written)
422    }
423}
424
425/// Options for [deriving a line selection](Terminal::select_line)
426/// from a terminal grid reference.
427///
428/// If [`with_whitespace`](Self::with_whitespace) is not called,
429/// Ghostty's default line-trim whitespace codepoints are used.
430#[derive(Clone, Debug)]
431pub struct SelectLineOptions<'t, 'ws> {
432    inner: ffi::TerminalSelectLineOptions,
433    _phan: (PhantomData<&'t ffi::Terminal>, PhantomData<&'ws [char]>),
434}
435impl<'t, 'ws> SelectLineOptions<'t, 'ws> {
436    /// Create a new set of options for [deriving a line selection](Terminal::select_line),
437    /// from the given grid reference.
438    pub fn new(grid_ref: GridRef<'t>) -> Self {
439        Self {
440            inner: ffi::TerminalSelectLineOptions {
441                ref_: grid_ref.inner,
442                whitespace: std::ptr::null(),
443                whitespace_len: 0,
444                semantic_prompt_boundary: false,
445                ..ffi::sized!(ffi::TerminalSelectLineOptions)
446            },
447            _phan: (PhantomData, PhantomData),
448        }
449    }
450
451    /// Specify the codepoints to trim from the start and end of the line.
452    pub fn with_whitespace(mut self, value: &'ws [char]) -> Self {
453        // Note: it's always safe to reinterpret char as a u32,
454        // as long as no mutation occurs.
455        self.inner.whitespace = value.as_ptr().cast();
456        self.inner.whitespace_len = value.len();
457        self
458    }
459
460    /// Specify whether semantic prompt state changes should bound the line
461    /// selection.
462    pub fn with_semantic_prompt_boundary(mut self, value: bool) -> Self {
463        self.inner.semantic_prompt_boundary = value;
464        self
465    }
466}
467
468/// Options for [deriving a word selection](Terminal::select_word)
469/// from a terminal grid reference.
470///
471/// If [`with_boundary_codepoints`](Self::with_boundary_codepoints)
472/// is not called, Ghostty's default word-boundary codepoints are used.
473#[derive(Clone, Debug)]
474pub struct SelectWordOptions<'t, 'bc> {
475    inner: ffi::TerminalSelectWordOptions,
476    _phan: (PhantomData<&'t ffi::Terminal>, PhantomData<&'bc [char]>),
477}
478impl<'t, 'bc> SelectWordOptions<'t, 'bc> {
479    /// Create a new set of options for [deriving a word selection](Terminal::select_word),
480    /// from the given grid reference.
481    pub fn new(grid_ref: GridRef<'t>) -> Self {
482        Self {
483            inner: ffi::TerminalSelectWordOptions {
484                ref_: grid_ref.inner,
485                ..ffi::sized!(ffi::TerminalSelectWordOptions)
486            },
487            _phan: (PhantomData, PhantomData),
488        }
489    }
490
491    /// Specify the word-boundary codepoints.
492    pub fn with_boundary_codepoints(mut self, value: &'bc [char]) -> Self {
493        // Note: it's always safe to reinterpret char as a u32,
494        // as long as no mutation occurs.
495        self.inner.boundary_codepoints = value.as_ptr().cast();
496        self.inner.boundary_codepoints_len = value.len();
497        self
498    }
499}
500
501/// Options for [deriving the nearest word selection](Terminal::select_word_between)
502/// between two grid references.
503///
504/// If [`with_boundary_codepoints`](Self::with_boundary_codepoints)
505/// is not called, Ghostty's default word-boundary codepoints are used.
506#[derive(Debug)]
507pub struct SelectWordBetweenOptions<'t, 'bc> {
508    inner: ffi::TerminalSelectWordBetweenOptions,
509    _phan: (PhantomData<&'t ffi::Terminal>, PhantomData<&'bc [char]>),
510}
511impl<'t, 'bc> SelectWordBetweenOptions<'t, 'bc> {
512    /// Create a new set of options for
513    /// [deriving the nearest word selection](Terminal::select_word_between),
514    /// from the two given grid references.
515    pub fn new(start: GridRef<'t>, end: GridRef<'t>) -> Self {
516        Self {
517            inner: ffi::TerminalSelectWordBetweenOptions {
518                start: start.inner,
519                end: end.inner,
520                ..ffi::sized!(ffi::TerminalSelectWordBetweenOptions)
521            },
522            _phan: (PhantomData, PhantomData),
523        }
524    }
525
526    /// Specify the word-boundary codepoints.
527    pub fn with_boundary_codepoints(mut self, value: &'bc [char]) -> Self {
528        // Note: it's always safe to reinterpret char as a u32,
529        // as long as no mutation occurs.
530        self.inner.boundary_codepoints = value.as_ptr().cast();
531        self.inner.boundary_codepoints_len = value.len();
532        self
533    }
534}
535
536/// Options for [one-shot formatting of a terminal selection](Terminal::format_selection_alloc).
537///
538/// If [`with_selection`](Self::with_selection) is not called, the formatter defaults to
539/// formatting the terminal's active selection. If there is no active
540/// selection, formatting returns `Ok(None)`.
541///
542/// The selection is formatted from the terminal's active screen using the same
543/// formatting semantics as [`Formatter`](crate::fmt::Formatter).
544/// For copy/clipboard behavior matching Ghostty's Screen.selectionString(),
545/// use plain output with unwrap and trim both set to true.
546#[derive(Debug)]
547pub struct FormatOptions<'t, 's> {
548    inner: ffi::TerminalSelectionFormatOptions,
549    _phan: PhantomData<&'s Selection<'t>>,
550}
551impl<'t, 's> FormatOptions<'t, 's> {
552    /// Create a new set of options for one-shot formatting of a
553    /// terminal selection.
554    pub fn new() -> Self {
555        Self {
556            inner: ffi::TerminalSelectionFormatOptions {
557                ..ffi::sized!(ffi::TerminalSelectionFormatOptions)
558            },
559            _phan: PhantomData,
560        }
561    }
562    /// Specify the output format to emit.
563    pub fn with_emit_format(mut self, value: Format) -> Self {
564        self.inner.emit = value.into();
565        self
566    }
567    /// Specify whether to unwrap soft-wrapped lines.
568    pub fn with_unwrap(mut self, value: bool) -> Self {
569        self.inner.unwrap = value;
570        self
571    }
572    /// Specify whether to trim trailing whitespace on non-blank lines.
573    pub fn with_trim(mut self, value: bool) -> Self {
574        self.inner.trim = value;
575        self
576    }
577    /// Specify the selection to format in place of the terminal's active selection.
578    ///
579    /// The selection must be a [valid snapshot selection](Selection#preconditions)
580    /// for this terminal.
581    pub fn with_selection(mut self, value: &'s Selection<'t>) -> Self {
582        self.inner.selection = &value.inner;
583        self
584    }
585}
586impl Default for FormatOptions<'_, '_> {
587    fn default() -> Self {
588        Self::new()
589    }
590}
591
592/// Operation used to adjust a selection endpoint.
593///
594/// Adjustment mutates the selection's logical end endpoint, not whichever
595/// endpoint is visually bottom/right. This preserves keyboard and drag behavior
596/// for both forward and reversed selections.
597#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
598#[repr(u32)]
599#[non_exhaustive]
600pub enum Adjustment {
601    /// Move left to the previous non-empty cell, wrapping upward.
602    Left = ffi::SelectionAdjust::LEFT,
603    /// Move right to the next non-empty cell, wrapping downward.
604    Right = ffi::SelectionAdjust::RIGHT,
605    /// Move up one row at the current column,
606    /// or to the beginning of the line if already at the top.
607    Up = ffi::SelectionAdjust::UP,
608    /// Move down to the next non-blank row at the current column,
609    /// or to the end of the line if none exists.
610    Down = ffi::SelectionAdjust::DOWN,
611    /// Move to the top-left cell of the screen.
612    Home = ffi::SelectionAdjust::HOME,
613    /// Move to the right edge of the last non-blank row on the screen.
614    End = ffi::SelectionAdjust::END,
615    /// Move up by one terminal page height,
616    /// or to home if that would move past the top.
617    PageUp = ffi::SelectionAdjust::PAGE_UP,
618    /// Move down by one terminal page height,
619    /// or to end if that would move past the bottom.
620    PageDown = ffi::SelectionAdjust::PAGE_DOWN,
621    /// Move to the left edge of the current line.
622    BeginningOfLine = ffi::SelectionAdjust::BEGINNING_OF_LINE,
623    /// Move to the right edge of the current line.
624    EndOfLine = ffi::SelectionAdjust::END_OF_LINE,
625}
626
627/// Ordering of a selection's endpoints in terminal coordinates.
628///
629/// Mirrored orders are only produced by rectangular selections whose start
630/// and end endpoints are on opposite diagonal corners that are not simple
631/// top-left-to-bottom-right or bottom-right-to-top-left orderings.
632#[derive(Clone, Copy, Debug, PartialEq, Eq, int_enum::IntEnum)]
633#[repr(u32)]
634#[non_exhaustive]
635pub enum Order {
636    /// Start is before end in top-left to bottom-right order.
637    Forward = ffi::SelectionOrder::FORWARD,
638    /// End is before start in top-left to bottom-right order.
639    Reverse = ffi::SelectionOrder::REVERSE,
640    /// Rectangular selection from top-right to bottom-left.
641    MirroredForward = ffi::SelectionOrder::MIRRORED_FORWARD,
642    /// Rectangular selection from bottom-left to top-right.
643    MirroredReverse = ffi::SelectionOrder::MIRRORED_REVERSE,
644}