2022-08-17 07:19:50 +00:00
|
|
|
;;; sclang-help.el --- IDE for working with SuperCollider -*- coding: utf-8; -*-
|
2009-01-02 19:06:25 +00:00
|
|
|
;;
|
2022-07-30 17:15:38 +00:00
|
|
|
;; Copyright 2003 stefan kersten <steve@k-hornz.de>
|
|
|
|
|
|
|
|
;;; License:
|
|
|
|
|
2009-01-02 19:06:25 +00:00
|
|
|
;; 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 2 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, write to the Free Software
|
2009-04-20 08:38:27 +00:00
|
|
|
;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
2009-01-02 19:06:25 +00:00
|
|
|
;; USA
|
|
|
|
|
2022-07-30 17:15:38 +00:00
|
|
|
;;; Commentary:
|
|
|
|
;; Access SuperCollider help files.
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
2009-01-02 19:06:25 +00:00
|
|
|
(eval-when-compile
|
|
|
|
(require 'font-lock))
|
|
|
|
|
2022-07-30 17:15:38 +00:00
|
|
|
(require 'w3m)
|
2019-12-25 12:51:43 +00:00
|
|
|
(require 'cl-lib)
|
2022-07-30 17:15:38 +00:00
|
|
|
|
2009-01-02 19:06:25 +00:00
|
|
|
(require 'sclang-util)
|
|
|
|
(require 'sclang-interp)
|
|
|
|
(require 'sclang-language)
|
2021-08-16 23:57:43 +00:00
|
|
|
(require 'sclang-mode)
|
2021-07-25 18:58:21 +00:00
|
|
|
(require 'sclang-vars nil 'ignore-missing-file)
|
2009-01-02 19:06:25 +00:00
|
|
|
(require 'sclang-minor-mode)
|
|
|
|
|
2021-07-25 18:58:21 +00:00
|
|
|
(defun sclang-system-root ()
|
|
|
|
"Find the common install location for the platform."
|
|
|
|
(cond
|
|
|
|
((boundp 'sclang-system-data-dir)
|
|
|
|
sclang-system-data-dir)
|
|
|
|
|
|
|
|
((eql 'darwin system-type)
|
|
|
|
(expand-file-name "~/Library/Application Support/SuperCollider"))
|
|
|
|
|
|
|
|
((and (eql 'gnu/linux system-type)
|
|
|
|
(file-exists-p "/usr/local/share/SuperCollider"))
|
|
|
|
"/usr/local/share/SuperCollider")
|
|
|
|
|
|
|
|
((eql 'gnu/linux system-type)
|
|
|
|
"/usr/share/SuperCollider")))
|
|
|
|
|
|
|
|
(defcustom sclang-system-help-dir (expand-file-name "Help" (sclang-system-root))
|
|
|
|
"Installation dependent help directory."
|
2009-01-02 19:06:25 +00:00
|
|
|
:group 'sclang-interface
|
2021-07-25 18:58:21 +00:00
|
|
|
:type 'directory)
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defcustom sclang-help-path (list sclang-system-help-dir
|
2022-07-30 17:15:38 +00:00
|
|
|
"~/.local/share/SuperCollider/Help")
|
2019-12-25 12:51:43 +00:00
|
|
|
"List of directories where SuperCollider help files are kept."
|
2009-01-02 19:06:25 +00:00
|
|
|
:group 'sclang-interface
|
|
|
|
:version "21.4"
|
|
|
|
:type '(repeat directory))
|
|
|
|
|
2021-07-25 18:58:21 +00:00
|
|
|
(defcustom sclang-system-extension-dir (expand-file-name "Extensions" (sclang-system-root))
|
|
|
|
"Installation dependent extension directory."
|
|
|
|
:group 'sclang-interface
|
|
|
|
:type 'directory)
|
|
|
|
|
2009-01-02 19:06:25 +00:00
|
|
|
(defconst sclang-extension-path (list sclang-system-extension-dir
|
2022-07-30 17:15:38 +00:00
|
|
|
"~/.local/share/SuperCollider/Extensions")
|
2009-01-02 19:06:25 +00:00
|
|
|
"List of SuperCollider extension directories.")
|
|
|
|
|
|
|
|
(defcustom sclang-help-fill-column fill-column
|
2019-12-25 12:51:43 +00:00
|
|
|
"Column beyond which automatic line-wrapping in RTF help files should happen."
|
2009-01-02 19:06:25 +00:00
|
|
|
:group 'sclang-interface
|
|
|
|
:version "21.3"
|
|
|
|
:type 'integer)
|
|
|
|
|
|
|
|
(defcustom sclang-rtf-editor-program "ted"
|
|
|
|
"*Name of an RTF editor program used to edit SuperCollider help files."
|
|
|
|
:group 'sclang-programs
|
|
|
|
:version "21.3"
|
|
|
|
:type 'string)
|
|
|
|
|
|
|
|
(defcustom sclang-html-editor-program "html"
|
|
|
|
"*Name of an HTML editor program used to edit SuperCollider help files."
|
|
|
|
:group 'sclang-programs
|
|
|
|
:version "21.3"
|
|
|
|
:type 'string)
|
|
|
|
|
|
|
|
;; dynamically change certain html-tags when displaying in w3m-browser:
|
|
|
|
|
|
|
|
(defcustom sclang-help-filters
|
|
|
|
'(("p\\.p\\([0-9]+\\)" . "#p\\1")
|
|
|
|
("<p class=\"\\(.*\\)\">\\(.*\\)</p>" . "<div id=\"\\1\">\\2</div>"))
|
2022-07-30 17:15:38 +00:00
|
|
|
"Filters to replace html tags.
|
|
|
|
List of pairs of (regexp . filter) defining html-tags to be replaced
|
|
|
|
using the function `sclang-help-substitute-for-filters'."
|
2009-01-02 19:06:25 +00:00
|
|
|
:group 'sclang-interface
|
|
|
|
:type '(repeat (cons (string :tag "match") (string :tag "replacement"))))
|
|
|
|
|
|
|
|
(defun sclang-help-substitute-for-filters (&rest args)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Substitute various tags in SCs html-docs.
|
|
|
|
Optional argument ARGS unused?"
|
2009-01-02 19:06:25 +00:00
|
|
|
(mapcar #'(lambda (filter)
|
2022-07-30 17:15:38 +00:00
|
|
|
(let ((regexp (car filter))
|
|
|
|
(to-string (cdr filter)))
|
|
|
|
(goto-char (point-min))
|
|
|
|
(while (re-search-forward regexp nil t)
|
|
|
|
(replace-match to-string nil nil))))
|
|
|
|
sclang-help-filters))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
;; w3m's content-filtering system
|
|
|
|
(setq w3m-use-filter t)
|
|
|
|
|
2022-07-30 17:15:38 +00:00
|
|
|
;; checks if w3m-filter is loaded. Is `eval-after-load' necessary here?
|
2009-01-02 19:06:25 +00:00
|
|
|
(eval-after-load "w3m-filter"
|
|
|
|
'(add-to-list 'w3m-filter-rules
|
2022-07-30 17:15:38 +00:00
|
|
|
;; run on all files read by w3m...
|
2009-01-02 19:06:25 +00:00
|
|
|
'(".*" sclang-help-substitute-for-filters)))
|
|
|
|
|
|
|
|
(defvar sclang-help-topic-alist nil
|
|
|
|
"Alist mapping help topics to file names.")
|
|
|
|
|
|
|
|
(defvar sclang-help-topic-history nil
|
|
|
|
"List of recently invoked help topics.")
|
|
|
|
;; (defvar sclang-help-topic-ring-length 32)
|
|
|
|
;; (defvar sclang-help-topic-ring (make-ring sclang-help-topic-ring-length))
|
|
|
|
|
|
|
|
(defconst sclang-special-help-topics
|
|
|
|
'(("/" . "division")
|
|
|
|
("-" . "subtraction"))
|
|
|
|
"Alist of help topics with transcoded filenames.")
|
|
|
|
|
|
|
|
(defvar sclang-help-file nil)
|
|
|
|
(defvar sclang-current-help-file nil)
|
|
|
|
(make-variable-buffer-local 'sclang-help-file)
|
|
|
|
|
|
|
|
(defconst sclang-help-file-regexp
|
|
|
|
"\\(\\(\\(\\.help\\)?\\.\\(rtf\\|scd\\|html\\|htm\\)\\)\\|\\(\\.help\\.sc\\.html\\.htm\\)\\|\\.rtfd/TXT\\.rtf\\.html\\.htm\\)$"
|
|
|
|
"Regular expression matching help files.")
|
|
|
|
|
|
|
|
;; =====================================================================
|
|
|
|
;; utilities
|
|
|
|
;; =====================================================================
|
|
|
|
|
|
|
|
(defun sclang-get-help-file (topic)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Get the help file for TOPIC."
|
2009-01-02 19:06:25 +00:00
|
|
|
(let ((topic (or (cdr (assoc topic sclang-special-help-topics)) topic)))
|
|
|
|
(cdr (assoc topic sclang-help-topic-alist))))
|
|
|
|
|
|
|
|
(defun sclang-get-help-topic (file)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Get the help topic for FILE."
|
2009-01-02 19:06:25 +00:00
|
|
|
(let ((topic (car (rassoc file sclang-help-topic-alist))))
|
|
|
|
(or (car (rassoc topic sclang-special-help-topics)) topic)))
|
|
|
|
|
|
|
|
(defun sclang-help-buffer-name (topic)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Set the help buffer name to TOPIC."
|
2009-01-02 19:06:25 +00:00
|
|
|
(sclang-make-buffer-name (concat "Help:" topic)))
|
|
|
|
|
2022-07-30 17:15:38 +00:00
|
|
|
;; file predicate functions
|
|
|
|
|
2009-03-17 09:29:15 +00:00
|
|
|
(defun sclang-rtf-file-p (file)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Does an rtf FILE exist?"
|
2009-01-02 19:06:25 +00:00
|
|
|
(let ((case-fold-search t))
|
|
|
|
(string-match ".*\\.rtf$" file)))
|
|
|
|
|
2009-03-17 09:29:15 +00:00
|
|
|
(defun sclang-html-file-p (file)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Does an html FILE exist?"
|
|
|
|
(let ((case-fold-search t))
|
|
|
|
(string-match ".*\\.html?$" file)))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
2009-03-17 09:29:15 +00:00
|
|
|
(defun sclang-sc-file-p (file)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Does an sc FILE exist?"
|
2009-01-02 19:06:25 +00:00
|
|
|
(let ((case-fold-search t))
|
|
|
|
(string-match ".*\\.sc$" file)))
|
|
|
|
|
2009-03-17 09:29:15 +00:00
|
|
|
(defun sclang-scd-file-p (file)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Does an scd FILE exist?"
|
2009-01-02 19:06:25 +00:00
|
|
|
(let ((case-fold-search t))
|
|
|
|
(string-match ".*\\.scd$" file)))
|
|
|
|
|
2009-03-17 09:29:15 +00:00
|
|
|
(defun sclang-help-file-p (file)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Is FILE a help file?"
|
2009-03-17 09:29:15 +00:00
|
|
|
(string-match sclang-help-file-regexp file))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
2022-07-30 17:15:38 +00:00
|
|
|
|
2009-03-17 09:29:15 +00:00
|
|
|
(defun sclang-help-topic-name (file)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Get the help topic from FILE."
|
|
|
|
(when (string-match sclang-help-file-regexp file)
|
|
|
|
(cons (file-name-nondirectory (replace-match "" nil nil file 1))
|
|
|
|
file)))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
;; =====================================================================
|
|
|
|
;; rtf parsing
|
|
|
|
;; =====================================================================
|
|
|
|
|
|
|
|
(defconst sclang-rtf-face-change-token "\0")
|
|
|
|
|
|
|
|
(defun sclang-fill-rtf-syntax-table (table)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Fill RTF syntax TABLE."
|
2009-01-02 19:06:25 +00:00
|
|
|
(modify-syntax-entry ?\\ "/" table)
|
|
|
|
(modify-syntax-entry ?\" "." table)
|
|
|
|
(modify-syntax-entry ?\{ "(" table)
|
|
|
|
(modify-syntax-entry ?\} ")" table)
|
|
|
|
(modify-syntax-entry ?\( "." table)
|
|
|
|
(modify-syntax-entry ?\) "." table)
|
|
|
|
(modify-syntax-entry ?\[ "." table)
|
|
|
|
(modify-syntax-entry ?\] "." table)
|
|
|
|
table)
|
|
|
|
|
|
|
|
(defvar sclang-rtf-syntax-table (sclang-fill-rtf-syntax-table (make-syntax-table))
|
|
|
|
"Syntax table used for RTF parsing.")
|
|
|
|
|
|
|
|
(defvar sclang-rtf-font-map '((Helvetica . variable-pitch)
|
2022-07-30 17:15:38 +00:00
|
|
|
(Helvetica-Bold . variable-pitch)
|
|
|
|
(Monaco . nil)))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
2019-12-25 12:51:43 +00:00
|
|
|
(cl-defstruct sclang-rtf-state
|
2009-01-02 19:06:25 +00:00
|
|
|
output font-table font face pos)
|
|
|
|
|
2019-12-25 12:51:43 +00:00
|
|
|
(cl-macrolet ((rtf-p (pos) `(plist-get (text-properties-at ,pos) 'rtf-p)))
|
2009-01-02 19:06:25 +00:00
|
|
|
(defun sclang-rtf-p (pos) (rtf-p pos))
|
|
|
|
(defun sclang-code-p (pos) (not (rtf-p pos))))
|
|
|
|
|
|
|
|
(defmacro with-sclang-rtf-state-output (state &rest body)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Wrap rtf STATE output around BODY."
|
2009-01-02 19:06:25 +00:00
|
|
|
`(with-current-buffer (sclang-rtf-state-output ,state)
|
|
|
|
,@body))
|
|
|
|
|
|
|
|
(defmacro sclang-rtf-state-add-font (state font-id font-name)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Add font to STATE font table using FONT-ID and FONT-NAME."
|
2009-01-02 19:06:25 +00:00
|
|
|
`(push (cons ,font-id (intern ,font-name)) (sclang-rtf-state-font-table ,state)))
|
|
|
|
|
|
|
|
(defmacro sclang-rtf-state-apply (state)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Apply STATE to rtf output."
|
2020-04-02 06:28:29 +00:00
|
|
|
(let ((pos (cl-gensym))
|
2022-07-30 17:15:38 +00:00
|
|
|
(font (cl-gensym))
|
|
|
|
(face (cl-gensym)))
|
2009-01-02 19:06:25 +00:00
|
|
|
`(with-current-buffer (sclang-rtf-state-output ,state)
|
|
|
|
(let ((,pos (or (sclang-rtf-state-pos ,state) (point-min)))
|
2022-07-30 17:15:38 +00:00
|
|
|
(,font (cdr (assq
|
|
|
|
(cdr (assoc
|
|
|
|
(sclang-rtf-state-font ,state)
|
|
|
|
(sclang-rtf-state-font-table ,state)))
|
|
|
|
sclang-rtf-font-map)))
|
|
|
|
(,face (sclang-rtf-state-face ,state)))
|
|
|
|
(when (> (point) ,pos)
|
|
|
|
(if ,font
|
|
|
|
(add-text-properties
|
|
|
|
,pos (point)
|
|
|
|
(list 'rtf-p t 'rtf-face (append (list ,font) ,face))))
|
|
|
|
(setf (sclang-rtf-state-pos ,state) (point)))))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defmacro sclang-rtf-state-set-font (state font)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Set FONT in STATE."
|
2009-01-02 19:06:25 +00:00
|
|
|
`(progn
|
|
|
|
(sclang-rtf-state-apply ,state)
|
|
|
|
(setf (sclang-rtf-state-font ,state) ,font)))
|
|
|
|
|
|
|
|
(defmacro sclang-rtf-state-push-face (state face)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Push FACE to STATE."
|
2020-04-02 06:28:29 +00:00
|
|
|
(let ((list (cl-gensym)))
|
2009-01-02 19:06:25 +00:00
|
|
|
`(let ((,list (sclang-rtf-state-face state)))
|
|
|
|
(sclang-rtf-state-apply ,state)
|
|
|
|
(unless (memq ,face ,list)
|
2022-07-30 17:15:38 +00:00
|
|
|
(setf (sclang-rtf-state-face ,state)
|
|
|
|
(append ,list (list ,face)))))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defmacro sclang-rtf-state-pop-face (state face)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Pop FACE from STATE."
|
2020-04-02 06:28:29 +00:00
|
|
|
(let ((list (cl-gensym)))
|
2009-01-02 19:06:25 +00:00
|
|
|
`(let* ((,list (sclang-rtf-state-face ,state)))
|
|
|
|
(sclang-rtf-state-apply ,state)
|
|
|
|
(setf (sclang-rtf-state-face ,state) (delq ,face ,list)))))
|
|
|
|
|
|
|
|
(defun sclang-parse-rtf (state)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Parse rtf STATE."
|
|
|
|
(while (not (eobp))
|
2009-01-02 19:06:25 +00:00
|
|
|
(cond ((looking-at "{")
|
2022-07-30 17:15:38 +00:00
|
|
|
;; container
|
|
|
|
(let ((beg (point)))
|
|
|
|
(with-syntax-table sclang-rtf-syntax-table
|
|
|
|
(forward-list 1))
|
|
|
|
(save-excursion
|
|
|
|
(save-restriction
|
|
|
|
(narrow-to-region (1+ beg) (1- (point)))
|
|
|
|
(goto-char (point-min))
|
|
|
|
(sclang-parse-rtf-container state)
|
|
|
|
(widen)))))
|
|
|
|
((or (looking-at "\\\\\\([{}\\\n]\\)")
|
|
|
|
(looking-at "\\\\\\([^\\ \n]+\\) ?"))
|
|
|
|
;; control
|
|
|
|
(let ((end (match-end 0)))
|
|
|
|
(sclang-parse-rtf-control state (match-string 1))
|
|
|
|
(goto-char end)))
|
|
|
|
((looking-at "\\([^{\\\n]+\\)")
|
|
|
|
;; normal text
|
|
|
|
(let ((end (match-end 0))
|
|
|
|
(match (match-string 1)))
|
|
|
|
(with-sclang-rtf-state-output state (insert match))
|
|
|
|
(goto-char end)))
|
|
|
|
(t
|
|
|
|
;; never reached (?)
|
|
|
|
(forward-char 1)))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defun sclang-parse-rtf-container (state)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Parse RTF container. STATE."
|
|
|
|
(cond ((looking-at "\\\\rtf1") ; document
|
|
|
|
(goto-char (match-end 0))
|
|
|
|
(sclang-parse-rtf state))
|
|
|
|
((looking-at "\\\\fonttbl") ; font table
|
|
|
|
(goto-char (match-end 0))
|
|
|
|
(while (looking-at "\\\\\\(f[0-9]+\\)[^ ]* \\([^;]*\\);[^\\]*")
|
|
|
|
(sclang-rtf-state-add-font state (match-string 1) (match-string 2))
|
|
|
|
(goto-char (match-end 0))))
|
|
|
|
((looking-at "{\\\\NeXTGraphic \\([^\\]+\\.[a-z]+\\)") ; inline graphic
|
|
|
|
(let* ((file (match-string 1))
|
|
|
|
(image (and file (create-image (expand-file-name file)))))
|
|
|
|
(with-sclang-rtf-state-output
|
|
|
|
state
|
|
|
|
(if image
|
|
|
|
(insert-image image)
|
|
|
|
(sclang-rtf-state-push-face state 'italic)
|
|
|
|
(insert file)
|
|
|
|
(sclang-rtf-state-pop-face state 'italic)))))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defun sclang-parse-rtf-control (state ctrl)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Parse RTF control chars. STATE CTRL."
|
2009-01-02 19:06:25 +00:00
|
|
|
(let ((char (aref ctrl 0)))
|
|
|
|
(cond ((memq char '(?{ ?} ?\\))
|
2022-07-30 17:15:38 +00:00
|
|
|
(with-sclang-rtf-state-output state (insert char)))
|
|
|
|
((or (eq char ?\n)
|
|
|
|
(string= ctrl "par"))
|
|
|
|
(sclang-rtf-state-apply state)
|
|
|
|
(with-sclang-rtf-state-output
|
|
|
|
state
|
|
|
|
(when (sclang-rtf-p (line-beginning-position))
|
|
|
|
(fill-region (line-beginning-position) (line-end-position)
|
|
|
|
t t))
|
|
|
|
(insert ?\n)))
|
|
|
|
((string= ctrl "tab")
|
|
|
|
(with-sclang-rtf-state-output state (insert ?\t)))
|
|
|
|
((string= ctrl "b")
|
|
|
|
(sclang-rtf-state-push-face state 'bold))
|
|
|
|
((string= ctrl "b0")
|
|
|
|
(sclang-rtf-state-pop-face state 'bold))
|
|
|
|
((string-match "^f[0-9]+$" ctrl)
|
|
|
|
(sclang-rtf-state-set-font state ctrl)))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defun sclang-convert-rtf-buffer (output)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Convert rtf buffer. OUTPUT."
|
2009-01-02 19:06:25 +00:00
|
|
|
(let ((case-fold-search nil)
|
2022-07-30 17:15:38 +00:00
|
|
|
(fill-column sclang-help-fill-column))
|
2009-01-02 19:06:25 +00:00
|
|
|
(save-excursion
|
|
|
|
(goto-char (point-min))
|
|
|
|
(when (looking-at "{\\\\rtf1")
|
2022-07-30 17:15:38 +00:00
|
|
|
(let ((state (make-sclang-rtf-state)))
|
|
|
|
(setf (sclang-rtf-state-output state) output)
|
|
|
|
(sclang-parse-rtf state)
|
|
|
|
(sclang-rtf-state-apply state))))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
;; =====================================================================
|
|
|
|
;; help mode
|
|
|
|
;; =====================================================================
|
|
|
|
|
|
|
|
(defun sclang-fill-help-syntax-table (table)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Fill help syntax TABLE."
|
2009-01-02 19:06:25 +00:00
|
|
|
;; make ?- be part of symbols for selection and sclang-symbol-at-point
|
|
|
|
(modify-syntax-entry ?- "_" table))
|
|
|
|
|
|
|
|
(defun sclang-fill-help-mode-map (map)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Fill sclang help mode keymap MAP."
|
2009-01-02 19:06:25 +00:00
|
|
|
(define-key map "\C-c}" 'bury-buffer)
|
|
|
|
(define-key map "\C-c\C-v" 'sclang-edit-help-file))
|
|
|
|
|
|
|
|
(defmacro sclang-help-mode-limit-point-to-code (&rest body)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Limit point to code BODY."
|
2020-04-02 06:28:29 +00:00
|
|
|
(let ((min (cl-gensym))
|
2022-07-30 17:15:38 +00:00
|
|
|
(max (cl-gensym))
|
|
|
|
(res (cl-gensym)))
|
2009-01-02 19:06:25 +00:00
|
|
|
`(if (and (sclang-code-p (point))
|
2022-07-30 17:15:38 +00:00
|
|
|
(not (or (bobp) (eobp)))
|
|
|
|
(sclang-code-p (1- (point)))
|
|
|
|
(sclang-code-p (1+ (point))))
|
|
|
|
(let ((,min (previous-single-property-change (point) 'rtf-p (current-buffer) (point-min)))
|
|
|
|
(,max (next-single-property-change (point) 'rtf-p (current-buffer) (point-max))))
|
|
|
|
(let ((,res (progn ,@body)))
|
|
|
|
(cond ((< (point) ,min) (goto-char ,min) nil)
|
|
|
|
((> (point) ,max) (goto-char ,max) nil)
|
|
|
|
(t ,res)))))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defun sclang-help-mode-beginning-of-defun (&optional arg)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Move to beginning of function (or back ARG)."
|
2009-01-02 19:06:25 +00:00
|
|
|
(interactive "p")
|
|
|
|
(sclang-help-mode-limit-point-to-code (sclang-beginning-of-defun arg)))
|
|
|
|
|
|
|
|
(defun sclang-help-mode-end-of-defun (&optional arg)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Move to end of function (or forward ARG)."
|
2009-01-02 19:06:25 +00:00
|
|
|
(interactive "p")
|
|
|
|
(sclang-help-mode-limit-point-to-code (sclang-end-of-defun arg)))
|
|
|
|
|
|
|
|
(defun sclang-help-mode-fontify-region (start end loudly)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Fontify region from START to END and LOUDLY."
|
2018-07-28 14:31:58 +00:00
|
|
|
(cl-flet ((fontify-code
|
2022-07-30 17:15:38 +00:00
|
|
|
(start end loudly)
|
|
|
|
(funcall 'font-lock-default-fontify-region start end loudly))
|
|
|
|
(fontify-non-code
|
|
|
|
(start end loudly)
|
|
|
|
(while (< start end)
|
|
|
|
(let ((value (plist-get (text-properties-at start) 'rtf-face))
|
|
|
|
(end (next-single-property-change start 'rtf-face (current-buffer) end)))
|
|
|
|
(add-text-properties start end (list 'face (append '(variable-pitch) (list value))))
|
|
|
|
(setq start end)))))
|
2009-01-02 19:06:25 +00:00
|
|
|
(let ((modified (buffer-modified-p)) (buffer-undo-list t)
|
2022-07-30 17:15:38 +00:00
|
|
|
(inhibit-read-only t) (inhibit-point-motion-hooks t)
|
|
|
|
(inhibit-modification-hooks t)
|
|
|
|
deactivate-mark buffer-file-name buffer-file-truename
|
|
|
|
(pos start))
|
2009-01-02 19:06:25 +00:00
|
|
|
(unwind-protect
|
2022-07-30 17:15:38 +00:00
|
|
|
(while (< pos end)
|
|
|
|
(let ((end (next-single-property-change pos 'rtf-p (current-buffer) end)))
|
|
|
|
(if (sclang-rtf-p pos)
|
|
|
|
(fontify-non-code pos end loudly)
|
|
|
|
(fontify-code pos end loudly))
|
|
|
|
(setq pos end)))
|
|
|
|
(when (and (not modified) (buffer-modified-p))
|
|
|
|
(set-buffer-modified-p nil))))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
(defun sclang-help-mode-indent-line ()
|
2022-07-30 17:15:38 +00:00
|
|
|
"Indent sclang code in documentation."
|
2009-01-02 19:06:25 +00:00
|
|
|
(if (sclang-code-p (point))
|
|
|
|
(sclang-indent-line)
|
|
|
|
(insert "\t")))
|
|
|
|
|
|
|
|
(define-derived-mode sclang-help-mode sclang-mode "SCLangHelp"
|
|
|
|
"Major mode for displaying SuperCollider help files.
|
|
|
|
\\{sclang-help-mode-map}"
|
|
|
|
(let ((file (or (buffer-file-name)
|
2022-07-30 17:15:38 +00:00
|
|
|
(and (boundp 'sclang-current-help-file)
|
|
|
|
sclang-current-help-file))))
|
2009-01-02 19:06:25 +00:00
|
|
|
(when file
|
|
|
|
(set-visited-file-name nil)
|
|
|
|
(setq buffer-auto-save-file-name nil)
|
|
|
|
(save-excursion
|
2022-07-30 17:15:38 +00:00
|
|
|
(when (sclang-rtf-file-p file)
|
|
|
|
(let ((tmp-buffer (generate-new-buffer " *RTF*"))
|
|
|
|
(modified-p (buffer-modified-p)))
|
|
|
|
(unwind-protect
|
|
|
|
(progn
|
|
|
|
(sclang-convert-rtf-buffer tmp-buffer)
|
|
|
|
(read-only-mode)
|
|
|
|
(erase-buffer)
|
|
|
|
(insert-buffer-substring tmp-buffer))
|
|
|
|
(and (buffer-modified-p) (not modified-p) (set-buffer-modified-p nil))
|
|
|
|
(kill-buffer tmp-buffer))))))
|
2009-01-02 19:06:25 +00:00
|
|
|
(set (make-local-variable 'sclang-help-file) file)
|
|
|
|
(setq font-lock-defaults
|
2022-07-30 17:15:38 +00:00
|
|
|
(append font-lock-defaults
|
|
|
|
'((font-lock-fontify-region-function . sclang-help-mode-fontify-region))))
|
2009-01-02 19:06:25 +00:00
|
|
|
(set (make-local-variable 'beginning-of-defun-function) 'sclang-help-mode-beginning-of-defun)
|
2022-07-30 17:15:38 +00:00
|
|
|
(set (make-local-variable 'indent-line-function) 'sclang-help-mode-indent-line)))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
;; =====================================================================
|
|
|
|
;; help file access
|
|
|
|
;; =====================================================================
|
|
|
|
|
|
|
|
(defun sclang-skip-help-directory-p (path)
|
|
|
|
"Answer t if PATH should be skipped during help file indexing."
|
|
|
|
(let ((directory (file-name-nondirectory path)))
|
2019-12-25 12:51:43 +00:00
|
|
|
(cl-some (lambda (regexp) (string-match regexp directory))
|
2022-07-30 17:15:38 +00:00
|
|
|
;; skip "." ".." "CVS" ".svn" and "_darcs" directories
|
|
|
|
'("\\.\\'" "\\.\\.\\'" "^CVS\\'" "^\\.svn$" "^_darcs\\'"))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defun sclang-filter-help-directories (list)
|
|
|
|
"Remove paths to be skipped from LIST of directories."
|
2019-12-25 12:51:43 +00:00
|
|
|
(cl-remove-if (lambda (x)
|
2022-07-30 17:15:38 +00:00
|
|
|
(or (not (file-directory-p x))
|
|
|
|
(sclang-skip-help-directory-p x)))
|
|
|
|
list))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defun sclang-directory-files-save (directory &optional full match nosort)
|
2022-07-30 17:15:38 +00:00
|
|
|
"List files in DIRECTORY (optionally FULL MATCH NOSORT) or nil."
|
2009-01-02 19:06:25 +00:00
|
|
|
(condition-case nil
|
|
|
|
(directory-files directory full match nosort)
|
|
|
|
(error nil)))
|
|
|
|
|
|
|
|
;; (defun sclang-extension-help-directories ()
|
|
|
|
;; "Build a list of help directories for extensions."
|
2018-07-28 14:31:58 +00:00
|
|
|
;; (cl-flet ((flatten (seq)
|
2022-07-30 17:15:38 +00:00
|
|
|
;; (if (null seq)
|
|
|
|
;; seq
|
|
|
|
;; (if (listp seq)
|
|
|
|
;; (reduce 'append (mapcar #'flatten seq))
|
|
|
|
;; (list seq)))))
|
2009-01-02 19:06:25 +00:00
|
|
|
;; (flatten
|
|
|
|
;; (mapcar
|
|
|
|
;; (lambda (dir)
|
2022-07-30 17:15:38 +00:00
|
|
|
;; (mapcar
|
|
|
|
;; (lambda (dir)
|
|
|
|
;; (remove-if-not
|
|
|
|
;; 'file-directory-p
|
|
|
|
;; (sclang-directory-files-save dir t "^[Hh][Ee][Ll][Pp]$" t)))
|
|
|
|
;; (sclang-filter-help-directories (sclang-directory-files-save dir t))))
|
2009-01-02 19:06:25 +00:00
|
|
|
;; sclang-extension-path))))
|
|
|
|
|
|
|
|
;; (defun sclang-help-directories ()
|
|
|
|
;; "Answer list of help directories to be indexed."
|
|
|
|
;; (append sclang-help-path (sclang-extension-help-directories)))
|
|
|
|
|
|
|
|
(defun sclang-help-directories ()
|
|
|
|
"Answer list of help directories to be indexed."
|
|
|
|
(append sclang-help-path sclang-extension-path))
|
|
|
|
|
|
|
|
(defun sclang-make-help-topic-alist (dirs result)
|
|
|
|
"Build a help topic alist from directories in DIRS, with initial RESULT."
|
|
|
|
(if dirs
|
|
|
|
(let* ((files (sclang-directory-files-save (car dirs) t))
|
2022-07-30 17:15:38 +00:00
|
|
|
(topics (remq nil (mapcar 'sclang-help-topic-name files)))
|
|
|
|
(new-dirs (sclang-filter-help-directories files)))
|
|
|
|
(sclang-make-help-topic-alist
|
|
|
|
(append new-dirs (cdr dirs))
|
|
|
|
(append topics result)))
|
2009-01-02 19:06:25 +00:00
|
|
|
(sort result (lambda (a b) (string< (car a) (car b))))))
|
|
|
|
|
|
|
|
(defun sclang-index-help-topics ()
|
|
|
|
"Build an index of help topics searching in the various help file locations."
|
|
|
|
(interactive)
|
|
|
|
(setq sclang-help-topic-alist nil)
|
|
|
|
(let ((case-fold-search nil)
|
2022-07-30 17:15:38 +00:00
|
|
|
(max-specpdl-size 10000)
|
|
|
|
(max-lisp-eval-depth 10000))
|
2009-01-02 19:06:25 +00:00
|
|
|
(sclang-message "Indexing help topics ...")
|
|
|
|
(setq sclang-help-topic-alist
|
2022-07-30 17:15:38 +00:00
|
|
|
(sclang-make-help-topic-alist (sclang-help-directories) nil))
|
2009-01-02 19:06:25 +00:00
|
|
|
(sclang-message "Indexing help topics ... Done")))
|
|
|
|
|
|
|
|
(defun sclang-edit-html-help-file ()
|
|
|
|
"Edit the help file associated with the current buffer.
|
|
|
|
Switches w3m to edit mode (actually HTML mode)."
|
|
|
|
(interactive)
|
2022-07-30 17:15:38 +00:00
|
|
|
(w3m-edit-current-url))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defun sclang-edit-help-code ()
|
|
|
|
"Edit the help file to make code variations.
|
2022-07-30 17:15:38 +00:00
|
|
|
Switches to text mode with `sclang-minor-mode'."
|
2009-01-02 19:06:25 +00:00
|
|
|
(interactive)
|
|
|
|
(w3m-copy-buffer)
|
2022-07-30 17:15:38 +00:00
|
|
|
;; (text-mode)
|
2009-01-02 19:06:25 +00:00
|
|
|
(sclang-mode)
|
2022-07-30 17:15:38 +00:00
|
|
|
(read-only-mode)
|
|
|
|
(rename-buffer "*SC_Help:CodeEdit*"))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defun sclang-edit-help-file ()
|
|
|
|
"Edit the help file associated with the current buffer.
|
|
|
|
Either visit file internally (.sc) or start external editor (.rtf)."
|
|
|
|
(interactive)
|
|
|
|
(if (and (boundp 'sclang-help-file) sclang-help-file)
|
|
|
|
(let ((file sclang-help-file))
|
2022-07-30 17:15:38 +00:00
|
|
|
(if (file-exists-p file)
|
|
|
|
(if (sclang-rtf-file-p file)
|
|
|
|
(start-process (sclang-make-buffer-name (format "HelpEditor:%s" file))
|
|
|
|
nil sclang-rtf-editor-program file)
|
|
|
|
(find-file file))
|
|
|
|
(if (sclang-html-file-p file)
|
|
|
|
(w3m-edit-current-url)
|
|
|
|
;; (find-file file)
|
|
|
|
)
|
|
|
|
(sclang-message "Help file not found")))
|
2009-01-02 19:06:25 +00:00
|
|
|
(sclang-message "Buffer has no associated help file")))
|
|
|
|
|
|
|
|
(defun sclang-help-topic-at-point ()
|
|
|
|
"Answer the help topic at point, or nil if not found."
|
|
|
|
(save-excursion
|
|
|
|
(with-syntax-table sclang-help-mode-syntax-table
|
|
|
|
(let (beg end)
|
2022-07-30 17:15:38 +00:00
|
|
|
(skip-syntax-backward "w_")
|
|
|
|
(setq beg (point))
|
|
|
|
(skip-syntax-forward "w_")
|
|
|
|
(setq end (point))
|
|
|
|
(goto-char beg)
|
|
|
|
(car (assoc (buffer-substring-no-properties beg end)
|
|
|
|
sclang-help-topic-alist))))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defun sclang-goto-help-browser ()
|
2022-07-30 17:15:38 +00:00
|
|
|
"Switch to the *w3m* buffer to browse help files."
|
2009-01-02 19:06:25 +00:00
|
|
|
(interactive)
|
|
|
|
(let* ((buffer-name "*w3m*")
|
2022-07-30 17:15:38 +00:00
|
|
|
(buffer (get-buffer buffer-name)))
|
2009-01-02 19:06:25 +00:00
|
|
|
(if buffer
|
2022-07-30 17:15:38 +00:00
|
|
|
(switch-to-buffer buffer)
|
2009-01-02 19:06:25 +00:00
|
|
|
;; else
|
|
|
|
(let* ((buffer-name "*SC_Help:w3m*")
|
2022-07-30 17:15:38 +00:00
|
|
|
(buffer2 (get-buffer buffer-name)))
|
|
|
|
(if buffer2
|
|
|
|
(switch-to-buffer buffer2)
|
|
|
|
;; else
|
|
|
|
(sclang-find-help "Help"))))
|
2009-01-02 19:06:25 +00:00
|
|
|
(if buffer
|
2022-07-30 17:15:38 +00:00
|
|
|
(with-current-buffer buffer
|
|
|
|
(rename-buffer "*SC_Help:w3m*")
|
|
|
|
;;(setq buffer-read-only false)
|
|
|
|
(sclang-help-minor-mode)))))
|
2009-01-02 19:06:25 +00:00
|
|
|
|
|
|
|
(defun sclang-find-help (topic)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Find help for TOPIC."
|
2009-01-02 19:06:25 +00:00
|
|
|
(interactive
|
|
|
|
(list
|
|
|
|
(let ((topic (or (and mark-active (buffer-substring-no-properties (region-beginning) (region-end)))
|
2011-09-30 09:45:52 +00:00
|
|
|
(sclang-help-topic-at-point)
|
|
|
|
"Help")))
|
2009-01-02 19:06:25 +00:00
|
|
|
(completing-read (format "Help topic%s: " (if (sclang-get-help-file topic)
|
2011-09-30 09:45:52 +00:00
|
|
|
(format " (default %s)" topic) ""))
|
|
|
|
sclang-help-topic-alist nil t nil 'sclang-help-topic-history topic))))
|
2009-01-02 19:06:25 +00:00
|
|
|
(let ((file (sclang-get-help-file topic)))
|
|
|
|
(if file
|
2011-09-30 09:45:52 +00:00
|
|
|
(if (file-exists-p file)
|
|
|
|
(let* ((buffer-name (sclang-help-buffer-name topic))
|
|
|
|
(buffer (get-buffer buffer-name)))
|
|
|
|
(unless buffer
|
|
|
|
(if (sclang-html-file-p file)
|
|
|
|
(w3m-find-file file)
|
|
|
|
;; (sclang-goto-help-browser)
|
|
|
|
;; not a sclang-html file
|
|
|
|
(setq buffer (get-buffer-create buffer-name))
|
|
|
|
(with-current-buffer buffer
|
|
|
|
(insert-file-contents file)
|
|
|
|
(let ((sclang-current-help-file file)
|
|
|
|
(default-directory (file-name-directory file)))
|
|
|
|
(sclang-help-mode))
|
|
|
|
(set-buffer-modified-p nil)))
|
|
|
|
(switch-to-buffer buffer))
|
|
|
|
(if (sclang-html-file-p file)
|
2022-07-30 17:15:38 +00:00
|
|
|
(sclang-goto-help-browser)))
|
2011-09-30 09:45:52 +00:00
|
|
|
(sclang-message "Help file not found") nil)
|
2009-01-02 19:06:25 +00:00
|
|
|
(sclang-message "No help for \"%s\"" topic) nil)))
|
|
|
|
|
2011-09-30 09:45:52 +00:00
|
|
|
|
|
|
|
(defun sclang-open-help-gui ()
|
2022-07-30 17:15:38 +00:00
|
|
|
"Open SCDoc Help Browser."
|
2011-09-30 09:45:52 +00:00
|
|
|
(interactive)
|
2022-07-30 17:15:38 +00:00
|
|
|
(sclang-eval-string (sclang-format "Help.gui")))
|
2011-09-30 09:45:52 +00:00
|
|
|
|
2011-10-01 11:16:04 +00:00
|
|
|
(defvar sclang-scdoc-topics (make-hash-table :size 16385)
|
|
|
|
"List of all scdoc topics.")
|
|
|
|
|
|
|
|
(sclang-set-command-handler
|
|
|
|
'helpSymbols
|
|
|
|
(lambda (list-of-symbols)
|
|
|
|
(mapcar (lambda (arg)
|
|
|
|
(puthash arg nil sclang-scdoc-topics))
|
2022-07-30 17:15:38 +00:00
|
|
|
list-of-symbols)))
|
2011-10-01 11:16:04 +00:00
|
|
|
|
2011-09-30 09:45:52 +00:00
|
|
|
(defun sclang-find-help-in-gui (topic)
|
2022-07-30 17:15:38 +00:00
|
|
|
"Search for TOPIC in Help Browser."
|
2011-09-30 09:45:52 +00:00
|
|
|
(interactive
|
|
|
|
(list
|
2011-10-01 11:16:04 +00:00
|
|
|
(let ((topic (sclang-symbol-at-point)))
|
2022-07-30 17:15:38 +00:00
|
|
|
(completing-read
|
|
|
|
(format "Help topic%s: " (if topic
|
|
|
|
(format " (default %s)" topic)
|
|
|
|
""))
|
|
|
|
sclang-scdoc-topics nil nil nil 'sclang-help-topic-history topic))))
|
2011-10-01 11:16:04 +00:00
|
|
|
(if topic
|
2022-07-30 17:15:38 +00:00
|
|
|
(sclang-eval-string
|
|
|
|
(sclang-format "HelpBrowser.openHelpFor(%o)" topic))
|
|
|
|
(sclang-eval-string
|
|
|
|
(sclang-format "Help.gui"))))
|
2011-10-01 11:16:04 +00:00
|
|
|
|
2011-09-30 09:45:52 +00:00
|
|
|
|
2009-01-02 19:06:25 +00:00
|
|
|
;; =====================================================================
|
|
|
|
;; module setup
|
|
|
|
;; =====================================================================
|
|
|
|
|
2011-10-01 11:16:04 +00:00
|
|
|
(add-hook 'sclang-library-startup-hook
|
|
|
|
(lambda ()
|
|
|
|
(sclang-perform-command 'helpSymbols)
|
|
|
|
(condition-case nil
|
|
|
|
(sclang-index-help-topics)
|
|
|
|
(error nil))))
|
|
|
|
|
|
|
|
(add-hook 'sclang-library-shutdown-hook
|
|
|
|
(lambda ()
|
|
|
|
(clrhash sclang-scdoc-topics)))
|
|
|
|
|
2022-07-30 17:15:38 +00:00
|
|
|
(add-to-list 'auto-mode-alist '("\\.rtf\\'" . sclang-help-mode))
|
|
|
|
|
2009-01-02 19:06:25 +00:00
|
|
|
;; ========= ADDITION for HTML help files?? ============
|
|
|
|
;; (add-to-list 'auto-mode-alist '("\\.html$" . sclang-help-mode))
|
|
|
|
;; (setq mm-text-html-renderer 'w3m)
|
|
|
|
;; (setq mm-inline-text-html-with-images t)
|
|
|
|
;; (setq mm-inline-text-html-with-w3m-keymap nil)
|
|
|
|
;; =====================================================
|
2022-07-30 17:15:38 +00:00
|
|
|
|
2009-01-02 19:06:25 +00:00
|
|
|
(sclang-fill-help-syntax-table sclang-help-mode-syntax-table)
|
|
|
|
(sclang-fill-help-mode-map sclang-help-mode-map)
|
|
|
|
|
|
|
|
(provide 'sclang-help)
|
|
|
|
|
2022-07-30 17:15:38 +00:00
|
|
|
;;; sclang-help.el ends here
|