;; $Id: pari-help.el,v 1.4 2003/04/08 00:43:14 karim Exp $
;; pari-help.el --  part of pari.el GP/PARI editing support package.

;; documentation functions
;; See pariemacs.txt  for more details.
 
(provide 'pari-help)

;; pari.el will use the variable 'gp-c-array-createdp
;; and the functions 'gp-cpl-init, 'gp-menu-quit.
;; Also extends pari-mode-hook to set some key-bindings.

;; Of pari.el, it uses:
;; variables:
;;     gp-file-name, gp-version
;; functions: 
;;     gp-window-manager, gp-wait-for-output, gp-get-shell,
;;     gp-background, gp-meta-cmd-general,
;;     gp-info-wind-conf, gp-add-symbol, gp-backward-wind-conf, gp-show-help
;; Of pari-messages.el, it uses: functions: gp-messager.
;; Of pari-completion.el, it uses:
;;     functions: gp-find-word-to-complete, gp-add-symbol, gp-show-help
;;     variables: gp-c-array.
(eval-and-compile
(unless (fboundp 'gp-messager)
  (defun gp-messager (no) (print "Feature pari-messages is absent." "*Messages*")))
;;
(unless (fboundp 'gp-info-wind-conf)
  (defun gp-info-wind-conf nil (message "Main program pari.el is absent !!")))
(unless (fboundp 'gp-store-wind-conf)
  (defun gp-store-wind-conf nil (message "Main program pari.el is absent !!")))
(unless (fboundp 'gp-backward-wind-conf)
  (defun gp-backward-wind-conf nil (message "Main program pari.el is absent !!")))
(unless (fboundp 'gp-restore-wind-conf)
  (defun gp-restore-wind-conf nil (message "Main program pari.el is absent !!")))
(unless (fboundp 'gp-background)
  (defun gp-background nil (message "Main program pari.el is absent !!")))
(unless (fboundp 'gp-wait-for-output)
  (defun gp-wait-for-output (pt &optional o p n) (message "Main program pari.el is absent !!")))
(unless (fboundp 'gp-get-shell)
  (defun gp-get-shell (pn pb cmd) (message "Main program pari.el is absent !!")))
(unless (fboundp 'gp-meta-cmd-general)
  (defun gp-meta-cmd-general (cmd wo) (message "Main program pari.el is absent !!")))
;;
(unless (fboundp 'gp-find-word-to-complete)
  (defun gp-find-word-to-complete nil (print "Feature pari-completion is absent." "*Messages*")))
(unless (fboundp 'gp-show-help)
  (defun gp-show-help (s) (print "Feature pari-completion is absent." "*Messages*")))
(unless (fboundp 'gp-add-symbol)
  (defun gp-add-symbol (s) (print "Feature pari-completion is absent." "*Messages*"))))

(defvar gp-c-array nil "Defined in pari-completion.el")
;; The next variables are here to pacify the compiler !
;; Do *not* assign any value to them or they may override ....
(defvar gp-file-name)
(defvar gp-version)
(defvar gp-gphelp-dir)
(defvar gp-pariemacs)
;;; help menu part:

;; Topology of a menu-buffer : three parts delimited
;; by 'gp-menu-start-simple/'gp-menu-end-simple
;;    'gp-menu-start-special/'gp-menu-end-special
;;    'gp-menu-start-keywords/'gp-menu-end-keywords
;; The first part is made of items displayed on gp-menu-nbcol columns
;; of width gp-menu-width; selecting an item in this region will
;; ask 'gp-get-man-entry. The second part is made of longer items
;; displayed on a single columns and selecting them will also
;; call 'gp-get-man-entry. The third part is made of keywords
;; displayed on a single column which when selected will call
;; 'gp-get-apropos.
(defvar gp-menu-start-simple 0
"Value of point at the beginning of the first menu-region")
(defvar gp-menu-end-simple 0
"Value of point at the end of the first menu-region")
(defvar gp-menu-width 1)
(defvar gp-menu-nbcol 1)
(defvar gp-menu-start-special 0
"Value of point at the beginning of the second menu-region")
(defvar gp-menu-end-special 0
"Value of point at the end of the second menu-region")
(defvar gp-menu-start-keywords 0
"Value of point at the beginning of the third menu-region")
(defvar gp-menu-end-keywords 0
"Value of point at the end of the third menu-region")

(mapcar 'make-variable-buffer-local
  '(gp-menu-start-simple gp-menu-end-simple
    gp-menu-start-special gp-menu-end-special gp-menu-start-keywords
    gp-menu-end-keywords))

(defvar gp-menu-map nil
  "Local keymap used in gp menu buffer.")

(when (null gp-menu-map)
(let ((map (make-sparse-keymap)))
(define-key map "\C-n"    (function gp-menu-next))
(define-key map "\C-p"    (function gp-menu-previous))
(define-key map "\C-m"    (function gp-menu-select))
(define-key map [mouse-2] (function gp-menu-mouse-select))
(define-key map "q"       (function gp-menu-quit))
(define-key map "\C-v"    (function gp-menu-C-v))
(define-key map "\M-v"    (function gp-menu-M-v))
(define-key map [right]   (function gp-menu-right))
(define-key map [left]    (function gp-menu-left))
(setq gp-menu-map map)))

(defvar gp-c-array-createdp nil
"t if the file gp-menu is already used for completion")

;;; Browser part:
(defvar gp-browser-2-map nil "Keymap used in `gp-browser-2-mode'.")

(when (null gp-browser-2-map)
(setq gp-browser-2-map (make-sparse-keymap))
(define-key gp-browser-2-map "\C-m"     (function gp-browser-2-select))
(define-key gp-browser-2-map [mouse-2]  (function gp-browser-2-mouse-select))
(define-key gp-browser-2-map [right]    (function gp-menu-right))
(define-key gp-browser-2-map [left]     (function gp-menu-left)))

(defvar gp-browser-1-map nil "Keymap used in `gp-browser-1-mode'.")

(when (null gp-browser-1-map)
(setq gp-browser-1-map (make-sparse-keymap))
(define-key gp-browser-1-map "\C-m"    (function gp-browser-1-select))
(define-key gp-browser-1-map [mouse-2] (function gp-browser-1-mouse-select)))

(defvar gp-browser-main-alist nil)
(defvar gp-browser-width 0
"The alist `gp-browser-main-alist' consists of conses (NUM . STRING).
`gp-browser-width' is the length of the largest STRING.")

(defvar gp-browser-follow-alist nil)
(defvar gp-main-frame nil "The main frame if non nil.")
(defvar gp-browser-frame nil "GP-browser frame if non nil.")
(defvar gp-browser-process nil "A simple gp process opened to build the browser.")
(defcustom gp-browser-style 3
"*Style for GP-browser. Three styles are possible, 1, 2 and 3.
Best to try them on."
:type 'integer   :group 'gp-miscellana)

;;--------------
;; GP HELP MODE
;;--------------

(defsubst gp-has-spacep (word)
  "T if WORD contains a space and NIL otherwise"
  (not (eq (memq ?\  (string-to-list word)) nil)))

(defun gp-display-raw-menu (lst start end)
  (beginning-of-line)
  (set start (point))
  (save-excursion
    (mapcar
      (lambda (astring)
        (insert astring "\n")
        (put-text-property (- (1- (point)) (length astring)) (1- (point))
                           'mouse-face 'highlight))
      lst)
    (set end (point))
    (insert "\n")))

(defsubst gp-display-special-menu (lst)
  (gp-display-raw-menu lst 'gp-menu-start-special 'gp-menu-end-special))

(defsubst gp-display-keywords-menu (lst)
  (gp-display-raw-menu lst 'gp-menu-start-keywords 'gp-menu-end-keywords))

(defun gp-split-menu (lst)
  (let ((lst-simple nil) (lst-special nil))
    (mapcar
      (lambda (astring)
        (add-to-list (if (gp-has-spacep astring)
                         'lst-special
                         'lst-simple) astring))
      lst)
    (list lst-simple lst-special)))

(defun gp-display-simple-menu (lst)
  "Display the list of strings LST on several columns and sets
the values of `gp-menu-start-simple', `gp-menu-end-simple'."
  (let*((length-list (sort (mapcar 'length lst) '>))
        (how-many (length lst))
        (gp-menu-width (car length-list))  ; maximal width
        gp-menu-main-width                 ; usual width + spaces
        (gp-menu-percent-exceptions 30)    ; maximal percentage of exceptionnal words
        (gp-menu-spaces 2)                 ; spaces between two columns
        (where (max 0 (1- (floor (* how-many gp-menu-percent-exceptions) 100)))))
       ;; Exceptionnal items will be spread over two columns.

       ;; There may be too many elements of exceptional width:
       (if (and (< (1+ where) how-many)
                (= (nth (1+ where) length-list) (nth where length-list)))
           (while (and (<= where 0)
                       (= (nth (1+ where) length-list) (nth where length-list)))
                  (setq where (1- where))))
       (if (> 0 where) (setq where 0))
       ;; The exceptional width may be too large:
       (while (and (<= where 0)
                   (< (+ gp-menu-spaces (* 2 (nth where length-list))) gp-menu-width))
              (setq where (1- where)))
       (if (> 0 where) (setq where 0))
       (setq gp-menu-main-width (nth where length-list))

       ;; Add some spaces between columns:
      (setq gp-menu-main-width (+ gp-menu-spaces gp-menu-main-width))
      ;; Compute 'gp-menu-nbcol:

      (if (< how-many (/ (window-height) 2))
          (setq gp-menu-nbcol 1)
          (setq gp-menu-nbcol (max 1 (floor (- (window-width) gp-menu-spaces) gp-menu-main-width))))
      (if (= gp-menu-nbcol 1)
          (setq gp-menu-main-width (+ gp-menu-spaces gp-menu-width)))

      ;; Display the list:
      (beginning-of-line)
      (setq gp-menu-start-simple (point))
      (save-excursion
      (let ((wherex 1))
        (mapcar
          (lambda (astring)
            (if (<= (length astring) (- gp-menu-main-width gp-menu-spaces))
                (progn
                   (insert astring)
                   (put-text-property (- (point) (length astring)) (point)
                                      'mouse-face 'highlight)
                   (if (< wherex gp-menu-nbcol)
                       (progn (setq wherex (1+ wherex))
                              (insert-char ?\  (- gp-menu-main-width (length astring))))
                       (setq wherex 1)
                       (insert "\n")))
              (if (< wherex gp-menu-nbcol)
                  (progn
                    (insert astring)
                    (put-text-property (- (point) (length astring)) (point)
                                       'mouse-face 'highlight)
                    (if (< wherex (1- gp-menu-nbcol))
                        (progn (setq wherex (+ 2 wherex))
                               (insert-char ?\  (- (* 2 gp-menu-main-width) (length astring))))
                        (setq wherex 1)
                        (insert "\n")))
                (insert "\n" astring)
                (setq wherex 1)
                (put-text-property (- (point) (length astring)) (point)
                                   'mouse-face 'highlight)
                (if (< wherex (1- gp-menu-nbcol))
                    (progn (setq wherex (+ 2 wherex))
                           (insert-char ?\  (- (* 2 gp-menu-main-width) (length astring))))
                    (setq wherex 1)
                    (insert "\n")))))
          lst)
        (or (= wherex 1) (insert "\n")))
      (message (gp-messager 38))
      (setq gp-menu-end-simple (point)))))
        
(defun gp-menu nil
  "Major-mode for the gp menu buffer.
The available commands are
\\{gp-menu-map}"
  (interactive)
  (gp-window-manager "*gp-menu*" 'gp-beginning)
  (setq major-mode 'gp-menu mode-name "GP MENU")
  (use-local-map gp-menu-map))

(defsubst gp-menu-info nil
  (message (gp-messager 18)))

(defun gp-menu-next ()
  "Move down one line of the gp help menu. (Go to top if at the end.)"
  (interactive)
  (gp-menu-info)
  (forward-line 1)
  (if (eobp)
    (progn (ding)
           (goto-char (point-min))
           (re-search-forward "###\n" nil t))))

(defun gp-menu-previous ()
  "Move up one line of the gp help menu. (Go to bottom if at the top.)"
  (interactive)
  (gp-menu-info)
  (forward-line -1)
  (if (or (bobp) (looking-at "###\n"))
      (progn (ding) (goto-char (point-max)) (beginning-of-line))))

(defun gp-menu-C-v nil (interactive) (scroll-up) (gp-menu-info))

(defun gp-menu-M-v nil (interactive) (scroll-down) (gp-menu-info))

(defun gp-menu-right nil
  (interactive)
  (if (and (> (point) (1- gp-menu-start-simple))
           (< (point) gp-menu-end-simple))
      ;; multiple columns display:
      (progn
        (if (re-search-forward "[\n\t ][a-zA-Z]" nil t)
            (forward-char -1)))
      ;; single column display:
      (forward-char 1)))

(defun gp-menu-left nil
  (interactive)
  (if (and (> (point) (1- gp-menu-start-simple))
           (< (point) gp-menu-end-simple))
      ;; multiple columns display:
      (progn
        (if (re-search-backward "\\([\n\t ]\\|^\\)\\([a-zA-Z]+\\)[\n\t ]" nil t)
            (goto-char (match-beginning 2))))
      ;; single column display:
      (forward-char -1)))

(defsubst gp-menu-get-beg nil
  (save-excursion
    (re-search-backward "\\`\\|[ \n]" nil t)
    (match-end 0)))

(defsubst gp-menu-get-end nil
  (save-excursion
    (re-search-forward "\\'\\|[ \n]" nil t)
    (match-beginning 0)))

(defun gp-menu-select nil
  "Select a subject from the main menu, or a manual entry from a subject menu."
  (interactive)
  (cond ((and (> (point) (1- gp-menu-start-simple))
              (< (point) gp-menu-end-simple))
         (gp-get-man-entry
           (buffer-substring-no-properties (gp-menu-get-beg) (gp-menu-get-end))))
        ((and (> (point) (1- gp-menu-start-special))
              (< (point) gp-menu-end-special))
         (gp-get-man-entry
           (buffer-substring-no-properties (line-beginning-position)
                             (line-end-position))))
        ((and (> (point) (1- gp-menu-start-keywords))
              (< (point) gp-menu-end-keywords))
         (gp-get-apropos
           (buffer-substring-no-properties (line-beginning-position)
                             (line-end-position))))
        (t (message (gp-messager 19)))))

(defun gp-menu-mouse-select (event)
  (interactive "e")
  (mouse-set-point event)
  (gp-menu-select))

;;; We start the browser.

(defun gp-browser-cmd (cmd to-buffer &optional nomessage slow-down)
  ;; A GP process is supposedly running.
  (set-buffer "*Simple PARI*")
  (goto-char (point-max))
  ;; Make gp send text to the buffer end, so we can move it to-buffer.
    (set-marker (process-mark gp-browser-process) (point))
    (let ((temp (point)))
      ;; Send the meta command to gp.
      (process-send-string gp-browser-process (concat cmd "\n"))
      ;; Wait for the gp-prompt to be sent.
      (gp-wait-for-output temp nomessage gp-browser-process)
      (if slow-down (sit-for 0 slow-down))

      (let ((copy (buffer-substring-no-properties temp (point-max))))
        (delete-region temp (point-max))
        (set-buffer (get-buffer-create to-buffer))
        (erase-buffer)
        (insert copy)
        (beginning-of-line)  ; We remove last prompt line.
        (delete-region (point) (point-max))
        (goto-char (point-min)))))

(defun gp-browser-1-mode nil ""
  (interactive)
  (setq major-mode 'gp-browser-1)
  (use-local-map gp-browser-1-map))

(defun gp-browser-2-mode nil ""
  (interactive)
  (setq major-mode 'gp-browser-2)
  (setq gp-menu-start-simple (point-min))
  (use-local-map gp-browser-2-map))

(defun gp-browser-1-select nil
  (interactive)
  (remove-text-properties (point-min) (point-max) '(face))
  (put-text-property (line-beginning-position) (line-end-position)
                     'face 'underline)
  (let ((prop (get-text-property (point) 'follow)))
    (if (numberp prop) (gp-browser-follow prop))))

(defun gp-browser-1-mouse-select (event)
  (interactive "e")
  (mouse-set-point event)
  (gp-browser-1-select))

(defun gp-browser-2-select nil
  (interactive)
  (if (or (eobp) (looking-at "[ \n\t]+")) (skip-chars-backward " \n\t"))
  (gp-call-gphelp (window-width (get-buffer-window "*gp-function description*"))
                  (thing-at-point 'word) "*gp-function description*" "-detex")
  ;; Replace ESC[.?m by nothing: (\033 or \e)
  (set-buffer "*gp-function description*")
  (gp-replace "\033\\[.?m" ""))

(defun gp-browser-2-mouse-select (event)
  (interactive "e")
  (mouse-set-point event)
  (gp-browser-2-select))

(defun gp-browser-follow (num)
  (set-buffer "*gp-functions list*")
  (select-window (get-buffer-window "*gp-functions list*"))
  (erase-buffer)
  (gp-display-simple-menu (cdr (assq num gp-browser-follow-alist)))
  (setq gp-menu-end-simple (point-max)))

(defun gp-browser-main nil
  (setq major-mode 'gp-browser-1-mode)
  (mapcar (lambda (acons)
             (let ((temp (point)))
                  (insert (cdr acons))
                  (put-text-property temp (point) 'follow (car acons))
                  (put-text-property temp (point) 'mouse-face 'highlight)    
                  (insert "\n")))  gp-browser-main-alist)
  (goto-char (point-min)))

(defsubst gp-browser-common nil
   (setq gp-browser-frame
               (make-frame (list '(name . "gp-browser")
                                 (cons 'width (frame-width gp-main-frame)))))
   (select-frame gp-browser-frame)
   (switch-to-buffer "*gp-browser*")
   (gp-browser-1-mode)
   (erase-buffer))

(defun gp-browser nil
  (interactive)
  (or gp-browser-main-alist
      (gp-make-browser))
  (setq gp-main-frame (selected-frame))
  (cond ((= gp-browser-style 1)
         (gp-browser-common)
         (split-window nil (1+ (length gp-browser-main-alist)))
         (other-window 1)
         (switch-to-buffer "*gp-functions list*")
         (gp-browser-2-mode)
         (split-window nil 12)
         (other-window 1)
         (switch-to-buffer "*gp-function description*"))
        ((= gp-browser-style 2)
         (gp-browser-common)
         (split-window nil (+ 5 (length gp-browser-main-alist)))
         (other-window 1)
         (switch-to-buffer "*gp-function description*")
         (select-window (get-buffer-window "*gp-browser*"))
         (split-window nil (+ 4 gp-browser-width) t)
         (other-window 1)
         (switch-to-buffer "*gp-functions list*")
         (gp-browser-2-mode))
        ((= gp-browser-style 3)
         (gp-browser-common)
         (split-window nil (+ 4 gp-browser-width) t)
         (other-window 1)
         (switch-to-buffer "*gp-functions list*")
         (gp-browser-2-mode)
         (other-window -1)
         (split-window nil (1+ (length gp-browser-main-alist)))
         (other-window 1)
         (switch-to-buffer "*gp-function description*")))
  (set-buffer "*gp-browser*")
  (select-window (get-buffer-window "*gp-browser*"))
  (gp-browser-main)
  (set-default-font (frame-parameter gp-main-frame 'font))
  (select-frame gp-main-frame))

(defsubst gp-split-to-strings (to)
  (let ((res nil))
    (while (re-search-forward "\\([^ \n\t]+\\)\\( \\|\n\\|\t\\)" to t)
      (setq res (nconc res (list (match-string 1)))))
    res))

(defun gp-make-browser nil
  ;; Make 'gp-browser-main-alist:
  (message (gp-messager 37))
  (get-buffer-create "*Simple PARI*")
  (set-buffer "*Simple PARI*")
  (setq gp-browser-process
    (gp-get-shell "simple-pari" "*Simple PARI*" (concat gp-file-name " -s 1000 -p 10 --emacs")))

  ;; We should run the hook as the prompt may have
  ;; been changed in the .gprc:
  (run-hooks 'pari-mode-hook)
  (gp-wait-for-output 1 t gp-browser-process)
  (gp-browser-cmd "?" "*gp-browser*" t)
  (goto-char (point-min))
  (forward-line 2) ; Skip item 0.
  (while (re-search-forward " +\\([1-90]+\\): \\([^\n]*\\)\n" nil t)
    (setq gp-browser-main-alist
       (nconc gp-browser-main-alist
             (list (cons (string-to-number (match-string 1)) (match-string 2))))
       gp-browser-width (max gp-browser-width (length (match-string 2)))))
  ;; Remove last item :
  (setq gp-browser-main-alist (nreverse (nthcdr 1 (nreverse gp-browser-main-alist))))
  ;; Make 'gp-browser-follow-alist:
  (setq gp-browser-follow-alist
   (mapcar
     (lambda (num)
       (erase-buffer)
       (gp-browser-cmd (concat "?" (number-to-string num)) "*gp-browser*" t 300)
       (cons num (gp-split-to-strings (point-max))))
     (mapcar 'car gp-browser-main-alist)))
  (gp-browser-cmd "\q" "*gp-browser*" t)
  (kill-buffer "*Simple PARI*")
  (message (gp-messager 6)))


;;--------------------
;; TeX AND USUAL INFO
;;--------------------

(defun gp-call-gphelp (win-size word output-buffer opt)
  "Explicit."
  (shell-command
    (concat "env COLUMNS=" (number-to-string win-size) " "
            gp-gphelp-dir "gphelp " opt " \"" word "\"") output-buffer))

(defun gp-replace (a b)
  "Replace the regexp a by the string b everywhere in the current buffer"
  ;; b may be an expression whose value is a string, like
  ;; (buffer-substring-no-properties (match-beginning 0) (match-end 0))
  (save-excursion
   (goto-char (point-min))
   (while (re-search-forward a nil t)
     (replace-match (eval b) t t))))

(defmacro gp-ask-name-wisely (this-type)
  "Ask in the minibuffer for a \"this-type\" name and provide a default"
 (`
  (list
  (let* ( ;; get the word before point into word:
             (word (gp-find-word-to-complete))
;; get the argument from the minibuffer into arg
             (arg
               (progn
                 (define-key minibuffer-local-completion-map " " 'self-insert-command)
                  ;; It is usually 'minibuffer-complete-word, but C-i does that.
                 (completing-read
                   (concat (, this-type)
                     (if (intern-soft word gp-c-array)
;; If the word before point is a gp function, offer it as default.
                         (concat " [Default " word "]" )) ": ")
;; use gp-c-array as the completion array
                   gp-c-array))))
      (define-key minibuffer-local-completion-map " " 'minibuffer-complete-word)
      (if (equal arg "")
;; If the argument supplied is "", and word is a gp symbol, use it as default.
;; (Do not use "" as fn in anycase, so otherwise use " ", which will not
;; produce a help window.)
        (if (intern-soft word gp-c-array) word " ") 
;; Else use the arg.
        arg)))))

(defun gp-get-TeX-man-entry (fn)
  "Similar to '??fn' under GP"
  (interactive (gp-ask-name-wisely (gp-messager 21)))

    (gp-call-gphelp (window-width) fn nil "")
    (if (buffer-live-p (get-buffer "*Shell Command Output*"))
      (save-excursion
          (set-buffer "*Shell Command Output*")
          (cond ((looking-at "\n") (kill-buffer nil))
                ((save-excursion
                   (goto-char (point-min))
                   (search-forward " not found !" nil t))
                 (kill-buffer nil)
                 (message (gp-messager 20) fn))))
      (message "")))

(defun gp-get-man-entry (fn)
  "Get the description of fn from chapter 3 of the manual
via gphelp, and display the result in a new window.
If there is no entry for fn in the manual, send ?fn to gp.
If a definition is found, add fn to the array of possible completions"

  (interactive (gp-ask-name-wisely "Function"))
  (let ((wind (selected-window)))
    ;; We switch to the buffer *gp-help* and erase its content:
    (set-buffer (get-buffer-create "*gp-help*"))
    (erase-buffer)
    (gp-call-gphelp (window-width (get-buffer-window "*gp-help*")) fn t "-detex")
    ;; Replace ESC[.?m by nothing: (\033 or \e)
    (gp-replace "\033\\[.?m" "")

    (goto-char (point-min))
    (if (save-excursion
           (goto-char (- (point-max) 13))
           (looking-at " not found !"))
           ;; If gp was not running then start it.
           (if (gp-background)
             (progn
               (gp-meta-cmd-general (concat "?" fn) nil)
               ;; which sets the buffer "*gp-help*".
               (if (looking-at " *\\*\\*\\* *unknown identifier")
                 (progn
                   (gp-window-manager "*gp-help*" 'gp-remove-help-now)
                   (message (gp-messager 20) fn))
               (if (looking-at " *\\*\\*\\* *user defined variable")
                 (progn
                   (gp-window-manager "*gp-help*" 'gp-remove-help-now)
                   (message (gp-messager 22) fn))
               ;; Else tell user how to remove the help window:
               (gp-window-manager "*gp-help*" 'gp-show-help)
               (gp-info-wind-conf)
               ;; and let the completion system know about the function name:
               (gp-add-symbol fn)))))
         ;; Else show the help buffer and tell user how to remove help window:
         (gp-window-manager "*gp-help*" 'gp-show-help)
         (gp-info-wind-conf))
    (select-window wind)))  ; End of 'gp-get-man-entry

(defun gp-buffer-to-double-list nil
  (let ((lst nil))
    (save-excursion
      (while (not (eobp))
         (add-to-list 'lst
                (buffer-substring-no-properties (line-beginning-position)
                                  (line-end-position)))
         (forward-line 1)))
    (delete-region (point) (point-max))
    (gp-split-menu lst)))

(defun gp-get-apropos (exp)
"Show in gp-menu-mode the functions or sections
in whose description the expression EXP appears.
Similar to \"??? exp\" in gp."
  (interactive (gp-ask-name-wisely (gp-messager 32)))

  (gp-window-manager "*gp-menu*" 'gp-beginning)
  (insert (format (concat "\n" (gp-messager 31) "\n") exp))
  (insert "\n###\n")  ; To give this buffer the format of a gp-menu file.
  (gp-call-gphelp 50 exp t "-k -raw")
  ;; Replace ESC[.?m by nothing: (\033 or \e)
  (gp-replace "\033\\[.?m" "")

  (search-backward "\n###\n")
  (forward-char 5)
  (if (eobp)
      (progn
        (kill-buffer "*gp-menu*")
        (gp-backward-wind-conf)
        (message (gp-messager 23) exp))
      (set-buffer "*gp-menu*")
      (let ((adoublelist (gp-buffer-to-double-list)))
        (gp-display-simple-menu (car adoublelist))
        (goto-char (point-max))
        (and (car adoublelist) (insert "\n"))
        (gp-display-special-menu (nth 1 adoublelist)))
      (setq gp-menu-start-keywords 0 gp-menu-end-keywords 0)
      (setq major-mode 'gp-menu mode-name "GP MENU")
      (use-local-map gp-menu-map)
      (setq buffer-read-only t)
      (goto-char (point-min))
      (search-forward "\n###\n")
      (gp-menu-info)))

;;------------
;; TeX MANUAL
;;------------

;; The line ";;;###autoload" is useless.
;; It will be useful when pari.el will be part
;; of the usual distribution of emacs.
;;;###autoload
(defun gpman()
  "Start up xdvi with the gp manual."

  (interactive)
;; Run gp-mode-hook in case it specifies a different version of the manual.
  (run-hooks 'pari-mode-hook 'gp-mode-hook)
  (gp-get-TeX-man-entry ""))

(defun gp-tutorial()
  "Start up xdvi with the gp tutorial."

  (interactive)
;; Run gp-mode-hook in case it specifies a different version of the tutorial.
  (run-hooks 'pari-mode-hook 'gp-mode-hook)
  (gp-get-TeX-man-entry "tutorial"))

;;-------------------
;; Help on gp-*-mode
;;-------------------

(defun gp-show-pariemacs nil
  "Show pariemacs.txt on another window."
  (interactive)
;; Run gp-mode-hook in case it specifies a different version of pariemacs.txt.
  (run-hooks 'pari-mode-hook 'gp-mode-hook)
;; On a well-configured system, the variable gp-pariemacs contains the name of the
;; proper file. But since users requiring this file may precisely be the ones
;; who do not know how to configure stuff properly ...
  (let ((wind (selected-window))
        (where-it-is "")
        (to-be-tested (list "/usr/local/lib/pari/emacs/"
                            "/usr/local/lib/pari/"
                            "/usr/local/share/lib/pari/"
                            "/usr/local/share/lib/pari/emacs/"
                            "/usr/share/lib/pari/"
                            "/usr/share/lib/pari/emacs/"
                            (concat "/usr/local/lib/pari-" gp-version "/")
                            (concat "/usr/local/lib/pari-" gp-version "/emacs/")
                            (concat "/usr/local/share/lib/pari-" gp-version "/")
                            (concat "/usr/local/share/lib/pari-" gp-version "/emacs/")
                            (concat "/usr/share/lib/pari-" gp-version "/emacs/")
                            (concat "/usr/share/lib/pari-" gp-version "/"))))

    ;; Locate pariemacs.txt:
    (if (file-exists-p gp-pariemacs)
        (progn (setq where-it-is gp-pariemacs))
      (mapcar (lambda (afile) (if (file-exists-p afile) (setq where-it-is afile)))
              (mapcar (lambda (apath) (expand-file-name (concat apath "/pariemacs.txt")))
                      (append to-be-tested load-path))))

    (if (not (string-equal where-it-is ""))
      (progn
        ;; We switch to the buffer *gp-help* and erase its content:
        (set-buffer (get-buffer-create "*gp-help*"))
        (erase-buffer)
        (message where-it-is)  ;; tell *Messages* which version of pariemacs is used.
        (insert-file where-it-is)
        ;; Show the help buffer and tell user how to remove help window:
        (gp-window-manager "*gp-help*" 'gp-show-help)
        (setq buffer-read-only t)
        (search-forward "Common commands" nil t)
        (beginning-of-line) (set-window-start (selected-window) (point))
        (gp-info-wind-conf)
        (select-window wind))
      ;; Tell the user the file was not found:
      (gp-show-help (gp-messager 39))))
  )  ; End of 'gp-show-pariemacs

;;----------------- Outsiders :

(defun gp-menu-quit nil
  "Switch the *PARI* buffer if it exists, or (other-buffer) if it does not."
  (interactive)
  (gp-window-manager "*gp-menu*" 'gp-remove-help-now)
  (if (get-buffer-window "*gp-help*")
      (progn (gp-info-wind-conf)
             (if (string= (buffer-name) "*gp-help*")
                 (select-window (other-window 1))))))

(defun gp-cpl-init nil
"Add all the commands listed by gphelp -k \"\" to the obarray
used for completion."
  (save-excursion
    (set-buffer (get-buffer-create "*gp-menu*"))
    (erase-buffer)
    (gp-call-gphelp 100 " " t "-k -raw")
    (gp-replace "\033\\[.?m" "")
    (let ((adoublelist (gp-buffer-to-double-list)))
      (mapcar 'gp-add-symbol (car adoublelist)))
    (kill-buffer "*gp-menu*")))

(defconst gp-manual-menu
   (list
   ["Browser" gp-browser :active t :included (eq window-system 'x)
                         :key-sequence nil]
   '("Info ..."
      ["on Subject..." gp-get-apropos t]
      ["on Function..." gp-get-man-entry t])
   '("TeX Info"
      ["Manual" gpman :active t :key-sequence nil]     
      ["Tutorial" gp-tutorial :active t :key-sequence nil]
      ["on Function ..." gp-get-TeX-man-entry :active t :key-sequence nil])
   (vector (gp-messager 63) 'gp-show-pariemacs 't)))

(add-hook 'pari-mode-hook
  '(lambda nil 
     (define-key gp-map "\M-?"          (function gp-get-man-entry))
     (define-key gp-map "\M-H"          (function gp-get-apropos))
     (define-key gp-script-map "\M-?"   (function gp-get-man-entry))
     (define-key gp-script-map "\M-H"   (function gp-get-apropos))
     (unless gp-c-array-createdp
       (gp-cpl-init)
       (setq gp-c-array-createdp t))))

(add-hook 'menu-bar-update-hook
  '(lambda nil
     (when (memq major-mode '(gp-mode gp-script-mode))
       (easy-menu-change '("GP") "----" nil (gp-messager 48))
       (mapcar
        (lambda (item)
          (if (listp item)
              (easy-menu-change '("GP") (car item) (cdr item) (gp-messager 48))
            (when GP-script-menu-map
              (easy-menu-add-item GP-script-menu-map nil item (gp-messager 48)))
            (when GP-menu-map
              (easy-menu-add-item GP-menu-map nil item (gp-messager 48)))
            ))
        gp-manual-menu)
       (easy-menu-change '("GP") "----" nil (gp-messager 48)))))

;; pari-help.el ends here.
