How to trim a string in zsh

Trimming a string means removing any space at the beginning or end of it. It's something I need to do often when parsing the output of commands.

The easiest way I found to do it in zsh is to cast the string into an array, and back into a string with myvar="${=myVar}".

The ${=} syntax splits the string as a list of arguments (so, separated by spaces), and wrapping it in "" casts it back into a string.

One could use a similar trick by using echo " one two three " | xargs, but this would require spawning a new process.

Slicing an array in zsh

To get only part of a specific array in zsh, you have to use the [@]:X:Y syntax, where X is the start of your slice and Y the end of it.

You can omit the Y or use negative indices.

For example, ${myArray[@]:1} slices the array by removing its first element

Check if an array contains a value

zsh doesn't have a way to check if a value is in an array, but can tell us (using ${(ie)}) the index of the element.

The i means inverse subscript, meaning that instead of accessing a value by its index, we want to access the index by its value.

e is for exact match; without it zsh will return the index of any value that contains the substring, instead of exactly matching it.

The trick is that if the value is not found, zsh will return the length of the array + 1.

So, to summarize, to check if the value zsh is in the array myArray, you would test it that way:

if [[ ${myArray[(ie)zsh]} -lt ${#myArray} ]]; then
  # ${#myArray} return the length of the array
fi

Alternatively, if the string you're looking for is dynamic ($myVar), you'll need one more wrapping level: ${myArray[(ie)${myVar}]}.

Custom completion methods in zsh

I needed to define a custom completion function to suggest files when typing vfa <TAB>. vfa is my alias around git-file-add. I needed it to suggest modified/added files.

Telling zsh about the completion function

I started by telling zsh which completion method to call when completion git-file-add with this code:

compdef _git-files-dirty git-file-add

The convention in zsh is that completion methods should start with _, hence the _git-files-dirty. I didn't name if _git-file-add, because I wanted the name to reflect what it displayed, not the method it was completing

Weaving it together

I created a _.git-files-dirty in my custom zsh config ./config/zsh/completion/compdef directory.

I also need to tell zsh about this path, by adding it to its fpath.

Note that the order here is important:

  • Add the folder to fpath
  • Run compinit
  • Call compdef

Writing the completion function

The _git-files-dirty file must follow a strict convention for zsh to pick it up.

  • It must start with #compdef on its first line
  • It must contain a function named _git-files-dirty

The core of the function itself is free form, the important thing it need to do is build an array of the suggestions and store it in a variable

Once the array if built, it must call _describe -V "Header name" {variableName}.

Note that it should not pass the variable directly (using $), but actually passing the name of the variable.

The -V forces the suggestions to be displayed in the exact order they are saved in the array (otherwise they are alphabetically sorted).

Calling external scripts in the shell in vimscript

Some of my vimscripts need to call external commands, through the shell CLI. I learned some quirks the hard way, and documenting them here

The basics

Calling an external command is done through system('external-command'). If you need to pass arguments you must wrap them in shellescape(myArgument) or it will mess up spaces and quotes.

The quirks

If you echom the result and see it through :messages, you might see some ^@ weird characters. Those are how vim displays new lines, but not how they are encoded in the result.

To split the result on new lines, you should run split(myResult, "\n") and not split(myResult, '^@').