ilhan özgen xian

> index / blog : editors

on text editing



My favourite editor is gnu ed(1). It’s a mature line-based editor that is still actively maintained. It has a small set of commands that can be very effectively combined. It is―in my opinion―the best text editor, especially in the absence of a graphical environment. I wrote my PhD thesis with ed(1) and still prefer it over other editors.

gnu ed(1) doesn't have syntax highlighting, but M. Fuchs has implemented a fork with syntax highlighting.

I don't know anything about retrocomputing, software minimalism, or oddball text editors. I just like ed.

General usage

The general use of ed(1) is terse and relies on regular expression operators, which are applied linewise.

For example, to change arbitrary text inside brackets on a given line, one can execute s/(.*)/(re), where re is a regular excodession. Similarly, we can put a blank line after lines matching a pattern g/re/s/$/\, trim leading whitespace s/^[ ]*/, or trailing whitespace s/[ ]*$/. The g operator loops the substitution over the entire file.

ed(1) has some special operators. \( and \) can be used to capture regex. The command g/canon/s/\(canon\/\)/\1index.html evaluates \1 to canon and sunstitutes canon with canon/index.html throughout the text. The operator % evaluates to current file name. ! pipes the arguments to the shell.

External tools

ed(1) can utilise other command line tools for different purposes.

For example, we can use fmt to break lines at 72 chars:

e !fmt -w72 %

A more complicated use-case is formatting a selection. This can be achieved by combining sed with fmt as:

r !sed -n 'n,mp' % | fmt -w 72

where n and m are line numbers. Another solution would be to write the selection to a temporary file w n,m tmp.txt and then pipe that one to fmt and read it back into the buffer.


The easiest way to script ed is using here scripts:

ed -s file <<< $'H\n[ed commands]\nw'


What do you expect from a text editor? For what I do, I'm pretty happy with ed.

But if you need to work with multiple files, ed(1) is not the strongest programme, because it can only open one file at a time.


Remember, the so-called "visual" editors have been placed here by ed to tempt the faithless. Do not give in! Just use ed(1).

However, if you need to work with multiple files and don't want to use ed, gnu emacs is a nice editor with a graphical user interface.

When using emacs, it is sometimes advised to launch it as a daemon with server-start and to keep it running for the entire session. This allows you to open other files from the command line through the emacsclient command. Files will open in the emacs window that is already open.

However, I prefer to start a single emacs instance and use eshell inside it. The advantage of eshell over your operating system's terminal emulator is that files can be opened directly with C-f. Other emacs keybindings are also consistent inside eshell.

The manual

The emacs manual is very good. Within emacs, it can be evoked from everywhere by executing C-h r. It this doesn't work, it's probably not installed. In debian, it can be installed through apt install emacs-common-non-dfsg.

Useful functions

emacs comes with many useful built-in functionalities, which are well-documented in the aforementioned manual. Additional functionalities can be added through external repositories.

Hippie expand

When activated, Hippie Expand provides low-overhead auto-completion, based on text in open buffers. Start typing a word, and then invoke auto-completion with M-/. Hippie Expand completes the word by cycling through different auto-completion mechanisms that can be specified in the configuration file―see below. If the correct expansion of the word has been passed by accident, use C-/ to cycle back.

Keyboard macros

emacs lets you record and re-execute any chain of commands, which are called keyboard macros. We use C-x ( to start and C-x ) to end the recording. Once recorded, the macro can be executed repeatedly through C-x e. This is useful if the same action has to be performed repeatedly throughout the text and there is no good regular expression to capture. It allows for a more general structural editing than the regular expression-based workflow in, for example, ed(1).

apply-macro-to-region-lines runs the keyboard macro on each line in the region. I also sometimes just execute the macro a specific [number] of times via C-[number] C-x e.


gnus is a news and email reader that ships with emacs. It lets you subscribe to email inboxes and, for example, RSS feeds.

gnus hides emails that have been read. To view these older emails, use /o, which displays all email. Login credentials are stored in $HOME/.authinfo.

De- and encryption. gnus can de- and encrypt emails using GnupPG. If the key is setup with the corresponding email address, emacs encrypts and decrypts emails without the need for further configuration. Emails are encrypted via C-c C-m c o. Decryption works automatically, if you receive an encrypted message and you have the correct key added to your keyring.

Address book. An address book to store e-mail addresses can be managed using the Insidious Big Brother Database (bbdb). The package can be installed from the gnu repository via the emacs package manager. Once it is installed, it needs to be configured in the .gnus.el and init.el files as shown in the next section.

Start the address book on its own via M-x bbdb and leave the regex field empty. This lists all entries. New entries can be added via c (create). Add all addresses you want, save with s and close bbdb with q (quit).

Now, when writing an email and filling out the address, bbdb will suggest contacts and cycle through them in another buffer.

Delay email sending. Gnus can schedule emails to be sent at a certain time. This requires the following line in init.el:

(setq message-draft-headers '(References From))
(gnus-demon-add-handler 'gnus-delay-send-queue 60 nil)

The second line ensures the correct time stamp. The last two lines set up a demon to automatically push out queued messages every 60 min. In a message buffer, press C-c C-j instead of C-c C-c. The message is put into a folder called "delayed".

Unmark email. Emails marked as important in other email programmes can be unmarked in gnus through M-u


dired is a file browser that is shipped with emacs that can be invoked through M-x dired.

Selecting multiple files. Navigate in the directory holding the files you want to search in. Using m, mark all files you want to modify.

If there are too many files to mark by hand, one can execute M-x find-dired and then provide suitable flags, such as -name "*.cpp". This opens another dired buffer with the files that match the regex. If you want to select all matches, press t, which toggles marks.

Replacing regular expression in several files. In dired, we can replace a regular expression across several files. Select multiple files as described above. Execute dired-do-find-regexp-and-replace or, alternatively, press Q. Follow the instructions. The files need to be explicitly saved—see below.

Saving multiple files. For a large number of files, one can execute M-x ibuffer. Mark all unsaved files with * u and save them via S, that is to say Shift+s.

Filtering files. In order to search by filename and open the list in a dired buffer, use M-x find-name-dired.


pdf-tools is a suite to view and modify pdf files. In my opinion, it works better than the built-in doc-view, which relies on converting pdf files into png files. It can be installed through the package-manager from melpa.

If you only occasionally view pdf documents with emacs you don't need this package.


ebib is a BibTeX reference manager for emacs, written by J. Kremers. It can be installed through melpa. It has been recommended to me by Göktuǧ Kayaalp.

ebib is very straight-forward to use. One thing I struggled with was to export individual entries. Göktuǧ provided an answer:

In the top buffer you can hit C e to copy the selected entry's bibtex. Also x is for export, it prompts for a database name (i.e. a bibtex file opened in ebib, open files with o, creates .bib file if it doesn't exist, close database with ~c~). You can also mark with m and do bulk export. Sadly it doesn't visibly indicate marked entries by default so it's a bit difficult to work with marks.

Underrated functionalities


Configuration file

;; -*- mode: emacs-lisp -*-
;; -- init.el: emacs config

;; -- built in

(require 'package)
(add-to-list 'package-archives
	     '("melpa-stable" . "") t)

(setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3")
(setq inhibit-splash-screen t)

(appt-activate t)
(savehist-mode t)

;; dired

(put 'dired-find-alternate-file 'disabled nil)
(setq delete-by-moving-to-trash t)

;; hippie expand

(global-set-key (kbd "M-/") 'hippie-expand)
(setq hippie-expand-try-functions-list '(try-expand-dabbrev

;; ido

(require 'ido)
(setq ido-enable-flex-matching t)
(setq ido-everywhere t)
(ido-mode t)

;; eww

(setq browse-url-browser-function 'eww-browse-url)
(setq browse-url-generic-program "dillo")
;; (setq eww-search-prefix "")

;; org

   '((calc . t)
     (shell . t)
     (gnuplot . t)
     (python . t)
     (R . t)
     (C . t)))

;;(setq org-html-validation-link nil)
(setq org-log-done t)
org-agenda-files (list "~/Documents/writing/webpage/org/"))

;;(add-hook 'org-mode-hook (lambda () (variable-pitch-mode t)))

;; gnus

;; add time stamp when sending email
(setq message-draft-headers '(References From))
;; push out queued emails every 60 minutes
(gnus-demon-add-handler 'gnus-delay-send-queue 60 nil)

(require 'bbdb)
(setq bbdb-north-american-phone-numbers-p nil)
(setq bbdb-user-mail-names
      (regexp-opt '("")))
;; cycling while completing email addresses
(setq bbdb-complete-name-allow-cycling t)
(setq bbdb-use-pop-up nil)

;; -- custom functions

(load "~/.emacs.d/elisp/custom-functions.el")

;; -- third party library

(require 'pdf-tools)
(require 'ebib)

;; -- append automatic configurations