Mercurial > hg > xemacs-beta
diff lisp/hyperbole/hypb.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 4103f0995bd7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/hyperbole/hypb.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,559 @@ +;;!emacs +;; +;; FILE: hypb.el +;; SUMMARY: Miscellaneous Hyperbole support features. +;; USAGE: GNU Emacs Lisp Library +;; KEYWORDS: extensions, hypermedia +;; +;; AUTHOR: Bob Weiner +;; ORG: Brown U. +;; +;; ORIG-DATE: 6-Oct-91 at 03:42:38 +;; LAST-MOD: 30-Oct-95 at 21:23:19 by Bob Weiner +;; +;; This file is part of Hyperbole. +;; Available for use and distribution under the same terms as GNU Emacs. +;; +;; Copyright (C) 1991-1995, Free Software Foundation, Inc. +;; Developed with support from Motorola Inc. +;; +;; DESCRIPTION: +;; DESCRIP-END. + +;;; ************************************************************************ +;;; Other required Elisp libraries +;;; ************************************************************************ + +(mapcar 'require '(hversion hact)) + +;;; ************************************************************************ +;;; Public variables +;;; ************************************************************************ + +(defconst hypb:help-buf-suffix " Hypb Help*" + "Suffix attached to all native Hyperbole help buffer names.") + +;;; ************************************************************************ +;;; Public functions +;;; ************************************************************************ + +(defun hypb:call-process-p (program infile &optional predicate &rest args) + "Calls an external PROGRAM with INFILE for input. +If PREDICATE is given, it is evaluated in a buffer with the PROGRAM's +output and the result returned. If PREDICATE is nil, returns t iff +program has no output or just a 0-valued output. +Rest of ARGS are passed as arguments to PROGRAM." + (let ((buf (get-buffer-create "*test-output*")) + (found)) + (save-excursion + (set-buffer buf) (setq buffer-read-only nil) (erase-buffer) + (apply 'call-process program infile buf nil args) + (setq found + (if predicate + (eval predicate) + (or (= (point-max) 1) ;; No output, consider cmd a success. + (and (< (point-max) 4) + (string= (buffer-substring 1 2) "0"))))) + (set-buffer-modified-p nil) + (kill-buffer buf)) + found)) + + +(defun hypb:chmod (op octal-permissions file) + "Uses OP and OCTAL-PERMISSIONS integer to set FILE permissions. +OP may be +, -, xor, or default =." + (let ((func (cond ((eq op '+) (function logior)) + ((eq op '-) (function + (lambda (p1 p2) (logand (lognot p1) p2)))) + ((eq op 'xor) (function logxor)) + (t (function (lambda (p1 p2) p1)))))) + (set-file-modes file (funcall func (hypb:oct-to-int octal-permissions) + (file-modes file))))) + +(defun hypb:cmd-key-string (cmd-sym &optional keymap) + "Returns a single pretty printed key sequence string bound to CMD-SYM. +Global keymap is used unless optional KEYMAP is given." + (if (and cmd-sym (symbolp cmd-sym) (fboundp cmd-sym)) + (let* ((get-keys (function + (lambda (cmd-sym keymap) + (key-description (where-is-internal + cmd-sym keymap 'first))))) + (keys (funcall get-keys cmd-sym keymap))) + (concat "{" + (if (string= keys "") + (concat (funcall get-keys 'execute-extended-command nil) + " " (symbol-name cmd-sym) " RTN") + keys) + "}")) + (error "(hypb:cmd-key-string): Invalid cmd-sym arg: %s." cmd-sym))) + +;;;###autoload +(defun hypb:configuration (&optional out-buf) + "Insert Emacs configuration information at the end of optional OUT-BUF or the current buffer." + (save-excursion + (and out-buf (set-buffer out-buf)) + (goto-char (point-max)) + (delete-blank-lines) (delete-blank-lines) + (let ((start (point))) + (insert (format "I use:\tEditor: %s\n\tHyperbole: %s\n" + (if (boundp 'epoch::version) + epoch::version + (hypb:replace-match-string + " of .+" (emacs-version) "" t)) + hyperb:version)) + (if (and (boundp 'system-configuration) (stringp system-configuration)) + (insert (format "\tSys Type: %s\n" system-configuration))) + (insert (format "\tOS Type: %s\n\tWindow Sys: %s\n" + system-type (or window-system hyperb:window-system + "None"))) + (if (and (boundp 'hmail:reader) hmail:reader) + (insert (format "\tMailer: %s\n" + (cond ((eq hmail:reader 'rmail-mode) "RMAIL") + ((eq hmail:reader 'vm-mode) + (concat "VM " vm-version)) + ((and (eq hmail:reader 'mh-show-mode) + (string-match "v ?\\([0-9]+.[0-9]+\\)" + mh-e-RCS-id)) + (concat "MH-e " + (substring mh-e-RCS-id + (match-beginning 1) + (match-end 1)))) + ((eq hmail:reader 'pm-fdr-mode) + (concat "PIEmail " pm-version)) + )))) + (if (and (boundp 'hnews:reader) (boundp 'gnus-version) hnews:reader) + (insert (format "\tNews Rdr: %s\n" gnus-version))) + (if (and (boundp 'br-version) (stringp br-version)) + (insert (format "\tOO-Browser: %s\n" br-version))) + (untabify start (point))))) + +(if (fboundp 'copy-tree) + (fset 'hypb:copy-sublists 'copy-tree) + ;; + ;; This function is derived from a copylefted function. + ;; Define hypb:copy-sublists if not a builtin. This version + ;; is a Lisp translation of the C version in Lemacs 19.8. + ;; Copyright (C) 1985, 1986, 1987, 1992, 1993 Free Software Foundation, Inc. + ;; Available for use and distribution under the GPL. + ;; + (defun hypb:copy-sublists (obj &optional vector-p) + "Return a copy of a list and substructures. +The argument is copied, and any lists contained within it are copied +recursively. Circularities and shared substructures are not preserved. +Second arg VECP causes vectors to be copied, too. Strings are not copied." + (cond ((consp obj) + (let (rest) + (setq obj (copy-sequence obj) + rest obj) + (while (consp rest) + (let ((elt (car rest))) + (if quit-flag (top-level)) + (if (or (consp elt) (vectorp elt)) + (setcar rest (hypb:copy-sublists elt vector-p))) + (if (vectorp (cdr rest)) + (setcdr rest (hypb:copy-sublists (cdr rest) vector-p))) + (setq rest (cdr rest)))))) + ((and (vectorp obj) obj) + (let ((i (length obj)) + (j 0) + elt) + (setq obj (copy-sequence obj)) + (while (< j i) + (setq elt (aref obj j)) + (if quit-flag (top-level)) + (if (or (consp elt) (vectorp elt)) + (aset obj j (hypb:copy-sublists elt vector-p))) + (setq j (1+ j)))))) + obj)) + +(defun hypb:debug () + "Loads Hyperbole hbut.el source file and sets debugging traceback flag." + (interactive) + (or (featurep 'hinit) (load "hsite")) + (or (and (featurep 'hbut) + (let ((func (hypb:indirect-function 'ebut:create))) + (not (or (hypb:v19-byte-code-p func) + (eq 'byte-code + (car (car (nthcdr 3 (hypb:indirect-function + 'ebut:create))))))))) + (load "hbut.el")) + (setq debug-on-error t)) + +(defun hypb:domain-name () + "Returns current Internet domain name with '@' prepended or nil if none." + (let* ((dname-cmd (or (file-exists-p "/usr/bin/domainname") + (file-exists-p "/bin/domainname"))) + (dname (or (getenv "DOMAINNAME") + (if dname-cmd + (hypb:call-process-p + "domainname" nil + '(substring (buffer-string) 0 -1)))))) + (if (or (and dname (string-match "\\." dname)) + (let* ((src "/etc/resolv.conf") + (src-buf-exists-p (get-file-buffer src))) + (and (file-exists-p src) (file-readable-p src) + (save-excursion + (set-buffer (find-file-noselect src)) + (goto-char (point-min)) + (if (re-search-forward "^domain[ \t]+\\([^ \t\n]+\\)" + nil t) + (setq dname (buffer-substring (match-beginning 1) + (match-end 1)))) + (or src-buf-exists-p (kill-buffer nil)) + dname)))) + (concat "@" dname)))) + +(defun hypb:error (&rest args) + "Signals an error typically to be caught by 'hui:menu'." + (let ((msg (apply 'format args))) + (put 'error 'error-message msg) + (error msg))) + +(defun hypb:functionp (obj) +"Returns t if OBJ is a function, nil otherwise." + (cond + ((symbolp obj) (fboundp obj)) + ((subrp obj)) + ((hypb:v19-byte-code-p obj)) + ((consp obj) + (if (eq (car obj) 'lambda) (listp (car (cdr obj))))) + (t nil))) + +(defun hypb:function-copy (func-symbol) + "Copies FUNC-SYMBOL's body for overloading. Returns copy of body." + (if (fboundp func-symbol) + (let ((func (hypb:indirect-function func-symbol))) + (cond ((listp func) (copy-sequence func)) + ((subrp func) (error "(hypb:function-copy): `%s' is a primitive; can't copy body." + func-symbol)) + ((and (hypb:v19-byte-code-p func) (fboundp 'make-byte-code)) + (let ((new-code (append func nil))) ; turn it into a list + (apply 'make-byte-code new-code))) + (t (error "(hypb:function-copy): Can't copy function body: %s" func)) + )) + (error "(hypb:function-copy): `%s' symbol is not bound to a function." + func-symbol))) + +(defun hypb:function-overload (func-sym prepend &rest new-forms) + "Redefine function named FUNC-SYM by either PREPENDing (or appending if nil) rest of quoted NEW-FORMS." + (let ((old-func-sym (intern + (concat "*hypb-old-" + (symbol-name func-sym) + "*")))) + (or (fboundp old-func-sym) + (fset old-func-sym (hypb:function-copy func-sym))) + (let* ((old-func (hypb:indirect-function old-func-sym)) + (old-param-list (action:params old-func)) + (param-list (action:param-list old-func)) + (old-func-call + (list (if (memq '&rest old-param-list) + ;; Have to account for extra list wrapper from &rest. + (cons 'apply + (cons (list 'quote old-func-sym) param-list)) + (cons old-func-sym param-list))))) + (eval (append + (list 'defun func-sym old-param-list) + (delq nil + (list + (documentation old-func-sym) + (action:commandp old-func-sym))) + (if prepend + (append new-forms old-func-call) + (append old-func-call new-forms))))))) + +(defun hypb:function-symbol-replace (func-sym sym-to-replace replace-with-sym) + "Replaces in body of FUNC-SYM SYM-TO-REPLACE with REPLACE-WITH-SYM. +All occurrences within lists are replaced. Returns body of modified FUNC-SYM." + (let ((body (hypb:indirect-function func-sym)) + (arg-vector) (arg)) + (if (listp body) + ;; assume V18 byte compiler + (setq arg-vector + (car (delq nil (mapcar + (function + (lambda (elt) + (and (listp elt) + (vectorp (setq arg-vector (nth 2 elt))) + arg-vector))) + body)))) + ;; assume V19 byte compiler (eq (compiled-function-p body) t) + (setq arg (aref body 2) + arg-vector (if (vectorp arg) arg)) + ) + (if arg-vector + ;; Code is byte-compiled. + (let ((i (1- (length arg-vector)))) + (setq arg nil) + (while (and (not arg) (>= i 0)) + (if (eq (setq arg (aref arg-vector i)) sym-to-replace) + (aset arg-vector i replace-with-sym) + (setq arg nil i (1- i))))) + ;; Code is not byte-compiled. + ;; Only replaces occurrence of symbol as an element of a list. + (hypb:map-sublists + (function + (lambda (atom list) (if (eq atom sym-to-replace) + (let ((again t)) + (while (and again list) + (if (eq (car list) atom) + (progn (setcar list replace-with-sym) + (setq again nil)) + (setq list (cdr list)))))))) + body) + ) + body)) + +(defun hypb:help-buf-name (&optional prefix) + "Returns a Hyperbole help buffer name for current buffer. +With optional PREFIX string, uses it rather than buffer name." + (let ((bn (or prefix (buffer-name)))) + (if (string-match " Hypb " bn) + (buffer-name (generate-new-buffer bn)) + (concat "*" bn hypb:help-buf-suffix)))) + +(defun hypb:indirect-function (obj) + "Return the function at the end of OBJ's function chain. +Resolves autoloadable function symbols properly." + (let ((func + (if (fboundp 'indirect-function) + (indirect-function obj) + (while (symbolp obj) + (setq obj (symbol-function obj))) + obj))) + ;; Handle functions with autoload bodies. + (if (and (symbolp obj) (listp func) (eq (car func) 'autoload)) + (let ((load-file (car (cdr func)))) + (load load-file) + ;; Prevent infinite recursion + (if (equal func (symbol-function obj)) + (error "(hypb:indirect-function): Autoload of '%s' failed" obj) + (hypb:indirect-function obj))) + func))) + +(defun hypb:insert-region (buffer start end invisible-flag) + "Insert into BUFFER the contents of a region from START to END in the current buffer. +INVISIBLE-FLAG, if non-nil, means invisible text in an outline region is +copied, otherwise, it is omitted." + (let ((from-koutline (eq major-mode 'kotl-mode))) + (append-to-buffer buffer start end) + (save-excursion + (set-buffer buffer) + (let ((first (- (point) (- end start))) + (last (point))) + ;; Remove from buffer any copied text that was hidden if invisible-flag + ;; is nil. + (if invisible-flag + ;; Show all hidden text within the copy. + (subst-char-in-region first last ?\r ?\n t) + ;; Remove hidden text. + (goto-char first) + (while (search-forward "\r" last t) + (delete-region (1- (point)) (progn (end-of-line) (point))))) + ;; + ;; If region came from a koutline, remove any characters with an + ;; invisible property which separate cells. + (if from-koutline + (kproperty:map + (function (lambda (prop) (delete-char 1))) 'invisible t)))))) + +(if (or hyperb:lemacs-p hyperb:emacs19-p) + (fset 'hypb:mark 'mark) + (defun hypb:mark (inactive-p) + "Return this buffer's mark value as integer, or nil if no mark. +INACTIVE-P non-nil means return value of mark even if region is not active +under Emacs version 19. +If you are using this in an editing command, you are most likely making +a mistake; see the documentation of `set-mark'." + (mark)) + ) +(if hyperb:lemacs-p + (fset 'hypb:mark-marker 'mark-marker) + (defun hypb:mark-marker (inactive-p) + "Return this buffer's mark as a marker object, or nil if no mark. +INACTIVE-P is unused, it is for compatibility with Lucid Emacs' +version of mark-marker." + (mark-marker)) + ) + +(defun hypb:map-sublists (func list) + "Applies FUNC to every atom found at any level of LIST. +FUNC must take two arguments, an atom and a list in which the atom is found. +Returns values from applications of FUNC as a list with the same +structure as LIST. FUNC is therefore normally used just for its side-effects." + (mapcar (function + (lambda (elt) (if (atom elt) + (funcall func elt list) + (hypb:map-sublists func elt))) + list))) + +(defun hypb:map-vector (func object) + "Returns list of results of application of FUNC to each element of OBJECT. +OBJECT should be a vector or byte-code object." + (if (not (or (vectorp object) (hypb:v19-byte-code-p object))) + (error "(hypb:map-vector): Second argument must be a vector or byte-code object.")) + (let ((end (length object)) + (i 0) + (result)) + (while (< i end) + (setq result (cons (funcall func (aref object i)) result) + i (1+ i))) + (nreverse result))) + +(defun hypb:mouse-help-file () + "Return the full path to the Hyperbole mouse key help file." + (let* ((hypb-man (expand-file-name "man/" hyperb:dir)) + (help-file (expand-file-name "hypb-mouse.txt" hypb-man))) + (if (or (file-exists-p help-file) + (file-exists-p + (setq help-file (expand-file-name + "hypb-mouse.txt" data-directory)))) + help-file + (error "(hypb:mouse-help-file): Non-existent file: \"%s\"" help-file)))) + +(if (or hyperb:lemacs-p hyperb:emacs19-p) + (fset 'hypb:push-mark 'push-mark) + (defun hypb:push-mark (&optional location nomsg activate-region) + "Set mark at LOCATION (point, by default) and push old mark on mark ring. +If the last global mark pushed was not in the current buffer, +also push LOCATION on the global mark ring. +Display `Mark set' unless the optional second arg NOMSG is non-nil. +Optional third arg ACTIVATE-REGION is ignored. + +Novice Emacs Lisp programmers often try to use the mark for the wrong +purposes. See the documentation of `set-mark' for more information." + (push-mark location nomsg)) + ) + +(defun hypb:replace-match-string (regexp str newtext &optional literal) + "Replaces all matches for REGEXP in STR with NEWTEXT string. +Optional LITERAL non-nil means do a literal replacement. +Otherwise treat \\ in NEWTEXT string as special: + \\& means substitute original matched text, + \\N means substitute match for \(...\) number N, + \\\\ means insert one \\. +NEWTEXT may instead be a function of one argument, the string to replace in, +that returns a replacement string." + (if (not (stringp str)) + (error "(hypb:replace-match-string): 2nd arg must be a string: %s" str)) + (if (or (stringp newtext) (hypb:functionp newtext)) + nil + (error "(hypb:replace-match-string): 3rd arg must be a string or function: %s" + newtext)) + (let ((rtn-str "") + (start 0) + (special) + match prev-start) + (while (setq match (string-match regexp str start)) + (setq prev-start start + start (match-end 0) + rtn-str + (concat + rtn-str + (substring str prev-start match) + (cond ((hypb:functionp newtext) (funcall newtext str)) + (literal newtext) + (t (mapconcat + (function + (lambda (c) + (if special + (progn + (setq special nil) + (cond ((eq c ?\\) "\\") + ((eq c ?&) + (substring str + (match-beginning 0) + (match-end 0))) + ((and (>= c ?0) (<= c ?9)) + (if (> c (+ ?0 (length + (match-data)))) + ;; Invalid match num + (error "(hypb:replace-match-string) Invalid match num: %c" c) + (setq c (- c ?0)) + (substring str + (match-beginning c) + (match-end c)))) + (t (char-to-string c)))) + (if (eq c ?\\) (progn (setq special t) nil) + (char-to-string c))))) + newtext "")))))) + (concat rtn-str (substring str start)))) + +(defun hypb:supercite-p () + "Returns non-nil iff the Emacs add-on supercite package is in use." + (let (hook-val) + (if (memq t (mapcar + (function + (lambda (hook-var) + (and (boundp hook-var) + (progn (setq hook-val (symbol-value hook-var)) + (cond ((listp hook-val) + (if (memq 'sc-cite-original hook-val) + t)) + ((eq hook-val 'sc-cite-original))))))) + '(mail-citation-hook mail-yank-hooks))) + t))) + +;;; Next function is copied from a copylefted function: +;;; Copyright (C) 1987, 1988 Kyle E. Jones +(if (or hyperb:lemacs-p hyperb:emacs19-p) + (defun hypb:window-list-all-frames (&optional mini) + "Returns a list of Lisp window objects for all Emacs windows in all frames. +Optional first arg MINI t means include the minibuffer window +in the list, even if it is not active. If MINI is neither t +nor nil it means to not count the minibuffer window even if it is active." + (let* ((first-window (next-window + (previous-window (selected-window) nil t t) + mini t t)) + (windows (cons first-window nil)) + (current-cons windows) + (w (next-window first-window mini t t))) + (while (not (eq w first-window)) + (setq current-cons (setcdr current-cons (cons w nil))) + (setq w (next-window w mini t t))) + windows))) + +;;; Next function is copied from a copylefted function: +;;; Copyright (C) 1987, 1988 Kyle E. Jones +(defun hypb:window-list (&optional mini) + "Returns a list of Lisp window objects for all Emacs windows in selected frame. +Optional first arg MINI t means include the minibuffer window +in the list, even if it is not active. If MINI is neither t +nor nil it means to not count the minibuffer window even if it is active." + (let* ((first-window (next-window + (previous-window (selected-window)) mini)) + (windows (cons first-window nil)) + (current-cons windows) + (w (next-window first-window mini))) + (while (not (eq w first-window)) + (setq current-cons (setcdr current-cons (cons w nil))) + (setq w (next-window w mini))) + windows)) + +(defun hypb:v19-byte-code-p (obj) + "Return non-nil iff OBJ is an Emacs V19 byte compiled object." + (or (and (fboundp 'compiled-function-p) (compiled-function-p obj)) + (and (fboundp 'byte-code-function-p) (byte-code-function-p obj)))) + +;;; ************************************************************************ +;;; Private functions +;;; ************************************************************************ + +(defun hypb:oct-to-int (oct-num) + "Returns octal integer OCTAL-NUM converted to a decimal integer." + (let ((oct-str (int-to-string oct-num)) + (dec-num 0)) + (and (string-match "[^0-7]" oct-str) + (error (format "(hypb:oct-to-int): Bad octal number: %s" oct-str))) + (mapconcat (function + (lambda (o) + (setq dec-num (+ (* dec-num 8) + (if (and (>= o ?0) (<= o ?7)) + (- o ?0)))))) + oct-str "") + dec-num)) + +;;; ************************************************************************ +;;; Private variables +;;; ************************************************************************ + +(provide 'hypb)