Subject: RE: bug#2887: Suggestions for simple.el

From: "Drew Adams" <[email-masked]>
Date: Tue, 7 Apr 2009 09:09:32 -0700

> >   C-x j    backward-delete-word
> >   C-x C-j  delete-word
> >   C-x x    kill-line-or-region
> >   M-n      pull-line-down
> >   M-p      pull-line-up
> >   C-M-z    zap-back-to-char
> >   C-M-m    zap-up-to-char
> >   C-x C-a  delete-all-blank-lines
> >   M-&      delete-indentation-nospace
> >   C-x w    goto-longest-line
> >   C-x y    downcase-word-or-region
> >   C-x C-y  upcase-word-or-region
> 
> I think I will prefer to leave those unbound for now, waiting for more
> generally useful commands, or more general agreement that they are
> generally useful.

So it sounds as if you're actually going to add these things to Emacs (even if
you don't bind them by default)?

If so, that's quite surprising, especially since the policy has always been
_not_ to add functions simply because they _might_ be generally useful. Not to
mention the tradition of case-by-case discussion in emacs-devel first, including
discussion of rationale (e.g. use cases).

Whatever. In that case, below is my version of `goto-longest-line', published
under that name over two years ago.

I also sent it to emacs-devel on two occasions: 2008-1-23 (thread "find longest
lines during isearch") and 2008-06-21 (thread "Another neat Eclispe'ism").
http://lists.gnu.org/archive/html/emacs-devel/2008-01/msg01611.html
http://lists.gnu.org/archive/html/emacs-devel/2008-06/msg01442.html

My version has these advantages:

* If the region is active, it is restricted to the region.

* If repeated, it goes to the longest line after the cursor.

* You can use it to search from point forward only, by using `C-SPC' first.

* It highlights the line (using `hl-line-highlight').

* It returns, as a list: the line #, its length, a list of other lines just as
long (there can be more than one "longest line"), and the number of lines
checked.

* Interactively, it echoes all of that info. Example messages:
  Line 234: 76 chars, (459 lines measured)
  Line 234: 76 chars, Others: {239, 313} (459 lines measured)

I bind [(control end)] to this command in `isearch-mode-map' (when
`window-system'). This is the way I typically call it: `C-s C-end'. Repeatedly
move to longest line after point: `C-x C-end C-end...'. And `C-g' returns you to
the point where searching starts, so you can use it to see where long lines are
without necessarily moving there and staying. I also bind it to `C-x L', but I
typically use it from Isearch.

A related command is `goto-long-line', which goes to the first line that is at
least N chars long. N is the `fill-column' by default, or provided explicitly by
`C-u'.

If you are really interested in adding general, miscellaneous commands and
functions that might be useful ("waiting for more generally useful commands"), I
have plenty more, and there are hundreds more by others, on Emacs Wiki and
elsewhere. 

The `goto-long*-line' commands and others are here:
http://www.emacswiki.org/emacs/misc-cmds.el. And there is a wiki page loaded
with commands for finding, visualizing, and moving to long lines:
http://www.emacswiki.org/emacs/FindLongLines.

I really didn't think this was how Emacs development proceeded, but apparently
there has been a change (?).

FWIW, I think emacs-devel is the proper place to discuss additions and other
changes to Emacs. It's OK for enhancement suggestions to be submitted to the
bugs list, but concrete discussion about adding or changing Emacs code should be
done in emacs-devel, IMO. And such discussion should precede actually making
changes.

;---------------8<-----------------------
(defun goto-longest-line (beg end)
  "Go to the first of the longest lines in the region or buffer.
If the region is active, it is checked.
If not, the buffer (or its restriction) is checked.

Returns a list of three elements:

 (LINE LINE-LENGTH OTHER-LINES LINES-CHECKED)

LINE is the first of the longest lines measured.
LINE-LENGTH is the length of LINE.
OTHER-LINES is a list of other lines checked that are as long as LINE.
LINES-CHECKED is the number of lines measured.

Interactively, a message displays this information.

If there is only one line in the active region, then the region is
deactivated after this command, and the message mentions only LINE and
LINE-LENGTH.  

If this command is repeated, it checks for the longest line after the
cursor.  That is *not* necessarily the longest line other than the
current line.  That longest line could be before or after the current
line.

To search only from the current line forward, not throughout the
buffer, you can use `C-SPC' to set the mark, then use this
\(repeatedly)."
  (interactive
   (if (or (not mark-active) (null (mark)))
       (list (point-min) (point-max))
     (if (< (point) (mark))
         (list (point) (mark))
       (list (mark) (point)))))
  (when (and (not mark-active) (= beg end))
    (error "The buffer is empty"))
  (when (and mark-active (> (point) (mark))) (exchange-point-and-mark))
  (when (< end beg) (setq end (prog1 beg (setq beg end))))
  (when (eq this-command last-command)
    (forward-line 1) (setq beg (point)))
  (goto-char beg)
  (when (eobp) (error "End of buffer"))
  (cond ((<= end (save-excursion
                   (goto-char beg) (forward-line 1) (point)))
         (beginning-of-line)
         (when (require 'hl-line nil t)
           (let ((hl-line-mode t)) (hl-line-highlight))
           (add-hook 'pre-command-hook #'hl-line-unhighlight nil t))
         (let ((lineno (line-number-at-pos))
               (chars (save-excursion (end-of-line) (current-column))))
           (message "Only line %d: %d chars" lineno chars)
           (let ((visible-bell t)) (ding))
           (setq mark-active nil)
           (list lineno chars nil 1)))
        (t
         (let* ((start-line (line-number-at-pos))
                (max-width 0)
                (line start-line)
                long-lines col)
           (when (eobp) (error "End of buffer"))
           (while (and (not (eobp)) (< (point) end))
             (end-of-line)
             (setq col (current-column))
             (when (>= col max-width)
               (if (= col max-width)
                   (setq long-lines (cons line long-lines))
                 (setq long-lines (list line)))
               (setq max-width col))
             (forward-line 1)
             (setq line (1+ line)))
           (setq long-lines (nreverse long-lines))
           (let ((lines long-lines))
             (while (and lines (> start-line (car lines))) (pop lines))
             (goto-char (point-min))
             (when (car lines) (forward-line (1- (car lines)))))
           (when (require 'hl-line nil t)
             (let ((hl-line-mode t)) (hl-line-highlight))
             (add-hook 'pre-command-hook #'hl-line-unhighlight nil t))
           (when (interactive-p)
             (let ((others (cdr long-lines)))
               (message
                "Line %d: %d chars%s (%d lines measured)"
                (car long-lines) max-width
                (concat
                 (and others
                      (format ", Others: {%s}"
                              (mapconcat
                               (lambda (line) (format "%d" line))
                               (cdr long-lines) ", "))))
                (- line start-line))))
           (list (car long-lines) max-width (cdr long-lines)
                 (- line start-line))))))