• Stars
    star
    114
  • Rank 308,031 (Top 7 %)
  • Language
    Shell
  • License
    MIT License
  • Created almost 3 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

automatically quote arguments to commands like `git commit -m`

zsh-autoquoter

zsh-autoquoter is a zle widget ("zsh plugin") that will automatically put quotes around arguments to certain commands. So instead of having to decide which type of quotes to suffer through:

$ git commit -m 'we haven'\''t seen the last of this "feature"'
$ git commit -m "we haven't seen the last of this \"feature\""

You can just write English:

$ git commit -m we haven't seen the last of this "feature"

And let zsh-autoquoter do the rest.

And if you use zsh-syntax-highlighting, it'll even highlight these "autoquoted strings" distinctly, so you'll never be surprised when zsh-autoquoter fires.

Check out this blog post for a fancy asciinema demo of zsh-autquoter in action.

Behavior

Configure command prefixes that you want to be autoquoted by setting the ZAQ_PREFIXES array in your ~/.zshrc:

ZAQ_PREFIXES=('git commit -m' 'ssh *')

By default this array is empty. You need to opt into autoquote behavior.

ZAQ_PREFIXES is an array of shell patterns that will be matched against your input.

Shell patterns are kind of like goofy regular expressions, where * means .* and # means * and ## means +. So while git commit -m would work as a pattern to match a literal prefix, I would suggest this more flexible alternative:

ZAQ_PREFIXES+=('git commit( [^ ]##)# -[^ -]#m')
git commit -a -m hi hello
                 ^^^^^^^^
git commit -am hi hello
               ^^^^^^^^
git commit -m hi hello
              ^^^^^^^^

And while ssh * will "skip one argument" and then quote the rest, you should probably use a pattern that allows flags and triggers after the first positional argument:

ZAQ_PREFIXES+=('ssh( [^ ]##)# [^ -][^ ]#')
ssh -Y user@host some 'complex' * | remote <"command"
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Writing patterns

Wow those are complicated, huh? Yeah. And they're not even very complete: for example, you can't type:

ssh 'some hostname with a space in it' something

Because the pattern doesn't recognize that 'some hostname with a space in it' is a single positional argument. Patterns operate on characters, even though we think about command arguments in terms of words. But parsing those out would be a whole thing.

Note that [^ -][^ ]# at the end of a pattern matches exactly one positional argument because zsh-autoquoter always adds an implicit space to the end of your patterns, and searches for the shortest matching prefix.

You can debug a pattern using the following expression:

setopt EXTENDED_GLOB
input='ssh user@host some command'
prefix='ssh( [^ ]##)# [^ -][^ ]#'
print ${input#$~prefix }

Which is exactly how zsh-autoquoter is implemented. If that prints the entire input string, your pattern didn't work. If it only prints a subset of it, then that is the subset that will be autoquoted.

See section 14.8.1 Glob Operators for a more thorough description of pattern syntax.

Special characters

zsh-autoquoter runs before your shell has a chance to expand or glob or parse the command you typed, so it works to quote any shell syntax:

$ ssh user@host * globs and | pipes and > redirects and # comments oh my

There is one exception:

Double escaping

zsh-autoquoter won't add quotes if there already are quotes, so you can still type:

$ git commit -m 'i forgot that i installed zaq'

And it will not double-escape anything.

When doing this check, zsh-autoquoter will look for a fully quoted argument string. So you can still write:

$ git commit -m 'twas the night before christmas
$ git commit -m "fix" the latest "bug"

And it will be autoquoted correctly.

I highly recommend enabling syntax highlighting (see below) to make it obvious when zsh-autoquoter is going to fire and when it is not.

History

Commands will be rewritten before they're added to history, so your ~/.zsh_history will reflect an accurate list of commands you ran, not commands you typed. But because zsh-autoquoter ignores already-quoted entries, you can always up-enter and re-run the same command.

Yes, you could imagine wanting it the other way, but I just cannot figure out how to make that work with zsh.

Installation

Manually

Download zsh-autoquoter.zsh and source it from your ~/.zshrc file. Then make sure that you add some prefixes:

source ~/src/zsh-autoquoter/zsh-autoquoter.zsh
ZAQ_PREFIXES=('git commit( [^ ]##)# -[^ -]#m' 'ssh( [^ ]##)# [^ -][^ ]#')
# If using zsh-syntax-highlighting:
# ZSH_HIGHLIGHT_HIGHLIGHTERS+=(zaq)

Antigen:

$ antigen bundle ianthehenry/zsh-autoquoter

oh-my-zsh:

Clone this repo into your custom plugins directory:

$ git clone https://github.com/ianthehenry/zsh-autoquoter.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autoquoter

And then add it to the plugins list in your ~/.zshrc before you source oh-my-zsh:

plugins+=(zsh-autoquoter)
source "$ZSH/oh-my-zsh.sh"

Syntax highlighting

If you use the excellent zsh-syntax-highlighting package, you can enable the zaq highlighter by adding the following line to your ~/.zshrc:

ZSH_HIGHLIGHT_HIGHLIGHTERS+=(zaq)

You can customize how it looks by setting the zaq:string key:

ZSH_HIGHLIGHT_STYLES[zaq:string]="fg=green,underline"

If you don't use zsh-syntax-highlighting, you should check it out. It's great. You'll never typo a command again.

hey why don't you just write better commit messages

Okay yeah look I'm using git commit -m as an example a lot, but I actually wrote zsh-autoquoter so that I could add notes and todo list items from the command line more easily.

Not to say that git commit -m is bad -- it's great if you're using a patch queue workflow. But zsh-autoquoter does not condone vague one-line commit messages.

More Repositories

1

sd

a cozy nest for your scripts
Shell
704
star
2

bauble

a playground for making 3D art with lisp and math
Janet
378
star
3

basilica

It's kinda like a forum.
Haskell
114
star
4

mixologician

optimize your home bar with ✨logic programming✨
Prolog
75
star
5

judge

self-modifying test library for janet
Janet
68
star
6

toodle.studio

turtle graphics playground
TypeScript
66
star
7

dotfiles

My dotfiles. For easy clonin'.
Emacs Lisp
49
star
8

macaroni

macro spaghetti code
Janet
28
star
9

ocamlsyntax.com

How do you do that recursive GADT thing again?
CSS
27
star
10

cmd

command-line argument parser for Janet
Janet
24
star
11

jimmy

Janet bindings for the persistent data structure library "immer"
C++
18
star
12

tightrope

Making Slackbots is hard! But wait: now it's easy.
Haskell
14
star
13

janet-stuff

an index of all of my public-facing janet projects
11
star
14

pat

a better pattern matching macro for janet
Janet
10
star
15

sd-nix

some helpers for working with nix
Shell
10
star
16

privy

inline output for the ivy array language
Janet
9
star
17

effing.js

Function functions.
CoffeeScript
9
star
18

bytemap

text-based canvas
Janet
9
star
19

basilica-client

An Om client for basilica
Clojure
8
star
20

janet-cross-compile

demonstration of using zig cc to cross-compile native janet binaries
C
8
star
21

codemirror-lang-janet

Janet support for CodeMirror 6
TypeScript
7
star
22

memegen

A meme generator for discriminating meme enthusiasts
Haskell
7
star
23

banquet

fancy tables
Janet
7
star
24

aoc-2023

advent of code 2023 in janet
Janet
6
star
25

square

alternate PEG syntax for Janet
Janet
5
star
26

petal

my pet array language
Rust
5
star
27

janet-midi

Janet bindings for RtMidi, a cross-platform MIDI library
C++
5
star
28

janet-module

a simple module macro
Janet
5
star
29

utf8-parser

A fun way to learn about UTF-8 and parser combinators
Haskell
5
star
30

janet-clipper

Janet bindings for clipper, a polygon utility library
C++
3
star
31

to-do

a very simple plain-text todo list
Janet
3
star
32

zsh-expander

predictable fzf-based completion for zsh
Shell
3
star
33

cmp

comparison combinators for Janet
Janet
3
star
34

rogue

A roguelike, in Haskell
Haskell
3
star
35

janet-whereami

a janet wrapper around the whereami library
C
2
star
36

matchbook

Declarative pattern matching for JavaScript
CoffeeScript
2
star
37

heap

comparator-based min-max heap for janet
Janet
2
star
38

httprintf

Some scripts for creating a development HTTP server.
Shell
2
star
39

steno

command-line snapshot testing tool
Janet
2
star
40

spoon

command line interface for lambda soup
OCaml
2
star
41

territory

An abstract game of territory control made in a single sitting
Objective-C
1
star
42

nix-zshell

Hack to make nix-shell play nicely with zsh
Shell
1
star
43

brinks

Algebraic data types for JavaScript
JavaScript
1
star
44

privy-mode

emacs major mode for the ivy array language and privy preprocessor
Emacs Lisp
1
star
45

overtone-demo

A demo of overtone. For a tech talk.
Clojure
1
star
46

dismissive-clojure

Send emails... to the future!
Clojure
1
star
47

ctrl-c

Being interrupted as a service
Shell
1
star
48

inline

Inline: offline outlining.
JavaScript
1
star
49

rundown

running with markdown
Rust
1
star
50

ianthehenry

1
star
51

dismissive

Emailing the future as a service
Haskell
1
star
52

tales

An adventure in collaborative storytelling.
Haskell
1
star