Here's something I could think of, however, take it with a grain of salt, because it may be overly complex, and you don't want to bring this much complexity into what you do:
(defstruct command-state
action next-state)
(defmacro define-action-states (name condition &rest actions)
(labels ((%make-command-state
(action name)
`(make-command-state :action (lambda () ,action))))
`(let ((head ,(%make-command-state (car actions) name)))
(defvar ,name nil)
(setq ,name head)
,@(loop for action in (cdr actions)
collect
`(setf (command-state-next-state ,name)
,(%make-command-state action name)
,name (command-state-next-state ,name)))
(setf (command-state-next-state ,name) head
,name head)
(defun ,(intern (concat (symbol-name name) "-command")) ()
(when ,condition
(unwind-protect
(funcall (command-state-action ,name))
(setq ,name (command-state-next-state ,name))))))))
(define-action-states print-names (= 1 1)
(message "first state")
(message "second state")
(message "third state")
(message "fourth state"))
(print-names-command)
;; will print messages looping through them,
;; each time you call it
I've made it to use a struct
, so that you could add more conditions, independent of the state itself, for example, but mostly so the names would be more self-explanatory.
Also, probably, that's not the place you should really care about efficiency - so far your fingers cannot outrun the eLisp interpreter, it's all good ;)
Here's something I did to your code to possibly improve it a bit (now the worst case scenario will only check 5 conditions instead of 6 :)
(defun smart-killer ()
(interactive)
(let* ((properties (symbol-plist 'smart-killer))
(counter (plist-get properties :counter)))
(if (region-active-p)
(kill-region (region-beginning) (region-end))
(if (eq last-repeatable-command 'smart-killer)
(if (> counter 3)
(message "Kill ring is already filled with paragraph.")
(if (> counter 2)
(progn
(yank)
(kill-new "")
(mark-paragraph -1)
(kill-region (region-beginning) (region-end)))
(if (> counter 1)
(kill-region (point) (line-beginning-position))
(kill-line))))
(when (not (looking-at "\\<\\|\\>")) (backward-word)) ; begin/end of word
(kill-word 1))
(plist-put properties :counter (mod (1+ counter) 5)))))
(put 'smart-killer :counter 0)