← More about keyboards

Overview

This post describes a QMK macro for a button that selects the current word, assuming conventional text editing hotkeys. Press it again to extend the selection to the following word. The effect is similar to word selection (W) in the Kakoune editor.

Effect of pressing the macro repeatedly.

Line selection: Similarly, press the button with shift to select the current line, and press it again to extend the selection to the following line.

Clearing the selection: During a selection, press Esc to make the macro tap right arrow to deselect and leave the cursor at the end of the selection. Or press or directly to deselect and choose which selection endpoint to jump the cursor to.

Add it to your keymap

If you are new to QMK macros, see my macro buttons post for an intro.

Step 1: In your keymap.c, add a custom keycode for activating the macro and use the new keycode somewhere in your layout. I’ll name it SELWORD, but you can call it anything you like.

enum custom_keycodes {
  SELWORD = SAFE_RANGE,
  // Other custom keys...
};

Step 2: Handle the macro from your process_record_user() function by calling process_select_word(), passing your custom keycode as the third argument:

#include "features/select_word.h"

bool process_record_user(uint16_t keycode, keyrecord_t* record) {
  if (!process_select_word(keycode, record, SELWORD)) { return false; }
  // Your macros ...

  return true;
}

Step 3: In your rules.mk file, add

SRC += features/select_word.c

Step 4: In the directory containing your keymap.c, create a features subdirectory and copy select_word.h and select_word.c there. This is the meat of the implementation.

Note for Mac users: The implementation assumes Windows/Linux editing hotkeys by default. Uncomment the #define MAC_HOTKEYS line in select_word.c for Mac hotkeys. The Mac implementation is untested, let me know if it has problems.

Idle timeout

Optionally, Select Word may be configured to clear its internal state if the keyboard is idle for some time. This is useful to improve behavior when using Select Word and a mouse together. In your config.h, define SELECT_WORD_TIMEOUT with a time in milliseconds:

#define SELECT_WORD_TIMEOUT 2000  // When idle, clear state after 2 seconds.

and in your keymap.c, define (or add to) matrix_scan_user() as

void matrix_scan_user(void) {
  select_word_task();
  // Other tasks...
}

The default behavior (when SELECT_WORD_TIMEOUT isn’t set, or set to 0) is that Select Word never times out, and in this case it isn’t necessary to call select_word_task().

Explanation

The macro checks for events involving sel_keycode. For word selection, the first press of the macro sends the keys Ctrl+→, Ctrl+← to move the cursor to the beginning of the word, then holds Ctrl+Shift+→ to select to the end of the word. On subsequent presses, Ctrl+Shift+→ is pressed again to extend the selection to the next word.

For line selection, the macro sends Home, Shift+End on the first press, then on subsequent presses.

The state variable keeps track of whether the macro has done the initial press and whether it is making a word vs. line selection.

← More about keyboards