Indenting even more nicely

I’ve been using this for some time, as I mentioned a few days ago:

(defun indent-buffer ()
  "Indent an entire buffer using the current syntax settings."
   (goto-char (point-min))
   (while (re-search-forward "\t" nil t)
     (replace-match " " t t)))
  (indent-region (point-min) (point-max)))

With the massive re-indenting process that inspired that post, I came across the “need” to also re-fill comments (ie, reformat comments to fit within our in-house 120-character right margins) at the same time.

The resulting functions look like this:

(defun comment-forward ()
 "Advance the point to the next comment."
 (skip-syntax-forward "^<!")         ; next comment-like char
   ;; actually starts a comment; skip delimiter
   ((eql (face-at-point) 'font-lock-comment-delimiter-face)
    (skip-syntax-forward "<!"))
   ;; end of file
   ((>= (point) (1- (point-max)))
   ;; not a comment here (maybe in a string or something)
(defun fill-all-comments ()
 "Re-fill all comments in the buffer"
   (goto-char (point-min))
   (while (comment-forward)
(defun replace-all-tabs-with-spaces ()
 "Replace every TAB byte with a space."
   (goto-char (point-min))
   (while (re-search-forward "\t" nil t)
     (replace-match " " t t))))
(defun indent-buffer ()
 "Indent an entire buffer using the current syntax settings."
 (indent-region (point-min) (point-max))

The next step, of course, is to figure out how to nicely break up over-long code lines …



PS. That didn’t last long.

I replaced the use of replace-all-tabs-with-spaces with the nicer:

(defun strip-indentation ()
  "Replace   leading   whitespace   (tab/spaces)   in   preparation   of
re-indenting. Skips whitespace within comments or strings."
   (goto-char (point-min))
   (while (re-search-forward "^[ \t]+" nil t)
          (unless (member (face-at-point) '(font-lock-string-face
            (replace-match " " t t)))))

Dear Recruiter:

Dear Recruiter:

I want to believe that you’re trying to make the best of a bad situation. I know you’re probably not an idiot, but you’re probably not really experienced with hiring senior programmers.

I know, this post probably seems condescending or rude, but the fact is, we have been burned … a lot.

You probably know by now that programmers, as a rule, hate recruiters. That’s a pretty strong word, there … hate. The sad truth is, programmers are “in demand” in the job market, and for every five programmers out there, two of them are bad at it. But, they’re working, too … because for every five programmers, there are fifteen jobs available.

Seriously, go to Google, and type “Why do programmers hate recruiters?” Today, there are “[a]bout 628,000 results.” That’s just the tip of the iceberg. Your compatriots have set the bar really, really low.

Let’s make you a commission.

Your job is to find a good candidate, right? OK. First things first.

Step 1.

Find out what on Earth you expect us to do.

Seriously. Literally — I’ve counted this out before — I have gone through 58 e-mails from recruiters before finding one that actually mentions what the job is.

I know, I try to believe that you’re not an idiot, but that kind of basic stupid mistake is really hard to come back from.

Hint: None of these things count as a job description:

  • A bare job title like “Senior Systems Programmer” or “Mobile Application Developer.”
  • Names of products, operating systems, or programming languages: “iOS Programmer,” “Perl programmer,” Java, Linux, Apple, Lisp, …
  • A list of desirable skills or experience.
  • The name of some college degree or other.

Here are some actual job descriptions:

  • Develop a new operator control application stack for management of immersive (hardware/software) simulation devices
  • Implement the server side of a real-time, sports-based game
  • Develop an MMO-RPG game engine for thousands of simultaneous players
  • Develop back-end utility applications for internal maintenance of a shared, real-time data set
  • Develop embedded software for person-to-person communications with internationalization and localization to many language environments, including CJK languages
  • Create a web-based GUI wrapper to interact with a legacy mainframe application over the 3278 terminal interface
  • Maintain Electronic Data Interchange (EDI) software between a variety of trading partners using formats such as fixed-width EBCDIC records and X12N structured streams
  • Create Internet “chat” programs with web-based and VR-based front-ends
  • Develop multimedia, interactive presentations for WWW and DVD distribution
  • Develop software for high-volume printing press systems to streamline order proofing, production, and shipping processes

The job description should read like something that would appear on a résumé.

Step 2. At least pretend to read our résumé.

They’re longer than you’d like, I’m sure. Most senior developers have a CV section that runs on for a page or two, at least, and probably some skills summary stuff that goes on for another couple pages.

Read it. You might not understand all the tech terms, but at least you’ll have some hope of finding out something useful or interesting about us.

We are not your customer. We are your “victim.” Your goal is, of course, to hire programmers for your customer. That won’t work out too well if you haven’t even looked at our résumés. You might get the college kids with no practical experience, but you’re not going to get someone for a leadership rôle.

Step 2(a). Spell our names right.

You should be able to get at least that much.

PS: Trying to reference us on a first-name basis comes across as … smarmy. And, if you can’t correctly figure out what given name we use, it’s a bit of a red flag, too, isn’t it?

I’ve seen a Viet colleague get e-mail that mistakenly had her family and given names backwards (… since her family name, as is common in East Asia, came first). I routinely get mail with my given name misspelled or truncated, even though there’s a specific FAQ on my résumé “explaining” my name.

Step 3. Write a reasonable e-mail.

You want to reach a programmer? Great. Send an e-mail.

Don’t phone. Don’t even think about a phone call. Seriously. What if we’re at work? We’re sending you to voicemail, and deleting it without listening. Really. What if we’re at home? Same thing. We have caller ID. If you’re not in our phonebook, we push the big red ✗.

We all have e-mail. It echoes to our PC, our laptop, our tablet, our mobile phone, maybe our watch or something. We get your e-mail the second you sent it.

And we glance at it and read the first six words or so of the subject.

And then, we delete it or hit “reply with canned message → buzz off.”

Your only hope of getting that e-mail actually read is to get your foot in the door with that e-mail.

Make the subject memorable. “Can we talk?” is not it. “Urgent need…” is a turn-off. “Senior Developer” is not helpful.

Try cutting down an actual job description (see above) into about 5-6 words. “Game developer for FPS physics engine” is a good example.

Then, put your e-mail together like you’re writing a telegraph. Time is money, and we are probably reading your e-mail on a cell phone while we’re doing something else that is, to us, much more interesting.

Here’s a good template to work from:

Hello, Ms Susie Programmer; I found your résumé at <LINK> and I see that you’ve been working on firmware for some simulation and game devices.

I have a position here at CompanyCo. designing a new videogame system combining physical simulator systems with augmented reality components. I thought you might be interested. Some of the early press coverage of our prototype is here: <LINK>

If you might be interested (or know someone who would be) please drop me a note at your earliest convenience.

That’s about it.

HINT: do not paste some 5-line (or 20-line!) signature with a bunch of graphics. Seriously, that’s tacky.

That is about the most you’re going to get us to read. That’s the sort of pitch that actually gets good candidates to respond.

See Also:

Why Developers Hate Recruiters & What You Can Do to Change This

Why recruiting sucks

Death to Recruiters

Multiple keyboards, multiple layouts

If you have more than one (physical) keyboard connected to your Fedora box, with different layouts, you can load multiple key-maps at the same time.

Yes, technically, Gnome doesn’t permit this. The switcher in the GUI will reset you to one keymap whenever you log in, switch users, or put the machine to sleep. But, for a little inconvenience, you can get your layouts to work in parallel.

What I use is a little script named k that lives in /usr/local/bin. It’s a pretty simple script, once you work out what it needs to do.

How X does keys

Your keyboard connects up through a kernel device driver — almost certainly, the USB or “AT” (as in, the IBM PC/AT that introduced the round DIN keyboard socket) interface. This gets translated into a character device special file that programs (eg, X) can read from; perhaps something like /dev/input/event6.

X connects this character device special file to its XInput layer as one input device, and gets from it scancodes — more-or-less, these are codes that mean something like “the fourth key on the second row,” but don’t carry any symbolic meaning. XKB then translates these into keyboard symbols, like “x” or “b” or “Right Shift” or “Control + System Request.” (If you use a more complex writing-system, another level of translation might be in play, as well; perhaps converting Pin1Yin1 sequences into Han characters, or stacking Jamo into syllable blocks … but, you already knew about that, if you speak Chinese, Japanese, or Korean.)

Normally, Gnome sets XKB to use the same mapping for all connected keyboards. We’re going to break that.

In my case, I use a highly customized modified USB Dvorak keyboard, but I want to leave the internal keyboard in my laptop and the PS/2-connected alternate keyboard on my desktop machine as “normal” US Qwerty layouts.

Gathering the pieces together

First, you need to know the X keymap that you want as your base. Go through the Settings panel Region & Language → Input Sources to set it up as you would normally; then, extract its internal code name with (from a Terminal) — gsettings get org.gnome.desktop.input-sources sources — you’ll see a result like [(‘xkb’, ‘us’)] — the code you want here is ‘us’. Repeat for your other keyboard’s map (my other keyboard is type spacey, which you’ll see below).

Then, let’s identify your keyboards’ ID’s at the XInput level — this is where the physical keys’ signals are coming in, before XKB assigns them to key symbols. Just run xinput (again, from a terminal) and you’ll see something with a “Virtual core keyboard” and a set of “slave keyboards” under it. If you have a keyboard on a PS/2 or AT connector, you’ll probably see something like “AT Translated Set 2 keyboard;” USB keyboards are just, “USB Keyboard.” (You’ll probably also see video devices, laptop hot-keys, or other “keyboard-like” devices listed, which you can totally ignore for this purpose.)

To clarify which keyboard is which, you might use xinput list-props nn to see the details of a particular device. EG: if you have two USB keyboards, one of which is 9 and the other is 13, xinput list-props 9 might show that one of them has Device Node (270): “/dev/input/event8” … and find /sys/devices -name event8 might give you a beautiful string like /sys/devices/pci0000:00/0000:00:14.0/usb1/1-13/1-13.2/1-13.2:1.0/0003:04D9:0169.000B/input/input28/event8 (OK, maybe it’s not so beautiful.) The “nice” thing about that is that you can lsusb to find the brand name of your keyboard and match it up to the numbers in the middle of that sequence — eg. “Bus 001 Device 085: ID 04d9:0169 Holtek Semiconductor, Inc.” If you have two keyboards with the same model, you could have a problem here …

Building the script

So, on to the script. There are three steps we’ll need to perform:

  1. Set the default key map for all devices;
  2. Locate the keyboard XInput device ID(s) of the one(s) that will be different;
  3. Change only its (their) keymap(s).

The second step, unsurprisingly, is the “hard part.”

The script, with step 2 skipped, looks something like

setxkbmap 'us'
for device in $(TODO MAGIC)
   setxkbmap -device $device 'spacey'

So … what goes into TODO MAGIC … ?

If your keyboards are visibly distinct in the xinput list, you could do something like:

⇒ xinput -list | grep ‘USB Keyboard’

… to locate the one you want. This will give you a small list, like:

↳ USB Keyboard id=12 [slave keyboard (3)]
↳ USB Keyboard id=13 [slave keyboard (3)]

(Note that these are both the same physical keyboard, in my case.)

If that’s what you wanted, you can trim the results with

… | cut -d= -f2 | cut -f1

which, all together, gives us

⇒ xinput -list | grep ‘USB Keyboard’ | cut -d= -f2 | cut -f 1

… Just what we wanted for the for loop.

setxkbmap us
for device in $(xinput -list | grep 'USB Keyboard' | cut -d= -f2 | cut -f 1)
 setxkbmap -device $device spacey

The above is more-or-less identical to what I use.

Multiple USB keyboards?

If you need to do more work to narrow down the device, you might have to wrangle the USB vendor/product ID’s into play.

for device in $( xinput … )
    dev=$(xinput list-props $device | grep 'Device Node' | cut -d: -f2)
    devpath=$(find /sys/devices -name $(basename $dev))
    case "$devpath" in
          setxkbmap -device $device spacey

This is using a relatively obscure shell feature: case “$devpath” in *”$usbid”*) … ;; esac is checking whether the usbid string exists within the (longer) devpath string. You could think of this as a way of asking, “if devpath contains the substring usbid.” (If you’re unfamiliar with Bourne shell, note that esac is case spelled backwards. In the same way, the end of an if block is fi. do, however, ends with done.)

Gnome Session

In order to make life a little more pleasant, I have also added a session script to run k whenever I log in.

In ~/.config/autostart/keyboard I have:

[Desktop Entry]
Name=Set keyboard maps
GenericName=Set keyboard maps
Comment=Set keyboard maps

And, likewise, for GDM:

 ⇒ sudo su - gdm -s /bin/bash
-bash-4.3$ mkdir -p ~/.config/autostart
-bash-4.3$ cat >  ~/.config/autostart/keyboard.desktop
[Desktop Entry]
Name=Set keyboard maps
GenericName=Set keyboard maps
Comment=Set keyboard maps
-bash-4.3$ logout

I do still have to run this manually, eg, after switching users or the machine sleeping. Thus, the immensely short name “k,” so I’m not fumbling around trying to enter a long name on the wrong keymap.


My actual k script has these two lines to set key repeats properly, due to deficiencies in my spacey key map:

xset r 66 ; xset r 105

Maybe some day I’ll fix the keymaps, but, for now, this works for me.