跨越一条线的“语义”运动

时间:2013-01-09 18:02:28

标签: emacs elisp

考虑以下Lisp代码行:

        (some-function 7 8 | 9) ;; some comment. note the extra indentation

该点位于'8'和'9'之间。如果我执行(move-beginning-of-line),则该点将位于该行的绝对开头,而不是'('。

同样适用于move-end-of-line:如果我执行一次,我会发现将点放在')',如果我第二次执行它,则更好地放在行的绝对末尾。有些IDE表现得那样。

我试图实现这个但是卡住了,我的解决方案在缓冲区结束时表现得特别糟糕,并且在迷你缓冲区也是如此。 是否有提供此功能的库?

4 个答案:

答案 0 :(得分:5)

我不知道任何库,但可以在几行Elisp中完成。

对于行部分的开头,捆绑的函数beginning-of-line-textback-to-indentationM-m)移动到行的“有趣”部分的开头。 back-to-indentation仅忽略空格,而beginning-of-line-text忽略fill prefix(在编程语言中,这通常是注释标记,如果在注释中)。有关如何在实际行和逻辑行的开头之间进行切换,请参阅Smart home in Emacs

对于行结尾部分,以下函数实现了您所描述的内容。函数end-of-line-code移动到行的末尾,除了尾随空格和可选的尾随注释。函数end-of-line-or-code执行此操作,除非该点已经在目标位置,或者该行仅包含空格和注释,该点将移动到实际行的末尾。

(defun end-of-line-code ()
  (interactive "^")
  (save-match-data
    (let* ((bolpos (progn (beginning-of-line) (point)))
           (eolpos (progn (end-of-line) (point))))
      (if (comment-search-backward bolpos t)
          (search-backward-regexp comment-start-skip bolpos 'noerror))
      (skip-syntax-backward " " bolpos))))

(defun end-of-line-or-code ()
  (interactive "^")
  (let ((here (point)))
    (end-of-line-code)
    (if (or (= here (point))
        (bolp))
        (end-of-line))))

答案 1 :(得分:2)

一些建议几乎可以满足您的要求:

在lisp代码中,您可以使用sexp移动命令对所需内容进行排序。要从中间的某个位置到达表达式的开头,请使用绑定到backward-up-list的{​​{1}}。在您的示例中,这将带您进入左括号。要向后移动列表中的各个元素,请使用绑定到M-C-u的{​​{1}}; backward-sexp以相反的方式移动,并且绑定到M-C-b。从性别开始,您可以使用forward-sexp跳到下一个;与M-C-f相反。

这些命令中没有一个实际上是在查看您所在的物理线路,因此它们将在多条线路上返回或前进。

其他选项包括Ace Jump mode,这是一种非常灵活的方式,可以快速导航到屏幕上可见的任何单词的开头。这可能会消除您使用特定于行的命令的需要。为了在一行内快速移动,我通常使用M-C-nM-C-p来跳过单词。点击M-fM-b时按住M键的速度非常快,以至于我大部分时间默认使用该键。

编辑:

忘了另一个很好的命令 - b,绑定到f。这将使您返回到行中的第一个非空白字符。您可以建议这在第一次通话时表现正常,然后在第二次通话时备份到该行的开头:

back-to-indentation

答案 2 :(得分:1)

我刚刚编写了这两个具有您正在寻找的行为的函数。

(defun move-beginning-indent ()
  (interactive)
  (if (eq last-command this-command)
      (beginning-of-line)
    (back-to-indentation))
)


(defun move-end-indent ()
  (interactive)
  (if (eq last-command this-command)
      (end-of-line)
    (end-of-line)
    (search-backward-regexp "\\s)" nil t)   ; searches backwards for a 
    (forward-char 1))                       ; closed delimiter such as ) or ]
)

(global-set-key [f7] 'move-beginning-indent)          
(global-set-key [f8] 'move-end-indent)  

试试看,他们应该按照你想要的方式行事。

答案 3 :(得分:1)

我用这个:

(defun beginning-of-line-or-text (arg)
  "Move to BOL, or if already there, to the first non-whitespace character."
  (interactive "p")
  (if (bolp)
      (beginning-of-line-text arg)
    (move-beginning-of-line arg)))
(put 'beginning-of-line-or-text 'CUA 'move)
;; <home> is still bound to move-beginning-of-line
(global-set-key (kbd "C-a") 'beginning-of-line-or-text)

(defun end-of-code-or-line ()
  "Move to EOL. If already there, to EOL sans comments.
    That is, the end of the code, ignoring any trailing comment
    or whitespace.  Note this does not handle 2 character
    comment starters like // or /*.  Such will not be skipped."
  (interactive)
  (if (not (eolp))
      (end-of-line)
    (skip-chars-backward " \t")
    (let ((pt (point))
          (lbp (line-beginning-position))
          (comment-start-re (concat (if comment-start
                                        (regexp-quote
                                         (replace-regexp-in-string
                                          "[[:space:]]*" "" comment-start))
                                      "[^[:space:]][[:space:]]*$")
                                    "\\|\\s<"))
          (comment-stop-re "\\s>")
          (lim))
      (when (re-search-backward comment-start-re lbp t)
        (setq lim (point))
        (if (re-search-forward comment-stop-re (1- pt) t)
            (goto-char pt)
          (goto-char lim)               ; test here ->
          (while (looking-back comment-start-re (1- (point)))
            (backward-char))
          (skip-chars-backward " \t"))))))
(put 'end-of-code-or-line 'CUA 'move)
;; <end> is still bound to end-of-visual-line
(global-set-key (kbd "C-e") 'end-of-code-or-line)