What it is

shpaste is a small Emacs client for paste.sr.ht, the pastebin service from SourceHut. It lets me share a snippet without leaving the editor: select some text, run a command, and the paste URL is already in my kill-ring, ready to paste into a chat or an email.

It is built on the SourceHut GraphQL API and does just enough to be useful: create pastes, list them, open them, delete them. Nothing more.

The project lives at sr.ht/~bounga/shpaste, where you’ll find the source, the ticket tracker, and the mailing lists.

What it does

  • Create a paste from the region or from the whole buffer. The resulting URL is pushed to the kill-ring and shown in the echo area.
  • Browse your pastes in a dedicated list buffer, sorted and tabulated.
  • From that buffer: open a paste, copy its URL, delete it, or refresh the list.
  • Choose a visibility per paste — public, unlisted, or private.
  • Point it at a self-hosted SourceHut instance if you don’t use the public one.

The list buffer looks like this:

The shpaste-list buffer

Requirements

  • Emacs 29.1 or later
  • plz (GNU ELPA) and curl

Installation

shpaste is not on MELPA yet — the package is only a few weeks old. MELPA Stable support will come later, built from the version tags. In the meantime, install it straight from the repository.

On vanilla Emacs 29.1+, package-vc-install clones and builds it from source:

(package-vc-install "https://git.sr.ht/~bounga/shpaste")

On Emacs 30+, you can let use-package do the same with :vc:

(use-package shpaste
  :vc (:url "https://git.sr.ht/~bounga/shpaste" :rev :newest))

In Doom Emacs, declare the recipe in packages.el:

(package! shpaste
  :recipe (:type git
           :host nil
           :repo "https://git.sr.ht/~bounga/shpaste"
           :files ("*.el")))

Then configure it in config.el. This is the setup I use, with a paste leader menu and the list-buffer bindings:

(use-package! shpaste
  :commands (shpaste-list
             shpaste-create-from-region
             shpaste-create-from-buffer)
  :init
  (setq shpaste-default-visibility 'unlisted)
  (map! :leader
        (:prefix ("P" . "paste")
         :desc "List my pastes"      "l" #'shpaste-list
         :desc "Paste buffer"        "p" #'shpaste-create-from-buffer
         :desc "Paste region"        "r" #'shpaste-create-from-region))
  :config
  (map! :map shpaste-list-mode-map
        :n "RET" #'shpaste-list-open
        :n "w"   #'shpaste-list-copy-url
        :n "d"   #'shpaste-list-delete
        :n "gr"  #'shpaste-list-refresh))

Getting a token

shpaste authenticates with a SourceHut OAuth2 personal access token. Generate one at meta.sr.ht/oauth2 with the PASTES grant in read-write mode, then store it in auth-source with the host set to your instance — for example in ~/.authinfo.gpg:

machine paste.sr.ht password <YOUR_TOKEN>

There is no shpaste-token variable: the token is only ever read from auth-source. If you want the details of how that lookup works and why I chose it, I wrote a whole post about it: Using auth-source in a Real Emacs Package.

Usage

Command Action
shpaste-create-from-region Create a paste from the region; URL to kill-ring
shpaste-create-from-buffer Create a paste from the whole buffer
shpaste-list Browse your pastes in a list buffer

Inside the shpaste-list buffer:

Key Action
RET Open the paste
w Copy its URL
d Delete it
g Refresh the list

Configuration

Two options, both optional:

  • shpaste-instance (default "paste.sr.ht") — set it to a self-hosted host. It is used both to build the API endpoint and as the auth-source lookup key, so the machine field above must match it.
  • shpaste-default-visibility (default unlisted) — one of public, unlisted, or private.

Wrapping up

That’s the whole tool. shpaste is young (currently 0.1.0) and MIT licensed. If you live in Emacs and use SourceHut, give it a try — and if you hit a rough edge or have an idea, the project page is at sr.ht/~bounga/shpaste.

Have comments or want to discuss this topic?

Send an email to ~bounga/public-inbox@lists.sr.ht