editor_types/
application.rs

1//! # Application Customization
2//!
3//! ## Overview
4//!
5//! This module contains traits that library consumers can use for extending modalkit to better fit
6//! their own needs.
7//!
8//! ## Example
9//!
10//! ```
11//! use std::fmt;
12//! use std::path::PathBuf;
13//!
14//! use editor_types::{
15//!     application::{
16//!         ApplicationAction,
17//!         ApplicationError,
18//!         ApplicationWindowId,
19//!         ApplicationContentId,
20//!         ApplicationInfo,
21//!         ApplicationStore,
22//!     },
23//!     context::EditContext,
24//!     prelude::*,
25//! };
26//! use keybindings::SequenceStatus;
27//!
28//! // Unique identifier for a review.
29//! #[derive(Clone, Debug, Eq, Hash, PartialEq)]
30//! struct ReviewId(usize);
31//!
32//! // Unique identifier for a user.
33//! #[derive(Clone, Debug, Eq, Hash, PartialEq)]
34//! struct UserId(usize);
35//!
36//! #[derive(Clone, Debug, Eq, PartialEq)]
37//! enum CodeReviewAction {
38//!     // Approve a review for merging.
39//!     Approve(ReviewId),
40//!
41//!     // Leave a comment on a line in a file in a review.
42//!     Comment(ReviewId, PathBuf, usize, String),
43//!
44//!     // Show more lines around the hunk.
45//!     ExpandHunk(Count),
46//!
47//!     // Merge changes after review and approval.
48//!     Merge(ReviewId),
49//! }
50//!
51//! impl ApplicationAction for CodeReviewAction {
52//!     fn is_edit_sequence(&self, _: &EditContext) -> SequenceStatus {
53//!         match self {
54//!             CodeReviewAction::Approve(..) => SequenceStatus::Break,
55//!             CodeReviewAction::Comment(..) => SequenceStatus::Break,
56//!             CodeReviewAction::ExpandHunk(..) => SequenceStatus::Atom,
57//!             CodeReviewAction::Merge(..) => SequenceStatus::Break,
58//!         }
59//!     }
60//!
61//!     fn is_last_action(&self, _: &EditContext) -> SequenceStatus {
62//!         match self {
63//!             CodeReviewAction::Approve(..) => SequenceStatus::Atom,
64//!             CodeReviewAction::Comment(..) => SequenceStatus::Atom,
65//!             CodeReviewAction::ExpandHunk(..) => SequenceStatus::Atom,
66//!             CodeReviewAction::Merge(..) => SequenceStatus::Atom,
67//!         }
68//!     }
69//!
70//!     fn is_last_selection(&self, _: &EditContext) -> SequenceStatus {
71//!         match self {
72//!             CodeReviewAction::Approve(..) => SequenceStatus::Ignore,
73//!             CodeReviewAction::Comment(..) => SequenceStatus::Ignore,
74//!             CodeReviewAction::ExpandHunk(..) => SequenceStatus::Ignore,
75//!             CodeReviewAction::Merge(..) => SequenceStatus::Ignore,
76//!         }
77//!     }
78//!
79//!     fn is_switchable(&self, _: &EditContext) -> bool {
80//!         match self {
81//!             CodeReviewAction::Approve(..) => false,
82//!             CodeReviewAction::Comment(..) => false,
83//!             CodeReviewAction::ExpandHunk(..) => false,
84//!             CodeReviewAction::Merge(..) => false,
85//!         }
86//!     }
87//! }
88//!
89//! struct CodeReviewStore {
90//!     user: UserId,
91//! }
92//!
93//! impl ApplicationStore for CodeReviewStore {}
94//!
95//! #[derive(Clone, Debug, Eq, Hash, PartialEq)]
96//! enum CodeReviewWindowId {
97//!     // A window that shows a code review.
98//!     Review(ReviewId),
99//!
100//!     // A window that shows a user's open reviews.
101//!     User(UserId),
102//! }
103//!
104//! impl ApplicationWindowId for CodeReviewWindowId {}
105//!
106//! #[derive(Clone, Debug, Eq, Hash, PartialEq)]
107//! enum CodeReviewContentId {
108//!     // Different buffer used by the command bar.
109//!     Command(CommandType),
110//!
111//!     // Buffer for a comment left on a line.
112//!     Review(ReviewId, usize),
113//! }
114//!
115//! impl ApplicationContentId for CodeReviewContentId {}
116//!
117//! #[derive(Debug)]
118//! enum CodeReviewError {
119//!     NoReview(ReviewId),
120//! }
121//!
122//! impl fmt::Display for CodeReviewError {
123//!    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124//!        match self {
125//!            CodeReviewError::NoReview(id) => write!(f, "No review with ID {:?}", id),
126//!        }
127//!    }
128//! }
129//!
130//! impl ApplicationError for CodeReviewError {}
131//!
132//! #[derive(Clone, Debug, Eq, PartialEq)]
133//! enum CodeReviewInfo {}
134//!
135//! impl ApplicationInfo for CodeReviewInfo {
136//!     type Error = CodeReviewError;
137//!     type Action = CodeReviewAction;
138//!     type Store = CodeReviewStore;
139//!     type WindowId = CodeReviewWindowId;
140//!     type ContentId = CodeReviewContentId;
141//!
142//!     fn content_of_command(ct: CommandType) -> CodeReviewContentId {
143//!         CodeReviewContentId::Command(ct)
144//!     }
145//! }
146//! ```
147use std::fmt::{Debug, Display};
148use std::hash::Hash;
149
150use crate::context::EditContext;
151use crate::prelude::CommandType;
152use keybindings::SequenceStatus;
153
154/// Trait for objects that describe application-specific actions.
155///
156/// Implementors of this trait can be used with [Action::Application]. This can then be used to
157/// create additional keybindings and commands on top of the more editing-specific ones provided
158/// by this crate.
159///
160/// [Action::Application]: crate::Action::Application
161pub trait ApplicationAction: Clone + Debug + Eq + PartialEq + Send {
162    /// Allows controlling how application-specific actions are included in
163    /// [RepeatType::EditSequence](crate::prelude::RepeatType::EditSequence).
164    fn is_edit_sequence(&self, ctx: &EditContext) -> SequenceStatus;
165
166    /// Allows controlling how application-specific actions are included in
167    /// [RepeatType::LastAction](crate::prelude::RepeatType::LastAction).
168    fn is_last_action(&self, ctx: &EditContext) -> SequenceStatus;
169
170    /// Allows controlling how application-specific actions are included in
171    /// [RepeatType::LastSelection](crate::prelude::RepeatType::LastSelection).
172    fn is_last_selection(&self, ctx: &EditContext) -> SequenceStatus;
173
174    /// Allows controlling whether an application-specific action should be retried in
175    /// a new buffer when the currently targeted buffer returns a `WrongBuffer`-style error.
176    ///
177    /// For example, jumping to a mark in a different buffer when the current one doesn't
178    /// contain the mark.
179    fn is_switchable(&self, ctx: &EditContext) -> bool;
180}
181
182impl ApplicationAction for () {
183    fn is_edit_sequence(&self, _: &EditContext) -> SequenceStatus {
184        SequenceStatus::Break
185    }
186
187    fn is_last_action(&self, _: &EditContext) -> SequenceStatus {
188        SequenceStatus::Ignore
189    }
190
191    fn is_last_selection(&self, _: &EditContext) -> SequenceStatus {
192        SequenceStatus::Ignore
193    }
194
195    fn is_switchable(&self, _: &EditContext) -> bool {
196        false
197    }
198}
199
200/// Trait for application-specific errors.
201pub trait ApplicationError: Debug + Display {}
202
203impl ApplicationError for String {}
204
205/// Trait for objects that hold application-specific information.
206pub trait ApplicationStore {}
207
208impl ApplicationStore for () {}
209
210/// Trait for window identifiers in an application.
211pub trait ApplicationWindowId: Clone + Debug + Eq + Hash + Send {}
212
213impl ApplicationWindowId for () {}
214impl ApplicationWindowId for usize {}
215impl ApplicationWindowId for Option<usize> {}
216impl ApplicationWindowId for String {}
217
218/// Trait for identifiers of specific content within a window in an application.
219pub trait ApplicationContentId: Clone + Debug + Eq + Hash + Send {}
220
221impl ApplicationContentId for () {}
222impl ApplicationContentId for usize {}
223impl ApplicationContentId for Option<usize> {}
224impl ApplicationContentId for String {}
225
226/// Trait for objects that describe application-specific behaviour and types.
227#[allow(unused)]
228pub trait ApplicationInfo: Clone + Debug + Eq + PartialEq {
229    /// An application-specific error type.
230    type Error: ApplicationError;
231
232    /// The type for application-specific actions.
233    type Action: ApplicationAction;
234
235    /// The type for application-specific storage.
236    type Store: ApplicationStore;
237
238    /// The type for application-specific windows.
239    type WindowId: ApplicationWindowId;
240
241    /// The type for application-specific content within a window.
242    type ContentId: ApplicationContentId;
243
244    /// Get the [ApplicationContentId] used to show a given command type.
245    fn content_of_command(cmdtype: CommandType) -> Self::ContentId;
246}
247
248/// A default implementor of [ApplicationInfo] for consumers that don't require any customization.
249#[derive(Clone, Debug, Eq, PartialEq)]
250pub enum EmptyInfo {}
251
252impl ApplicationInfo for EmptyInfo {
253    type Error = String;
254    type Action = ();
255    type Store = ();
256    type WindowId = String;
257    type ContentId = String;
258
259    fn content_of_command(cmdtype: CommandType) -> String {
260        match cmdtype {
261            CommandType::Search => "*search*".into(),
262            CommandType::Command => "*command*".into(),
263        }
264    }
265}