An iTerm2 shortcut for a little productivity boost

2018-11-12

I still love Elixir and reach for it when hobby time permits. Elixir's pipe operator |> is a boon for code readability. As time went on, I wanted it to be easier to type.

I experimented with a few different Vim mappings to make it more comfortable and eventually landed on the following in my ~/.vim/ftplugin/elixir.vim file:

" type ctrl+return to start a new line with |>
inoremap <buffer> <F13> <ESC>o\|><space>
nnoremap <buffer> <F13> o\|><space>

" type ctrl+\ to add an inline |>
inoremap <buffer> <C-\> \|>
nnoremap <buffer> <C-\> A<space>\|><space>

Now any time Vim receives an F13 keypress, it creates a new line starting with |> and I can start typing the next part of the pipeline.

Wait, F13? My keyboard stops at F12! How is this better than manually starting a new line and typing |>? Fortunately iTerm2 makes it easy to trigger F13 with a custom Key Mapping.

iTerm2 custom Key Mapping

(That's [25~ if you're copying and pasting.)

As you can see, I've mapped ctrl+return to send the escape sequence for F13 in iTerm2.

Since I've rebound caps locks to control, ctrl+return is a very convenient combo to press. ctrl+\ is conveniently physically close to ctrl+return so I find the mental mapping as a similar (but slightly different) concept both memorable and pleasing.

I'll admit this seems silly, but these sorts of optimizations for ergonomics keep things flowing.


If you're asking why I don't just map ctrl+return in Vim itself, the answer is
that terminal Vim can't distinguish ctrl+return from normal return (though
this can vary by terminal emulator).

Do you have a better suggestion or use a similar approach for your preferred terminal emulator? Hit me up on twitter @semanticart.


Easy Screenshots in Your CLI Pull Requests

2018-08-14

Are you using hub to create your Pull Requests? How are you handling embedding images? Wait... you are embedding images, aren't you? For my money, a screenshot is one of the most important parts of a PR body. The context provided by an image goes a long way towards understanding a proposed change. A picture is worth a thousand words and all that.

Until recently, I'd been using hub to create a pull request, and then editing the PR on GitHub to embed screenshots. This always felt a little clunky and I assumed I'd make it more seamless some day.

Cue the cheesy dramatic music: That day has arrived.

Some instructions here will be Mac-specific but I'm sure you can figure out how to tweak it for your setup.


Since a PR body is just markdown, the mechanics of embedding a screenshot are well understood:

  1. Take a screenshot

    • To avoid littering the desktop or other folders, I prefer to take the screenshot to the clipboard (by default, add CTRL to your shortcut)
  2. Upload the screenshot somewhere that is world-readable

    • For longevity, you should probably use one of your company's s3 buckets for work-related PRs.
  3. Add the markdown to render the screenshot to the PR body

Assuming you did step 1 correctly and have a screenshot in your clipboard, how do we upload it somewhere world readable?

We'll use two new commands (both available via homebrew). pngpaste is a great utility for pasting an image to a file. awscli has an s3 subcommand for uploading files to a bucket.

Besides that, we'll do a little work to make the filename unique and then massage the resulting url into the proper markdown for an image and we're golden:

And there ya go. No more editing the PR after the fact.

If you use vim, you're now a quick :r !s3_image away from embedding a screenshot straight from the clipboard to your PR body. Make it a proper mapping. Declare victory.


Executable Note-Taking — Writing Automated Tests as a Learning Method

2017-11-27

I'm trying a new (to me) approach to learning programming languages: writing automated tests as a learning method. This surely isn't a new idea but I wanted to share my experience.

The concept is pretty straightforward:

  1. Discover how something works
  2. Write a test to illustrate your understanding (aim for concise examples)
  3. Add more tests as-needed to probe the boundaries of your understanding

These tests are effectively executable note-taking. You should try it.

Benefits

  • As with traditional note-taking, the process of writing the tests help commit the concept to memory.
  • The tests are there to reference in the future if I forget how something works.
  • The tests' passing shows that my understanding is correct.
  • I can update the tests when my understanding improves or I find a better approach.
  • I can re-run the tests when a major version of the language is released to see what is deprecated.

You might worry that this effort is duplicating existing tests from the language itself. Right! But the purpose here is to solidify and exercise your understanding. (Do feel encouraged to consult the official tests afterwards as you can learn even more.)

An example

I needed to write a method in Elixir that could take an optional block as an argument (this is a common thing in Ruby/Rails). I did some digging and found the do: block approach (example). Next I wrote a couple tests in my local elixir_learning app to document my understanding:

describe "methods that take an optional block" do
  defmodule OptionalBlock do
    @moduledoc """
    This works by specifying a method with and without the `do: block`
    argument.
    """

    def foo(arg1, arg2) do
      [arg1, arg2]
    end

    def foo(arg1, arg2, do: block) do
      [arg1, arg2, block]
    end
  end

  test "without a block" do
    assert OptionalBlock.foo("test", %{a: 1}) == ["test", %{a: 1}]
  end

  test "with a block" do
    x = :example_result_of_block

    result =
      OptionalBlock.foo "test", %{a: 1} do
        x
      end

    assert result == ["test", %{a: 1}, :example_result_of_block]
  end
end

Now I understand the behavior. Whenever I forget how this works, I can come back to it for reference.

Further exploration

After writing the initial examples, I try to probe the edges of my understanding.

Looking at the code now, it isn't clear when block execution happens. Is it lazy (like in Ruby) or does it happen at function call time?

Let's write another test to find out:

# higher in the test file
import ExUnit.CaptureIO

test "time of block execution" do
  defmodule UnusedBlock do
    def foo(do: _block) do
      # intentionally not using _block
    end
  end

  assert capture_io(fn ->
           IO.puts("Before")

           UnusedBlock.foo do
             IO.puts("Block")
           end

           IO.puts("After")
         end) == "Before\nAfter\n"
end

Here we're making a guess at what the output might be. UnusedBlock.foo never uses block. If the do/end block is lazily-evaluated then we will only see the "Before" and "After" lines.

Running the test shows:

1) test methods that take an optional block time of block execution (OptionalBlockTest)
   test/optional_block_test.exs:36
   Assertion with == failed
   code:  assert capture_io(fn ->
            IO.puts("Before")
            UnusedBlock.foo() do
              IO.puts("Block")
            end
            IO.puts("After")
          end) == "Before\nAfter\n"
   left:  "Before\nBlock\nAfter\n"
   right: "Before\nAfter\n"
   stacktrace:
     test/optional_block_test.exs:43: (test)

Our guess was incorrect: the "Block" line is present. This means the block is not lazily-evaluated. It is evaluated at the time the function is invoked.

This behavior is good to remember for the future and worth codifying in the test. We update our assertion to include the "Block" output line (we do expect it now) and update the test name to mention how the block is always invoked even if it goes unused.

Now I know a little more about how Elixir handles block arguments by default and I have executable notes to look back on.

If you want to learn more, Henrik Nyh has helpful background info and discusses using macros for lazy evaluation in his excellent Elixir block keywords article.


I've only been using this approach for a short while so I can't comment on the long-term benefits or downsides. I can say that I've really been enjoying the benefits mentioned above — particularly being able to experiment with my understanding in a reproducible way.

See also: