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?

Malabarba
  • 22,878
  • 6
  • 78
  • 163
BigBird
  • 53
  • 3

1 Answers1

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 "}")))
Constantine
  • 9,072
  • 1
  • 34
  • 49