Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

287 lines
9.4 KiB

  1. ;;; loc-changes.el --- keep track of positions even after buffer changes
  2. ;; Copyright (C) 2015 Free Software Foundation, Inc
  3. ;; Author: Rocky Bernstein <rocky@gnu.org>
  4. ;; Version: 1.2
  5. ;; URL: http://github.com/rocky/emacs-loc-changes
  6. ;; Compatibility: GNU Emacs 24.x
  7. ;; This program is free software; you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; This program is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;; This package lets users or programs set marks in a buffer prior to
  19. ;; changes so that we can track the original positions after the
  20. ;; change.
  21. ;; One common use is say when debugging a program. The debugger has its static
  22. ;; notion of the file and positions inside that. However it may be convenient
  23. ;; for a programmer to edit the program but not restart execution of the program.
  24. ;; Another use might be in a compilation buffer for errors and
  25. ;; warnings which refer to file and line positions.
  26. ;;; Code:
  27. (make-variable-buffer-local 'loc-changes-alist)
  28. (defvar loc-changes-alist '()
  29. "A buffer-local association-list (alist) of line numbers and
  30. their corresponding markers in the buffer. The 'key' is the line number; the value
  31. the marker"
  32. )
  33. (defun loc-changes:follow-mark(event)
  34. (interactive "e")
  35. (let* ((pos (posn-point (event-end event)))
  36. (mark (get-text-property pos 'mark)))
  37. (switch-to-buffer-other-window (marker-buffer mark))
  38. (goto-char (marker-position mark))
  39. ))
  40. (defun loc-changes:alist-describe (&optional opt-buffer)
  41. "Display buffer-local variable loc-changes-alist. If BUFFER is
  42. not given, the current buffer is used. Information is put in an
  43. internal buffer called *Describe*."
  44. (interactive "")
  45. (let ((buffer (or opt-buffer (current-buffer)))
  46. (alist))
  47. (with-current-buffer buffer
  48. (setq alist loc-changes-alist)
  49. (unless (listp alist) (error "expecting loc-changes-alist to be a list"))
  50. )
  51. (switch-to-buffer (get-buffer-create "*Describe*"))
  52. (setq buffer-read-only 'nil)
  53. (delete-region (point-min) (point-max))
  54. (dolist (assoc alist)
  55. (put-text-property
  56. (insert-text-button
  57. (format "line %d: %s\n" (car assoc) (cdr assoc))
  58. 'action 'loc-changes:follow-mark
  59. 'help-echo "mouse-2: go to this location")
  60. (point)
  61. 'mark (cdr assoc)
  62. )
  63. )
  64. (setq buffer-read-only 't)
  65. ))
  66. ;;;###autoload
  67. (defun loc-changes-goto-line (line-number &optional column-number)
  68. "Position `point' at LINE-NUMBER of the current buffer. If
  69. COLUMN-NUMBER is given, position `point' at that column just
  70. before that column number within the line. Note that the beginning of
  71. the line starts at column 0, so the column number display will be one less
  72. than COLUMN-NUMBER. For example COLUMN-NUMBER 1 will set before the first
  73. column on the line and show 0.
  74. The Emacs `goto-line' docstring says it is the wrong to use that
  75. function in a Lisp program. So here is something that I proclaim
  76. is okay to use in a Lisp program."
  77. (interactive
  78. (if (and current-prefix-arg (not (consp current-prefix-arg)))
  79. (list (prefix-numeric-value current-prefix-arg))
  80. ;; Look for a default, a number in the buffer at point.
  81. (let* ((default
  82. (save-excursion
  83. (skip-chars-backward "0-9")
  84. (if (looking-at "[0-9]")
  85. (string-to-number
  86. (buffer-substring-no-properties
  87. (point)
  88. (progn (skip-chars-forward "0-9")
  89. (point)))))))
  90. ;; Decide if we're switching buffers.
  91. (buffer
  92. (if (consp current-prefix-arg)
  93. (other-buffer (current-buffer) t)))
  94. (buffer-prompt
  95. (if buffer
  96. (concat " in " (buffer-name buffer))
  97. "")))
  98. ;; Read the argument, offering that number (if any) as default.
  99. (list (read-number (format "Goto line%s: " buffer-prompt)
  100. (list default (line-number-at-pos)))
  101. buffer))))
  102. (unless (wholenump line-number)
  103. (error "Expecting line-number parameter `%s' to be a whole number"
  104. line-number))
  105. (unless (> line-number 0)
  106. (error "Expecting line-number parameter `%d' to be greater than 0"
  107. line-number))
  108. (let ((last-line (line-number-at-pos (point-max))))
  109. (unless (<= line-number last-line)
  110. (error
  111. "Line number %d should not exceed %d, the number of lines in the buffer"
  112. line-number last-line))
  113. (goto-char (point-min))
  114. (forward-line (1- line-number))
  115. (if column-number
  116. (let ((last-column
  117. (save-excursion
  118. (move-end-of-line 1)
  119. (current-column))))
  120. (cond ((not (wholenump column-number))
  121. (message
  122. "Column ignored. Expecting column-number parameter `%s' to be a whole number"
  123. column-number))
  124. ((<= column-number 0)
  125. (message
  126. "Column ignored. Expecting column-number parameter `%d' to be a greater than 1"
  127. column-number))
  128. ((>= column-number last-column)
  129. (message
  130. "Column ignored. Expecting column-number parameter `%d' to be a less than %d"
  131. column-number last-column))
  132. (t (forward-char (1- column-number)))))
  133. )
  134. (redisplay)
  135. )
  136. )
  137. (defun loc-changes-add-elt (pos)
  138. "Add an element `loc-changes-alist'. The car will be POS and a
  139. marker for it will be created at the point."
  140. (setq loc-changes-alist
  141. (cons (cons pos (point-marker)) loc-changes-alist)))
  142. ;;;###autoload
  143. (defun loc-changes-add-and-goto (line-number &optional opt-buffer)
  144. "Add a marker at LINE-NUMBER and record LINE-NUMBER and its
  145. marker association in `loc-changes-alist'."
  146. (interactive
  147. (if (and current-prefix-arg (not (consp current-prefix-arg)))
  148. (list (prefix-numeric-value current-prefix-arg))
  149. ;; Look for a default, a number in the buffer at point.
  150. (let* ((default
  151. (save-excursion
  152. (skip-chars-backward "0-9")
  153. (if (looking-at "[0-9]")
  154. (string-to-number
  155. (buffer-substring-no-properties
  156. (point)
  157. (progn (skip-chars-forward "0-9")
  158. (point)))))))
  159. ;; Decide if we're switching buffers.
  160. (buffer
  161. (if (consp current-prefix-arg)
  162. (other-buffer (current-buffer) t)))
  163. (buffer-prompt
  164. (if buffer
  165. (concat " in " (buffer-name buffer))
  166. "")))
  167. ;; Read the argument, offering that number (if any) as default.
  168. (list (read-number (format "Goto line%s: " buffer-prompt)
  169. (list default (line-number-at-pos)))
  170. buffer))))
  171. (let ((buffer (or opt-buffer (current-buffer))))
  172. (with-current-buffer buffer
  173. (loc-changes-goto-line line-number)
  174. (loc-changes-add-elt line-number)
  175. ))
  176. )
  177. ;;;###autoload
  178. (defun loc-changes-clear-buffer (&optional opt-buffer)
  179. "Remove all location-tracking associations in BUFFER."
  180. (interactive "bbuffer: ")
  181. (let ((buffer (or opt-buffer (current-buffer)))
  182. )
  183. (with-current-buffer buffer
  184. (setq loc-changes-alist '())
  185. ))
  186. )
  187. ;;;###autoload
  188. (defun loc-changes-reset-position (&optional opt-buffer no-insert)
  189. "Update `loc-changes-alist' so that the line number of point is
  190. used to when aline number is requested.
  191. Updates any existing line numbers referred to in marks at this
  192. position.
  193. This may be useful for example in debugging if you save the
  194. buffer and then cause the debugger to reread/reevaluate the file
  195. so that its positions are will be reflected."
  196. (interactive "")
  197. (let* ((line-number (line-number-at-pos (point)))
  198. (elt (assq line-number loc-changes-alist)))
  199. (let ((buffer (or opt-buffer (current-buffer)))
  200. )
  201. (with-current-buffer buffer
  202. (if elt
  203. (setcdr elt (point))
  204. (unless no-insert
  205. (loc-changes-add-elt line-number)
  206. )
  207. ))
  208. )
  209. ))
  210. (defun loc-changes-goto (position &optional opt-buffer no-update)
  211. "Go to the position inside BUFFER taking into account the
  212. previous location marks. Normally if the position hasn't been
  213. seen before, we will add a new mark for this position. However if
  214. NO-UPDATE is set, no mark is added."
  215. (unless (wholenump position)
  216. (error "Expecting line-number parameter `%s' to be a whole number"
  217. position))
  218. (let ((elt (assq position loc-changes-alist)))
  219. (if elt
  220. (let ((marker (cdr elt)))
  221. (unless (markerp marker)
  222. (error "Internal error: loc-changes-alist is not a marker"))
  223. (goto-char (marker-position marker)))
  224. (if no-update
  225. (loc-changes-goto-line position)
  226. (loc-changes-add-and-goto position))
  227. )
  228. )
  229. )
  230. ;;;; ChangeLog:
  231. ;; 2015-03-01 Stefan Monnier <monnier@iro.umontreal.ca>
  232. ;;
  233. ;; loc-changes/loc-changes.el: Fix footer
  234. ;;
  235. ;; 2015-02-27 rocky <rocky@gnu.org>
  236. ;;
  237. ;; Add 'packages/loc-changes/' from commit
  238. ;; 'efbe022eaeef0ccc54ffe219216974a786c3301c'
  239. ;;
  240. ;; git-subtree-dir: packages/loc-changes git-subtree-mainline:
  241. ;; 771168c071a65dde3d55c320381419917be3aaff git-subtree-split:
  242. ;; efbe022eaeef0ccc54ffe219216974a786c3301c
  243. ;;
  244. ;; 2015-02-15 rocky <rocky@gnu.org>
  245. ;;
  246. ;; Oops - should have added in branch externals/<pkg>
  247. ;;
  248. ;; 2015-02-15 rocky <rocky@gnu.org>
  249. ;;
  250. ;; Add 'packages/loc-changes/' from commit
  251. ;; '8447baff7cb4839ef8d1d747a14e5da85d0cee5b'
  252. ;;
  253. ;; git-subtree-dir: packages/loc-changes git-subtree-mainline:
  254. ;; ca75b19e7c0093093b43b49ef8f0d3e6ab0dc15d git-subtree-split:
  255. ;; 8447baff7cb4839ef8d1d747a14e5da85d0cee5b
  256. ;;
  257. (provide 'loc-changes)
  258. ;;; loc-changes.el ends here