sniprun

repository·master·Indexed 23 days ago

https://github.com/michaelb/sniprun

A high-performance Neovim plugin for executing code snippets directly from the editor. Written in Lua and Rust, it supports both interpreted and compiled languages with REPL-like stateful execution. It offers multiple visualization modes including virtual text, floating windows, and terminals, and features a 'live mode' for real-time evaluation as you type.

Tokens
19.4K
Snippets
66
Records
115
Agent score
82%

What's inside sniprun

  1. Overview of Sniprun features and capabilities

    master

    Sniprun is a Neovim plugin (written in Lua and Rust) designed for fast, partial code testing. It blurs the line between a standard editor, a Jupyter-like notebook, and a REPL.

    Key Capabilities:

    • Language Support: Supports a wide range of compiled and interpreted languages. It can even run virtually any language via generic interpreters.
    • REPL-like Behavior: For supported languages (like Python, Julia, Lua, JS/TS via deno, Rust via evcxr, etc.), you can run code that depends on previously executed snippets, maintaining state like a REPL.
    • Result Display Modes: Results can be shown via multiple modes, which can be enabled simultaneously:
      • Classic: Standard output display.
      • Virtual Text/Virtual Line: Inline display in the buffer.
      • Temporary Floating Window: A pop-up window for results.
      • Terminal: Running output in a terminal buffer.
      • Notification: Using nvim-notify style popups.
    • Advanced Execution: Supports running GUI plots, network requests, Ansible playbooks, and even 'live mode' (executing on every keystroke).
    • Literate Programming: Supports Markdown, Orgmode, and Neorg.
  2. Implement a REPL-capable interpreter for sniprun

    master

    To make a language support REPL-like behavior (persisting variables across runs) in sniprun, you must implement the ReplLikeInterpreter trait instead of the standard Interpreter trait.

    There are two primary strategies:

    1. State Persistence: Using language-specific 'quirks' to save and load variables to/from a file (e.g., Python's klepto module).
    2. Pipe-based Runner (Recommended): Using a named pipe (FIFO) to communicate with a live, running interpreter process. The code sent by sniprun is piped into the FIFO, and the interpreter's output is written to a file. Sniprun monitors this file for specific 'landmarks' to determine when execution starts and ends.

    Note: You should disable interpreter prompts (e.g., setting sys.ps1 and sys.ps2 to "" in Python) to prevent them from polluting stdout.

  3. Understand language support levels

    master

    Sniprun supports languages at different levels of depth. Understanding these levels helps you know what kind of code you can run:

    • Unsupported/Untested: May work at a 'bloc' level using community configurations for the generic interpreter.
    • Line: Only single lines of code work (e.g., print([x**2 for x in range(10)])). Variables defined elsewhere will not be recognized.
    • Bloc: You can select any semantically correct piece of code (independent of indentation) in visual mode and run it.
    • Import: Supports external imports, allowing you to test code blocks that depend on external libraries.

    Note: Future/unsupported features include 'File' (recursive variable/function finding) and 'Project' (automatic project root detection and relative import handling).

  4. Known limitations of Sniprun

    master

    Sniprun may encounter issues with programs that:

    • Manipulate stdout or stderr directly.
    • Require input from stdin.
    • Print excessive lines or incorrect UTF-8 characters.
    • Access files using relative paths. Because Sniprun's current working directory is typically ~/.cache/sniprun, relative imports or file access may fail.
  5. Use the Lua_nvim interpreter in pseudo-REPL mode

    master

    The Lua_nvim interpreter operates in a pseudo-REPL (Read-Eval-Print Loop) mode that cannot be disabled. This means that variables and state persist between consecutive sniprun executions. Because it runs within Neovim, you have access to the standard Neovim Lua API functions during execution.

    When running Lua scripts line-by-line or block-by-block, the interpreter maintains the state of previously defined variables.

    a = 4 
    b = 6
    print(a+5) -- <- 9
    
    a = 0
    
    print(a + b) -- <- 6
  6. Understand Mathematica REPL-mode quirks

    master

    When repl_enable = {'Mathematica_original'} is active, Sniprun launches a WolframKernel instance in the background.

    Key constraints and behaviors:

    • Single Instance: The REPL session is only usable by one Neovim instance. Attempting to use it in multiple instances will likely cause crashes.
    • Lifecycle: The session closes when the last Neovim instance quits.
    • Startup: The very first SnipRun command executed in a Neovim instance is used solely to start the REPL kernel; the actual code selection is discarded. You must re-run your selection after the kernel has started.
    • Output Suppression: Just like in standard mode, suffix expressions with ; to suppress output. It is strongly recommended to suffix Plots with ; to avoid unwanted output behavior.
  7. Important API constraints and warnings

    master

    When using the Sniprun API, be aware of the following:

    • Buffer Coupling: Sniprun is tightly coupled to the current Neovim buffer and instance.
    • Interpreter Behavior: While there should be no dependencies for non-REPL or Bloc-Level interpreters, REPL-capable or Import-level (or higher) interpreters may attempt to fetch information from the current buffer. Ensure your environment accounts for this if running in non-standard contexts.
  8. How the F# fifo REPL implementation works

    master

    The FSharp_fifo interpreter uses a REPL (Read-Eval-Print Loop) pattern based on named pipes (FIFOs) to improve performance.

    When a FIFO-based interpreter receives its first run command, it forks to the background and executes an initialization script (ressources/init_repl.sh). This script sets up a named pipe to act as the communication channel between Sniprun and the language process.

    Note: Currently, the REPL mode for dotnet fsi is broken because dotnet fsi is sensitive to how its stdin is managed via pipes. If you are using a different language that supports a stable interactive mode (similar to python -i), the REPL mechanism should function correctly.

  9. Quickstart Sniprun installation and basic commands

    master

    To install Sniprun using a plugin manager (like lazy.nvim), use the following configuration:

    { "michaelb/sniprun", build = "sh install.sh" }

    Once installed, you can use these core Neovim commands:

    • :SnipRun: Executes the current code snippet (e.g., a visual selection or current line).
    • :'<,'>SnipRun: Executes the selected range in visual mode.
    • :SnipInfo: Displays information about the current snippet.
  10. Manually replicate the FIFO REPL pattern

    master

    If you want to test or replicate how Sniprun implements a REPL using named pipes (FIFOs), follow these steps:

    1. Create a named pipe: mkfifo pipe_in
    2. Create a launcher script (e.g., launcher.sh) that reads from the pipe and pipes into your interpreter:
      #!/bin/bash
      cat pipe_in | dotnet fsi 
    3. Launch the script in the background: bash ./launcher.sh & (or redirect output: bash ./launcher.sh > out.txt &)
    4. Keep the pipe open to prevent the interpreter from exiting: sleep 3600 > pipe_in &
    5. Send commands to the pipe: echo "printfn \" hey \" " > pipe_in or cat hello_world.fsx > pipe_in

    The results will appear in the terminal where the launcher was run or in the redirected output file.

    mkfifo pipe_in
    
    # Create launcher.sh
    #!/bin/bash
    cat pipe_in | dotnet fsi 
    
    # Launch in background
    bash ./launcher.sh & 
    
    # Keep pipe open
    sleep 3600 > pipe_in &
    
    # Send command
    echo "printfn \" hey \" " > pipe_in