diff options
author | Stefan Monnier <monnier@iro.umontreal.ca> | 2013-10-29 12:11:50 -0400 |
---|---|---|
committer | Stefan Monnier <monnier@iro.umontreal.ca> | 2013-10-29 12:11:50 -0400 |
commit | 3472b6c682817242f8b6134dea06a6ce4777e419 (patch) | |
tree | 5e1fb55e804a07a91ad431b95b1e168df0ea7bf1 /lisp | |
parent | 2d9783e0b938d0e3550fee7efbb3c6b8b5f18462 (diff) |
Add pre-redisplay-function and rectangular region
* lisp/rect.el: Use lexical-binding. Add new rectangular region support.
(rectangle-mark): New command.
(rectangle--region): New var.
(deactivate-mark-hook): Reset rectangle--region.
(rectangle--extract-region, rectangle--insert-for-yank)
(rectangle--highlight-for-redisplay)
(rectangle--unhighlight-for-redisplay): New functions.
(region-extract-function, redisplay-unhighlight-region-function)
(redisplay-highlight-region-function): Use them to handle
rectangular region.
* lisp/simple.el (region-extract-function): New var.
(delete-backward-char, delete-forward-char, deactivate-mark): Use it.
(kill-new, kill-append): Remove obsolete `yank-handler' argument.
(kill-region): Replace obsolete `yank-handler' arg with `region'.
(copy-region-as-kill, kill-ring-save): Add `region' argument.
(redisplay-unhighlight-region-function)
(redisplay-highlight-region-function): New vars.
(redisplay--update-region-highlight): New function.
(pre-redisplay-function): Use it.
(exchange-point-and-mark): Don't deactivate the mark before
reactivate-it anyway.
* lisp/comint.el (comint-kill-region): Remove yank-handler argument.
* lisp/delsel.el (delete-backward-char, backward-delete-char-untabify)
(delete-char): Remove property, since it's now part of their
default behavior.
(self-insert-iso): Remove property since this command doesn't exist.
* src/xdisp.c (prepare_menu_bars): Call Vpre_redisplay_function.
(syms_of_xdisp): Declare pre-redisplay-function.
(markpos_of_region): Remove function.
(init_iterator, compute_stop_pos, handle_face_prop)
(face_before_or_after_it_pos, reseat_to_string)
(get_next_display_element, window_buffer_changed)
(redisplay_internal, try_cursor_movement, redisplay_window)
(try_window_reusing_current_matrix, try_window_id, display_line)
(note_mode_line_or_margin_highlight, note_mouse_highlight)
(display_string, mouse_face_from_buffer_pos): Remove region handling.
* src/window.h (struct window): Remove field `region_showing'.
* src/dispextern.h (struct it): Remove region_beg/end_charpos.
(face_at_buffer_position, face_for_overlay_string)
(face_at_string_position): Update prototypes.
* src/xfaces.c (face_at_buffer_position, face_for_overlay_string)
(face_at_string_position): Remove `region_beg' and `region_end' args.
* src/fontset.c (Finternal_char_font):
* src/font.c (font_at, font_range): Adjust calls accordingly.
* src/insdel.c (Qregion_extract_function): New var.
(syms_of_insdel): Initialize it.
(prepare_to_modify_buffer_1): Use it.
Diffstat (limited to 'lisp')
-rw-r--r-- | lisp/ChangeLog | 27 | ||||
-rw-r--r-- | lisp/comint.el | 7 | ||||
-rw-r--r-- | lisp/delsel.el | 6 | ||||
-rw-r--r-- | lisp/rect.el | 148 | ||||
-rw-r--r-- | lisp/simple.el | 140 | ||||
-rw-r--r-- | lisp/subr.el | 1 |
6 files changed, 280 insertions, 49 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 885540cdba..988047c9bc 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,5 +1,32 @@ 2013-10-29 Stefan Monnier <monnier@iro.umontreal.ca> + * rect.el: Use lexical-binding. Add new rectangular region support. + (rectangle-mark): New command. + (rectangle--region): New var. + (deactivate-mark-hook): Reset rectangle--region. + (rectangle--extract-region, rectangle--insert-for-yank) + (rectangle--highlight-for-redisplay) + (rectangle--unhighlight-for-redisplay): New functions. + (region-extract-function, redisplay-unhighlight-region-function) + (redisplay-highlight-region-function): Use them to handle + rectangular region. + * simple.el (region-extract-function): New var. + (delete-backward-char, delete-forward-char, deactivate-mark): Use it. + (kill-new, kill-append): Remove obsolete `yank-handler' argument. + (kill-region): Replace obsolete `yank-handler' arg with `region'. + (copy-region-as-kill, kill-ring-save): Add `region' argument. + (redisplay-unhighlight-region-function) + (redisplay-highlight-region-function): New vars. + (redisplay--update-region-highlight): New function. + (pre-redisplay-function): Use it. + (exchange-point-and-mark): Don't deactivate the mark before + reactivate-it anyway. + * comint.el (comint-kill-region): Remove yank-handler argument. + * delsel.el (delete-backward-char, backward-delete-char-untabify) + (delete-char): Remove property, since it's now part of their + default behavior. + (self-insert-iso): Remove property since this command doesn't exist. + * emacs-lisp/package.el (package--download-one-archive) (describe-package-1): Don't query the user about final newline. diff --git a/lisp/comint.el b/lisp/comint.el index 7572e8baab..8e1b2105de 100644 --- a/lisp/comint.el +++ b/lisp/comint.el @@ -2679,7 +2679,7 @@ if necessary." (kill-whole-line count) (when (>= count 0) (comint-update-fence)))) -(defun comint-kill-region (beg end &optional yank-handler) +(defun comint-kill-region (beg end) "Like `kill-region', but ignores read-only properties, if safe. This command assumes that the buffer contains read-only \"prompts\" which are regions with front-sticky read-only @@ -2693,7 +2693,6 @@ prompts should stay at the beginning of a line. If this is not the case, this command just calls `kill-region' with all read-only properties intact. The read-only status of newlines is updated using `comint-update-fence', if necessary." - (declare (advertised-calling-convention (beg end) "23.3")) (interactive "r") (save-excursion (let* ((true-beg (min beg end)) @@ -2708,9 +2707,9 @@ updated using `comint-update-fence', if necessary." (if (listp end-lst) (memq 'read-only end-lst) t)))) (if (or (and (not beg-bolp) (or beg-bad end-bad)) (and (not end-bolp) end-bad)) - (kill-region beg end yank-handler) + (kill-region beg end) (let ((inhibit-read-only t)) - (kill-region beg end yank-handler) + (kill-region beg end) (comint-update-fence)))))) ;; Support for source-file processing commands. diff --git a/lisp/delsel.el b/lisp/delsel.el index 672c93443d..07a7a37db3 100644 --- a/lisp/delsel.el +++ b/lisp/delsel.el @@ -165,16 +165,10 @@ See `delete-selection-helper'." (not (run-hook-with-args-until-success 'self-insert-uses-region-functions)))) -(put 'self-insert-iso 'delete-selection t) - (put 'yank 'delete-selection 'yank) (put 'clipboard-yank 'delete-selection 'yank) (put 'insert-register 'delete-selection t) -(put 'delete-backward-char 'delete-selection 'supersede) -(put 'backward-delete-char-untabify 'delete-selection 'supersede) -(put 'delete-char 'delete-selection 'supersede) - (put 'newline-and-indent 'delete-selection t) (put 'newline 'delete-selection t) (put 'open-line 'delete-selection 'kill) diff --git a/lisp/rect.el b/lisp/rect.el index ec234b6514..44799f2616 100644 --- a/lisp/rect.el +++ b/lisp/rect.el @@ -1,4 +1,4 @@ -;;; rect.el --- rectangle functions for GNU Emacs +;;; rect.el --- rectangle functions for GNU Emacs -*- lexical-binding:t -*- ;; Copyright (C) 1985, 1999-2013 Free Software Foundation, Inc. @@ -412,6 +412,152 @@ with a prefix argument, prompt for START-AT and FORMAT." (apply-on-rectangle 'rectangle-number-line-callback start end format))) +;;; New rectangle integration with kill-ring. + +;; FIXME: lots of known problems with the new rectangle support: +;; - no key binding for mark-rectangle. +;; - no access to the `string-rectangle' functionality. +;; - lots of commands handle the region without paying attention to its +;; rectangular shape. + +(defvar-local rectangle--region nil + "If non-nil, the region is meant to delimit a rectangle.") + +(add-hook 'deactivate-mark-hook + (lambda () (kill-local-variable 'rectangle--region))) + +(add-function :around redisplay-highlight-region-function + #'rectangle--highlight-for-redisplay) +(add-function :around redisplay-unhighlight-region-function + #'rectangle--unhighlight-for-redisplay) +(add-function :around region-extract-function + #'rectangle--extract-region) + +;;;###autoload +(defun rectangle-mark () + "Toggle the region as rectangular." + (interactive) + (if rectangle--region + (kill-local-variable 'rectangle--region) + (unless (region-active-p) (push-mark-command t)) + (setq rectangle--region t))) + +(defun rectangle--extract-region (orig &optional delete) + (if (not rectangle--region) + (funcall orig delete) + (let* ((strs (funcall (if delete + #'delete-extract-rectangle + #'extract-rectangle) + (region-beginning) (region-end))) + (str (mapconcat #'identity strs "\n"))) + (when (eq last-command 'kill-region) + ;; Try to prevent kill-region from appending this to some + ;; earlier element. + (setq last-command 'kill-region-dont-append)) + (when strs + (put-text-property 0 (length str) 'yank-handler + `(rectangle--insert-for-yank ,strs t) + str) + str)))) + +(defun rectangle--insert-for-yank (strs) + (push (point) buffer-undo-list) + (let ((undo-at-start buffer-undo-list)) + (insert-rectangle strs) + (setq yank-undo-function + (lambda (_start _end) + (undo-start) + (setcar undo-at-start nil) ;Turn it into a boundary. + (while (not (eq pending-undo-list (cdr undo-at-start))) + (undo-more 1)))))) + +(defun rectangle--highlight-for-redisplay (orig start end window rol) + (cond + ((not rectangle--region) + (funcall orig start end window rol)) + ((and (eq 'rectangle (car-safe rol)) + (eq (nth 1 rol) (buffer-modified-tick)) + (eq start (nth 2 rol)) + (eq end (nth 3 rol))) + rol) + (t + (save-excursion + (let* ((nrol nil) + (old (if (eq 'rectangle (car-safe rol)) + (nthcdr 4 rol) + (funcall redisplay-unhighlight-region-function rol) + nil)) + (ptcol (progn (goto-char start) (current-column))) + (markcol (progn (goto-char end) (current-column))) + (leftcol (min ptcol markcol)) + (rightcol (max ptcol markcol))) + (goto-char start) + (while (< (point) end) + (let* ((mleft (move-to-column leftcol)) + (left (point)) + (mright (move-to-column rightcol)) + (right (point)) + (ol + (if (not old) + (let ((ol (make-overlay left right))) + (overlay-put ol 'window window) + (overlay-put ol 'face 'region) + ol) + (let ((ol (pop old))) + (move-overlay ol left right (current-buffer)) + ol)))) + ;; `move-to-column' may stop before the column (if bumping into + ;; EOL) or overshoot it a little, when column is in the middle + ;; of a char. + (cond + ((< mleft leftcol) ;`leftcol' is past EOL. + (overlay-put ol 'before-string + (spaces-string (- leftcol mleft))) + (setq mright (max mright leftcol))) + ((and (> mleft leftcol) ;`leftcol' is in the middle of a char. + (eq (char-before left) ?\t)) + (setq left (1- left)) + (move-overlay ol left right) + (goto-char left) + (overlay-put ol 'before-string + (spaces-string (- leftcol (current-column))))) + ((overlay-get ol 'before-string) + (overlay-put ol 'before-string nil))) + (cond + ((< mright rightcol) ;`rightcol' is past EOL. + (let ((str (make-string (- rightcol mright) ?\s))) + (put-text-property 0 (length str) 'face 'region str) + ;; If cursor happens to be here, draw it *before* rather than + ;; after this highlighted pseudo-text. + (put-text-property 0 1 'cursor t str) + (overlay-put ol 'after-string str))) + ((and (> mright rightcol) ;`rightcol' is in the middle of a char. + (eq (char-before right) ?\t)) + (setq right (1- right)) + (move-overlay ol left right) + (goto-char right) + (let ((str (make-string (- rightcol (current-column)) ?\s))) + (put-text-property 0 (length str) 'face 'region str) + (overlay-put ol 'after-string str))) + ((overlay-get ol 'after-string) + (overlay-put ol 'after-string nil))) + (when (= leftcol rightcol) + ;; Make zero-width rectangles visible! + (overlay-put ol 'after-string + (concat (propertize " " + 'face '(region (:height 0.2))) + (overlay-get ol 'after-string)))) + (push ol nrol)) + (forward-line 1)) + (mapc #'delete-overlay old) + `(rectangle ,(buffer-modified-tick) ,start ,end ,@nrol)))))) + +(defun rectangle--unhighlight-for-redisplay (orig rol) + (if (not (eq 'rectangle (car-safe rol))) + (funcall orig rol) + (mapc #'delete-overlay (nthcdr 4 rol)) + (setcar (cdr rol) nil))) + (provide 'rect) ;;; rect.el ends here diff --git a/lisp/simple.el b/lisp/simple.el index 1f1b5cb376..cd4df60e39 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -874,6 +874,18 @@ instead of deleted." :group 'killing :version "24.1") +(defvar region-extract-function + (lambda (delete) + (when (region-beginning) + (if (eq delete 'delete-only) + (delete-region (region-beginning) (region-end)) + (filter-buffer-substring (region-beginning) (region-end) delete)))) + "Function to get the region's content. +Called with one argument DELETE. +If DELETE is `delete-only', then only delete the region and the return value +is undefined. If DELETE is nil, just return the content as a string. +If anything else, delete the region and return its content as a string.") + (defun delete-backward-char (n &optional killflag) "Delete the previous N characters (following if N is negative). If Transient Mark mode is enabled, the mark is active, and N is 1, @@ -895,8 +907,8 @@ the end of the line." (= n 1)) ;; If a region is active, kill or delete it. (if (eq delete-active-region 'kill) - (kill-region (region-beginning) (region-end)) - (delete-region (region-beginning) (region-end)))) + (kill-region (region-beginning) (region-end) 'region) + (funcall region-extract-function 'delete-only))) ;; In Overwrite mode, maybe untabify while deleting ((null (or (null overwrite-mode) (<= n 0) @@ -927,8 +939,9 @@ KILLFLAG is set if N was explicitly specified." (= n 1)) ;; If a region is active, kill or delete it. (if (eq delete-active-region 'kill) - (kill-region (region-beginning) (region-end)) - (delete-region (region-beginning) (region-end)))) + (kill-region (region-beginning) (region-end) 'region) + (funcall region-extract-function 'delete-only))) + ;; Otherwise, do simple deletion. (t (delete-char n killflag)))) @@ -3417,7 +3430,7 @@ The comparison is done using `equal-including-properties'." :group 'killing :version "23.2") -(defun kill-new (string &optional replace yank-handler) +(defun kill-new (string &optional replace) "Make STRING the latest kill in the kill ring. Set `kill-ring-yank-pointer' to point to it. If `interprogram-cut-function' is non-nil, apply it to STRING. @@ -3432,13 +3445,6 @@ When the yank handler has a non-nil PARAM element, the original STRING argument is not used by `insert-for-yank'. However, since Lisp code may access and use elements from the kill ring directly, the STRING argument should still be a \"useful\" string for such uses." - (if (> (length string) 0) - (if yank-handler - (put-text-property 0 (length string) - 'yank-handler yank-handler string)) - (if yank-handler - (signal 'args-out-of-range - (list string "yank-handler specified for empty string")))) (unless (and kill-do-not-save-duplicates ;; Due to text properties such as 'yank-handler that ;; can alter the contents to yank, comparison using @@ -3466,19 +3472,15 @@ argument should still be a \"useful\" string for such uses." (setq kill-ring-yank-pointer kill-ring) (if interprogram-cut-function (funcall interprogram-cut-function string))) -(set-advertised-calling-convention - 'kill-new '(string &optional replace) "23.3") -(defun kill-append (string before-p &optional yank-handler) +(defun kill-append (string before-p) "Append STRING to the end of the latest kill in the kill ring. If BEFORE-P is non-nil, prepend STRING to the kill. If `interprogram-cut-function' is set, pass the resulting kill to it." (let* ((cur (car kill-ring))) (kill-new (if before-p (concat string cur) (concat cur string)) (or (= (length cur) 0) - (equal yank-handler (get-text-property 0 'yank-handler cur))) - yank-handler))) -(set-advertised-calling-convention 'kill-append '(string before-p) "23.3") + (equal nil (get-text-property 0 'yank-handler cur)))))) (defcustom yank-pop-change-selection nil "Whether rotating the kill ring changes the window system selection. @@ -3539,7 +3541,7 @@ move the yanking point; just return the Nth kill forward." :type 'boolean :group 'killing) -(defun kill-region (beg end &optional yank-handler) +(defun kill-region (beg end &optional region) "Kill (\"cut\") text between point and mark. This deletes the text from the buffer and saves it in the kill ring. The command \\[yank] can retrieve it from there. @@ -3559,19 +3561,24 @@ Supply two arguments, character positions indicating the stretch of text Any command that calls this function is a \"kill command\". If the previous command was also a kill command, the text killed this time appends to the text killed last time -to make one entry in the kill ring." +to make one entry in the kill ring. + +The optional argument REGION if non-nil, indicates that we're not just killing +some text between BEG and END, but we're killing the region." ;; Pass point first, then mark, because the order matters ;; when calling kill-append. - (interactive (list (point) (mark))) + (interactive (list (point) (mark) 'region)) (unless (and beg end) (error "The mark is not set now, so there is no region")) (condition-case nil - (let ((string (filter-buffer-substring beg end t))) + (let ((string (if region + (funcall region-extract-function 'delete) + (filter-buffer-substring beg end 'delete)))) (when string ;STRING is nil if BEG = END ;; Add that string to the kill ring, one way or another. (if (eq last-command 'kill-region) - (kill-append string (< end beg) yank-handler) - (kill-new string nil yank-handler))) + (kill-append string (< end beg)) + (kill-new string nil))) (when (or string (eq last-command 'kill-region)) (setq this-command 'kill-region)) (setq deactivate-mark t) @@ -3582,7 +3589,7 @@ to make one entry in the kill ring." ;; We should beep, in case the user just isn't aware of this. ;; However, there's no harm in putting ;; the region's text in the kill ring, anyway. - (copy-region-as-kill beg end) + (copy-region-as-kill beg end region) ;; Set this-command now, so it will be set even if we get an error. (setq this-command 'kill-region) ;; This should barf, if appropriate, and give us the correct error. @@ -3592,26 +3599,31 @@ to make one entry in the kill ring." (barf-if-buffer-read-only) ;; If the buffer isn't read-only, the text is. (signal 'text-read-only (list (current-buffer))))))) -(set-advertised-calling-convention 'kill-region '(beg end) "23.3") ;; copy-region-as-kill no longer sets this-command, because it's confusing ;; to get two copies of the text when the user accidentally types M-w and ;; then corrects it with the intended C-w. -(defun copy-region-as-kill (beg end) +(defun copy-region-as-kill (beg end &optional region) "Save the region as if killed, but don't kill it. In Transient Mark mode, deactivate the mark. If `interprogram-cut-function' is non-nil, also save the text for a window system cut and paste. +The optional argument REGION if non-nil, indicates that we're not just copying +some text between BEG and END, but we're copying the region. + This command's old key binding has been given to `kill-ring-save'." - (interactive "r") + (interactive "r\np") + (let ((str (if region + (funcall region-extract-function) + (filter-buffer-substring beg end)))) (if (eq last-command 'kill-region) - (kill-append (filter-buffer-substring beg end) (< end beg)) - (kill-new (filter-buffer-substring beg end))) + (kill-append str (< end beg)) + (kill-new str))) (setq deactivate-mark t) nil) -(defun kill-ring-save (beg end) +(defun kill-ring-save (beg end &optional region) "Save the region as if killed, but don't kill it. In Transient Mark mode, deactivate the mark. If `interprogram-cut-function' is non-nil, also save the text for a window @@ -3620,10 +3632,13 @@ system cut and paste. If you want to append the killed line to the last killed text, use \\[append-next-kill] before \\[kill-ring-save]. +The optional argument REGION if non-nil, indicates that we're not just copying +some text between BEG and END, but we're copying the region. + This command is similar to `copy-region-as-kill', except that it gives visual feedback indicating the extent of the region being copied." - (interactive "r") - (copy-region-as-kill beg end) + (interactive "r\np") + (copy-region-as-kill beg end region) ;; This use of called-interactively-p is correct because the code it ;; controls just gives the user visual feedback. (if (called-interactively-p 'interactive) @@ -4203,8 +4218,7 @@ run `deactivate-mark-hook'." (or (x-selection-owner-p 'PRIMARY) (null (x-selection-exists-p 'PRIMARY)))) (x-set-selection 'PRIMARY - (buffer-substring (region-beginning) - (region-end)))))) + (funcall region-extract-function nil))))) (if (and (null force) (or (eq transient-mark-mode 'lambda) (and (eq (car-safe transient-mark-mode) 'only) @@ -4289,9 +4303,60 @@ mode is enabled. Usually, such commands should use also checks the value of `use-empty-active-region'." (and transient-mark-mode mark-active)) -(defvar mark-ring nil + +(defvar redisplay-unhighlight-region-function + (lambda (rol) (when (overlayp rol) (delete-overlay rol)))) + +(defvar redisplay-highlight-region-function + (lambda (start end window rol) + (if (not (overlayp rol)) + (let ((nrol (make-overlay start end))) + (funcall redisplay-unhighlight-region-function rol) + (overlay-put nrol 'window window) + (overlay-put nrol 'face 'region) + nrol) + (unless (and (eq (overlay-buffer rol) (current-buffer)) + (eq (overlay-start rol) start) + (eq (overlay-end rol) end)) + (move-overlay rol start end (current-buffer))) + rol))) + +(defun redisplay--update-region-highlight (window) + (with-current-buffer (window-buffer window) + (let ((rol (window-parameter window 'internal-region-overlay))) + (if (not (region-active-p)) + (funcall redisplay-unhighlight-region-function rol) + (let* ((pt (window-point window)) + (mark (mark)) + (start (min pt mark)) + (end (max pt mark)) + (new + (funcall redisplay-highlight-region-function + start end window rol))) + (unless (equal new rol) + (set-window-parameter window 'internal-region-overlay + new))))))) + +(defun redisplay--update-region-highlights (windows) + (with-demoted-errors "redisplay--update-region-highlights: %S" + (if (null windows) + (redisplay--update-region-highlight (selected-window)) + (unless (listp windows) (setq windows (window-list-1 nil nil t))) + (if highlight-nonselected-windows + (mapc #'redisplay--update-region-highlight windows) + (let ((msw (and (window-minibuffer-p) (minibuffer-selected-window)))) + (dolist (w windows) + (if (or (eq w (selected-window)) (eq w msw)) + (redisplay--update-region-highlight w) + (funcall redisplay-unhighlight-region-function + (window-parameter w 'internal-region-overlay))))))))) + +(add-function :before pre-redisplay-function + #'redisplay--update-region-highlights) + + +(defvar-local mark-ring nil "The list of former marks of the current buffer, most recent first.") -(make-variable-buffer-local 'mark-ring) (put 'mark-ring 'permanent-local t) (defcustom mark-ring-max 16 @@ -4466,7 +4531,6 @@ mode temporarily." (temp-highlight (eq (car-safe transient-mark-mode) 'only))) (if (null omark) (error "No mark set in this buffer")) - (deactivate-mark) (set-mark (point)) (goto-char omark) (cond (temp-highlight diff --git a/lisp/subr.el b/lisp/subr.el index 05bbe6ea64..ae1db6652d 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -2818,6 +2818,7 @@ if it's an autoloaded macro." val)) ;;;; Support for yanking and text properties. +;; Why here in subr.el rather than in simple.el? --Stef (defvar yank-handled-properties) (defvar yank-excluded-properties) |