如何跳转到最长行?

就是想查找到当前文件的最长行,并定位到那一行。不知道有没有内置的功能。

https://www.emacswiki.org/emacs/FindLongLines 这里提到了一个库,但不想为这个功能添加这么多代码。

(defun goto-longest-line ()
  (interactive)
  (let ((current-line 0)
        (current-column 0))
    (save-excursion
      (goto-char (point-min))
      (catch 'end-of-file
        (while t
          (end-of-line)
          (when (< current-column (current-column))
            (setq current-line (line-number-at-pos)
                  current-column (current-column)))
          (unless (zerop (forward-line 1))
            (throw 'end-of-file nil)))))
    (goto-line current-line)))
3 个赞

用awk,一般都是一行代码

这个 bash 脚本应该比 emacs 快不少。


  fast-jump-to-longest-line.sh:

  #!/bin/bash

  ## 使用方法  sh fast-jump-to-longest-line.sh your.file.name

  arr2=($(awk '{print length}' $1))

  for i in "${!arr2[@]}"; do
      if ((arr2[i]>arr2_max)); then
          arr2_max=${arr2[i]}
          max_indexes=($i)
      elif [[ "${arr2[i]}" == "$arr2_max" ]]; then
          max_indexes+=($i)
      fi
  done

  whichline=$(expr $max_indexes + 1)

  vi +"$whichline" $1

(对于一些临时性需求,最好能一行搞定的 )

Vim 版:

:let list = map(getline(1,"$"),{_,v -> strlen(v)}) | exe index(list,max(list))+1

这样的一行无论是对记忆还是输入仍然是不小的考验

而且,这里还有个存在分歧的地方,最长是指字符个数还是显示宽度?

如果是显示宽度,就不能仅凭 strlen 判断了。

还行吧,没那么麻烦。Vimscript 内置的一些函数如 map、filter 还有 lambda 都是比较好用的,比如上面的除了 $ 宏和 :exe 这俩 Vim 特有的,其它与 JavaScript、python 等现代语言是高度一致的,没什么记忆负担。

从我的经验来看,一些小需求难得用上一回。存储、维护成本其实很高,实属那类食之无味弃之可惜的“鸡肋”。忙着写别的东西呢,不想分心特意去取名存储。

至于显示宽度,有个内置函数 strdisplaywidth 可以试一试。

我自己也用elisp写了一个,用到了 dash.el,没有加跳转功能,只是打印出 indeces。

(defun qi/longest-line ()
  (interactive)
  (let* ((content    (buffer-string))
         (lines      (split-string content "\n"))
         (lengths    (mapcar #'length lines))
         (max-length (-max lengths))
         (indices    (mapcar #'1+ (-elem-indices max-length lengths))))
    (message "max-length: %d, positions: %s" max-length indices)))
1 个赞

加了 ivy 的一个版本:

(defun qi/longest-line ()
  (interactive)
  (let* ((content    (buffer-string))
         (lines      (split-string content "\n"))
         (lengths    (mapcar #'length lines))
         (max-length (-max lengths))
         (indices    (-elem-indices max-length lengths))
         (lines      (-select-by-indices indices lines))
         (indices+1  (mapcar #'1+ indices)))
    (ivy-read (format "max-length [%d]: " max-length)
              (cl-mapcar #'cons lines indices+1)
              :action (lambda (indice) (goto-line (cdr indice)))
              :caller 'qi/longest-line)))
1 个赞

如我前面说的:

如果最长是指字符个数,8 \s 大于 4 \t。如果是指显示宽度,结果通常反过来。显示宽度更符合直观感受:

				;; 4 tabs
        ;; 8 spaces

找出最长(字符个数最多)的行,无需定义那么多 let 变量:

(let ((index 0))
  (->> (string-lines (buffer-string))
       (--map (cons (length it) (cl-incf index)))
       (--sort (> (car it) (car other)))
       (cdar)))

不用 dash 也没有很繁琐:

(let ((index 0))
  (cdar (sort (mapcar (lambda (it) (cons (length it) (cl-incf index)))
                      (string-lines (buffer-string)))
              (lambda (it other) (> (car it) (car other))))))

Emacs 有个 buffer-line-statistics 函数,它返回:

The data is returned as a list, and the first element is the number of lines in the buffer, the second is the length of the longest line, and the third is the mean line length. The lengths returned are in bytes, not characters.

差一点就赢过了所有的方案。

1 个赞

其实需求就是buffer里的内容复制后,粘贴到文本框时横向的滚动条太长,想看看长的行能不能处理一下。

那为啥不用 fill-regionauto-fill-mode

因为那一行不是单独成段落,会把整个文件搞乱。是日志文件,想尽量保持原样。

那可以用 auto-fill-mode,只会改变显示方式不会实际修改內容

是从 Emacs 里向 Jira 复制,而且不是要修改日志文件,只是在展示时更合理。自动修改的结果很可能不是我想要的,如果其中有更有逻辑的部分,想把它单独提取成行,不一定是在任意空白符处换行。