5

I want to create a skeleton, triggered by the abbrev keyword 'func' which checks its context and runs one of two possible sub-skeletons.

Here's what I have:

(define-skeleton my-func-skeleton "func skeleton"
  ""
  (if (looking-back "\n")
      ;; A function at left of screen is a top level function.
      '((setq str (skeleton-read "Function name: "))
        "// " str " ." \n
        "func " str "(" @ ") {" \n
        _ @ \n
        -1 "}")
     ;; Otherwise it is an anonymous function. 
     '("func () {" \n
       _ \n
       -1 "}")))

(expand-add-abbrevs go-mode-abbrev-table
                    '(("func" my-func-skeleton)))

But this just recursively executes the appropriate sub-skeleton until I quit.

How do I make expressions within skeletons return sub-skeletons that work correctly?

4

The manual says the following.

Subskeletons are inserted recursively, not once, but as often as the user enters something at the subskeletons interactor. Thus there must be a str in the subskeleton. They can also be used non-interactively, when prompt is a lisp-expression that returns successive list-elements.

(Emphasis is mine, see section Skeleton Language for more.)

Here's what this means:

By default it repeats until you hit RET or C-g:

(define-skeleton skeleton-repeat-until-tired "repeat until tired"
  ""                                     ; empty top-level interactor
  ("Enter something here, or press %s: " ; stops on empty input string
   "You entered '" str "'.\n"))

To execute a subskeleton once, use ("") as the interactor:

(define-skeleton skeleton-once "executes a subskeletons once"
  ""                                    ; empty top-level interactor

  (("")
   "insert stuff\n"))

This is just a special case of an interactor used to loop over several strings:

(define-skeleton skeleton-loop-1 "subskeletons as loops"
  ""                                    ; empty top-level interactor

  (("a" "b" "c")                        ; list if strings, execute subskeleton once per element
   "str is " str "\n"))

You can compute these strings based on the context, too:

(defun my-strings ()
  (list (format "you were at line %d" (line-number-at-pos))))

(define-skeleton skeleton-loop-2 "subskeletons as loops"
  ""                                    ; empty top-level interactor

  `(,(my-strings)                        ; list if strings, execute subskeleton once per element
    "str is " str "\n"))

Here's one way to use this information to fix your skeleton:

(define-skeleton my-func-skeleton "func skeleton"
  ""
  (if (looking-back "\n")
      ;; A function at left of screen is a top level function.
      `((,(skeleton-read "Function name: "))
        "// " str " ." \n
        "func " str "(" @ ") {" \n
        _ @ \n
        -1 "}")
     ;; Otherwise it is an anonymous function. 
    '(("")
      "func () {" \n
       _ \n
       -1 "}")))
| improve this answer | |
  • Wonderful explanation. – BigBird Nov 26 '14 at 23:36

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.