Haskeline

repository·master·Indexed 42 days ago

https://github.com/haskell/haskeline

A Haskell-native library providing a rich line-editing interface for command-line applications, serving as an alternative to GNU Readline. It features history recall, incremental search, Unicode support, and compatibility with POSIX and Windows. The library includes support for custom tab completion via completionCmd, a Kill Ring for cut/paste operations, and an undo/redo mechanism using the Save typeclass.

Tokens
2.2K
Snippets
8
Records
13
Agent score
54%

What's inside Haskeline

  1. Overview of Haskeline

    master
    Haskeline is a Haskell library that provides a user interface for line input in command-line programs. It is designed to be a Haskell-native alternative to readline, offering a rich line-editing interface with features like history recall, incremental search, and Unicode support. It is compatible with POSIX systems (via terminfo) and Windows (via MinGW).
  2. Implement custom tab completion

    master
    Haskeline supports custom tab completion functions. These functions are highly flexible as they can be written to run in an arbitrary monad, allowing completion logic to interact with your application's state or environment.
  3. Customize Haskeline via ~/.haskeline

    master

    Users can customize their Haskeline experience by creating and modifying a ~/.haskeline configuration file. This file allows for the definition of:

    • User Preferences: General settings for the line-editing interface.
    • Custom Key Bindings: Mapping specific keys to actions within the interface.
  4. How the Undo/Redo mechanism works

    master

    The undo/redo system is managed by the Undo data type, which tracks two lists of states: pastUndo (the history of previous states) and futureRedo (the history of states that were undone and can be re-applied).

    State Transitions

    • Saving a state: When a new state is saved, it is prepended to pastUndo, and the futureRedo list is cleared. This prevents 'branching' histories.
    • Undoing: The current state is saved into futureRedo, and the top element of pastUndo is restored as the current state.
    • Redoing: The current state is saved into pastUndo, and the top element of futureRedo is restored as the current state.

    Requirements

    The state type s must implement the Save typeclass, which provides save (to capture a snapshot) and restore (to revert to a snapshot) methods.

  5. Configure completion display modes via `CompletionType`

    master

    The way completions are presented to the user is determined by the completionType field in your Prefs. The CompletionType type controls the interaction model when multiple matches exist:

    • MenuCompletion: Allows the user to cycle through available completions using the completion key.
    • ListCompletion: Displays the completions as a list. This may involve paging if the list is long or if completionPaging is enabled in Prefs.
    data CompletionType = MenuCompletion | ListCompletion
  6. Manage the Kill Ring with runKillRing

    master

    The Kill Ring is a stack-based buffer used for cut/paste operations (similar to a clipboard). To use any Kill Ring commands, you must wrap your action in runKillRing. This initializes an empty stack and provides the necessary environment for managing the ring's state.

    runKillRing takes a ReaderT (IORef KillRing) m a action and executes it within a MonadIO context.

    runKillRing :: MonadIO m => ReaderT (IORef KillRing) m a -> m a
  7. Perform undo and redo operations in Haskeline

    master

    Haskeline provides commands to manage undo and redo operations for the line state. These commands rely on the Save typeclass to capture and restore snapshots of the line state.

    • commandUndo: Reverts the current line state to the previous state stored in the undo history. This clears the 'redo' history.
    • commandRedo: Re-applies a state from the redo history. This moves the current state into the 'undo' history.

    To use these, your line state must implement the Save typeclass, and you must be operating within a monad that has Undo state (typically via UndoT).

    -- Assuming a monad m that supports Undo state and a state s that implements Save
    -- Use commandUndo to revert changes
    result <- commandUndo
    
    -- Use commandRedo to re-apply changes
    result <- commandRedo
  8. Execute kill and copy operations using KillHelper

    master

    Haskeline uses KillHelper to define how text is selected and removed (killed) or copied from the buffer.

    KillHelper Types

    • SimpleMove (InsertMode -> InsertMode): Used for standard movements where the text to be killed is the difference between the current position and a new position.
    • GenericKill (InsertMode -> ([Grapheme], InsertMode)): Provides full control over which graphemes are captured and what the resulting InsertMode should be. This is used for complex commands like dd or cc.

    Command Functions

    • killFromHelper: Removes text from the buffer and pushes it onto the Kill Ring. This operation is undoable.
    • killFromArgHelper: A variant of killFromHelper designed for use within ArgMode (when the command is part of a multi-argument sequence).
    • copyFromArgHelper: Copies text to the Kill Ring without modifying the current state (non-destructive).
    data KillHelper = SimpleMove (InsertMode -> InsertMode)
                    | GenericKill (InsertMode -> ([Grapheme], InsertMode))
    
    killFromHelper :: (MonadState KillRing m, MonadState Undo m, Save s, Save t) 
                   => KillHelper -> Command m s t
    
    killFromArgHelper :: (MonadState KillRing m, MonadState Undo m, Save s, Save t) 
                      => KillHelper -> Command m (ArgMode s) t
    
    copyFromArgHelper :: (MonadState KillRing m, MonadState s) 
                       => KillHelper -> Command m (ArgMode s) s
  9. Save current state to the undo history

    master

    To ensure that changes can be undone, you must manually trigger a save operation using saveForUndo. This function captures the current state and adds it to the pastUndo list. If the new state is identical to the most recent state in the history, it avoids creating a duplicate entry.

    This is typically used after a user action that modifies the input line.

    -- Within a Command m s s context where m has MonadState Undo
    saveForUndo s
  10. Perform paste operations with pasteCommand

    master

    The pasteCommand function allows you to insert the most recent item from the Kill Ring into the current state.

    It requires a function use that defines how the retrieved [Grapheme] (the text from the ring) should be applied to the current state s. It also integrates with the Undo mechanism, saving the current state before applying the paste so it can be undone.

    Parameters:

    • use: A function of type ([Grapheme] -> s -> s) that describes how to merge the pasted text into the state.
    pasteCommand :: (Save s, MonadState KillRing m, MonadState Undo m) 
                  => ([Grapheme] -> s -> s) 
                  -> Command m (ArgMode s) s
  11. Implement custom tab completion with `completionCmd`

    master

    To add tab completion functionality to your Haskeline-based application, use the completionCmd function. This function creates a KeyCommand that handles the logic of looking up completions, managing undo states, and presenting results to the user (either by inserting the best match or showing a list/menu).

    completionCmd requires the monad m to satisfy both MonadState Undo m and CommandMonad m.

    It behaves as follows:

    • If no completions are found: Triggers a RingBell effect.
    • If exactly one completion is found: Automatically inserts the completion (and a trailing space if the completion is considered 'finished').
    • If multiple completions are found: Presents them to the user based on the completionType configured in your Prefs (either MenuCompletion or ListCompletion).
    completionCmd :: (MonadState Undo m, CommandMonad m)
                    => Key -> KeyCommand m InsertMode InsertMode
  12. Reference: Completion types and logic

    master

    The following types and functions are used to manage the completion lifecycle in Haskeline:

    • CompletionFunc: A type for defining custom completion logic.
    • Completion: A data type representing a single completion result.
    • CompletionType: Determines if completions are shown as a menu or a list.
    • completionCmd: The primary constructor for a tab-completion key command.