Nahezu identische Befehle auf mehreren Tasten

7

Visual Studio hat diese nette Funktion, mit der Sie den Punkt auf etwas setzen können, drücken C-f3und beide finden die nächste Instanz des Dings unter dem Punkt und es wird sich daran erinnern. Sie können dann drücken f3, um die nächste Instanz zu finden und S-f3nach derselben Sache zu suchen, jedoch rückwärts.

Ich kann dasselbe in Emacs mit einem Elisp machen, das ich geschrieben habe. (Es ist sperrig und ich bin sicher schlecht, also werde ich es weglassen).

Was würde Ich mag zu tun ist für eine ähnliche Funktionalität zu ermöglichen, sagen wir, f2, f3, f4, und f5. Wenn Sie also auf C-f2Suchen nach dem Objekt unter dem Punkt drücken, indem Sie C-f3das Objekt irgendwo in einer Variablen / einem Assoziationsarray / Symbol speichern. Wenn Sie auf ein anderes Objekt drücken , wird Emacs das zweite Objekt speichern, das in einer bestimmten Variablen gespeichert wird, und ich kann suchen für das erste, indem f2ich in der Zukunft drücke, und ich kann nach dem zweiten suchen, indem ich f3in der Zukunft drücke.

Ich würde gerne eine einzige Funktion für das Control-X erstellen, eine andere für einfaches X und eine dritte für Shift-X, aber ich bin mir nicht sicher, wie ich das angehen soll.

Ist es besser, die Funktion nach dem aktuellen Tastendruck fragen zu lassen (a la this-single-command-keys) oder einen Weg zu finden, um einen Parameter an die interaktive Funktion zu übergeben ( ohne den Benutzer dazu aufzufordern, da er bereits eine Taste gedrückt hat)?

Wie gibt man zusätzliche Informationen an eine interaktive Funktion weiter?

MikeTheTall
quelle
Die Frage ist nicht sehr klar, IMO. Welche Infos und wie möchten Sie diese weitergeben? Sie können ein Präfixargument verwenden, um Informationen interaktiv zu übergeben. Ein Befehl kann nach Informationen fragen (das wollen Sie nicht, sagten Sie). Eine Funktion kann Informationen von globalen Variablen oder von einer Datei erhalten oder ... Was möchten Sie wann und wie übergeben? Wenn Sie einen Code haben, zeigen Sie ihn vielleicht - das ist wahrscheinlich klarer als Ihre Frage (bisher), IMO.
Drew
4
@Drew Ich denke, sobald Sie den irreführend allgemeinen Titel überwunden haben, gibt es dort ein spezifisches Designproblem: mehrere Befehle, die genau dasselbe tun, außer dass sie eine andere Variable verwenden, um den Status von einem Aufruf zum nächsten zu speichern.
Gilles 'SO - hör auf böse zu sein'
@ Gilles: Deshalb habe ich nicht dafür gestimmt, es als unklar zu schließen. Es könnte noch klarer sein. Wenn beispielsweise Variablen auf diese Weise verwendet werden sollen, worum geht es dann wirklich?
Drew
Q : „ ? Wie kann man zusätzliche Informationen in eine interaktive Funktion übergibt “ A : Dies wird normalerweise mit einem Kommando Präfix wie C-u; oder indem Sie den Benutzer auffordern, eine Auswahl aus dem Minibuffer einzugeben, oder den Benutzer auffordern, einen bestimmten Schlüssel auszuwählen; zB drücken Sie 1 für foo, drücken Sie 2 für bar, drücken Sie 3 für baz. [Ungefähr 90% der gesamten Frage sind interessant , aber für die allerletzte Frage / Antwort nicht relevant (meiner Meinung nach). Die vorletzte Frage sucht eine Meinung .]
Lawlist

Antworten:

4

Sie können einen Befehl an mehrere Tastenfolgen binden und mit auf die aufrufende Tastenfolge innerhalb des Befehls zugreifen this-command-keys. Dies wird mit dem folgenden Code demonstriert. Dort wird der gleiche Befehl my-commandwird indirekt auf die Schlüsselsequenzen gebunden F9, C-c aund C-c b. "Indirekt" bedeutet dabei, dass es eine dünne Hülle gibt my-command-wrapper. Dieser Wrapper wird als interaktiver Befehl verwendet und ist sehr einfach gehalten. Es sollte nicht instrumentiert werden, edebug-defunda der Rückgabewert von this-command-keysin der Debugging-Sitzung keinen Sinn ergibt.

Der Beispielcode zeigt, wie Sie separate Historien für die separaten aufrufenden Tastenfolgen aufbewahren können.

Wenn Sie die Tastenfolgen beim ersten Mal oder mit dem Präfix arg oder mit dem Shift-Modifikator aufrufen, können Sie den Argumentwert ändern, da my-commandandernfalls der letzte Argumentwert aus dem Verlauf verwendet wird.

(defvar my-command-history-alist nil
  "Association of key sequence")
(defvar my-command--history nil)

(defun my-command-wrapper (&rest args)
  "Thin wrapper for `my-command'.
Adds stringified `this-command-keys' as the first argument to ARGS.
Don't instrument this function with `edebug-defun' otherwise
`this-command-keys' gives the wrong answer!"
  (interactive)
  (apply #'my-command (format "%s" (this-command-keys))
     args))

(defun my-command (keys &optional what)
  "Depending on the call sequence KEYS and PREFIX for this command do something with argument WHAT."
  (unless what
   (let* ((key-history (assoc-string keys my-command-history-alist))
      (my-command--history (cdr key-history)))
     (if (or (null my-command--history)
         current-prefix-arg
         this-command-keys-shift-translated)
     (progn
       (setq what (read-string (format "Input string for key sequence \"%s\":" keys) (car my-command--history) 'my-command--history))
       (if key-history
           (setcdr key-history my-command--history)
         (push (cons keys my-command--history) my-command-history-alist)))
       (setq what (car my-command--history)))))
  (message "Doing something with \"%s\"" what))

(global-set-key (kbd "<f9>") #'my-command-wrapper)
(global-set-key (kbd "C-c a") #'my-command-wrapper)
(global-set-key (kbd "C-c b") #'my-command-wrapper)
Tobias
quelle
4

Schauen wir uns das logisch an: Sie möchten nahezu identische Befehle an C-f2und gebunden haben C-f3. Der einzige Unterschied zwischen diesen Befehlen besteht darin, ob sie das Objekt unter Punkt im f2Speicher oder im f3Speicher speichern. Dann müssen Sie entweder verschiedene Befehle erstellen oder einen einzelnen Befehl haben, dessen Verhalten davon abhängt, an welchen Schlüssel er gebunden ist.

Sie können einen Schlüssel an einen Befehl binden, der zum Zeitpunkt der Erstellung der Bindung erstellt wurde. Das Befehlsargument an define-keyund Freunde muss kein Befehlsname in Form eines Symbols sein, sondern kann ein Lambda-Ausdruck sein.

(global-set-key [C-f3] (lambda ()
                         (interactive)
                         …))

Das funktioniert, ist aber nicht sehr schön. Beispielsweise zeigen Hilfebefehle und Befehlsverläufe keinen Befehlsnamen an.

Sie können den Großteil des Codes in eine Funktion einfügen und kleine Wrapper-Funktionen definieren. Lassen Sie die Wrapper-Funktionen von einer Funktion oder einem Makro generieren, um zu vermeiden, dass viel Code wiederholt wird.

(defun repeat-search-thing-at-point-forward (memory)
  (search-forward (symbol-value memory)))
(defun repeat-search-thing-at-point-backward (memory)
  (search-backward (symbol-value memory)))
(defun search-thing-at-point (memory)
  "Search the thing at point.
Store the thing in MEMORY for a future search with
`repeat-search-thing-at-point-forward' and
`repeat-search-thing-at-point-backward'."
  (set memory (thing-at-point 'word))
  (repeat-search-thing-at-point-forward memory))
(defun define-search-thing-at-point (map key)
  "Define commands to search a thing at point "
  (let* ((memory-variable (intern (format "search-memory-%s" key)))
         (set-function (intern (format "search-thing-at-point-%s" key)))
         (forward-function (intern (format "repeat-search-thing-at-point-forward-%s" key)))
         (backward-function (intern (format "repeat-search-thing-at-point-backward-%s" key)))
         (forward-key (vector key))
         (backward-key (vector (list 'shift key)))
         (set-key (vector (list 'control key))))
    (eval `(progn
             (defvar ,memory-variable nil
               ,(format "The last thing searched with \\[%s]." set-function))
             (defun ,set-function ()
               ,(format "Search the thing at point.
Use \\[%s] and \\[%s] to repeat the search forward and backward
respectively." forward-function backward-function)
               (interactive "@")
               (search-thing-at-point ',memory-variable))
             (defun ,forward-function ()
               ,(format "Search forward for the last thing searched with \\[%s]." set-function)
               (interactive "@")
               (repeat-search-thing-at-point-forward ',memory-variable))
             (defun ,backward-function ()
               ,(format "Search backward for the last thing searched with \\[%s]." set-function)
               (interactive "@")
               (repeat-search-thing-at-point-backward ',memory-variable))
             (define-key map ',set-key #',set-function)
             (define-key map ',forward-key #',forward-function)
             (define-key map ',backward-key #',backward-function)
             t))))

(define-search-thing-at-point global-map 'f2)
(define-search-thing-at-point global-map 'f3)
(define-search-thing-at-point global-map 'f4)

Alternativ können Sie für jede Funktionalität einen einzelnen Befehl definieren (erste Suche, Vorwärts wiederholen, Rückwärts wiederholen). Dies ist etwas weniger flexibel (z. B. können Sie `search-thing-at-point-f2 nicht neu binden, H-swenn es Ihnen gefällt), aber viel weniger ausführlich.

Ein Befehl kann herausfinden, welcher Schlüssel ihn aufgerufen hat. Der einfachste Weg für Sie ist die Verwendung der Variablen last-command-event.

(defvar search-thing-memory nil
  "History of things searched with `search-thing-at-point'.")
(defun search-thing-at-point (key)
  "Search the thing at point.
Store the thing in MEMORY for a future search with
`repeat-search-thing-at-point-forward' and
`repeat-search-thing-at-point-backward'."
  (interactive (list (event-basic-type last-command-event)))
  (let ((thing (thing-at-point 'word))
    (memory (assq key search-thing-memory)))
    (if memory
    (setcdr memory thing)
      (setq search-thing-memory (cons (cons key thing)
                      search-thing-memory)))
    (search-forward thing)))
(defun repeat-search-thing-at-point-forward (key)
  "Repeat the last thing searched with `search-thing-at-point'
with a matching key binding."
  (interactive (list (event-basic-type last-command-event)))
  (search-forward (cdr (assq key search-thing-memory))))
(defun repeat-search-thing-at-point-backward (key)
  "Repeat the last thing searched with `search-thing-at-point'
with a matching key binding."
  (interactive (list (event-basic-type last-command-event)))
  (search-backward (cdr (assq key search-thing-memory))))

(global-set-key [C-f2] 'search-thing-at-point)
(global-set-key [C-f3] 'search-thing-at-point)
(global-set-key [C-f4] 'search-thing-at-point)
(global-set-key [f2] 'repeat-search-thing-at-point-forward)
(global-set-key [f3] 'repeat-search-thing-at-point-forward)
(global-set-key [f4] 'repeat-search-thing-at-point-forward)
(global-set-key [S-f2] 'repeat-search-thing-at-point-backward)
(global-set-key [S-f3] 'repeat-search-thing-at-point-backward)
(global-set-key [S-f4] 'repeat-search-thing-at-point-backward)

Ich denke nicht, dass Ihre vorgeschlagene Schnittstelle eine besonders nützliche Ergänzung zu Emacs ist. Die in Emacs integrierte integrierte Suche bietet einfache Möglichkeiten, um das Objekt punktgenau zu durchsuchen und frühere Suchvorgänge zu wiederholen.

Gilles 'SO - hör auf böse zu sein'
quelle
2

Das Paket befasst highlight-symbolsich eher mit Ihrem ursprünglichen Anwendungsfall als mit der Elisp-Programmierfrage und macht das, was Sie wollen. Ich bin seit vielen Jahren ein glücklicher Benutzer.

http://nschum.de/src/emacs/highlight-symbol/

Aus der Paketbeschreibung:

;; Add the following to your .emacs file:
;; (require 'highlight-symbol)
;; (global-set-key [(control f3)] 'highlight-symbol)
;; (global-set-key [f3] 'highlight-symbol-next)
;; (global-set-key [(shift f3)] 'highlight-symbol-prev)
;; (global-set-key [(meta f3)] 'highlight-symbol-query-replace)
;;
;; Use `highlight-symbol' to toggle highlighting of the symbol at
;; point throughout the current buffer.  Use `highlight-symbol-mode' to keep the
;; symbol at point highlighted.
;;
;; The functions `highlight-symbol-next', `highlight-symbol-prev',
;; `highlight-symbol-next-in-defun' and `highlight-symbol-prev-in-defun' allow
;; for cycling through the locations of any symbol at point.  Use
;; `highlight-symbol-nav-mode' to enable key bindings (M-p and M-p) for
;; navigation. When `highlight-symbol-on-navigation-p' is set, highlighting is
;; triggered regardless of `highlight-symbol-idle-delay'.
;;
;; `highlight-symbol-query-replace' can be used to replace the symbol.
Phil Hudson
quelle
Nett! Ich sah, dass es mehrere Optionen für diese Art von Dingen gab und benutzte HiLock tatsächlich, um einige Hervorhebungen vorzunehmen (hauptsächlich, weil es bereits in Emacs eingebaut ist (ich vergesse welche Version).
MikeTheTall
Auch - ich liebe, was Sie haben, aber die Frage wurde speziell gestellt, um das, was ich suche, in mehreren Schlüsseln zu "speichern". Ich würde dies also brauchen, um für F3 zu arbeiten und auch in der Lage zu sein, separat nach etwas anderem bei F4 zu suchen und dann bei F5 usw. nach einer dritten Sache zu suchen
MikeTheTall