hardscrabble 🍫

By Max Jacobson

Psst. Check out my RubyConf 2017 talk, There are no rules in Ruby.

blog posts

integrating vim with the mac clipboard

30 Jul 2016

Using terminal text editors has a lot of advantages, but for a while the biggest disadvantage I’ve felt as a vim user is that it’s kind of hard to interact with the system clipboard. I’m aware that there’s a concept called “registers” which are something like multiple clipboards that you can copy and paste from, and one of them is the system clipboard, and the others are all virtual, or something like this, but I haven’t taken the time to really learn how those work yet.

If I want to copy a helpful code snippet from Stack Overflow into vim and I copy it to the mac clipboard, and then press “command + v” to paste it into vim, the indentation gets totally screwed up. This is becuse vim is trying to help. It doesn’t know that I just pasted, it thinks that I was suddenly just typing super super fast and each newline character I “typed” caused it to helpfully auto-indent the appropriate amount. When I actually am typing, this is helpful. But when I’m pasting, it’s kind of annoying.

Pasting into vim doesn't work well

(You can see in this example that not only is the indentation screwed up, but also there is an extra end which vim-endwise helpfully tried to auto-insert)

The workaround I’ve used for a while is to always run :set paste before I paste, and then :set nopaste afterward. This mode doesn’t auto-indent. It also breaks a number of my other vim configurations, such as jk being an alias for the escape key.

Pretty annoying.

Copying text out of vim is even more difficult. I can use my mouse to highlight the text I want to copy and then press “command + c” to copy it, but this is pretty awful, too, because it’s very easy to accidentally copy things like line numbers (which are just text in the terminal, and your mouse doesn’t know to avoid it) or to even copy text from multiple files which you happen to have open side by side in split buffers, such that the code is totally broken when you paste it out again.

Copying from vim split buffer doesn't work well

My workaround for this is even worse! I generally close my splits, turn off line numbers (:set nonumber) and sometimes make my font smaller so I can fit the whole lines on my screen and select the text and copy it. When I do this, I generally pick up a bunch of trailing whitespace that wasn’t there in the source code. It totally stinks.

Sometimes I will just open the file in Atom so I can copy text in a sane way.

Other times I will run :! cat % | pbcopy to “shell out” to a bash command and copy the entire contents of the file to the clipboard.1

OK. So obviously that sucks, right? That’s just some context for how I’ve been doing things. I meant to look into a better technique and never got to it.

The other day at work I saw my coworker Will very seamlessly copy some text out of vim and paste it into Slack.

Scandalized, I asked him how he had done that. He told me he’s using neovim and it’s probably something neovim does.

I made a note to look into it. I’m open to the idea of using neovim instead of regular vim – I think it’s cool that you can run a terminal inside vim, which makes me wonder if I even need tmux…

One of the first things I found in my research was a neovim issue from April 2014 about how some vim configuration was working in vim but not neovim

… the follwing works perfectly fine with mainline vim, “y” and “p” work with X clipboard:

set clipboard=unnamedplus

but not for neovim.

I’ve tried setting it to:

set clipboard=unnamed

still works in vim, but not neovim.

Hm. Wait. Does this mean vim already supports clipboard integration this whole time and no one told me!?

Indeed, yes, and this is why I’m writing this blog post to tell you. I feel like there’s a good chance you already knew.

So yep, I added that second config option to my .vimrc and now it works great:

  • I can yank text from vim and then “command + v” it into other apps.
  • I can copy text from Stack Overflow and then “p” it into vim – no weird indentation behavior or anything

I may yet switch to neovim2 or learn about registers, but for now I don’t yet need to, and for that I celebrate.

  1. The ! means to run a bash command; the % will expand to refer to the file name; pbcopy is a mac thing for piping data to your clipboard. 

  2. Note that neovim did fix that issue and it does work now

the first useful thing I wrote in Rust

09 Jun 2016

I’ve been interested in the Rust programming language for a while, but it wasn’t until this week that I wrote something in it which I found useful.

Let’s rewind. I like to have a random, nice emoji in my shell prompt. It’s just to add a little flair to the proceedings, some color. The emoji don’t mean anything, they’re just for fun.

My shell prompt is set like this:

PROMPT="%F{grey}%C%f \$(random_nice_emoji) \$(git_prompt) "

random_nice_emoji is a command line program on my PATH. git_prompt is a shell function. The \$(...) syntax means that the program or function should be called each time the prompt is drawn, not just once when you first open your terminal.

I could have written random_nice_emoji as a shell function if I could figure out how to use shell arays, but I could not.

Instead I wrote it as a simple ruby script:

#!/usr/bin/env ruby

print %w(
  🐖
  😅
  🌸
  🐙
  🎑
  🖌
  ☕
  📊
  🐋
  🌈
  ✨
).sample

And my prompt looks like this:

my prompt, where each line includes a random fun emoji

But over time I noticed that it was kind of….. slow. And I started to wonder if maybe my fun affectation was worth it. Some benchmarking suggests that this program takes about a tenth of a second to run. That’s not a lot, really. But we can do better.

Maybe the shell function would be much faster, but yea, still don’t know how to use shell arrays.

So let’s try writing this little script as a Rust program – Rust is supposed to be fast!

To make a new command line program in Rust, you can use Cargo to scaffold the project:

cargo new random_nice_emoji --bin

The --bin part means that it will be a command line program. Without it, I think the idea is that you’re making a package which will be used in an application.

That command crates a directory called random_nice_emoji, and within that there is a file src/main.rs which is where you put your code which should run when the command line program is invoked.

Here’s what I came up with (I’m really new to Rust so this isn’t necessarily good code):

extern crate rand;
use rand::distributions::{IndependentSample, Range};

fn main() {
    // cool, friendly emoji that look fine against a black terminal background
    let list = vec!["🐖", "😅", "🌸", "🐙", "🎑", "🖌", "☕", "📊", "🐋", "🌈",
                    "✨"];
    let between = Range::new(0, list.len());
    let mut rng = rand::thread_rng();
    let index = between.ind_sample(&mut rng);
    let emoji = list[index];
    print!("{}", emoji);
}

I couldn’t find a super-simple sample method, so I did my best to adapt the example from the docs for the rand crate to achieve that behavior.

You can install it yourself with cargo install random_nice_emoji. Maybe I shouldn’t have released it because it’s not generally useful – but it’s very convenient for me so I can install it on multiple computers, for example.

And this one usually finishes in 0.006 seconds – 16 times faster. And it was maybe 5 times harder to write? I’m hopeful that if I get better at Rust, that will go down.

If you’re into Ruby and intrigured by Rust, I recommend checking out this Helix project which makes it easy to embed Rust code in Ruby projects to ease performance hot spots. I haven’t used Helix yet, but that talk does a really great job of explaining the idea and was really inspiring to me.