public
Created

  • Download Gist
soft-semicolons.el
Emacs Lisp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
;; 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.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.