Commit 4bfd70e9 authored by Karl Heuer's avatar Karl Heuer
Browse files

(skeleton-newline-indent-rigidly): New variable.

(skeleton-internal-1): Use it for indenting after \n because previous
behaviour was only useful for `sh-script.el' and old `ada.el'.  Other
modes now get their own indentation.
(skeleton, skeleton-modified, skeleton-point, skeleton-regions): `New'
variables for passing between the mutually recursive functions of
the skeleton engine.  Introduced to remove compiler warnings.
(skeleton-proxy): New argument `str' to make this settable when calling
a skeleton as a function.
(skeleton-insert): New argument `str' to pass down.  Element `\n'
now usually indents according to mode.  Subskeletons may also have
a list of strings as iterator.  Earlier modification also removed
meaning of `quit' -- I did not put it back in since it's useless.
When quitting out of a subskeleton while still wrapping around text
don't duplicate first line of that text.
(skeleton-end-hook): New hook useful say for modes that leave a `;' on
an empty line to indent right and then want to clean it up when doing
a skeleton there.
parent 61dee1e7
......@@ -52,10 +52,12 @@ Typical examples might be `upcase' or `capitalize'.")
(defvar skeleton-filter 'identity
"Function for transforming a skeleton-proxy's aliases' variable value.")
(defvar skeleton-untabify t
"When non-`nil' untabifies when deleting backwards with element -ARG.")
(defvar skeleton-newline-indent-rigidly nil
"When non-`nil', indent rigidly under current line for element `\\n'.
Else use mode's `indent-line-function'.")
(defvar skeleton-further-elements ()
"A buffer-local varlist (see `let') of mode specific skeleton elements.
......@@ -77,6 +79,11 @@ skeleton elements.")
(defvar skeleton-debug nil
"*If non-nil `define-skeleton' will override previous definition.")
;; reduce the number of compiler warnings
(defvar skeleton)
(defvar skeleton-modified)
(defvar skeleton-point)
(defvar skeleton-regions)
(defmacro define-skeleton (command documentation &rest skeleton)
......@@ -95,33 +102,38 @@ INTERACTOR and ELEMENT ... are as defined under `skeleton-insert'."
;; This command isn't meant to be called, only it's aliases with meaningful
;; names are.
(defun skeleton-proxy (&optional arg)
"Insert a skeleton defined by variable of same name (see `skeleton-insert').
(defun skeleton-proxy (&optional str arg)
"Insert skeleton defined by variable of same name (see `skeleton-insert').
Prefix ARG allows wrapping around words or regions (see `skeleton-insert').
This command can also be an abbrev expansion (3rd and 4th columns in
\\[edit-abbrevs] buffer: \"\" command-name)."
(interactive "*P")
\\[edit-abbrevs] buffer: \"\" command-name).
When called as a function, optional first argument STR may also be a string
which will be the value of `str' whereas the skeleton's interactor is then
(interactive "*P\nP")
(let ((function (nth 1 (backtrace-frame 1))))
(if (eq function 'nth) ; uncompiled lisp function
(setq function (nth 1 (backtrace-frame 5)))
(if (eq function 'byte-code) ; tracing byte-compiled function
(setq function (nth 1 (backtrace-frame 2)))))
(if (not (setq function (funcall skeleton-filter (symbol-value function))))
(if (or (eq this-command 'self-insert-command)
(eq this-command 'skeleton-pair-insert-maybe)
(eq this-command 'expand-abbrev))
(setq buffer-undo-list
(primitive-undo 1 buffer-undo-list)))
(if (memq this-command '(self-insert-command
(setq buffer-undo-list (primitive-undo 1 buffer-undo-list)))
(skeleton-insert function
(if (setq skeleton-abbrev-cleanup
(or (eq this-command 'self-insert-command)
(eq this-command 'skeleton-pair-insert-maybe)))
;; Pretend C-x a e passed the prefix arg to us
;; Pretend C-x a e passed its prefix arg to us
(if (or arg current-prefix-arg)
(prefix-numeric-value (or arg
(if (stringp str)
(if skeleton-abbrev-cleanup
(setq deferred-action-list t
deferred-action-function 'skeleton-abbrev-cleanup
......@@ -139,7 +151,7 @@ This command can also be an abbrev expansion (3rd and 4th columns in
(defun skeleton-insert (skeleton &optional no-newline regions)
(defun skeleton-insert (skeleton &optional no-newline skeleton-regions str)
"Insert the complex statement skeleton SKELETON describes very concisely.
If optional NO-NEWLINE is nil the skeleton will end on a line of its own.
......@@ -151,13 +163,17 @@ two contiguous marked points. If you marked A B C [] (where [] is the cursor)
in alphabetical order, the 3 interregions are simply the last 3 regions. But
if you marked B A [] C, the interregions are B-A, A-[], []-C.
Optional fourth STR is the value for the variable `str' within the skeleton.
When this is non-`nil' the interactor gets ignored, and this should be a valid
skeleton element.
SKELETON is made up as (INTERACTOR ELEMENT ...). INTERACTOR may be nil if
not needed, a prompt-string or an expression for complex read functions.
If ELEMENT is a string or a character it gets inserted (see also
`skeleton-transformation'). Other possibilities are:
\\n go to next line and align cursor
\\n go to next line and indent according to mode
_ interesting point, interregion here, point after termination
> indent line (or interregion if > _) according to major mode
& do next ELEMENT if previous moved point
......@@ -172,7 +188,8 @@ different inputs. The SKELETON is processed as often as the user enters a
non-empty string. \\[keyboard-quit] terminates skeleton insertion, but
continues after `resume:' and positions at `_' if any. If INTERACTOR in such
a subskeleton is a prompt-string which contains a \".. %s ..\" it is
formatted with `skeleton-subprompt'.
formatted with `skeleton-subprompt'. Such an INTERACTOR may also a list of
strings with the subskeleton being repeated once for each string.
Quoted lisp-expressions are evaluated evaluated for their side-effect.
Other lisp-expressions are evaluated and the value treated as above.
......@@ -185,26 +202,29 @@ available:
then: insert previously read string once more
help help-form during interaction with the user or `nil'
input initial input (string or cons with index) while reading str
quit non-nil after resume: section is entered by keyboard quit
v1, v2 local variables for memorising anything you want"
(and regions
(setq regions
(if (> regions 0)
v1, v2 local variables for memorising anything you want
When done with skeleton, but before going back to `_'-point call
`skeleton-end-hook' if that is non-`nil'."
(and skeleton-regions
(setq skeleton-regions
(if (> skeleton-regions 0)
(list (point-marker)
(save-excursion (forward-word regions) (point-marker)))
(setq regions (- regions))
;; copy regions - 1 elements from `mark-ring'
(save-excursion (forward-word skeleton-regions)
(setq skeleton-regions (- skeleton-regions))
;; copy skeleton-regions - 1 elements from `mark-ring'
(let ((l1 (cons (mark-marker) mark-ring))
(l2 (list (point-marker))))
(while (and l1 (> regions 0))
(while (and l1 (> skeleton-regions 0))
(setq l2 (cons (car l1) l2)
regions (1- regions)
skeleton-regions (1- skeleton-regions)
l1 (cdr l1)))
(sort l2 '<))))
(goto-char (car regions))
(setq regions (cdr regions)))
(goto-char (car skeleton-regions))
(setq skeleton-regions (cdr skeleton-regions)))
(let ((beg (point))
modified point resume: help input quit v1 v2)
skeleton-modified skeleton-point resume: help input v1 v2)
(or no-newline
(goto-char (prog1 (point)
......@@ -212,14 +232,15 @@ available:
(eval `(let ,skeleton-further-elements
(skeleton-internal-list skeleton)))
(skeleton-internal-list skeleton str)))
(run-hooks 'skeleton-end-hook)
(sit-for 0)
(or (pos-visible-in-window-p beg)
(goto-char beg)
(recenter 0)))
(if point
(goto-char point)))))
(if skeleton-point
(goto-char skeleton-point)))))
......@@ -232,13 +253,15 @@ i.e. we are handling the iterator of a subskeleton, returns empty string if
user didn't modify input.
While reading, the value of `minibuffer-help-form' is variable `help' if that
is non-`nil' or a default string."
(or no-newline
(setq skeleton-newline nil)
(or (symbol-value 'no-newline) ; cheat on compiler warning
(goto-char (prog1 (point)
(if recursive (setq skeleton-newline 2))
(indent-to (prog1
(let ((minibuffer-help-form (or help (if recursive "\
(let ((minibuffer-help-form (or (symbol-value 'help) (if recursive "\
As long as you provide input you will insert another subskeleton.
If you enter the empty string, the loop inserting subskeletons is
......@@ -252,7 +275,8 @@ You are inserting a skeleton. Standard text gets inserted into the buffer
automatically, and you are prompted to fill in the variable parts."))))
(setq str (if (stringp str)
(read-string (format str skeleton-subprompt)
(setq initial-input (or initial-input input)))
(setq initial-input (or initial-input
(symbol-value 'input))))
(eval str))))
(if (and recursive
(or (null str)
......@@ -262,30 +286,26 @@ automatically, and you are prompted to fill in the variable parts."))))
(signal 'quit t)
(defun skeleton-internal-list (skeleton &optional recursive)
(defun skeleton-internal-list (skeleton &optional str recursive)
(let* ((start (save-excursion (beginning-of-line) (point)))
(column (current-column))
(line (buffer-substring start
(save-excursion (end-of-line) (point))))
(str `(setq str (skeleton-read ',(car skeleton) nil ,recursive)))
(while (setq modified (eq opoint (point))
(or str
(setq str `(setq str (skeleton-read ',(car skeleton) nil ,recursive))))
(while (setq skeleton-modified (eq opoint (point))
opoint (point)
skeleton (cdr skeleton))
(condition-case quit
(skeleton-internal-1 (car skeleton))
(if (eq (cdr quit) 'recursive)
(setq recursive 'quit)
(while (if skeleton
(not (eq (car (setq skeleton (cdr skeleton)))
(setq recursive 'quit
skeleton (memq 'resume: skeleton))
;; remove the subskeleton as far as it has been shown
;; the subskeleton shouldn't have deleted outside current line
;; problematic when wrapping text starting on same line
(end-of-line skeleton-newline)
(delete-region start (point))
(insert line)
(move-to-column column)
......@@ -300,47 +320,60 @@ automatically, and you are prompted to fill in the variable parts."))))
(defun skeleton-internal-1 (element &optional literal)
(cond ((and (integerp element) ; -num
(< element 0))
(if skeleton-untabify
(backward-delete-char-untabify (- element))
(delete-backward-char (- element))))
((char-or-string-p element)
(insert-before-markers (if (and skeleton-transformation
(not literal))
(funcall skeleton-transformation element)
(cond ((char-or-string-p element)
(if (and (integerp element) ; -num
(< element 0))
(if skeleton-untabify
(backward-delete-char-untabify (- element))
(delete-backward-char (- element)))
(insert-before-markers (if (and skeleton-transformation
(not literal))
(funcall skeleton-transformation element)
((eq element '\n) ; actually (eq '\n 'n)
(indent-relative t))
(if (and skeleton-regions
(eq (nth 1 skeleton) '_))
(or (eolp)
(indent-region (point) (car skeleton-regions) nil))
(if skeleton-newline-indent-rigidly
(indent-to (prog1 (current-indentation)
((eq element '>)
(if (and regions
(if (and skeleton-regions
(eq (nth 1 skeleton) '_))
(indent-region (point) (car regions) nil)
(indent-region (point) (car skeleton-regions) nil)
((eq element '_)
(if regions
(if skeleton-regions
(goto-char (car regions))
(setq regions (cdr regions)))
(or point
(setq point (point)))))
(goto-char (car skeleton-regions))
(setq skeleton-regions (cdr skeleton-regions)))
(or skeleton-point
(setq skeleton-point (point)))))
((eq element '&)
(if modified
(if skeleton-modified
(setq skeleton (cdr skeleton))))
((eq element '|)
(or modified
(or skeleton-modified
(setq skeleton (cdr skeleton))))
((if (consp element)
(or (stringp (car element))
(consp (car element))))
(while (skeleton-internal-list element t)))
((if (consp element)
(eq 'quote (car element)))
((eq 'quote (car-safe element))
(eval (nth 1 element)))
((or (stringp (car-safe element))
(consp (car-safe element)))
(if (symbolp (car-safe (car element)))
(while (skeleton-internal-list element nil t))
(setq literal (car element))
(while literal
(skeleton-internal-list element (car literal))
(setq literal (cdr literal)))))
((null element))
((skeleton-internal-1 (eval element) t))))
;; Maybe belongs into simple.el or elsewhere
(define-skeleton local-variables-section
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment