Como definir marca no elisp e ter seleção de turno?

9

O comportamento normal do Emacs quando o modo de marca transitória está ativo é que, quando você faz uma seleção de turno, se o próximo comando for um movimento sem turno, a marca será desativada. Por exemplo, após os comandos M-l(para marcar a linha atual com a função abaixo) e C-f, a marca é desativada. Como emular esse comportamento do elisp depois (set-mark-command nil)?

Por exemplo:

(defun my-mark-current-line ()
  (interactive)
  (beginning-of-line)
  (set-mark-command nil)
  (end-of-line)
  (forward-char))

(global-set-key (kbd "M-l") 'my-mark-current-line)

Agora Ml Cf e a região crescerão, mas, em vez disso, quero que o comportamento padrão, ou seja, a região desative quando Cf e cresça com CSf.

EDIT : deve usar uma função diferente para set-mark-command que permite isso? Não consegui encontrar nenhum.

mikl
fonte
Eu acredito que isso não é possível (e posso estar errado). Enquanto uma região estiver ativa, os comandos de navegação mudarão a seleção. O uso da seleção de turnos C-S-fé análogo a C-SPC(ativar uma região) + C-f(navegação). Provavelmente, você pode obter o que deseja vinculando-se C-f a uma função de wrapper que primeiro desativa uma região, se ativa, e depois faz o que C-fdoes ( forward-char); e vincular C-S-fdiretamente a forward-char. Note que se você nunca usar emacs no modo terminal, C-fe C-S-fserão ambos se comportam como C-fcomo o terminal não pode distinguir entre os dois.
Kaushal Modi
Além disso, o wrapper e a ligação para os quais você C-faplicou também se aplicariam a todos os outros comandos de navegação usados.
Kaushal Modi
btw C-fafter M-lnão estende a região porque não existe uma região ativa no final de M-l(que é vinculada downcase-wordpor padrão).
Kaushal Modi
11
@KaushalModi Eu acho que o M-lreferido pelo OP não é a ligação (default downcase-word), mas a ligação do costumemy-mark-current-line
nispio
de fato @nispio.
Mikl # 10/16

Respostas:

8

Como a conversão de turnos e a ativação temporária da marca são manipuladas pelo loop de comando, você precisará chamar as versões interativas das funções de movimento para obter o comportamento apropriado da seleção de turnos:

;; (source: http://emacs.stackexchange.com/a/22166/93)
(defun my-mark-current-line ()
  (interactive)
  (beginning-of-line)
  (setq this-command-keys-shift-translated t)
  (call-interactively 'end-of-line)
  (call-interactively 'forward-char))

(global-set-key (kbd "M-l") 'my-mark-current-line)

Atualizar:

Desde que escrevi a resposta acima, dediquei um tempo para aprender um pouco mais sobre como a seleção de turnos realmente funciona sob o capô. Ele define o valor do símbolo transient-mark-modecomo uma célula de contras do formulário (only . OLDVAL), onde OLDVALé o valor anterior à seleção de turno.

A solução abaixo evita o uso de call-interactively, ativando a marca conforme necessário e definindo o valor apropriado de transient-mark-mode. Basicamente, considero esta solução menos invasiva que a primeira.

Como bônus, agora ele possui uma contagem de repetição opcional e estenderá a seleção atual em qualquer direção, se a marca já estiver ativa.

;; (source: http://emacs.stackexchange.com/a/22166/93)
(defun my-mark-current-line (&optional arg)
  "Uses shift selection to select the current line.
When there is an existing shift selection, extends the selection
in the appropriate direction to include current line."
  (interactive "p")
  (let ((oldval (or (cdr-safe transient-mark-mode) transient-mark-mode))
        (backwards (and mark-active (> (mark) (point))))
        (beg (and mark-active (mark-marker))))
    (unless beg
      (if backwards (end-of-line) (beginning-of-line))
      (setq beg (point-marker)))
    (if backwards (end-of-line (- 1 arg)) (beginning-of-line (+ 1 arg)))
    (unless mark-active
      (push-mark beg nil t))
    (setq transient-mark-mode (cons 'only oldval))))
nispio
fonte
11
Ambos funcionam perfeitamente e a coisa oldval é muito útil! Muito obrigado!
Mikl