0

I recently saw this image on r/unixporn.

How can I recreate the 'notebook' effect: the broad line-height and underline of each line, but in Emacs (iiuc this is achieved using rxvt + pixmaps in Linux).

3
1

Edit: I've added some Elisp code at the end of the answer that performs all of the changes mentioned and then evaluates the buffer it is run in.

One way to get this effect is by making some simple modifications to Steve Purcell's page-break-lines.el. (To future-proof this answer as much as possible, I am referring specifically to this version of the file from a March 4th 2020 commit). I can't post the code for this mode here since it is GPLv3. Fortunately, the changes necessary to get what you want are easy to make.

Note: before going any further, I highly recommend changing the names of all of these variables and functions so as they do not clash with those of the real page-break-lines. My recommendation is to use the prefix my-ruled-lines. This can be done by running (replace-string "page-break" "my-ruled"). Note that this and all other replace-string function calls in this answer should be run with the point at the top of the buffer containing the code. You should also change the minor-mode lighter too ((replace-string "PgLn" "Ruled")).

This package causes Emacs to display a customizable character repeated enough times to fit the width of the window every time it sees a form-feed character (e.g. by using a line of dashes). The only changes we need to make are to:

  1. have it work on the new-line character rather than the form-feed character
  2. still display the new-line character in addition to the replacement line

Accomplishing the first part is very easy, and can be done by running the function (replace-string "^L" "^J"). However, just making this change will make it so that Emacs doesn't display any newlines at all, which is not what we want.

Fortunately, all we have to do is to wrap our replacement characters with a newline on either side. This will put the put the replacement character line on a newline (but won't affect the line-numbering) and will put all future characters on the line after that. This can be done by modifying the following line in the function page-break-lines--update-display-table (which should now be renamed to something like my-ruled-lines--update-display-table). Note that this is line number 143 in Github's line numbering, but Emacs seems to eat an empty line when I paste it in:

(new-display-entry (vconcat (make-list width glyph))))

which should be changed to:

(new-display-entry (vconcat (list ?\^J) (make-list width glyph) (list ?\^J))))

Now you need to evaluate all of these functions (or save them to a .el file and load them). Now run the command M-x my-ruled-lines-mode (or whatever that command has been renamed to) in your desired buffer and it should work.

After you've done all of this you may want to tweak the face my-ruled-lines to get your ruled lines to appear more to your liking.

One caveat: it appears these changes break the ability of this minor mode to remove the display changes from the buffer-display-table. You can use the following function to turn off the ruled lines if disabling the minor mode alone does not work:

(defun turn-off-my-ruled-lines-mode ()
  (interactive)
  (my-ruled-lines-mode -1)
  (aset buffer-display-table ?\^J nil))

Script to make these changes

Since there are several individual steps to follow here, I've packaged all of the steps into a single Elisp function call. The code from page-break-lines.el should be pasted into a buffer then this code should be evaluated (i.e. by using M-:). This will make the changes and then define the minor mode.

(save-excursion
  (goto-char (point-min))
  (replace-string "page-break" "my-ruled")
  (goto-char (point-min))
  (replace-string "PgLn" "Ruled")
  (goto-char (point-min))
  (replace-string "^L" "^J")
  (goto-char (point-min))
  (replace-string "(new-display-entry (vconcat (make-list width glyph))))"
                  "(new-display-entry (vconcat (list ?\\^J) (make-list width glyph) (list ?\\^J))))")
  (goto-char (point-max))
  (newline)
  (insert "(defun turn-off-my-ruled-lines-mode ()\n  (interactive)\n  (my-ruled-lines-mode -1)\n  (aset buffer-display-table ?\\^J nil))")
  (eval-buffer))

Here is what the results look like when run on default Emacs 26.3: My-ruled-lines mode run on default Emacs

5
  • This is amazing, thank you so much! This definitely gives me something to tinker with. Unfortunately it seems to have a fairly adverse impact on buffer performance, but I will try and remedy that. – Antony Woods Jul 7 '20 at 8:15
  • I'd also be interested in figuring out how, if possible, to reduce the line height/spacing of the buffer. As in your image, the line-spacing is set to 0 and doesn't seem to take negative values, so I looked at the default-height parameter inside my-ruled-lines--update-display-table but wasn't able to do anything meaningful with this – Antony Woods Jul 7 '20 at 8:38
  • C-h f beginning-of-buffer: "This function is for interactive use only; in Lisp code use `(goto-char (point-min))' instead." – Drew Jul 7 '20 at 15:11
  • @Drew Thank you; I should know better. I've edited that portion of the code (along with end-of-buffer). – D. Gillis Jul 7 '20 at 15:25
  • @AntonyWoods Unfortunately Emacs seems to be treating the inserted glyph as if it is part of the same line as the preceding line. Emacs displays each line using the maximum potential height that could be displayed on that line (i.e. using the largest font-size on the line). This means Emacs uses the preceding line's font-size for determining the height of the glyph's line. Changing the height of the my-ruled-lines face changes the size of the glyph's component characters, but not the spacing between lines. There may be a way around this though. – D. Gillis Jul 7 '20 at 17:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.