Browse Source

Support for Shift Selection

Emacs 23 introduces `shift-selection-mode', and even turns it on by
default.  Therefore, it is fair to assume that users of Emacs 23 will
expect to be able to select a region by holding down shift will moving
the cursor.  This conflicts with the use of shifted cursor keys in
Org-mode for other purposes, in particular to "change the item under
the cursor", like it is done for time stamps, item bullet types, TODO
keywords and priorities.

This commit tries to support `shift-selection-mode' as much as possible,
while retaining these valuable commands keys for Org-mode.  The
following things are changed:

1. The range of contexts where Org's commands do act is reduced.

  - S-up and S-down no longer jump to the previous and next plain list
    item - you can use the paragraph commands C-up and C-down for this
    quite well.

  - S-left and S-right only change the bullet type of a plain list
    item when the cursor is *exactly* on the bullet, not just anywhere
    in the item line.

2. When a S-cursor key is used outside a special context, a region is
   started or extended.

3. When a region has already been started, it will be continue to
   extend even if the S-cursor keys move the cursor into a special
   context.

4. If you want S-cursor selection to work in headlines as well, you
   can set the variable `org-support-shift-select' to
   `even-in-headlines'.  Then you need to rely on `C-c C-t' to set
   TODO keywords, and on `C-c ,' to set priorities.

If you want everything to behave the way it was, set
`org-support-shift-select' to nil.
Carsten Dominik 16 years ago
parent
commit
773a3c624e
6 changed files with 158 additions and 35 deletions
  1. 0 1
      ChangeLog
  2. 6 0
      doc/ChangeLog
  3. 46 27
      doc/org.texi
  4. 7 0
      lisp/ChangeLog
  5. 6 0
      lisp/org-list.el
  6. 93 7
      lisp/org.el

+ 0 - 1
ChangeLog

@@ -1,4 +1,3 @@
-
 2009-01-19  Carsten Dominik  <carsten.dominik@gmail.com>
 
 	* Makefile (html_manual): Process the split html manual with the

+ 6 - 0
doc/ChangeLog

@@ -1,3 +1,9 @@
+2009-01-26  Carsten Dominik  <carsten.dominik@gmail.com>
+
+	* org.texi (Plain lists, TODO basics, Priorities)
+	(Multiple sets in one file, Conflicts): Document interaction with
+	`shift-selection-mode'.
+
 2009-01-24  Carsten Dominik  <carsten.dominik@gmail.com>
 
 	* org.texi (References): Add information about remote references.

+ 46 - 27
doc/org.texi

@@ -1302,7 +1302,11 @@ Insert a new item with a checkbox (@pxref{Checkboxes}).
 @kindex S-@key{down}
 @item S-@key{up}
 @itemx S-@key{down}
-Jump to the previous/next item in the current list.
+@cindex shift-selection-mode
+Jump to the previous/next item in the current list, but only if
+@code{shift-selection-mode} is off.  If not, you can still use paragraph
+jumping commands like @kbd{C-@key{up}} and @kbd{C-@key{down}} to quite
+similar effect.
 @kindex M-S-@key{up}
 @kindex M-S-@key{down}
 @item M-S-@key{up}
@@ -1338,7 +1342,9 @@ converted into a list item.
 @kindex S-@key{left}
 @kindex S-@key{right}
 @item S-@key{left}/@key{right}
-Also cycle bullet styles when in the first line of an item.
+This command also cycles bullet styles when the cursor in on the bullet or
+(when @code{org-support-shift-select} is @code{nil}) even when anywhere in
+the item line.
 @end table
 
 @node Drawers, Footnotes, Plain lists, Document Structure
@@ -3044,7 +3050,8 @@ more information.
 @itemx S-@key{left}
 Select the following/preceding TODO state, similar to cycling.  Useful
 mostly if more than two TODO states are possible (@pxref{TODO
-extensions}).
+extensions}).  See also @ref{Conflicts} for a discussion of the interaction
+with @code{shift-selection-mode}.
 @kindex C-c C-v
 @kindex C-c / t
 @cindex sparse tree, for TODO
@@ -3192,13 +3199,17 @@ keyword or using completion, you may also apply the following commands:
 These keys jump from one TODO subset to the next.  In the above example,
 @kbd{C-S-@key{right}} would jump from @code{TODO} or @code{DONE} to
 @code{REPORT}, and any of the words in the second row to @code{CANCELED}.
+Note that this key binding conflicts with @code{shift-selection-mode}
+(@pxref{Conflicts}).
 @kindex S-@key{right}
 @kindex S-@key{left}
 @item S-@key{right}
 @itemx S-@key{left}
-@kbd{S-@key{<left>}} and @kbd{S-@key{<right>}} and walk through
-@emph{all} keywords from all sets, so for example @kbd{S-@key{<right>}}
-would switch from @code{DONE} to @code{REPORT} in the example above.
+@kbd{S-@key{<left>}} and @kbd{S-@key{<right>}} and walk through @emph{all}
+keywords from all sets, so for example @kbd{S-@key{<right>}} would switch
+from @code{DONE} to @code{REPORT} in the example above.  See also
+@ref{Conflicts} for a discussion of the interaction with
+@code{shift-selection-mode}.
 @end table
 
 @node Fast access to TODO states, Per-file keywords, Multiple sets in one file, TODO extensions
@@ -3441,10 +3452,11 @@ agenda buffer with the @kbd{,} command (@pxref{Agenda commands}).
 @kindex S-@key{down}
 @item S-@key{up}
 @itemx S-@key{down}
-Increase/decrease priority of current headline@footnote{See also the
-option @code{org-priority-start-cycle-with-default'}.}.  Note that these
-keys are also used to modify time stamps (@pxref{Creating timestamps}).
-Furthermore, these keys are also used by CUA mode (@pxref{Conflicts}).
+Increase/decrease priority of current headline@footnote{See also the option
+@code{org-priority-start-cycle-with-default'}.}.  Note that these keys are
+also used to modify time stamps (@pxref{Creating timestamps}).  See also
+@ref{Conflicts} for a discussion of the interaction with
+@code{shift-selection-mode}.
 @end table
 
 You can change the range of allowed priorities by setting the variables
@@ -9275,33 +9287,40 @@ which makes using @file{footnote.el} unnecessary.
 
 @table @asis
 
-@cindex @file{allout.el}
-@item @file{allout.el} by Ken Manheimer
-Startup of Org may fail with the error message
-@code{(wrong-type-argument keymapp nil)} when there is an outdated
-version @file{allout.el} on the load path, for example the version
-distributed with Emacs 21.x.  Upgrade to Emacs 22 and this problem will
-disappear.  If for some reason you cannot do this, make sure that org.el
-is loaded @emph{before} @file{allout.el}, for example by putting
-@code{(require 'org)} early enough into your @file{.emacs} file.
+@cindex @code{shift-selection-mode}
+In Emacs 23, @code{shift-selection-mode} is on by default, meaning that
+cursor motions combined with the shift key should start or enlarge regions.
+This conflicts with the use of @kbd{S-@key{cursor}} commands in Org to change
+timestamps, TODO keywords, priorities, and item bullet types if the cursor is
+at such a location.  Org-mode tries to accommodate shift selection by (i)
+using it outside of the special contexts where special commands apply, and by
+(ii) extending an existing active region even if the cursor moves across a
+special context.  See the variable @code{org-support-shift-select} for more
+information, and for a way to control in which contexts shift selection
+should be overruled.
 
 @cindex @file{CUA.el}
 @item @file{CUA.el} by Kim. F. Storm
-Key bindings in Org conflict with the @kbd{S-<cursor>} keys used by
-CUA mode (as well as pc-select-mode and s-region-mode) to select and
-extend the region.  If you want to use one of these packages along with
-Org, configure the variable @code{org-replace-disputed-keys}.  When
-set, Org will move the following key bindings in Org files, and
-in the agenda buffer (but not during date selection).
+Key bindings in Org conflict with the @kbd{S-<cursor>} keys used by CUA mode
+(as well as pc-select-mode and s-region-mode) to select and extend the
+region.  In fact, Emacs 23 has this built-in in the form of
+@code{shift-selection-mode}, see previous paragraph.  If you are using Emacs
+23 you probably don't want to use another package for this purpose.  However,
+if you prefer to leave these keys to a different package while working in
+Org-mode, configure the variable @code{org-replace-disputed-keys}.  When set,
+Org will move the following key bindings in Org files, and in the agenda
+buffer (but not during date selection).
 
 @example
-S-UP    -> M-p             S-DOWN  -> M-n
-S-LEFT  -> M--             S-RIGHT -> M-+
+S-UP      ->  M-p             S-DOWN     ->  M-n
+S-LEFT    ->  M--             S-RIGHT    ->  M-+
+C-S-LEFT  ->  M-S--           C-S-RIGHT  ->  M-S-+
 @end example
 
 Yes, these are unfortunately more difficult to remember.  If you want
 to have other replacement keys, look at the variable
 @code{org-disputed-keys}.
+
 @item @file{windmove.el} by Hovav Shacham
 @cindex @file{windmove.el}
 Also this package uses the @kbd{S-<cursor>} keys, so everything written

+ 7 - 0
lisp/ChangeLog

@@ -1,5 +1,12 @@
 2009-01-26  Carsten Dominik  <carsten.dominik@gmail.com>
 
+	* org.el (org-support-shift-select): New option.
+	(org-shiftup, org-shiftdown, org-shiftright, org-shiftleft)
+	(org-shiftcontrolright, org-shiftcontrolleft): Support for shift
+	selection outside contexts.
+
+	* org-list.el (org-at-item-bullet-p): New function.
+
 	* org-jsinfo.el (org-infojs-handle-options): Remove unnecessary
 	variables.
 

+ 6 - 0
lisp/org-list.el

@@ -162,6 +162,12 @@ list, obtained by prompting the user."
 	((= llt ?\)) "\\([ \t]*\\([-+]\\|\\([0-9]+))\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
 	(t (error "Invalid value of `org-plain-list-ordered-item-terminator'")))))))
 
+(defun org-at-item-bullet-p ()
+  "Is point at the bullet of a plain list item?"
+  (and (org-at-item-p)
+       (not (member (char-after) '(?\  ?\t)))
+       (< (point) (match-end 0))))
+
 (defun org-in-item-p ()
   "It the cursor inside a plain list item.
 Does not have to be the first line."

+ 93 - 7
lisp/org.el

@@ -199,6 +199,41 @@ to add the symbol `xyz', and the package must have a call to
 	(const :tag "C  sqlinsert:         Convert Org-mode tables to SQL insertions" orgtbl-sqlinsert)
 	(repeat :tag "External packages" :inline t (symbol :tag "Package"))))
 
+(defcustom org-support-shift-select
+  (and (boundp 'shift-select-mode) shift-select-mode t)
+  "Non-nil means, make shift-cursor commands select text when possible.
+
+In Emacs 23, when `shift-select-mode' is on, shifted cursor keys start
+selecting a region, or enlarge thusly started regions.  In Org-mode,
+in special contexts, these same keys are used for other purposes, important
+enough to compete with shift selection.  Org tries to balance these needs
+by supporting `shift-select-mode' outside these special contexts, under
+control of this variable.
+
+When this variable is t and the cursor is not in a special context,
+Org-mode will support shift-selection for making and enlarging regions.
+
+Shift-cursor keys have special meanings at the following locations:
+- on a headline (changing TODO state and priority)
+- on a plain list item bullet (cycling the bullet type)
+- on a time stamp (changing the time)
+- in the BEGIN line of a clock table (changing the time block).
+
+If you set this variable to the symbol `even-in-headlines', then the
+keys will not change TODO states and priorities in headlines, to
+make shift selection work there as well.  This is not really a problem,
+because there are alternative commands `C-c C-t' and `C-c ,' to change
+TODO state and priority.
+
+However, when the cursor is on a timestamp, headline or not, shift-cursor
+commands will still edit the time stamp - this is just too good to give up.
+
+XEmacs user should have this variable set to nil."
+  :group 'org
+  :type '(choice
+	  (const :tag "Not at all" nil)
+	  (const :tag "When context allows" t)
+	  (const :tag "Even in headlines" even-in-headlines)))
 
 (defgroup org-startup nil
   "Options concerning startup of Org-mode."
@@ -13016,12 +13051,21 @@ Calls `org-timestamp-up' or `org-priority-up', or `org-previous-item',
 depending on context.  See the individual commands for more information."
   (interactive "P")
   (cond
+   ((and org-support-shift-select (org-region-active-p))
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'previous-line)))
    ((org-at-timestamp-p t)
     (call-interactively (if org-edit-timestamp-down-means-later
 			    'org-timestamp-down 'org-timestamp-up)))
-   ((org-on-heading-p) (call-interactively 'org-priority-up))
-   ((org-at-item-p) (call-interactively 'org-previous-item))
+   ((and (not (eq org-support-shift-select 'even-in-headlines))
+	 (org-on-heading-p))
+    (call-interactively 'org-priority-up))
+   ((and (not org-support-shift-select) (org-at-item-p))
+    (call-interactively 'org-previous-item))
    ((org-clocktable-try-shift 'up arg))
+   (org-support-shift-select
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'previous-line)))
    (t (call-interactively 'org-beginning-of-item) (beginning-of-line 1))))
 
 (defun org-shiftdown (&optional arg)
@@ -13030,11 +13074,21 @@ Calls `org-timestamp-down' or `org-priority-down', or `org-next-item'
 depending on context.  See the individual commands for more information."
   (interactive "P")
   (cond
+   ((and org-support-shift-select (org-region-active-p))
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'next-line)))
    ((org-at-timestamp-p t)
     (call-interactively (if org-edit-timestamp-down-means-later
 			    'org-timestamp-up 'org-timestamp-down)))
-   ((org-on-heading-p) (call-interactively 'org-priority-down))
+   ((and (not (eq org-support-shift-select 'even-in-headlines))
+	 (org-on-heading-p))
+    (call-interactively 'org-priority-down))
+   ((and (not org-support-shift-select) (org-at-item-p))
+    (call-interactively 'org-next-item))
    ((org-clocktable-try-shift 'down arg))
+   (org-support-shift-select 
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'next-line)))
    (t (call-interactively 'org-next-item))))
 
 (defun org-shiftright (&optional arg)
@@ -13048,11 +13102,21 @@ Depending on context, this does one of the following:
 - on a clocktable definition line, move time block into the future"
   (interactive "P")
   (cond
+   ((and org-support-shift-select (org-region-active-p))
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'forward-char)))
    ((org-at-timestamp-p t) (call-interactively 'org-timestamp-up-day))
-   ((org-on-heading-p) (org-call-with-arg 'org-todo 'right))
-   ((org-at-item-p) (org-call-with-arg 'org-cycle-list-bullet nil))
+   ((and (not (eq org-support-shift-select 'even-in-headlines))
+	 (org-on-heading-p))
+    (org-call-with-arg 'org-todo 'right))
+   ((or (and org-support-shift-select (org-at-item-bullet-p))
+	(and (not org-support-shift-select) (org-at-item-p)))
+    (org-call-with-arg 'org-cycle-list-bullet nil))
    ((org-at-property-p) (call-interactively 'org-property-next-allowed-value))
    ((org-clocktable-try-shift 'right arg))
+   (org-support-shift-select 
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'forward-char)))
    (t (org-shiftcursor-error))))
 
 (defun org-shiftleft (&optional arg)
@@ -13066,26 +13130,48 @@ Depending on context, this does one of the following:
 - on a clocktable definition line, move time block into the past"
   (interactive "P")
   (cond
+   ((and org-support-shift-select (org-region-active-p))
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'backward-char)))
    ((org-at-timestamp-p t) (call-interactively 'org-timestamp-down-day))
-   ((org-on-heading-p) (org-call-with-arg 'org-todo 'left))
-   ((org-at-item-p) (org-call-with-arg 'org-cycle-list-bullet 'previous))
+   ((and (not (eq org-support-shift-select 'even-in-headlines))
+	 (org-on-heading-p))
+    (org-call-with-arg 'org-todo 'left))
+   ((or (and org-support-shift-select (org-at-item-bullet-p))
+	(and (not org-support-shift-select) (org-at-item-p)))
+    (org-call-with-arg 'org-cycle-list-bullet 'previous))
    ((org-at-property-p)
     (call-interactively 'org-property-previous-allowed-value))
    ((org-clocktable-try-shift 'left arg))
+   (org-support-shift-select 
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'backward-char)))
    (t (org-shiftcursor-error))))
 
 (defun org-shiftcontrolright ()
   "Switch to next TODO set."
   (interactive)
   (cond
+   ((and org-support-shift-select (org-region-active-p))
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'forward-word)))
    ((org-on-heading-p) (org-call-with-arg 'org-todo 'nextset))
+   (org-support-shift-select 
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'forward-word)))
    (t (org-shiftcursor-error))))
 
 (defun org-shiftcontrolleft ()
   "Switch to previous TODO set."
   (interactive)
   (cond
+   ((and org-support-shift-select (org-region-active-p))
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'backward-word)))
    ((org-on-heading-p) (org-call-with-arg 'org-todo 'previousset))
+   (org-support-shift-select 
+    (let ((this-command-keys-shift-translated t))
+      (call-interactively 'backward-word)))
    (t (org-shiftcursor-error))))
 
 (defun org-ctrl-c-ret ()