help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: can I move back to the last edit position?


From: Bob Babcock
Subject: Re: can I move back to the last edit position?
Date: Sun, 12 Sep 2004 21:46:24 GMT
User-agent: Xnews/06.07.17

rokia <Rokia@batbug.org> wrote in ufz5oaxes.fsf@batbug.org:">news:ufz5oaxes.fsf@batbug.org:

> hi, can you send the goto-chg.el to me?
> I cant find it. :) thanks!!

It's at
http://groups.google.com/groups?q=goto-chg.el&hl=en&lr=&ie=UTF-8&selm=wun0uyustv.fsf%40symsoft.se&rnum=1

I think it's short enough to post in a text group, so here it is.
I hope it doesn't get broken by any line wrapping:

;;; goto-chg.el --- goto last change
;;--------------------------------------------------------------------
;;
;; Copyright (C) 2002, David Andersson <david@symsoft.se>
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 2 of
;; the License, or (at your option) any later version.
;;
;; This program is distributed in the hope that it will be
;; useful, but WITHOUT ANY WARRANTY; without even the implied
;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
;; PURPOSE.  See the GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public
;; License along with this program; if not, write to the Free
;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
;; MA 02111-1307 USA
;;
;;-------------------------------------------------------------------
;;
;; Author: David Andersson <david@symsoft.se>
;; Created: 16 May 2002
;; Version: 1.0
;;
;;; Commentary:
;;
;; Goto Last Change
;;
;; Goto the point of the most recent edit in the buffer.
;; Goto the second next edit, etc, when repeated.
;; Works by looking into buffer-undo-list to find points of change.
;;
;; You would probably like to bind it to a key, for example:
;;   (global-set-key [kp-decimal] 'goto-last-change)
;;
;; Works with emacs-19.29, 19.31, 20.7 and XEmacs-20.4.
;;
;;--------------------------------------------------------------------
;;
;;todo: rename file to "gotochange.el" or  "goto-chgs" ?
;;todo: rename goto-last-change -> goto-last-edit ?
;;todo: rename adjective "-last-" -> "-latest-" or "-most-recent-" ?
;;todo: there are some new, maybe useful, funcs in simple.el in emacs 20,
;;  for region undo. take a look.
;;todo: add functionality to visit changed point in text order, not only
;;  in backward cronological order. (naa, highlight-changes-mode does that).
;;todo: invers indication that a change has been saved or not
;;todo: rename glc-wm-default to glc-wm or something
;;todo: merge glc-wm-default and glc-wm-current, 'let' can change it temporarly
;;todo: highlight the range of text involved in the last cange?
;;
;;--------------------------------------------------------------------

;;; Code:

(defvar glc-wm-default 8 "*goto-last-change don't visit the same point twice. 
glc-wm-default tells how far around a visited point not to visit again.")
(defvar glc-wm-current 8 "Internal for goto-last-change.\nA copy of 
glc-wm-default or the ARG passed to goto-last-change.")
(defvar glc-probe-depth 0 "Internal for goto-last-change.\nIt is non-zero 
between successive goto-last-change.")

;;todo: find begin and end of line, then use it somewhere
;(defun glc-get-surrounding (pos)
;  (cons (- pos glc-wm-current) (+ pos glc-wm-current)))

(defun glc-adjust-pos (pos e)
  "Given POS, a buffer position before the edit E, compute and return
the \"same\" buffer position after E happend.
Exception: return nil if POS is closer than `glc-wm-current' to the edit E.
\nInsertion edits before POS returns a larger value.
Deletion edits before POS returns a smaller value.
\nThe edit E is an entry from the `buffer-undo-list'. See for details."
  (cond ((atom e)   ; nil==cmd boundary, or, num==changed pos
  pos)
 ((numberp (car e))  ; (beg . end)==insertion
  (cond ((< pos (- (car e) glc-wm-current)) pos)
        ((> pos (+ (car e) glc-wm-current)) (+ pos (- (cdr e) (car e))))
        (t nil)))
 ((stringp (car e))  ; (string . pos)==deletion
  (cond ((< pos (- (abs (cdr e)) glc-wm-current)) pos)
        ((> pos (+ (abs (cdr e)) (length (car e)) glc-wm-current)) (- pos 
(length (car e))))
        (t nil)))
 ((null (car e))   ; (nil prop val beg . end)==prop change
  (cond ((< pos (- (nth 3 e) glc-wm-current)) pos)
        ((> pos (+ (nthcdr 4 e) glc-wm-current)) pos)
        (t nil)))
 (t    ; (marker . dist)==marker moved
  pos)))

;; If recursive in stead of while-loopive, it tends to fill the call stack.
;; Isn't it tail optimized?
(defun glc-adjust-list (r)
  "R is list of edit entries in chronological order.
Pick the point of the first edit entry and update that point with
the second, third, etc, edit entries. Return the final updated point,
or nil if the point was closer than `glc-wm-current' to some edit in R.
\nR is basically a reversed slice from the buffer-undo-list."
  (if r
      ;; Get pos
      (let ((pos (glc-get-pos (car r))))
 (setq r (cdr r))
 ;; Walk back in reverse list
 (while (and r pos)
   (setq pos (glc-adjust-pos pos (car r)))
   (setq r (cdr r)))
 pos)
    ;;else
    nil))

(defun glc-get-pos (e)
  "If E represents an edit, return a position value in E, the position
where the edit took place. Return nil if E repesents no real change.
\nE is a entry in the buffer-undo-list."
  (cond ((numberp e) e)   ; num==changed position
 ((atom e) nil)   ; nil==command boundary
 ((numberp (car e)) (cdr e)) ; (beg . end)==insertion
 ((stringp (car e)) (abs (cdr e))) ; (string . pos)==deletion
 ((null (car e)) (nthcdr 4 e)) ; (nil ...)==text property change
 ((atom (car e)) nil)  ; (t ...)==file modification time
 (t nil)))   ; (marker ...)==marker moved

(defun glc-is-positionable (e)
  (and (not (numberp e)) (glc-get-pos e)))

(defun glc-is-filetime (e)
  "Return t if E indicates a buffer became \"modified\",
that is, it was previously saved or unchanged. Nil otherwise."
  (and (listp e) (eq (car e) t)))

(defun goto-last-change (arg)
"Go to the point where the last edit was made in the current buffer.
Repeat the command to go to the second last edit, etc.
\nIt does not go to the same point twice even if there has been many edits
in the same place.
You can set variable `glc-wm-default' to control how close is \"the same 
point\".
Default is 8. If ARG is non-nil it is used instead.
\nThis function uses undo information. If undo is disabled, so is this."
  (interactive "P")
  (cond ((not (eq this-command last-command))
  (setq glc-probe-depth 0)
  (setq glc-wm-current glc-wm-default)))
  (cond ((null buffer-undo-list)
  (error "Buffer has not been changed"))
 ((eq buffer-undo-list 't)
  (error "No change info (undo is disabled)")))
  (if (numberp arg)
      (setq glc-wm-current arg))
  (let (rev 
 pos 
 (n 0)
 (l buffer-undo-list) 
 (passed-save-entry (not (buffer-modified-p))))
    ;; Walk back and forth in the buffer-undo-list, each time one step deeper,
    ;; until we can walk back the whole list with a 'pos' taht is not coming
    ;; close to another edit.
    (while (null pos)
      (setq glc-probe-depth (1+ glc-probe-depth))
      (if (> n 150)
   (message "working..."))
      ;; Walk forward in buffer-undo-list, glc-probe-depth steps.
      ;; Build reverse list along the way
      (while (< n glc-probe-depth)
 (cond ((null l)
        (setq glc-probe-depth 0)
        (error "No further change info"))
       ((glc-is-positionable (car l))
        (setq n (1+ n))
        (setq rev (cons (car l) rev)))
       ((or passed-save-entry (glc-is-filetime (car l)))
        (setq passed-save-entry t)))
 (setq l (cdr l)))
      ;; Walk back in reverse list, from older to newer edits.
      ;; Adjusting pos along the way.
      (setq pos (glc-adjust-list rev)))
    ;; Found a place not previously visited, in 'pos'.
    (if (> n 150)
 (message ""))   ; remove message "working..."
    (if passed-save-entry
 (message "(This change is saved)"))
    (goto-char pos)))

(provide 'goto-chg)

;;; goto-chg.el ends here

reply via email to

[Prev in Thread] Current Thread [Next in Thread]