X-Git-Url: https://code.delx.au/gnu-emacs-elpa/blobdiff_plain/ca75b19e7c0093093b43b49ef8f0d3e6ab0dc15d..af43c43b33726e9212f1050f37ab550f5a6abee3:/packages/loc-changes/loc-changes.el diff --git a/packages/loc-changes/loc-changes.el b/packages/loc-changes/loc-changes.el new file mode 100644 index 000000000..417f2fe78 --- /dev/null +++ b/packages/loc-changes/loc-changes.el @@ -0,0 +1,226 @@ +;;; loc-changes.el --- Helps users and programs keep track of positions even after buffer changes. + +;; Author: Rocky Bernstein +;; Version: 1.1 +;; URL: http://github.com/rocky/emacs-loc-changes +;; Compatibility: GNU Emacs 24.x + +;; Copyright (C) 2013-2014 Rocky Bernstein + +;; 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 3 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, see +;; . + +;;; Commentary: + +;; This package lets users or programs set marks in a buffer prior to +;; changes so that we can track the original positions after the +;; change. + +;; One common use is say when debugging a program. The debugger has its static +;; notion of the file and positions inside that. However it may be convenient +;; for a programmer to edit the program but not restart execution of the program. + +;; Another use might be in a compilation buffer for errors and +;; warnings which refer to file and line positions. + +;; Should be available via Melpa. + + +;;; Code: + +(make-variable-buffer-local 'loc-changes-alist) +(defvar loc-changes-alist '() + "A buffer-local association-list (alist) of line numbers and +their corresponding markers in the buffer. The 'key' is the line number; the value +the marker" + ) + +;;;###autoload +(defun loc-changes-goto-line (line-number &optional column-number) + "Position `point' at LINE-NUMBER of the current buffer. If +COLUMN-NUMBER is given, position `point' at that column just +before that column number within the line. Note that the beginning of +the line starts at column 0, so the column number display will be one less +than COLUMN-NUMBER. For example COLUMN-NUMBER 1 will set before the first +column on the line and show 0. + +The Emacs `goto-line' docstring says it is the wrong to use that +function in a Lisp program. So here is something that I proclaim +is okay to use in a Lisp program." + (interactive + (if (and current-prefix-arg (not (consp current-prefix-arg))) + (list (prefix-numeric-value current-prefix-arg)) + ;; Look for a default, a number in the buffer at point. + (let* ((default + (save-excursion + (skip-chars-backward "0-9") + (if (looking-at "[0-9]") + (string-to-number + (buffer-substring-no-properties + (point) + (progn (skip-chars-forward "0-9") + (point))))))) + ;; Decide if we're switching buffers. + (buffer + (if (consp current-prefix-arg) + (other-buffer (current-buffer) t))) + (buffer-prompt + (if buffer + (concat " in " (buffer-name buffer)) + ""))) + ;; Read the argument, offering that number (if any) as default. + (list (read-number (format "Goto line%s: " buffer-prompt) + (list default (line-number-at-pos))) + buffer)))) + (unless (wholenump line-number) + (error "Expecting line-number parameter `%s' to be a whole number" + line-number)) + (unless (> line-number 0) + (error "Expecting line-number parameter `%d' to be greater than 0" + line-number)) + (let ((last-line (line-number-at-pos (point-max)))) + (unless (<= line-number last-line) + (error + "Line number %d should not exceed %d, the number of lines in the buffer" + line-number last-line)) + (goto-char (point-min)) + (forward-line (1- line-number)) + (if column-number + (let ((last-column + (save-excursion + (move-end-of-line 1) + (current-column)))) + (cond ((not (wholenump column-number)) + (message + "Column ignored. Expecting column-number parameter `%s' to be a whole number" + column-number)) + ((<= column-number 0) + (message + "Column ignored. Expecting column-number parameter `%d' to be a greater than 1" + column-number)) + ((>= column-number last-column) + (message + "Column ignored. Expecting column-number parameter `%d' to be a less than %d" + column-number last-column)) + (t (forward-char (1- column-number))))) + ) + (redisplay) + ) + ) + +(defun loc-changes-add-elt (pos) + "Add an element `loc-changes-alist'. The car will be POS and a +marker for it will be created at the point." + (setq loc-changes-alist + (cons (cons pos (point-marker)) loc-changes-alist))) + +;;;###autoload +(defun loc-changes-add-and-goto (line-number &optional opt-buffer) + "Add a marker at LINE-NUMBER and record LINE-NUMBER and its +marker association in `loc-changes-alist'." + (interactive + (if (and current-prefix-arg (not (consp current-prefix-arg))) + (list (prefix-numeric-value current-prefix-arg)) + ;; Look for a default, a number in the buffer at point. + (let* ((default + (save-excursion + (skip-chars-backward "0-9") + (if (looking-at "[0-9]") + (string-to-number + (buffer-substring-no-properties + (point) + (progn (skip-chars-forward "0-9") + (point))))))) + ;; Decide if we're switching buffers. + (buffer + (if (consp current-prefix-arg) + (other-buffer (current-buffer) t))) + (buffer-prompt + (if buffer + (concat " in " (buffer-name buffer)) + ""))) + ;; Read the argument, offering that number (if any) as default. + (list (read-number (format "Goto line%s: " buffer-prompt) + (list default (line-number-at-pos))) + buffer)))) + + (let ((buffer (or opt-buffer (current-buffer)))) + (with-current-buffer buffer + (loc-changes-goto-line line-number) + (loc-changes-add-elt line-number) + )) + ) + +;;;###autoload +(defun loc-changes-clear-buffer (&optional opt-buffer) + "Remove all location-tracking associations in BUFFER." + (interactive "bbuffer: ") + (let ((buffer (or opt-buffer (current-buffer))) + ) + (with-current-buffer buffer + (setq loc-changes-alist '()) + )) + ) + +;;;###autoload +(defun loc-changes-reset-position (&optional opt-buffer no-insert) + "Update `loc-changes-alist' so that the line number of point is +used to when aline number is requested. + +Updates any existing line numbers referred to in marks at this +position. + +This may be useful for example in debugging if you save the +buffer and then cause the debugger to reread/reevaluate the file +so that its positions are will be reflected." + (interactive "") + (let* ((line-number (line-number-at-pos (point))) + (elt (assq line-number loc-changes-alist))) + (let ((buffer (or opt-buffer (current-buffer))) + ) + (with-current-buffer buffer + (if elt + (setcdr elt (point)) + (unless no-insert + (loc-changes-add-elt line-number) + ) + )) + ) + )) + + +(defun loc-changes-goto (position &optional opt-buffer no-update) + "Go to the position inside BUFFER taking into account the +previous location marks. Normally if the position hasn't been +seen before, we will add a new mark for this position. However if +NO-UPDATE is set, no mark is added." + (unless (wholenump position) + (error "Expecting line-number parameter `%s' to be a whole number" + position)) + (let ((elt (assq position loc-changes-alist))) + (if elt + (let ((marker (cdr elt))) + (unless (markerp marker) + (error "Internal error: loc-changes-alist is not a marker")) + (goto-char (marker-position marker))) + (if no-update + (loc-changes-goto-line position) + (loc-changes-add-and-goto position)) + ) + ) + ) + +(provide 'loc-changes) + +;;; loc-changes.el ends here