home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / misc / context.el < prev    next >
Encoding:
Text File  |  1992-11-23  |  10.7 KB  |  283 lines

  1. ;;; File: context.el
  2. ;;; Date: Oct '92
  3. ;;;
  4. ;;; LCD Archive Entry:
  5. ;;; context|David Neves|neves@ils.nwu.edu|
  6. ;;; Save some context between editing sessions.|
  7. ;;; 92-11-16||~/misc/context.el.Z|
  8. ;;;
  9. ;;; Author: david neves, neves@ils.nwu.edu
  10. ;;;
  11. ;;; Documentation:
  12. ;;; Save some context between editing sessions.  Currently only
  13. ;;; the location of the point is saved (in the file ~/.emacs_context).
  14. ;;; Thereafter, whenever a file is read into a buffer you will find 
  15. ;;; yourself back at the point where you left off.
  16. ;;;
  17. ;;; The context is saved when the user types Meta-x save-context or
  18. ;;; when exiting Emacs with the save-context flag set to true.
  19. ;;;
  20. ;;; To use:
  21. ;;; Put the following 2 lines in your .emacs file
  22. ;;; (load "context.elc") ;i.e. after byte compiling this file with
  23. ;;;                      ;Meta-x byte-compile-file
  24. ;;; (read-context)       ;reads the context from the context file.
  25. ;;; The only user command is "meta-x cdired".  It runs DIRED on your
  26. ;;; list of context files.
  27. ;;;
  28. ;;; Changes:
  29. ;;; Oct '92    Cdired Flags to tcsh are now -fc rather than -f-c
  30. ;;; ===
  31. ;;; 1988
  32. ;;; Mon May  8 handle the tcsh shell. don't read in its .cshrc file.
  33. ;;; Fri Sep 23 quote file names for ls in cdired in case they have 
  34. ;;;            shell sensitive characters in them.
  35. ;;; Thu Sep 15 move RMAIL check to a check for rmail-mode.
  36. ;;;            context is saved even for files whose point is at
  37. ;;;            the beginning.
  38. ;;; Wed Sep 7  Sys V shells don't like ; to start lines.  Fixed.
  39. ;;;            Other misc changes made.
  40. ;;; Sun Sep 4  Installed cdired command to run DIRED on context files.
  41. ;;; Wed Nov 25 Don't save context of files/directories in context-ignore-files
  42. ;;; Tue Nov 24 reverting a buffer now turns off context
  43. ;;;
  44. ;;; Known bugs:
  45. ;;; -  if dired is run on the home directory then the cdired buffer is
  46. ;;;    overwritten.
  47. ;;; -  cdired is slow.
  48. ;;; -  In older versions of 18.x emacs...
  49. ;;;   If you read in a file by giving it as an argument to emacs,
  50. ;;;      e.g. emacs foo
  51. ;;;   you will find yourself at the beginning of foo, not at the context
  52. ;;;   point.  Solution: get a newer version of Emacs.
  53. ;;; -  If you are running multiple Emacs then the last one exited will
  54. ;;;   determine the final form of the context file.  I'm not sure
  55. ;;;   how to fix this.
  56.  
  57. (defconst context-file "~/.emacs_context" "*File for Emacs context")
  58.  
  59. (defvar context-alist nil "Association list holding some file context.
  60.   The structure is ( (file-name1 point) (file-name2 point) ...)")
  61.  
  62. (defvar context-max-size 50 ;why 50?  why not?
  63.   "*Maximum number of files that context is saved for.
  64. If not a number (e.g. nil) then the number of files is allowed to
  65. grow arbitrarily large.  This will result in slower performance because
  66. the context-alist is searched linearly.")
  67.  
  68. (defvar context-flag t
  69.   "*If non-nil the `save-context' command will always be run before Emacs is
  70. exited and context will be applied to files that are read in.  In other words,
  71. you can turn off all context processing by setting this flag to nil.")
  72.  
  73. (defvar context-ignore-files
  74.   (list "/tmp")  ;use "list" so one can evaluate expressions
  75.   "*List of files and directories to ignore for context processing")
  76.  
  77. ;;; change kill-emacs so that context will be saved out when you leave emacs.
  78. (if (not (fboundp 'original-kill-emacs))
  79.     (fset 'original-kill-emacs (symbol-function 'kill-emacs)))
  80.  
  81. ;;; Call get-context when a file is loaded into a buffer.
  82. ;;; Should only add get-context to file-file-hooks if it isn't there.
  83. ;;;  (just in case this file is loaded more than once.)
  84. (if (not (memq 'get-context find-file-hooks))
  85.     (setq find-file-hooks (cons 'get-context find-file-hooks)))
  86.  
  87. ;;; Turn off context processing when reverting a buffer so you stay
  88. ;;; at the current point rather than being sent to the context point.
  89. (if (null revert-buffer-function)
  90.     (setq revert-buffer-function 
  91.       (function (lambda (&optional arg noconfirm) 
  92.               (let ((context-flag nil)
  93.                 (revert-buffer-function nil))
  94.             (revert-buffer arg noconfirm))))))
  95.  
  96. (defun read-context ()
  97.    "Read in an Emacs context.  Usually done when Emacs is initially called.
  98.     This function should be called in .emacs ."
  99.    (interactive)
  100.       (if (not (file-exists-p context-file)) (setq context-alist nil)
  101.     (load context-file t t t)))
  102.  
  103. (defmacro second (l)  (list 'car (list 'cdr l)))
  104. (defmacro context-get-point (l)  (list 'second l))
  105.  
  106. ;;; Apply the context that is saved for the current file.
  107. ;;; Called in find-file-hooks (i.e. when a file is loaded).
  108. ;;; Doesn't apply context if context-flag is nil.
  109. (defun get-context nil
  110.   (if context-flag
  111.       (let* ((buf (current-buffer))
  112.          (file-name (buffer-file-name buf))
  113.          file-data)
  114.     (if (null file-name) nil
  115.       (setq file-data (assoc file-name context-alist))
  116.       (if (null file-data) nil
  117.         (goto-char (context-get-point file-data)))))))
  118.  
  119. (defun save-context ()
  120.   "Save context (currently, the point) of all Emacs buffers.
  121. The context information goes into a file whose name is stored 
  122. in the variable 'context-file')."
  123.   (interactive)
  124.   (save-excursion
  125.     (mapcar (function read-buffer-context) (buffer-list))
  126.     (let ((buf (get-buffer-create "*context*"))
  127.       nth-part)
  128.       (cond ((numberp context-max-size)
  129.          (setq nth-part (nthcdr (1- context-max-size) context-alist))
  130.          (if nth-part (rplacd nth-part nil))));reduce size of context-alist
  131.       (set-buffer buf)
  132.       (erase-buffer)
  133.       (insert "(setq context-alist '(")
  134.       (mapcar (function (lambda (l) 
  135.               ;; print function in 18.4x outputs 2 newlines
  136.               ;; so use terpri and prin1 instead
  137.               (terpri buf)
  138.               (prin1 l buf))) context-alist)
  139.       (insert "))")
  140.       (if (file-exists-p context-file) (delete-file context-file))
  141.       (write-region 1 (point-max) context-file nil 'nomessage)
  142.       (kill-buffer buf))))
  143.  
  144. ;;; place buffer context in the list "context-alist".
  145. ;;; If it already exists in that list then also move that
  146. ;;; information to the front of the alist.
  147. (defun read-buffer-context (buf)
  148.   (let ((file-name (buffer-file-name buf))
  149.     buffer-data
  150.     assoc-result
  151.     before
  152.     point-loc
  153.     file-data)
  154.     (set-buffer buf)
  155.     (setq pointloc (point))
  156.     (setq buffer-data (list pointloc)) ;only save the point
  157.     (if (or (null file-name) 
  158.         ;; rmail assumes point is at position 1 when RMAIL
  159.         ;; file is read in.
  160.         (eq major-mode 'rmail-mode)  ;thanks Graham
  161.         (context-ignore-file file-name)) nil
  162.       (setq assoc-result (context-assoc file-name context-alist))
  163.       (setq file-data (car assoc-result))
  164.       (if (null file-data) (setq context-alist 
  165.                  (cons (cons file-name buffer-data) 
  166.                        context-alist))
  167.     (rplacd file-data buffer-data) ;associate new context with file name
  168.     ;; move (file data) to front of alist.
  169.     ;; The first n entries are deleted when emacs is finished.
  170.     (setq before (second assoc-result))
  171.     (if (null before) nil                  ;already at front
  172.       (rplacd before (cdr (cdr before)))   ;else splice it out
  173.       (setq context-alist (cons file-data context-alist)))))))
  174.  
  175.  
  176. (defun kill-emacs (&optional query)
  177.   "End this Emacs session.
  178. Prefix ARG or optional first ARG non-nil means exit with no questions asked,
  179. even if there are unsaved buffers.  If Emacs is running non-interactively
  180. and ARG is an integer, then Emacs exits with ARG as its exit code.
  181.  
  182. If the variable `context-flag' is non-nil,
  183. the function save-context will be called first."
  184.   (interactive "P")
  185.   (if context-flag (save-context))
  186.   (original-kill-emacs query))
  187.  
  188. ;;; returns true if no context should be saved out for filename
  189. (defun context-ignore-file (filename)
  190.   (let ((ignore-list context-ignore-files)
  191.         (answer nil))
  192.    (while (and ignore-list (null answer))
  193.      (if (context-match (car ignore-list) filename) (setq answer t)
  194.        (setq ignore-list (cdr ignore-list))))
  195.    answer))
  196.  
  197.  
  198. ;;; version of assoc that returns 2 values (in a list)
  199. ;;; (pair found, position before it)
  200. ;;; e.g. (context-assoc 'foo '((a b) (c d) (foo bar) (e f)))
  201. ;;;      ((foo bar) ((c d) (foo bar) (e f)))
  202. ;;; We are also returning the position before it
  203. ;;;  so that we can splice it out of the list with rplacd.
  204. ;;; if car of result is nil then failure - we failed to find the item.
  205. ;;; if cadr of result is nil then the item is at the front of the list.
  206. (defun context-assoc (key alist)
  207.   (let ((before nil) (current alist))
  208.     (if (equal key (car (car current))) nil
  209.       (setq current (cdr current))
  210.       (while (and current (not (equal key (car (car current)))))
  211.     (setq before current)
  212.     (setq current (cdr current))))
  213.     (list (car current) before)))
  214.  
  215. ;;; is str1 at the front of str2?
  216. (defun context-match (str1 str2)
  217.   (let ((result (string-match str1 str2)))
  218.     (and (numberp result) (zerop result))))
  219.  
  220. (defun context-restore (arg)
  221.   (interactive "x Type # of files to load or directory name: ")
  222.   (let ((calist context-alist)
  223.     filename)
  224.     (if (numberp arg)
  225.     (progn (let ((temp arg))
  226.          (while (not (zerop temp))
  227.            (setq filename (car (car calist)))
  228.            (and (stringp filename) (file-exists-p filename)
  229.             (find-file filename))
  230.            (setq calist (cdr calist))
  231.            (setq temp (1- temp)))))
  232.       (setq arg (expand-file-name arg))
  233.       (while calist
  234.     (setq filename (car (car calist)))
  235.     (and (string-match (prin1-to-string arg) filename)
  236.          (file-exists-p filename)
  237.          (message filename) (find-file filename))
  238.     (setq calist (cdr calist))))))
  239.     
  240.  
  241. (autoload 'dired-mode "dired")
  242. (defun cdired nil 
  243.   "Apply DIRED to files for which some state was saved.
  244. The first time cdired is called multiple calls to ls are made
  245. and the cdired buffer is created.  Subsequent calls to cdired
  246. return the user to that cdired buffer."
  247.   (interactive)
  248.   (let* ((dirname " *context-dired*")
  249.      (homedirectory (expand-file-name "~/"))
  250.      (homelength (length homedirectory)) 
  251.      (buffer (get-buffer-create dirname))
  252.      (lsstring "")
  253.      (lsswitches (concat "ls " dired-listing-switches " "))
  254.      (shell-name (file-name-nondirectory shell-file-name))
  255.      (nocshrc "")
  256.      filename) 
  257.     (switch-to-buffer buffer)
  258.     ;;; don't load .cshrc file if using /bin/csh
  259.     (if (or (string=  shell-name "csh")
  260.         (string=  shell-name "tcsh"))
  261.     (setq nocshrc "f"))
  262.     (if (not (= (point-min) (point-max))) nil  ;is buffer empty?
  263.       (setq default-directory homedirectory) 
  264.       (mapcar (function (lambda (filepair)
  265.                 (setq filename (car filepair))    
  266.         (if (file-exists-p filename)
  267.               (setq lsstring (concat lsswitches "'"
  268.                      (context-strip-homedir filename) "';"
  269.                        lsstring)))))
  270.           (reverse context-alist))
  271.       (message "Running ls on context files ...")
  272.       (call-process shell-file-name nil buffer nil
  273.             (concat "-" nocshrc "c") lsstring)
  274.       (message "")
  275.       (goto-char (point-min))
  276.       (dired-mode homedirectory))))
  277.  
  278. ;;; strip off home directory from start of filenames.
  279. (defun context-strip-homedir (filename)
  280.   (if (context-match homedirectory filename) (substring filename homelength)
  281.     filename))
  282.  
  283.