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}