kern.el in emacs

21 views
Skip to first unread message

Benjamin Bradshaw

unread,
May 28, 2025, 2:48:10 PMMay 28
to starstarhug
Hello,
Is anyone using the kern.el in the humdrum repo? It's not highlighting anything for me, and C-h m doesn't give me any help either. Looking in the file it seems that it has definitions for various font faces, and they show up in the faces menus, so I know they're there. Kern mode just doesn't seem to be using the highlighting. I'm not quite sure how to make that work. I'm guessing it may be emacs has changed something in the last 11 years since that mode was updated?

Thanks,
Ben

Craig Sapp

unread,
May 28, 2025, 4:11:03 PMMay 28
to stars...@googlegroups.com
Hi Ben,

Here is the file you are referring to:

What flavor/version of emacs are you using, as that may be important?

I don't use emacs, but checking with ChatGPT, it says the file has some outdated code and suggests this update:

;;; kern-mode.el --- Major mode for editing Humdrum **kern files

(require 'cl-lib)

(defvar kern-mode-hook nil)

(defvar kern-mode-map
  (let ((map (make-keymap)))
    (define-key map (kbd "C-j") 'newline-and-indent)
    (define-key map [tab] 'kern-insert-tab)
    (define-key map (kbd "C-M-n") 'next-measure)
    (define-key map (kbd "C-M-p") 'previous-measure)
    (define-key map (kbd "C-M-m") 'go-to-measure)
    (define-key map (kbd "C-M-h") 'show-header)
    map)
  "Keymap for kern-mode.")

(add-to-list 'auto-mode-alist '("\\.krn\\'" . kern-mode))

(defun end-of-line-p ()
  (or (null (char-after)) (char-equal (char-after) ?\n)))

(defun blank-line-p ()
  (and (= (current-column) 0)
       (end-of-line-p)))

(defun next-measure ()
  (interactive)
  (when (re-search-forward "^=" nil t)
    (beginning-of-line)))

(defun previous-measure ()
  (interactive)
  (when (re-search-backward "^=" nil t)
    (beginning-of-line)))

(defun show-header ()
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (when (re-search-forward "^\\*\\*.*$" nil t)
      (let ((header-line (buffer-substring (line-beginning-position)
                                           (line-end-position))))
        (message "%s" header-line)))))

(defun go-to-measure ()
  (interactive)
  (let ((m (read-from-minibuffer "measure #: ")))
    (if (re-search-forward (concat "^=" m) nil t)
        (beginning-of-line)
      (when (re-search-backward (concat "^=" m) nil t)
        (beginning-of-line)))))

(defun copy-line ()
  (interactive)
  (kill-new (buffer-substring (line-beginning-position) (line-end-position))))

(defun kern-newline ()
  (interactive "*")
  (unless (= (line-number-at-pos) 1)
    (save-excursion
      (forward-line -1)
      (let ((prev (buffer-substring (point) (line-end-position))))
        (cl-loop repeat (cl-count ?\t prev) do (insert "\t."))))
    (delete-horizontal-space))
  (newline))

(defun kill-column ()
  (interactive)
  (when (re-search-backward "^\\*\\*" nil t)
    (let ((start (point)))
      (when (re-search-forward "^\\*-\\t" nil t)
        (kill-rectangle start (point))))))

(defun newline-insert-measure ()
  (interactive)
  (let (last-measure row)
    (save-excursion
      (setq row (buffer-substring (line-beginning-position) (line-end-position))))
    (save-excursion
      (when (re-search-backward "=\\([0-9]+\\)$" nil t)
        (cl-destructuring-bind (num . _) (read-from-string (match-string 1))
          (setq last-measure (1+ num)))))
    (newline)
    (let ((fields (cl-count ?\t row)))
      (cl-loop for i from 1 to fields do
               (insert (format "=%d\t" last-measure)))
      (insert (format "=%d" last-measure)))))

(defun kern-insert-tab ()
  (interactive)
  (let ((last-char (char-before)))
    (cond
     ((blank-line-p)
      (message "You need to insert some code."))
     ((and (char-after) (char-equal (char-after) ?!))
      (when (re-search-forward "\\*\\*" nil t)
        (backward-char 2)))
     ((and (char-equal last-char ?\t) (end-of-line-p))
      (message "You need to insert some code."))
     ((not (end-of-line-p))
      (re-search-forward "\t" nil t))
     (t (insert "\t")))))

(defgroup kern-faces nil
  "Faces for Humdrum **kern mode"
  :group 'faces)

(defface kern-bar-face
  '((t (:foreground "yellow" :background "dim gray")))
  "Face for barlines"
  :group 'kern-faces)

(defface kern-global-comment-face
  '((t (:foreground "firebrick")))
  "Face for global comments (!...)"
  :group 'kern-faces)

(defface kern-exclusive-face
  '((t (:foreground "blue1")))
  "Face for exclusive interpretation (**...)"
  :group 'kern-faces)

(defface kern-interpretation-face
  '((t (:foreground "forest green")))
  "Face for other interpretations (*...)"
  :group 'kern-faces)

(defface kern-notes
  '((t (:foreground "orange")))
  "Face for notes and durations"
  :group 'kern-faces)

(defvar kern-font-lock-keywords
  '(("^!.*" . 'kern-global-comment-face)
    ("^\\*\\*.*" . 'kern-exclusive-face)
    ("^\\*[^*].*" . 'kern-interpretation-face)
    ("^=\\([0-9]+\\)?[:!|]*$" . 'kern-bar-face)
    ("[0-9.]+[a-grA-G#n-]+" . 'kern-notes))
  "Font-lock keywords for kern-mode.")

(defun kern-align-tabs-region (start end)
  "Align selected region to tab stops. Replaces spaces with tabs."
  (interactive "r")
  (let ((tab-width 12))
    (untabify start end)
    (align-regexp start end "\\(\\s-*\\)\t" 1 1 t)
    (tabify start end)))

(defun kern-mode ()
  "Major mode for editing Humdrum **kern files."
  (interactive)
  (kill-all-local-variables)
  (use-local-map kern-mode-map)
  (setq-local font-lock-defaults '(kern-font-lock-keywords))
  (setq-local indent-line-function 'indent-to-left-margin)
  (setq-local indent-tabs-mode t)
  (setq-local tab-width 12)
  (setq-local tab-stop-list (number-sequence 12 240 12))
  (setq-local comment-start "!")
  (setq major-mode 'kern-mode)
  (setq mode-name "KERN")
  (setq-local imenu-generic-expression
              '(("Barline" "^=\\([0-9]+\\)" 1)
                ("Interpretation" "^\\*\\*\\(.*\\)" 1)))
  (run-hooks 'kern-mode-hook))

(provide 'kern-mode)
;;; kern-mode.el ends here

If that works for you, then I can update the file on the repository.

Documentation (provided by ChatGPT):

📄 kern-mode.el for Humdrum **kern Files

🔧 Features Overview

  • Syntax highlighting:

    • !!... global comments → firebrick

    • **... exclusive interpretations → blue

    • *... regular interpretations → forest green

    • =... barlines → yellow on gray

    • 4c, 2g#, 1r (note+duration) → orange

  • Custom keybindings:

    • C-M-n → next barline

    • C-M-p → previous barline

    • C-M-m → go to measure number

    • C-M-h → show the header line (**kern)

  • Smart tab navigation:

    • TAB cycles through tabbed fields on a line

  • Measure insertion:

    • Adds next measure number aligned across spines

  • Newline auto-fill:

    • Pressing RET copies above row’s tab structure with dots

  • Region alignment:

    • M-x kern-align-tabs-region aligns spines by tabs

  • Imenu support:

    • Accessible via M-x imenu or the menu bar for:

      • Barline numbers (=12)

      • Interpretations (**kern)


🧰 Installation and Setup Instructions

✅ 1. Save the Mode File

Save the updated file as:

~/.emacs.d/lisp/kern-mode.el

Or any path you prefer.


✅ 2. Edit Your .emacs or init.el

Add the following:

(add-to-list 'load-path "~/.emacs.d/lisp/") ; adjust path if needed (require 'kern-mode) (add-to-list 'auto-mode-alist '("\\.krn\\'" . kern-mode))

This will:

  • Load kern-mode.el on startup

  • Automatically activate it for .krn files


✅ 3. Usage

Open a .krn file. You should see:

  • Syntax highlighting for Humdrum structures

  • Spines aligned under tab stops

  • Ability to jump or insert measures


⚙️ Optional: Keybinding for Region Alignment

If you want to bind M-x kern-align-tabs-region to a key (e.g. C-c C-a), add this:

(define-key kern-mode-map (kbd "C-c C-a") 'kern-align-tabs-region)

Add it near the end of kern-mode function or in your Emacs config.


🧪 Testing Tips

  • Try opening a .krn file and press C-M-n and C-M-p to jump between =barlines.

  • Press RET to insert a new line that mimics tab alignment with . placeholders.

  • Use M-x imenu to jump to sections or barlines.

  • Select a misaligned region and run M-x kern-align-tabs-region.


-=+Craig


--
--
This is a message is from the **HUG newgroup.
To post to this group, send email to stars...@googlegroups.com
To unsubscribe from this group, send email to
starstarhug...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/starstarhug?hl=en
---
You received this message because you are subscribed to the Google Groups "starstarhug" group.
To unsubscribe from this group and stop receiving emails from it, send an email to starstarhug...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/starstarhug/b4b07153-f186-45e8-a803-7b963c2f0a43n%40googlegroups.com.

Benjamin Bradshaw

unread,
May 28, 2025, 5:29:53 PMMay 28
to stars...@googlegroups.com
Hi Craig,
That didn't do it either, but ChatGPT did help me figure it out. I think the problem is there is nothing in the hook. What's the best way for me to share the changes I made to the file to get it working? I've never really collaborated on github, I've just used it for syncing my own projects and haven't dealt with issues or pull requests or the like.

Thanks,
--
suu.eduBENJAMIN BRADSHAW, DM   |   ADJUNCT PROFESSOR
MUSIC DEPARTMENT
SOUTHERN UTAH UNIVERSITY
Center for Music Technology   
 


On Wed, May 28, 2025 at 2:11 PM Craig Sapp <crai...@gmail.com> wrote:
Hi Ben, Here is the file you are referring to: https: //github. com/humdrum-tools/humdrum/blob/b55e10a99f1be42da637438eebd9d01cd30c2259/syntax-highlighting/emacs/kern. el What flavor/version of emacs are you using, as that may be important? I don't

Craig Sapp

unread,
May 28, 2025, 5:47:14 PMMay 28
to stars...@googlegroups.com
Hi Ben,

You can paste the version of the file that you got working into **HUG, or if you have a github account, you can add an issue by going to this page:


This should be easy to do: then clicking on the green "new issue" button.  In the contents of the issue, you can paste the text of the file contents, and then add three backticks on lines by themselves:

```
[contents of file here]
```

before and after the text content.

-=+Craig




Reply all
Reply to author
Forward
0 new messages