Custom Emacs functions No. 5 - Navigation

Spring cleaning

Let me start by saying this. Over the time I have accumulated some cruft (in this case function) that needed to be gone. Partly because some of the functionalities are built in and partly because my solutions were honestly nonsensical.

Now I have cleaned up the modules at issue today and we can go on talking about the remaining functions that are still of use to me.

The remaining stuff

Windows splitting

This are my versions of commands to split windows. The built in versions (split-window-below & split-window-right) do keep the cursor in the original window. I don’t like that one bit. About 95% of the time when I split a window, I want the cursor to follow and perform whatever task in the new window. Hence these two simple functions:

Split like I think

(defun timu-nav-split-and-follow-below ()
  "Split the selected window in two with the new window is bellow.
This uses `split-window-below' but follows with the cursor."
  (interactive)
  (split-window-below)
  (other-window 1))

(defun timu-nav-split-and-follow-right ()
  "Split the selected window in two with the new window is to the right.
This uses `split-window-right' but follows with the cursor."
  (interactive)
  (split-window-right)
  (other-window 1))

Find me a file - but not here

I addition to splitting the window the following two functions put straight me into a minibuffer find-file completion.

(defun timu-nav-find-file-below ()
  "Open file with `find-file' & `read-file-name' in a split window bellow."
  (interactive)
  (split-window-below)
  (other-window 1)
  (find-file (read-file-name "Find file: " nil) t))

(defun timu-nav-find-file-right ()
  "Open file with `find-file' & `read-file-name' in a split window to the right."
  (interactive)
  (split-window-right)
  (other-window 1)
  (find-file (read-file-name "Find file: " nil) t))

Split at the bottom

This one creates a spit bellow all other windows in the frame.

(defun timu-nav-split-bellow-all ()
  "Split the current buffer horizontally with new window bellow all other one.
The size ratio is 60 (top) to 40 (bottom).
Credit: https://emacs.stackexchange.com/a/60459/30874."
  (interactive)
  (split-window
   (frame-root-window)
   (truncate
    (* (window-total-height (frame-root-window)) 0.60)) 'below))

I don’t like this order

When two windows are showing it can be useful or even warranted to switch the orientation of these:

(defun timu-nav-toggle-split-direction ()
  "Toggle window split from vertical to horizontal.
This work the other way around as well.
Credit: https://github.com/olivertaylor/dotfiles/blob/master/emacs/init.el"
  (interactive)
  (if (> (length (window-list)) 2)
      (error "Can't toggle with more than 2 windows")
    (let ((was-full-height (window-full-height-p)))
      (delete-other-windows)
      (if was-full-height
          (split-window-vertically)
        (split-window-horizontally))
      (save-selected-window
        (other-window 1)
        (switch-to-buffer (other-buffer))))))

Elevate me

Some of the files that need editing of course require root privileges.

(defun timu-nav-find-file-as-root ()
  "Like `find-file', but automatically edit the file with root-privileges.
This uses Tramp to apend sudo to path, if the file is not writable by user."
  (interactive)
  (let ((file (read-file-name "Open file as root: ")))
    (if (file-writable-p file)
        (progn
          (find-file file)
          (message "File is writable, no need to open as root."))
      (find-file (concat "/sudo::" file)))))

What about tabs?

The tab feature in Emacs is quite welcome addition, But I like my new tabs to always be created visiting the scratch buffer. Bare in mind timu-scratch-create in this command is a custom function as well. It will however be handled in a later post. For all intense and purposes for now it can be substituted by startup--get-buffer-create-scratch.

(defun timu-nav-tab-bar-new-tab ()
  "Create a new tab an then switch to the scratch buffer.
If it has been closed, then create one."
  (interactive)
  (tab-bar-new-tab)
  (timu-scratch-create))

This one helps reducing the keybindings needed for navigating tabs. Either switch to the next tab or create a new one.

(defun timu-nav-switch-or-new-tab ()
  "Switch to the next tab if there are more than 1 tab.
Create a new tab if there are just 1 tab."
  (interactive)
  (if (length> (frame-parameter nil 'tabs) 1)
      (tab-bar-switch-to-next-tab)
    (timu-nav-tab-bar-new-tab)))

Pop the windows!

popper.el is a package that now belongs in my arsenal. Effectively it is a corner stone of my navigation. This one sometimes help me to see all the popup windows at once.

(defun timu-nav-popper-toggle-all ()
  "Toggle all pupups at once with `popper.el'."
  (interactive)
  (popper-toggle-latest 16))

Let’s find stuff

Both CLI commands ripgrep and find get a good use by consult (which is part of my completion system) for searching.

Both consult commands at issue here (consult-ripgrep & consult-find) start the search in the default-directory. I however do like to select the starting directory first. This is possible with the universal argument. Hence the following to functions.

Searching file contents:

(defun timu-nav-consult-rg ()
  "Function for `consult-ripgrep' with the `universal-argument'."
  (interactive)
  (consult-ripgrep (list 4)))

Searching file names:

(defun timu-nav-consult-fd ()
  "Function for `consult-find' with the `universal-argument'."
  (interactive)
  (consult-find (list 4)))

Where is my project?

Whenever I want to go to a project I always – I mean always – want to open a file. The default command project-switch-project However gives me some options/actions (including Eshell, VC-Dir and more). This is the reason for the following simple function.

(defun timu-nav-project-switch-project (dir)
  "\"Switch\" to another project by running an Emacs command.
Directly use `project-find-file' instead of getting prompted.

When called in a program, it will use the project corresponding
to directory DIR."
  (interactive (list (project-prompt-project-dir)))
    (let ((project-current-directory-override dir))
    (project-find-file)))

Bring emacs to the system

With the help of the package emacs-everywhere I can edit the content of most text areas/boxes in macOS within an Emacs pop-up window.

In case I already started typing text this functions helps with the populating of the Emacs pop-up with that text.

(defun timu-nav-emacs-everywhere-copy ()
  "Copy the contents of the text area to be edited."
  (interactive)
  (do-applescript
   (concat
    "set frontmostApplication to path to frontmost application\n"
    "tell application \"System Events\"\n"
    "	keystroke \"c\" using {command down}\n"
    "end tell\n")))

The end

Granted some functions here can be simply lambdas in the definition of keybindings. However I like the order creaaating custom functions – however small (and maybe superfluous) they are – provides me. Sue me! ;)