* RE: bug#2887: Suggestions for simple.el
2009-04-07 14:02 ` Stefan Monnier
@ 2009-04-07 16:09 ` Drew Adams
2009-04-07 17:18 ` Stefan Monnier
` (3 more replies)
2009-04-07 16:09 ` Drew Adams
2009-04-18 0:08 ` Arni Magnusson
2 siblings, 4 replies; 27+ messages in thread
From: Drew Adams @ 2009-04-07 16:09 UTC (permalink / raw)
To: 'Stefan Monnier', 2887, 'Arni Magnusson'; +Cc: emacs-devel
> > 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))))))
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-07 16:09 ` Drew Adams
@ 2009-04-07 17:18 ` Stefan Monnier
2009-04-07 17:18 ` Stefan Monnier
` (2 subsequent siblings)
3 siblings, 0 replies; 27+ messages in thread
From: Stefan Monnier @ 2009-04-07 17:18 UTC (permalink / raw)
To: Drew Adams; +Cc: 2887, emacs-devel, 'Arni Magnusson'
> So it sounds as if you're actually going to add these things to Emacs
> (even if you don't bind them by default)?
No.
Stefan
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: bug#2887: Suggestions for simple.el
2009-04-07 16:09 ` Drew Adams
2009-04-07 17:18 ` Stefan Monnier
@ 2009-04-07 17:18 ` Stefan Monnier
2009-04-07 17:22 ` Chong Yidong
2009-04-07 17:22 ` Chong Yidong
3 siblings, 0 replies; 27+ messages in thread
From: Stefan Monnier @ 2009-04-07 17:18 UTC (permalink / raw)
To: Drew Adams; +Cc: 2887, emacs-devel, 'Arni Magnusson'
> So it sounds as if you're actually going to add these things to Emacs
> (even if you don't bind them by default)?
No.
Stefan
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-07 16:09 ` Drew Adams
2009-04-07 17:18 ` Stefan Monnier
2009-04-07 17:18 ` Stefan Monnier
@ 2009-04-07 17:22 ` Chong Yidong
2009-04-07 17:22 ` Chong Yidong
3 siblings, 0 replies; 27+ messages in thread
From: Chong Yidong @ 2009-04-07 17:22 UTC (permalink / raw)
To: Drew Adams; +Cc: 2887, emacs-devel, 'Arni Magnusson'
"Drew Adams" <drew.adams@oracle.com> writes:
>> 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.
I don't think that's what Stefan was saying (but he can speak for
himself). You're right, of course, that it's not a good idea to add
functions simply because they *might* be useful, especially since
simple.el is loaded by default.
I have not looked through bug#2887 in detail, but my impression is that
most of the suggested functions aren't quite suitable for default
inclusion in Emacs. They are, however, Valuable Excercises For the
Novice Emacs Lisp coder. Maybe someone should set up a page on
Emacswiki for this sort of thing.
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: bug#2887: Suggestions for simple.el
2009-04-07 16:09 ` Drew Adams
` (2 preceding siblings ...)
2009-04-07 17:22 ` Chong Yidong
@ 2009-04-07 17:22 ` Chong Yidong
2009-04-07 17:26 ` Drew Adams
` (2 more replies)
3 siblings, 3 replies; 27+ messages in thread
From: Chong Yidong @ 2009-04-07 17:22 UTC (permalink / raw)
To: Drew Adams
Cc: 2887, emacs-devel, 'Stefan Monnier',
'Arni Magnusson'
"Drew Adams" <drew.adams@oracle.com> writes:
>> 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.
I don't think that's what Stefan was saying (but he can speak for
himself). You're right, of course, that it's not a good idea to add
functions simply because they *might* be useful, especially since
simple.el is loaded by default.
I have not looked through bug#2887 in detail, but my impression is that
most of the suggested functions aren't quite suitable for default
inclusion in Emacs. They are, however, Valuable Excercises For the
Novice Emacs Lisp coder. Maybe someone should set up a page on
Emacswiki for this sort of thing.
^ permalink raw reply [flat|nested] 27+ messages in thread
* RE: bug#2887: Suggestions for simple.el
2009-04-07 17:22 ` Chong Yidong
@ 2009-04-07 17:26 ` Drew Adams
2009-04-07 17:26 ` Drew Adams
2009-04-07 21:43 ` Stefan Monnier
2 siblings, 0 replies; 27+ messages in thread
From: Drew Adams @ 2009-04-07 17:26 UTC (permalink / raw)
To: 'Chong Yidong'
Cc: 2887, emacs-devel, 'Stefan Monnier',
'Arni Magnusson'
> most of the suggested functions aren't quite suitable for default
> inclusion in Emacs. They are, however, Valuable Excercises For the
> Novice Emacs Lisp coder. Maybe someone should set up a page on
> Emacswiki for this sort of thing.
Feel free to act on your own suggestion - anyone can edit EmacsWiki.
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-07 17:22 ` Chong Yidong
2009-04-07 17:26 ` Drew Adams
@ 2009-04-07 17:26 ` Drew Adams
2009-04-07 21:43 ` Stefan Monnier
2 siblings, 0 replies; 27+ messages in thread
From: Drew Adams @ 2009-04-07 17:26 UTC (permalink / raw)
To: 'Chong Yidong'; +Cc: 2887, emacs-devel, 'Arni Magnusson'
> most of the suggested functions aren't quite suitable for default
> inclusion in Emacs. They are, however, Valuable Excercises For the
> Novice Emacs Lisp coder. Maybe someone should set up a page on
> Emacswiki for this sort of thing.
Feel free to act on your own suggestion - anyone can edit EmacsWiki.
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-07 17:22 ` Chong Yidong
2009-04-07 17:26 ` Drew Adams
2009-04-07 17:26 ` Drew Adams
@ 2009-04-07 21:43 ` Stefan Monnier
2 siblings, 0 replies; 27+ messages in thread
From: Stefan Monnier @ 2009-04-07 21:43 UTC (permalink / raw)
To: Chong Yidong; +Cc: 2887
Actually, I do think that a "find longest line" command would be a good
addition to misc.el: it's not terribly hard to write, but it's not
trivial either.
Stefan
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-07 14:02 ` Stefan Monnier
2009-04-07 16:09 ` Drew Adams
@ 2009-04-07 16:09 ` Drew Adams
2009-04-18 0:08 ` Arni Magnusson
2 siblings, 0 replies; 27+ messages in thread
From: Drew Adams @ 2009-04-07 16:09 UTC (permalink / raw)
To: 'Stefan Monnier', 2887, 'Arni Magnusson'; +Cc: emacs-devel
> > 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))))))
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-07 14:02 ` Stefan Monnier
2009-04-07 16:09 ` Drew Adams
2009-04-07 16:09 ` Drew Adams
@ 2009-04-18 0:08 ` Arni Magnusson
2009-04-18 19:32 ` Stefan Monnier
2 siblings, 1 reply; 27+ messages in thread
From: Arni Magnusson @ 2009-04-18 0:08 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 2887
[-- Attachment #1: Type: TEXT/PLAIN, Size: 5540 bytes --]
Thanks Stefan, for forwarding my suggestions to emacs-devel. The
discussion raised many good points. After considering the viewpoints we
have heard so far, here are my (revised) opinions:
`backward-delete-word'
`delete-word'
I think Emacs should provide a simple way for beginning users to have
C-backspace and C-delete behave like they would expect, i.e. leaving the
clipboard intact. There are different ways to provide this, using a
variable and/or functions. Users should not need to write their own
functions for something this fundamental.
`kill-line-or-region'
C-k should probably be bound to this function. This would be appreciated
by many `transient-mark-mode' users. I haven't used Emacs without
`transient-mark-mode', but don't those people still want C-w bound to
`kill-region'?
`pull-line-down'
`pull-line-up'
These are admittedly simple tricks of lesser importance, but anyone trying
out the existing `transpose-lines' will read its documentation twice and
try to master pulling lines up or down, before giving up. I find myself
using these almost every day, with code, data, and config files.
`pos-at-beginning-of-line'
`pos-at-end-of-line'
You're right, it's best to avoid `goto-line'. I have reimplemented these
functions (see attachment) to improve the speed. I think they bridge an
obvious gap in Emacs Lisp, making it considerably easier to read and write
functions that operate on buffer text.
`zap-back-to-char'
`zap-up-to-char'
I believe these more useful than the existing `zap-to-char', which often
deletes the beginning of that important location, an opening brace or the
like.
`delete-trailing-white'
> I think [ \t\r] is a good default, and if we introduce a config var
> (which I'm not sure is worth the trouble), there's no reason to keep the
> special treatment of formfeed.
I agree that hardwiring [ \t\r] works fastest and is easy to use and
maintain. Attached is my proposed upgrade of this function, where cleaned
lines are counted.
`delete-all-blank-lines'
Vertical analog to `delete-trailing-white', which I use about as often.
Anyone trying out the existing `delete-blank-lines' will wonder whether
there is a keybinding to delete all blank lines, instead of just around
the point.
`delete-indentation-nospace'
Similar to `delete-indentation' but leaves no space between the joined
lines. I find myself using these almost every day, with prose, code, data,
and config files. I have bound the two functions to neighboring
keybindings.
`goto-longest-line'
I should probably withdraw this suggestion. After seeing Drew Adam's
version, I have concluded that my version is too clunky to be in
simple.el, and Drew's version is too large to be in simple.el. Despite
being a vertical analog to `end-of-buffer', simple.el should probably not
provide this functionality.
`downcase-word-or-region'
`upcase-word-or-region'
M-l and M-u could be bound to this function. This would be appreciated by
many `transient-mark-mode' users. I haven't used Emacs without
`transient-mark-mode', but don't those people still want C-x C-l and C-x
C-u bound to `downcase-region' and `upcase-region'?
---
By suggesting so many functions at once, I've taken more of your time than
I should. I'm very grateful for your hard work maintaining Emacs, and
discussions like this are worthwhile if they lead to improved editing
efficiency for users.
The functions are all very short, very convenient in my opinion, and
importantly they rhyme with functions that are already in simple.el.
> This said, I think those new commands, unbound to any key, shouldn't be
> placed in simple.el (which is preloaded) but into some other file. I'm
> tempted to say "misc.el", where we could stuff any number of "commands
> that users might like, but for which we couldn't come up with a good
> key-binding".
Ooo, I'm afraid misc.el might be a regrettable move in the long run. I
think descriptive package names are the way to go, as the base Emacs
distribution continues to grow. Perhaps simple.el could be split into
something like base-buffers.el, base-mark.el, etc. As it stands, after
careful examination of the base packages, I believe my suggested functions
belong in simple.el.
There are many commands in simple.el that are not bound to keys,
including:
* Variations of existing bound functions
`next-error-no-select', `previous-error-no-select'
`newline-and-indent', `reindent-then-newline-and-indent'
`undo-only'
* Modify buffer text
`forward-to-indentation', `backward-to-indentation'
`fixup-whitespace'
* Information
`what-line', `what-cursor-position'
`blink-matching-open'
* Others that I have bound
`copy-region-as-kill'
`insert-buffer'
My suggested commands are mainly in one category:
* Variations of existing bound functions
`backward-delete-word', `delete-word'
`kill-line-or-region'
`pull-line-down', `pull-line-up'
`zap-back-to-char', `zap-up-to-char'
`delete-indentation-nospace'
`downcase-word-or-region', `upcase-word-or-region'
* Modify buffer text
`delete-all-blank-lines'
There are mainly two ways users can find out about such unbound commands:
docstring cross-references, and brief entries in the Emacs/Emacs Lisp
manuals. The latter manual mentions practically all commands in simple.el.
Again, my apologies for the length of this message. If any of my
suggestions end up being accepted, I could add some cross-references to
their docstrings.
Best regards,
Arni
[-- Attachment #2: Type: TEXT/PLAIN, Size: 294 bytes --]
(defun pos-at-beginning-of-line (N) "Return the position at beginning of line N."
(save-excursion (goto-char (point-min))(line-beginning-position N)))
(defun pos-at-end-of-line (N) "Return the position at end of line N."
(save-excursion (goto-char (point-min))(line-end-position N)))
[-- Attachment #3: Type: TEXT/PLAIN, Size: 2368 bytes --]
(defun backward-delete-word (N) "Delete previous N words." (interactive "*p")(delete-word (- N)))
(defun delete-word (N) "Delete following N words." (interactive "*p")
(delete-region (point)(save-excursion (forward-word N)(point))))
(defun kill-line-or-region () "Kill region if selected, otherwise kill line." (interactive)
(if (and mark-active transient-mark-mode)(kill-region (point)(mark))(kill-line)))
(defun pull-line-down (N) "Pull line down N times." (interactive "*p")
(let ((col (current-column)))(kill-whole-line 1)(forward-line N)(yank 1)(pop kill-ring)(forward-line -1)
(move-to-column col)))
(defun pull-line-up (N) "Pull line up N times." (interactive "*p")
(let ((col (current-column)))(kill-whole-line 1)(forward-line (- N))(yank 1)(pop kill-ring)(forward-line -1)
(move-to-column col)))
(defun zap-back-to-char (char) "Delete region back to, but not including, CHAR." (interactive "cZap back to char: ")
(let ((case-fold-search nil))
(delete-region (point)(progn (search-backward (char-to-string char))(forward-char)(point)))))
(defun zap-up-to-char (char) "Delete region up to, but not including, CHAR." (interactive "cZap to char: ")
(let ((case-fold-search nil))
(delete-region (point)(progn (search-forward (char-to-string char))(backward-char)(point)))))
(defun delete-all-blank-lines () "Delete all blank lines in buffer." (interactive)
(save-excursion
(goto-char (point-min))
(let ((count 0))(while (search-forward "\n\n" nil t)(goto-char (point-min))
(while (search-forward "\n\n" nil t)(replace-match "\n")(setq count (+ count 1)))
(goto-char (point-min)))
(if (= (following-char) 10)(progn (delete-char 1)(setq count (+ count 1))))
(message "Deleted %d blank lines" count))))
(defun delete-indentation-nospace () "Join this line to previous with no whitespace at join." (interactive)
(delete-indentation)(delete-horizontal-space))
(defun downcase-word-or-region (N) "Downcase N words or region." (interactive "*p")
(if (and mark-active transient-mark-mode)(downcase-region (point)(mark))(downcase-word N)))
(defun upcase-word-or-region (N) "Upcase N words or region." (interactive "*p")
(if (and mark-active transient-mark-mode)(upcase-region (point)(mark))(upcase-word N)))
[-- Attachment #4: Type: TEXT/PLAIN, Size: 535 bytes --]
10,16c10,13
< (while (re-search-forward "\\s-$" nil t)
< (skip-syntax-backward "-" (save-excursion (forward-line 0) (point)))
< ;; Don't delete formfeeds, even if they are considered whitespace.
< (save-match-data
< (if (looking-at ".*\f")
< (goto-char (match-end 0))))
< (delete-region (point) (match-end 0))))))
---
> (let ((count 0))
> (while (re-search-forward "[ \t\r]+$" nil t)
> (replace-match "")(setq count (1+ count)))
> (message "Cleaned %d lines" count)))))
[-- Attachment #5: Type: TEXT/PLAIN, Size: 586 bytes --]
(defun delete-trailing-whitespace ()
"Delete all the trailing whitespace across the current buffer.
All whitespace after the last non-whitespace character in a line is deleted.
This respects narrowing, created by \\[narrow-to-region] and friends.
A formfeed is not considered whitespace by this function."
(interactive "*")
(save-match-data
(save-excursion
(goto-char (point-min))
(let ((count 0))
(while (re-search-forward "[ \t\r]+$" nil t)
(replace-match "")(setq count (1+ count)))
(message "Cleaned %d lines" count)))))
[-- Attachment #6: Type: TEXT/PLAIN, Size: 721 bytes --]
(defun delete-trailing-whitespace ()
"Delete all the trailing whitespace across the current buffer.
All whitespace after the last non-whitespace character in a line is deleted.
This respects narrowing, created by \\[narrow-to-region] and friends.
A formfeed is not considered whitespace by this function."
(interactive "*")
(save-match-data
(save-excursion
(goto-char (point-min))
(while (re-search-forward "\\s-$" nil t)
(skip-syntax-backward "-" (save-excursion (forward-line 0) (point)))
;; Don't delete formfeeds, even if they are considered whitespace.
(save-match-data
(if (looking-at ".*\f")
(goto-char (match-end 0))))
(delete-region (point) (match-end 0))))))
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-18 0:08 ` Arni Magnusson
@ 2009-04-18 19:32 ` Stefan Monnier
2009-04-19 1:13 ` Arni Magnusson
0 siblings, 1 reply; 27+ messages in thread
From: Stefan Monnier @ 2009-04-18 19:32 UTC (permalink / raw)
To: Arni Magnusson; +Cc: 2887
> `backward-delete-word'
> `delete-word'
> I think Emacs should provide a simple way for beginning users to have
> C-backspace and C-delete behave like they would expect, i.e. leaving
> the clipboard intact. There are different ways to provide this, using
> a variable and/or functions. Users should not need to write their own
> functions for something this fundamental.
I think the difference between these and the standard commands is too
small to deserve a separate command. There are plenty of ways to get
around the problem of "kill when I only want delete" (typically, doing
the kill after the C-y, or using M-y, ...).
I would only consider some general "kill-next-kill" feature which would
allow to turn any killing command into a deleting one
(e.g. a kill-next-kill command which would cause the subsequent
commands's effect on the kill-ring to be cancelled).
> `kill-line-or-region'
> C-k should probably be bound to this function. This would be appreciated by
> many `transient-mark-mode' users. I haven't used Emacs without
> transient-mark-mode', but don't those people still want C-w bound to
> kill-region'?
Yes, we couldn't really get rid of C-w, I think, but users of
transient-mark-mode could use that key for other things.
> `pull-line-down'
> `pull-line-up'
> These are admittedly simple tricks of lesser importance, but anyone trying
> out the existing `transpose-lines' will read its documentation twice and try
> to master pulling lines up or down, before giving up. I find myself using
> these almost every day, with code, data, and config files.
I don't find transpose-lines very useful, so I might accept a proposal
that makes it more useful, but not one that adds yet more refinements.
> `pos-at-beginning-of-line'
> `pos-at-end-of-line'
> You're right, it's best to avoid `goto-line'. I have reimplemented these
> functions (see attachment) to improve the speed. I think they bridge an
> obvious gap in Emacs Lisp, making it considerably easier to read and write
> functions that operate on buffer text.
The reimplementation doesn't change anything: its performance will still
suck on large files. It's just fundamentally an operation that is slow
and that we should generally avoid, so I don't want to add yet more ways
(additionally to goto-line) to do that.
The "obvious gap" that you talk about is only "obvious" if you think in
terms of lines, which is almost always the wrong way to think about the
text in Emacs.
> `zap-back-to-char'
> `zap-up-to-char'
> I believe these more useful than the existing `zap-to-char', which often
> deletes the beginning of that important location, an opening brace or
> the like.
Maybe a better approach would be to add config var to zap-to-char
whether it also zaps the char or not. In any case, I'd rather wait for
other people to support this option, since I have no idea how common
it is.
> `delete-trailing-white'
>> I think [ \t\r] is a good default, and if we introduce a config var (which
>> I'm not sure is worth the trouble), there's no reason to keep the special
>> treatment of formfeed.
> I agree that hardwiring [ \t\r] works fastest and is easy to use and
> maintain. Attached is my proposed upgrade of this function, where cleaned
> lines are counted.
Thanks. We'll consider using it for Emacs-23.2.
> `delete-all-blank-lines'
> Vertical analog to `delete-trailing-white', which I use about as
> often. Anyone trying out the existing `delete-blank-lines' will wonder
> whether there is a keybinding to delete all blank lines, instead of just
> around the point.
Can someone figure out a way to tweak flush-lines such that it can be
used for that purpose without having to jump through as many hooks?
Maybe we can just say that if you call flush-lines with an empty
argument (which currently would flush *all* lines) it will flush all
empty lines.
> `delete-indentation-nospace'
> Similar to `delete-indentation' but leaves no space between the joined
> lines. I find myself using these almost every day, with prose, code, data,
> and config files. I have bound the two functions to neighboring keybindings.
Here again, I'm not sure the difference is worth the trouble.
I'd rather make fixup-whitespace customizable so you can specify when to
keep a space and when not to. You can then customize it to never keep
a space, and then use M-^ for delete-indentation-nospace and M-^ SPC
when you do want a space.
> `downcase-word-or-region'
> `upcase-word-or-region'
> M-l and M-u could be bound to this function. This would be appreciated by
> many `transient-mark-mode' users.
Yes, I think it would be a good change.
> I haven't used Emacs without transient-mark-mode', but don't those
> people still want C-x C-l and C-x C-u bound to `downcase-region' and
> `upcase-region'?
It's possible, but it's less sure than with C-w because those commands
are used much less frequently, so it might be OK to just tell people to
use C-u C-x C-x M-u instead of C-x C-u.
Stefan
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-18 19:32 ` Stefan Monnier
@ 2009-04-19 1:13 ` Arni Magnusson
2009-04-19 1:40 ` Arni Magnusson
2009-04-19 3:14 ` Stefan Monnier
0 siblings, 2 replies; 27+ messages in thread
From: Arni Magnusson @ 2009-04-19 1:13 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 2887
>> `backward-delete-word'
>> `delete-word'
>
> I would only consider some general "kill-next-kill" feature which would
> allow to turn any killing command into a deleting one (e.g. a
> kill-next-kill command which would cause the subsequent commands's
> effect on the kill-ring to be cancelled).
This would mean two keystrokes to delete a word, right? First
`kill-next-kill' and then `kill-word'. It's an idea, but I still believe
that many users would appreciate binding single keystrokes to the
functions I suggested. They would most likely bind them to C-backspace and
C-delete.
>> `pos-at-beginning-of-line'
>> `pos-at-end-of-line'
>
> The reimplementation doesn't change anything: its performance will still
> suck on large files. It's just fundamentally an operation that is slow
> and that we should generally avoid, so I don't want to add yet more ways
> (additionally to goto-line) to do that.
I don't understand, I think these functions are blazing fast. In practice,
I would probably not use Emacs to perform 100,000 iterations on a 200 MB
file - but that's why I'm surprised to see that it takes less than a
second:
100000 M-x benchmark (pos-at-end-of-line 10) ; 0.421s
I'm using a small arg of 10, because that's the case where you predicted
that `line-end-position' would be much faster.
100000 M-x benchmark (line-end-position 10) ; 0.220s
This is necessarily faster, since `pos-at-end-of-line' calls
`line-end-position', but the (save-excursion (goto-char (point-min))
overhead is not as great as one might expect.
With few iterations and large arg, the speed is also comparable:
10 M-x benchmark (pos-at-end-of-line 100000) ; 0.156s
10 M-x benchmark (line-end-position 100000) ; 0.156s
Most things become more sluggish with a 200 MB file. Overall, I think
these functions make it considerably easier to read and write functions
that operate on buffer text - with minimal performance cost. In practice,
I use it without iteration, and with a 1 MB source code file it doesn't
register in a benchmark (0.000000s).
I hesitate to introduce another function to the discussion, but here's an
example where I use `pos-at-beginning-of-line' and `pos-at-end-of-line'.
Since you're the maintainer of newcomment.el, it might pique your
interest:
(defun comment-line-or-region ()
"Comment line or region."
(interactive)
(require 'newcomment)
(if (and mark-active transient-mark-mode)
(comment-region
(pos-at-beginning-of-line (line-number-at-pos (region-beginning)))
(pos-at-end-of-line (line-number-at-pos (region-end))))
(comment-region (line-beginning-position)(line-end-position))))
Same idea as `kill-line-or-region'. Without the `pos-at-beginning-of-line'
and `pos-at-end-of-line',
(comment-region
(region-beginning)
(region-end))
it would misbehave when the region consists of a partially selected first
and/or last line. Someone like you might be able to improve this function,
but it's served me well and has no discernible speed lag.
>> `delete-all-blank-lines'
>
> Can someone figure out a way to tweak flush-lines such that it can be
> used for that purpose without having to jump through as many hooks?
> Maybe we can just say that if you call flush-lines with an empty
> argument (which currently would flush *all* lines) it will flush all
> empty lines.
This is definitely an idea, making better use of the default value of the
regexp. But do you really mean flush all empty lines, or just the empty
lines below the point? The idea behind `delete-all-blank-lines' is to
really delete all empty lines, without moving the point, in one keystroke.
I could probably edit `flush-lines' to do exactly that, although it
operates only on text after the point for non-default regexps.
Phew, it looks like the rest of our discussion threads have closed
successfully, meaning that we have fully understood each others' ideas,
and the decisions will depend on your good judgement and the Emacs elders.
Cheers,
Arni
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-19 1:13 ` Arni Magnusson
@ 2009-04-19 1:40 ` Arni Magnusson
2009-04-19 3:14 ` Stefan Monnier
1 sibling, 0 replies; 27+ messages in thread
From: Arni Magnusson @ 2009-04-19 1:40 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 2887
> [...] understood each others' ideas [...]
Whoa, that should have been each other's ideas. Lisp programmers need to
be extra careful with their apostrophes :)
Arni
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-19 1:13 ` Arni Magnusson
2009-04-19 1:40 ` Arni Magnusson
@ 2009-04-19 3:14 ` Stefan Monnier
2009-04-19 13:41 ` Arni Magnusson
1 sibling, 1 reply; 27+ messages in thread
From: Stefan Monnier @ 2009-04-19 3:14 UTC (permalink / raw)
To: Arni Magnusson; +Cc: 2887
>> I would only consider some general "kill-next-kill" feature which would
>> allow to turn any killing command into a deleting one
>> (e.g. a kill-next-kill command which would cause the subsequent commands's
>> effect on the kill-ring to be cancelled).
> This would mean two keystrokes to delete a word, right?
Yes, at least. Maybe you can amortize it so it's only N+1 for N words.
> It's an idea, but I still believe that many users would appreciate
> binding single keystrokes to the functions I suggested.
Yes, that's where we disagree.
> (defun comment-line-or-region ()
> "Comment line or region."
> (interactive)
> (require 'newcomment)
> (if (and mark-active transient-mark-mode)
> (comment-region
> (pos-at-beginning-of-line (line-number-at-pos (region-beginning)))
> (pos-at-end-of-line (line-number-at-pos (region-end))))
> (comment-region (line-beginning-position)(line-end-position))))
A perfect example of the kind of performance bug that comes up when you
think in terms of lines, as encouraged by pos-at-beginning/end-of-line.
The above should be:
(defun comment-line-or-region ()
"Comment line or region."
(interactive)
(require 'newcomment)
(if (and mark-active transient-mark-mode)
(comment-region
(save-excursion (goto-char (region-beginning)) (line-beginning-position))
(save-excursion (goto-char (region-end)) (line-end-position)))
(comment-region (line-beginning-position) (line-end-position))))
line-number-at-pos is also a "function to avoid", just as bad as
goto-line. Your code will walk over the whole buffer 4 times (twice to
compute the line-number at region beg and end, then twice to find the
beg/end of those 2 lines).
>>> `delete-all-blank-lines'
>>
>> Can someone figure out a way to tweak flush-lines such that it can be used
>> for that purpose without having to jump through as many hooks? Maybe we
>> can just say that if you call flush-lines with an empty argument (which
>> currently would flush *all* lines) it will flush all empty lines.
> This is definitely an idea, making better use of the default value of the
> regexp. But do you really mean flush all empty lines, or just the empty
> lines below the point? The idea behind `delete-all-blank-lines' is to really
> delete all empty lines, without moving the point, in one keystroke. I could
> probably edit `flush-lines' to do exactly that, although it operates only on
> text after the point for non-default regexps.
I don't think the position-preservation is important enough to warrant
a different command. So do C-SPC M-< before and C-u C-SPC afterwards if
you want to preserve point. Or try to provide some way for flush-lines
to operate on the whole buffer, without having to move point.
Stefan
^ permalink raw reply [flat|nested] 27+ messages in thread
* bug#2887: Suggestions for simple.el
2009-04-19 3:14 ` Stefan Monnier
@ 2009-04-19 13:41 ` Arni Magnusson
0 siblings, 0 replies; 27+ messages in thread
From: Arni Magnusson @ 2009-04-19 13:41 UTC (permalink / raw)
To: Stefan Monnier; +Cc: 2887, bug-lisp-manual
[Arni Magnusson wrote:]
>> (defun pos-at-beginning-of-line (N)
>> "Return the position at beginning of line N."
>> (save-excursion
>> (goto-char (point-min))(line-beginning-position N)))
>>
>> (defun pos-at-end-of-line (N)
>> "Return the position at end of line N."
>> (save-excursion
>> (goto-char (point-min))(line-end-position N)))
>>
>> (defun comment-line-or-region ()
>> "Comment line or region."
>> (interactive)
>> (require 'newcomment)
>> (if (and mark-active transient-mark-mode)
>> (comment-region
>> (pos-at-beginning-of-line
>> (line-number-at-pos (region-beginning)))
>> (pos-at-end-of-line
>> (line-number-at-pos (region-end))))
>> (comment-region
>> (line-beginning-position)(line-end-position))))
[Stefan Monnier wrote:]
> A perfect example of the kind of performance bug that comes up when you
> think in terms of lines, as encouraged by pos-at-beginning/end-of-line.
> The above should be:
>
> (defun comment-line-or-region ()
> "Comment line or region."
> (interactive)
> (require 'newcomment)
> (if (and mark-active transient-mark-mode)
> (comment-region
> (save-excursion
> (goto-char (region-beginning))(line-beginning-position))
> (save-excursion
> (goto-char (region-end))(line-end-position)))
> (comment-region
> (line-beginning-position)(line-end-position))))
>
> line-number-at-pos is also a "function to avoid", just as bad as
> goto-line. Your code will walk over the whole buffer 4 times (twice to
> compute the line-number at region beg and end, then twice to find the
> beg/end of those 2 lines).
---
Aha, now I see what you mean. One should use relative motion in Emacs Lisp
programs and avoid referring to absolute line numbers (`goto-line',
`line-beginning-position', `line-end-position', `line-number-at-pos').
Thank you Stefan, for explaining this to me - now I would like to help
others to avoid using these functions in Emacs Lisp programs, when
possible. Couldn't this be mentioned in the docstrings and in the Emacs
Lisp Manual? They had already helped me by tagging a warning sign on
functions like:
`next-line', `previous-line'
`beginning-of-buffer', `end-of-buffer'
`replace-string', `replace-regexp'
`insert-file', `insert-buffer'
In my notes, I have also written that (goto-char (point-min)) is better
than (goto-line 1), but now I can't see where this is documented.
Besides the docstrings and the function entries in the manual, there is a
section in the manual called "Emacs Programming Tips" where the pitfalls
of *-line-* functions could be mentioned.
Thanks,
Arni
^ permalink raw reply [flat|nested] 27+ messages in thread