Comandos quase idênticos em várias chaves

7

O Visual Studio tem esse recurso interessante, onde você pode colocar um ponto em algo, pressionar C-f3e ele encontrará a próxima instância da coisa sob o ponto e ela se lembrará. Você pode pressionar f3para encontrar a próxima instância e S-f3procurar a mesma coisa, mas para trás.

Eu posso fazer a mesma coisa no Emacs usando algum elisp que escrevi. (É volumoso e tenho certeza ruim, então vou omitir).

O que eu gostaria de fazer é ativar a funcionalidade semelhante para, digamos, f2, f3, f4, e f5. Assim, pressionar pressionando C-f2procura a coisa sob o ponto, salvando-a em uma variável / matriz / símbolo de associação em algum lugar; pressionar C-f3uma coisa diferente faz com que o emacs salve a segunda coisa a ser salva em uma variável distinta, para que eu possa pesquisar pela primeira coisa pressionando f2no futuro, e eu posso procurar pela segunda coisa pressionando f3no futuro.

Eu adoraria criar uma única função para o Control-X, outra apenas para o X simples e uma terceira para o Shift-X, mas não tenho certeza de como abordar isso.

É melhor que a função solicite o pressionamento de tecla atual (a la this-single-command-keys) ou encontre uma maneira de passar um parâmetro para a função interativa ( sem avisar o usuário, pois ele já pressionou uma tecla)?

Como se passa informações adicionais para uma função interativa?

MikeTheTall
fonte
A questão não é muito clara, IMO. Quais informações e como você deseja transmiti-las? Você pode usar um argumento de prefixo para transmitir informações interativamente. Um comando pode solicitar informações (você não quer isso, você disse). Uma função pode obter informações de variáveis ​​globais ou de um arquivo ou ... O que você deseja passar, quando e como? Se você tiver algum código, talvez o mostre - isso provavelmente será mais claro do que sua pergunta (até agora), IMO.
Tirou
4
@ Drew, acho que depois que você passar pelo título geral enganador, há um problema de design específico: vários comandos que fazem exatamente a mesma coisa, exceto que eles usam uma variável diferente para armazenar o estado de uma chamada para a próxima.
Gilles 'SO- stop be evil'
@Gilles: É por isso que não votei para fechá-lo como incerto. Ainda poderia ser mais claro. Por exemplo, se as variáveis ​​devem ser usadas dessa maneira, o que realmente está em questão?
Tirou
P : " Como se passa informações adicionais para uma função interativa? " R : Normalmente, isso é feito com um prefixo de comando como C-u; ou, solicitando que o usuário insira uma seleção no minibuffer ou peça ao usuário para escolher uma chave específica; por exemplo, pressione 1 para foo, pressione 2 para bar, pressione 3 para baz. [Cerca de 90% de toda a pergunta é interessante , mas não relevante para a última pergunta / resposta (na minha opinião). A segunda à última pergunta busca uma opinião .]
lawlist 30/06

Respostas:

4

Você pode vincular um comando a várias seqüências de teclas e acessar a sequência de teclas de chamada no comando com this-command-keys. Isso é demonstrado com o seguinte código. Há o mesmo comando my-commandé indiretamente ligado às sequências de teclas F9, C-c a, e C-c b. Desse modo, "indiretamente" significa que existe um invólucro fino my-command-wrapper. Esse wrapper é usado como comando interativo e é mantido muito simples. Ele não deve ser instrumentado edebug-defunporque o valor de retorno da this-command-keyssessão de depuração não faz sentido.

O código de exemplo demonstra como você pode manter históricos separados para as sequências de teclas de chamada separadas.

Se você chamar as seqüências de teclas na primeira vez, ou com o prefixo arg ou com o modificador shift, poderá alterar o valor do argumento, my-commandcaso contrário, o último valor do argumento do histórico será usado.

(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
fonte
4

Vejamos isso logicamente: você deseja ter comandos quase idênticos vinculados a C-f2e C-f3. A única diferença entre esses comandos é se eles armazenam a coisa em questão na f2memória ou na f3memória. Então você precisa construir comandos diferentes ou precisa de um único comando cujo comportamento depende de qual chave está vinculada.

Você pode vincular uma chave a um comando que é construído no momento em que você cria a ligação. O argumento de comando para define-keye amigos não precisa ser um nome de comando na forma de um símbolo, pode ser uma expressão lambda.

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

Isso funciona, mas não é muito bom. Por exemplo, comandos de ajuda e históricos de comandos não mostram um nome de comando.

Você pode colocar a maior parte do código em uma função e definir funções pequenas do invólucro. Para evitar repetir muito código, faça com que uma função ou macro gere as funções do wrapper.

(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)

Como alternativa, você pode definir um único comando para cada funcionalidade (primeira pesquisa, repetir para frente, repetir para trás). Isso é um pouco menos flexível (por exemplo, você não pode refazer o comando `pesquisar-coisa-no-ponto-f2 para H-sse for necessário), mas muito menos detalhado.

Um comando pode encontrar qual chave o chamou. A maneira mais fácil para você é usar a variável 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)

Não acho que sua interface proposta seja uma adição particularmente útil ao Emacs. A pesquisa básica básica do Emacs tem maneiras fáceis de pesquisar a coisa no momento e repetir pesquisas anteriores.

Gilles 'SO- parar de ser mau'
fonte
2

Dirigindo seu caso de uso original em vez da pergunta de programação Elisp, o pacote highlight-symbolfaz o que você deseja. Sou um usuário feliz há muitos anos.

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

Na descrição do pacote:

;; 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
fonte
Agradável! Vi que havia várias opções para este tipo de coisa e realmente usado HiLock para fazer algum destaque (principalmente porque ele já está embutido no Emacs (esqueci qual versão).
MikeTheTall
Além disso - eu amo o que você tem, mas a pergunta foi feita especificamente para 'salvar' o que estou procurando em várias chaves. Então eu preciso disso para trabalhar para F3, e também para ser capaz de pesquisar separadamente para algo mais em F4 e, em seguida, procurar uma terceira coisa a F5, etc.
MikeTheTall