Skip to content
Create a gist now

Instantly share code, notes, and snippets.

;; This little hack makes it a bit easier to get colons in your programs. This
;; modifies the semicolon insert function such that if a string of semicolons
;; are followed by a non-whitespace character or a member of the list
;; *soft-semicolons-also-convert-on* but not a member of
;; *soft-semicolons-dont-convert-on*, then all of those semicolons are converted
;; into "full" colons.
;; Any command that is performed in between the typing of the last semicolon and
;; the typing of the next character will reset the string of semicolons that are
;; candidates for conversion to start at the current point.
;; This is primarily useful if you just dislike using the shift keys (like I do)
;; or because of the annoyance of reaching for the shift keys tend not to use
;; colons when they are optional (like using the purer keyword arguments in Loop
;; or Iterate over the ad-hoc keywords that actually polute the namespace).
;; This code is modified from GNU Emacs and, as such, is released under the
;; GPLv3 or later.
(defcustom *soft-semicolons-also-convert-on* '(9 tab)
"This variable marks characters that will trigger semicolon
conversion in addition to the non-whitespace printable
character requirement.")
(defcustom *soft-semicolons-dont-convert-on* '(?( ?))
"This variable allows you to exclude certain characters from
triggering conversion.")
(defun soft-semicolons (arg)
"Type semicolons like normal expect if they are immediately
followed by a non-whitespace character, in which case convert all
of the consequtive semicolons you were typing into colons."
(interactive "p")
(let ((keep-going t)
(start-point (point)))
(insert 59)
(while keep-going
(let ((event (read-event)))
(cond ((equal 59 event)
;; A Semicolon, insert and keep going
(clear-this-command-keys t)
(insert 59)
(setq last-input-event nil))
((member event *soft-semicolons-dont-convert-on*)
;; For these special cases, don't do any conversion
(setq keep-going nil))
;; A non-whitespace printable character or something in
;; *soft-semiclons-also-convert-on*
((or (member event *soft-semicolons-also-convert-on*)
(and (integerp event)
;; See if this is a printable character
(aref printable-chars event)
;; Rule out whitespace characters (which might also be
;; printable)
(not (member event '(9 10 13 32)))))
(let ((length (- (point) start-point)))
(delete-region start-point (point))
(insert-char 58 length))
;; ...and exit...
(setq keep-going nil))
;; Exit on anything else
(t (setq keep-going nil)))))
;; Push any residual command back onto unread-command-events to be read and
;; processed
(when last-input-event
(clear-this-command-keys t)
(setq unread-command-events (list last-input-event)))))
;; If you want to use this, you probably want to have something in your .emacs
;; file like:
;; (add-hook 'xxxx-mode-hook
;; (lambda ()
;; (define-key xxxx-mode-map (kbd ";") 'soft-semicolons)))
;; ...where "xxxx" might be replaced with "lisp", or "emacs-lisp", or
;; "lisp-interaction", or "slime", or all of them and more. You probably do not
;; want this to be a global binding as this will probably cause trouble with
;; something you wish to write sometime.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.