Skip to content

Commit

Permalink
documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
alexpasmantier committed Nov 5, 2024
1 parent dff8035 commit 6d48c87
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 36 deletions.
37 changes: 30 additions & 7 deletions crates/television/action.rs
Original file line number Diff line number Diff line change
@@ -1,48 +1,71 @@
use serde::{Deserialize, Serialize};
use strum::Display;

/// The different actions that can be performed by the application.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Display)]
pub enum Action {
// input actions
/// Add a character to the input buffer.
AddInputChar(char),
/// Delete the character before the cursor from the input buffer.
DeletePrevChar,
/// Delete the character after the cursor from the input buffer.
DeleteNextChar,
/// Move the cursor to the character before the current cursor position.
GoToPrevChar,
/// Move the cursor to the character after the current cursor position.
GoToNextChar,
/// Move the cursor to the start of the input buffer.
GoToInputStart,
/// Move the cursor to the end of the input buffer.
GoToInputEnd,
// rendering actions
/// Render the terminal user interface screen.
Render,
/// Resize the terminal user interface screen to the given dimensions.
Resize(u16, u16),
/// Clear the terminal user interface screen.
ClearScreen,
// results actions
/// Select the entry currently under the cursor.
SelectEntry,
/// Select the entry currently under the cursor and exit the application.
SelectAndExit,
/// Select the next entry in the currently focused list.
SelectNextEntry,
/// Select the previous entry in the currently focused list.
SelectPrevEntry,
/// Copy the currently selected entry to the clipboard.
CopyEntryToClipboard,
// navigation actions
GoToPaneUp,
GoToPaneDown,
GoToPaneLeft,
GoToPaneRight,
GoToNextPane,
GoToPrevPane,
// preview actions
/// Scroll the preview up by one line.
ScrollPreviewUp,
/// Scroll the preview down by one line.
ScrollPreviewDown,
/// Scroll the preview up by half a page.
ScrollPreviewHalfPageUp,
/// Scroll the preview down by half a page.
ScrollPreviewHalfPageDown,
/// Open the currently selected entry in the default application.
OpenEntry,
// application actions
/// Tick the application state.
Tick,
/// Suspend the application.
Suspend,
/// Resume the application.
Resume,
/// Quit the application.
Quit,
/// Toggle the help screen.
Help,
/// Signal an error with the given message.
Error(String),
/// No operation.
NoOp,
// channel actions
/// Toggle the remote control channel.
ToggleRemoteControl,
/// Toggle the remote control in `send to channel` mode.
ToggleSendToChannel,
}
44 changes: 44 additions & 0 deletions crates/television/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,29 @@ use crate::{
render::{render, RenderingTask},
};

/// The main application struct that holds the state of the application.
pub struct App {
/// The configuration of the application.
config: Config,
// maybe move these two into config instead of passing them
// via the cli?
tick_rate: f64,
frame_rate: f64,
/// The television instance that handles channels and entries.
television: Arc<Mutex<Television>>,
/// A flag that indicates whether the application should quit during the next frame.
should_quit: bool,
/// A flag that indicates whether the application should suspend during the next frame.
should_suspend: bool,
/// A sender channel for actions.
action_tx: mpsc::UnboundedSender<Action>,
/// The receiver channel for actions.
action_rx: mpsc::UnboundedReceiver<Action>,
/// The receiver channel for events.
event_rx: mpsc::UnboundedReceiver<Event<Key>>,
/// A sender channel to abort the event loop.
event_abort_tx: mpsc::UnboundedSender<()>,
/// A sender channel for rendering tasks.
render_tx: mpsc::UnboundedSender<RenderingTask>,
}

Expand Down Expand Up @@ -57,6 +67,20 @@ impl App {
})
}

/// Run the application main loop.
///
/// This function will start the event loop and the rendering loop and handle
/// all actions that are sent to the application.
/// The function will return the selected entry if the application is exited.
///
/// # Arguments
/// * `is_output_tty` - A flag that indicates whether the output is a tty.
///
/// # Returns
/// The selected entry (if any) if the application is exited.
///
/// # Errors
/// If an error occurs during the execution of the application.
pub async fn run(&mut self, is_output_tty: bool) -> Result<Option<Entry>> {
info!("Starting backend event loop");
let event_loop = EventLoop::new(self.tick_rate, true);
Expand Down Expand Up @@ -107,6 +131,16 @@ impl App {
}
}

/// Convert an event to an action.
///
/// This function will convert an event to an action based on the current
/// mode the television is in.
///
/// # Arguments
/// * `event` - The event to convert to an action.
///
/// # Returns
/// The action that corresponds to the given event.
async fn convert_event_to_action(&self, event: Event<Key>) -> Action {
match event {
Event::Input(keycode) => {
Expand Down Expand Up @@ -144,6 +178,16 @@ impl App {
}
}

/// Handle actions.
///
/// This function will handle all actions that are sent to the application.
/// The function will return the selected entry if the application is exited.
///
/// # Returns
/// The selected entry (if any) if the application is exited.
///
/// # Errors
/// If an error occurs during the execution of the application.
async fn handle_actions(&mut self) -> Result<Option<Entry>> {
while let Ok(action) = self.action_rx.try_recv() {
if action != Action::Tick && action != Action::Render {
Expand Down
52 changes: 29 additions & 23 deletions crates/television/channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ mod text;

/// The interface that all television channels must implement.
///
/// # Important
/// The `OnAir` requires the `Send` trait to be implemented as
/// well. This is necessary to allow the channels to be used in a
/// multithreaded environment.
/// # Note
/// The `OnAir` trait requires the `Send` trait to be implemented as well.
/// This is necessary to allow the channels to be used with the tokio
/// runtime, which requires `Send` in order to be able to send tasks between
/// worker threads safely.
///
/// # Methods
/// - `find`: Find entries that match the given pattern. This method does not
Expand Down Expand Up @@ -139,6 +140,25 @@ pub enum TelevisionChannel {
RemoteControl(remote_control::RemoteControl),
}

/// NOTE: this could/should be generated by a macro
impl TryFrom<&Entry> for TelevisionChannel {
type Error = String;

fn try_from(entry: &Entry) -> Result<Self, Self::Error> {
match entry.name.to_ascii_lowercase().as_ref() {
"env" => Ok(TelevisionChannel::Env(env::Channel::default())),
"files" => Ok(TelevisionChannel::Files(files::Channel::default())),
"gitrepos" => {
Ok(TelevisionChannel::GitRepos(git_repos::Channel::default()))
}
"text" => Ok(TelevisionChannel::Text(text::Channel::default())),
"stdin" => Ok(TelevisionChannel::Stdin(stdin::Channel::default())),
"alias" => Ok(TelevisionChannel::Alias(alias::Channel::default())),
_ => Err(format!("Unknown channel: {}", entry.name)),
}
}
}

macro_rules! variant_to_module {
(Files) => {
files::Channel
Expand Down Expand Up @@ -247,27 +267,13 @@ macro_rules! define_transitions {
}
}

// Define the transitions between the different channels.
//
// This is where the transitions between the different channels are defined.
// The transitions are defined as a list of tuples where the first element
// is the source channel and the second element is a list of potential target channels.
define_transitions! {
Text => [Files, Text],
Files => [Files, Text],
GitRepos => [Files, Text],
}

/// NOTE: this could/should be generated by a macro
impl TryFrom<&Entry> for TelevisionChannel {
type Error = String;

fn try_from(entry: &Entry) -> Result<Self, Self::Error> {
match entry.name.to_ascii_lowercase().as_ref() {
"env" => Ok(TelevisionChannel::Env(env::Channel::default())),
"files" => Ok(TelevisionChannel::Files(files::Channel::default())),
"gitrepos" => {
Ok(TelevisionChannel::GitRepos(git_repos::Channel::default()))
}
"text" => Ok(TelevisionChannel::Text(text::Channel::default())),
"stdin" => Ok(TelevisionChannel::Stdin(stdin::Channel::default())),
"alias" => Ok(TelevisionChannel::Alias(alias::Channel::default())),
_ => Err(format!("Unknown channel: {}", entry.name)),
}
}
}
43 changes: 37 additions & 6 deletions crates/television/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,56 @@ use devicons::FileIcon;

use crate::previewers::PreviewType;

/// NOTE: having an enum for entry types would be nice since it would allow
/// having a nicer implementation for transitions between channels. This would
/// permit implementing `From<EntryType>` for channels which would make the
/// channel convertible from any other that yields `EntryType`.
/// This needs pondering since it does bring another level of abstraction and
/// adds a layer of complexity.
// NOTE: having an enum for entry types would be nice since it would allow
// having a nicer implementation for transitions between channels. This would
// permit implementing `From<EntryType>` for channels which would make the
// channel convertible from any other that yields `EntryType`.
// This needs pondering since it does bring another level of abstraction and
// adds a layer of complexity.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Entry {
/// The name of the entry.
pub name: String,
/// The display name of the entry.
display_name: Option<String>,
/// An optional value associated with the entry.
pub value: Option<String>,
/// The optional ranges for matching characters in the name.
pub name_match_ranges: Option<Vec<(u32, u32)>>,
/// The optional ranges for matching characters in the value.
pub value_match_ranges: Option<Vec<(u32, u32)>>,
/// The optional icon associated with the entry.
pub icon: Option<FileIcon>,
/// The optional line number associated with the entry.
pub line_number: Option<usize>,
/// The type of preview associated with the entry.
pub preview_type: PreviewType,
}

impl Entry {
/// Create a new entry with the given name and preview type.
///
/// Additional fields can be set using the builder pattern.
/// ```
/// use television::entry::{Entry, PreviewType};
/// use television::devicons::FileIcon;
///
/// let entry = Entry::new("name".to_string(), PreviewType::EnvVar)
/// .with_display_name("display_name".to_string())
/// .with_value("value".to_string())
/// .with_name_match_ranges(vec![(0, 1)])
/// .with_value_match_ranges(vec![(0, 1)])
/// .with_icon(FileIcon::default())
/// .with_line_number(0);
/// ```
///
/// # Arguments
/// * `name` - The name of the entry.
/// * `preview_type` - The type of preview associated with the entry.
///
/// # Returns
/// A new entry with the given name and preview type.
/// The other fields are set to `None` by default.
pub fn new(name: String, preview_type: PreviewType) -> Self {
Self {
name,
Expand Down

0 comments on commit 6d48c87

Please sign in to comment.