summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--emacs/caml-help.el180
-rw-r--r--emacs/caml.el132
2 files changed, 226 insertions, 86 deletions
diff --git a/emacs/caml-help.el b/emacs/caml-help.el
index 9326532d3..881a5572e 100644
--- a/emacs/caml-help.el
+++ b/emacs/caml-help.el
@@ -170,7 +170,7 @@
(if (equal tag 'info)
(setq dir (car ocaml-lib-path)) ; XXX to be fixed
)
- (setq file (concat dir (ocaml-uncapitalize module) ".mli"))
+ (setq file (concat dir "/" (ocaml-uncapitalize module) ".mli"))
(message file)
(save-window-excursion
(set-buffer (get-buffer-create "*caml-help*"))
@@ -180,8 +180,10 @@
(insert-file-contents file))
(message "Module %s not found" module))
(while (re-search-forward
- "^\\([ \t]*val\\|let\\|external\\) \\([^ (:=]*\\)" (point-max) 'move)
- (setq alist (cons (match-string 2) alist)))
+ "\\([ \t]*val\\|let\\|external\\| [|]\\) \\([a-zA-Z_0-9'][a-zA-Z_0-9']*\\)\\|^ *[{]* \\([a-z_][A-Za-z_0-9]*\\) : [^;\n][^;\n]*;"
+ (point-max) 'move)
+ (pop-to-buffer (current-buffer))
+ (setq alist (cons (or (match-string 2) (match-string 3)) alist)))
(erase-buffer)
)
(setcdr tail alist)
@@ -235,7 +237,7 @@ Otherwise if ARG is true, close all modules and reset to default. "
(if (= (prefix-numeric-value arg) 4)
(setq ocaml-visible-modules 'lazy)
(let* ((modules (ocaml-visible-modules)) default)
- (if (null modules) (error-message "No visible module to close"))
+ (if (null modules) (error "No visible module to close"))
(unless (stringp arg)
(setq arg
(completing-read
@@ -277,7 +279,7 @@ with an optional non-nil argument.
(progn
(setq module (cons (match-beginning 1) (match-end 1)))
(goto-char (match-end 0))))
- (if (looking-at "\\<\\([a-z_][A-Za-z0-9_']*\\)\\>")
+ (if (looking-at "\\<\\([A-Za-z_][A-Za-z0-9_']*\\)\\>")
(setq entry (cons (match-beginning 1) (match-end 1))))))
(if show
(concat
@@ -392,6 +394,7 @@ where identifier is defined."
((not (string-equal pattern completion))
(delete-region beg end)
+ (goto-char beg)
(insert completion))
(t
@@ -558,25 +561,25 @@ command. An entry may be an info module or a complete file name."
If unspecified, MODULE and ENTRY are inferred from the position in the
current buffer using \\[ocaml-qualified-identifier]."
(interactive)
- (let ((window (selected-window)))
- (let ((info-section (assoc module (ocaml-info-alist))))
- (if info-section (info-other-window (cdr info-section))
- (ocaml-visible-modules)
- (let* ((module-info
- (or (assoc module (ocaml-module-alist))
- (and (file-exists-p
- (concat (ocaml-uncapitalize module) ".mli"))
- (ocaml-get-or-make-module module))))
- (location (cdr (cadr module-info))))
- (cond
- (location
- ; (view-file
- (view-file-other-window
- (concat location (ocaml-uncapitalize module) ".mli"))
- (bury-buffer (current-buffer)))
- (info-section (error "Aborted"))
- (t (error "No help for module %s" module))))
- ))
+ (let ((window (selected-window))
+ (info-section (assoc module (ocaml-info-alist))))
+ (if info-section
+ (info-other-window (cdr info-section))
+ (ocaml-visible-modules)
+ (let* ((module-info
+ (or (assoc module (ocaml-module-alist))
+ (and (file-exists-p
+ (concat (ocaml-uncapitalize module) ".mli"))
+ (ocaml-get-or-make-module module))))
+ (location (cdr (cadr module-info))))
+ (cond
+ (location
+ (view-file-other-window
+ (concat location (ocaml-uncapitalize module) ".mli"))
+ (bury-buffer (current-buffer)))
+ (info-section (error "Aborted"))
+ (t (error "No help for module %s" module))))
+ )
(if (stringp entry)
(let ((here (point))
(case-fold-search nil))
@@ -585,6 +588,9 @@ current buffer using \\[ocaml-qualified-identifier]."
(concat "\\(val\\|exception\\|external\\|[|{;]\\) +"
(regexp-quote entry))
(point-max) t)
+ (re-search-forward
+ (concat "type [^{]*{[^}]*" (regexp-quote entry) " :")
+ (point-max) t)
(progn
(if (window-live-p window) (select-window window))
(error "Entry %S not found in module %S"
@@ -596,24 +602,32 @@ current buffer using \\[ocaml-qualified-identifier]."
(message "Help for entry %s not found in module %s"
entry module)
(goto-char here)))))
+ (ocaml-link-activate (cdr info-section))
(if (window-live-p window) (select-window window))
))
(defun caml-help (arg)
"Find help for qualified identifiers.
-It attemps to recognize an qualified identifier of the form Module . entry
-around point using function \\[ocaml-qualified-identifier].
+It attemps to recognize an qualified identifier of the form
+``Module . entry'' around point using function `ocaml-qualified-identifier'.
-If Module is undefined it finds it from indentifier and visible modules,
-or asks the user interactively.
+If Module is undetermined it is temptatively guessed from the identifier name
+and according to visible modules. If this is still unsucessful, the user is
+then prompted for a Module name.
-It then opens the info documentation for Module if available or
-to the Module.mli file otherwises, and searches for entry.
+The documentation for Module is first seach in the info manual if available,
+then in the ``module.mli'' source file. The entry is then searched in the documentation.
-With prefix arg 0, it recomputes visible modules and their content.
-With prefix arg 4, prompts for Module and identifier instead
-of using contextual values.
+Visible modules are computed only once, at the first call.
+Modules can be made visible explicitly with `ocaml-open-module' and
+hidden with `ocaml-close-module'.
+
+Prefix arg 0 forces recompilation of visible modules (and their content)
+from the file content.
+
+Prefix arg 4 prompts for Module and identifier instead of guessing values
+from the possition of point in the current buffer.
"
(interactive "p")
(let ((module) (entry) (module-entry))
@@ -662,6 +676,99 @@ of using contextual values.
(ocaml-goto-help module entry)
))
+;; auto-links
+
+(defconst ocaml-link-regexp
+ "\\(type\\|and\\) \\('[a-z] +\\|(\\('[a-z], *\\)*'[a-z])\\|\\) *\\([a-zA-Z0-9_]*\\)\\( *$\\| =\\)")
+(defconst ocaml-longident-regexp
+ "\\([A-Z][a-zA-Z_0]*\\)[.]\\([a-zA-Z][A-Za-z0-9_]*\\)")
+
+(defvar ocaml-links nil
+ "Local links in the current of last info node or interface file.
+
+The car of the list is a key that indentifies the module to prevent
+recompilation when next help command is relative to the same module.
+The cdr is a list of elments, each of which is an string and a pair of
+buffer positions."
+)
+(make-variable-buffer-local 'ocaml-links)
+
+(defun ocaml-info-links (section)
+ (if (and ocaml-links section (equal (car ocaml-links) section))
+ (cdr ocaml-links)
+ (save-excursion
+ (goto-char (point-min))
+ (let ((regexp (concat (if (equal major-mode 'Info-mode) "^ - " "^")
+ ocaml-link-regexp))
+ (all))
+ (while (re-search-forward regexp (point-max) t)
+ (setq all
+ (cons (cons (match-string 4)
+ (cons (match-beginning 4)
+ (match-end 4)))
+ all)))
+ (setq ocaml-links (cons section all))
+ ))))
+
+(defvar ocaml-link-map (make-sparse-keymap))
+(define-key ocaml-link-map [mouse-2] 'ocaml-link-goto)
+
+(defun ocaml-link-goto (click)
+ (interactive "e")
+ (let* ((start (event-start click))
+ (pos (posn-point start))
+ (buf (window-buffer (posn-window start)))
+ (window (selected-window))
+ (link))
+ (setq link
+ (with-current-buffer buf
+ (buffer-substring (previous-property-change
+ pos buf (- pos 100))
+ (next-property-change
+ pos buf (+ pos 100)))))
+ (if (string-match (concat "^" ocaml-longident-regexp "$") link)
+ (ocaml-goto-help (match-string 1 link) (match-string 2 link))
+ (if (not (equal (window-buffer window) buf))
+ (switch-to-buffer-other-window buf))
+ (if (setq link (assoc link (cdr ocaml-links)))
+ (progn
+ (goto-char (cadr link))
+ (recenter 1)))
+ (if (window-live-p window) (select-window window))
+ )))
+
+(cond
+ ((and (x-display-color-p)
+ (not (memq 'ocaml-link-face (face-list))))
+ (make-face 'ocaml-link-face)
+ (set-face-foreground 'ocaml-link-face "Purple")))
+
+
+(defun ocaml-link-activate (section)
+ (if (cdr (ocaml-info-links section))
+ (let ((regexp (concat "[^A-Za-z0-9'_]\\("
+ ocaml-longident-regexp "\\|"
+ (mapconcat 'car (cdr ocaml-links) "\\|")
+ "\\)[^A-Za-z0-9'_]"))
+ (case-fold-search nil))
+ (goto-char (point-min))
+ (unwind-protect
+ (save-excursion
+ (setq buffer-read-only nil)
+ (goto-char (point-min))
+ (while (re-search-forward regexp (point-max) t)
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'mouse-face 'highlight)
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'local-map ocaml-link-map)
+ (if (x-display-color-p)
+ (put-text-property (match-beginning 1) (match-end 1)
+ 'face 'ocaml-link-face)))
+ )
+ (setq buffer-read-only t))
+ )))
+
+
;; bindings
@@ -669,6 +776,9 @@ of using contextual values.
(boundp 'caml-mode-map)
(keymapp caml-mode-map)
(progn
+ (define-key caml-mode-map [?\C-c?i] 'ocaml-add-path)
+ (define-key caml-mode-map [?\C-c?]] 'ocaml-close-module)
+ (define-key caml-mode-map [?\C-c?[] 'ocaml-open-module)
(define-key caml-mode-map [?\C-c?\C-h] 'caml-help)
(define-key caml-mode-map [?\C-c?\t] 'caml-complete)
(let ((map (lookup-key caml-mode-map [menu-bar caml])))
@@ -676,6 +786,10 @@ of using contextual values.
(keymapp map)
(progn
(define-key map [separator-help] '("---"))
+ (define-key map [open] '("Open add path" . ocaml-add-path ))
+ (define-key map [close]
+ '("Close module for help" . ocaml-close-module))
+ (define-key map [open] '("Open module for help" . ocaml-open-module))
(define-key map [help] '("Help for identifier" . caml-help))
(define-key map [complete] '("Complete identifier" . caml-complete))
)
diff --git a/emacs/caml.el b/emacs/caml.el
index 05e722f53..09df4809d 100644
--- a/emacs/caml.el
+++ b/emacs/caml.el
@@ -489,13 +489,31 @@ have caml-electric-indent on, which see.")
(run-hooks 'caml-mode-hook))
(defun caml-set-compile-command ()
+ "Hook to set compile-command locally, unless there is a Makefile in the
+ current directory."
(interactive)
- (unless (or (file-exists-p "makefile")
+ (unless (or (null buffer-file-name)
+ (file-exists-p "makefile")
(file-exists-p "Makefile"))
- (make-local-variable 'compile-command)
- (setq compile-command
- (concat "ocamlc -c "
- (file-name-nondirectory buffer-file-name)))))
+ (let* ((filename (file-name-nondirectory buffer-file-name))
+ (basename (file-name-sans-extension filename))
+ (command nil))
+ (cond
+ ((string-match ".*\\.mli\$" filename)
+ (setq command "ocamlc -c"))
+ ((string-match ".*\\.ml\$" filename)
+ (setq command "ocamlc -c") ; (concat "ocamlc -o " basename)
+ )
+ ((string-match ".*\\.mll\$" filename)
+ (setq command "ocamllex"))
+ ((string-match ".*\\.mll\$" filename)
+ (setq command "ocamlyacc"))
+ )
+ (if command
+ (progn
+ (make-local-variable 'compile-command)
+ (setq compile-command (concat command " " filename))))
+ )))
(add-hook 'caml-mode-hook 'caml-set-compile-command)
@@ -696,11 +714,18 @@ variable caml-mode-indentation."
(defun forward-byte (count)
(if (> count 0)
(while (> count 0)
- (setq count (- count (caml-char-bytes (char-after))))
- (forward-char))
+ (let ((char (char-after)))
+ (if (null char)
+ (setq count 0)
+ (setq count (- count (caml-char-bytes (char-after))))
+ (forward-char))))
(while (< count 0)
- (setq count (+ count (caml-char-bytes (char-before))))
- (backward-char)))))
+ (let ((char (char-after)))
+ (if (null char)
+ (setq count 0)
+ (setq count (+ count (caml-char-bytes (char-before))))
+ (backward-char))))
+ )))
(require 'compile)
@@ -997,50 +1022,51 @@ Returns nil for the parenthesis openning a comment."
;;style is used, literals are never split across lines, so we don't
;;have to worry about bogus phrase breaks inside literals, while we
;;have to account for that possibility in comments.
- (save-excursion
- (let* ((cached-pos caml-last-noncomment-pos)
- (cached-begin (marker-position caml-last-comment-start))
- (cached-end (marker-position caml-last-comment-end)))
- (cond
- ((and cached-begin cached-end
- (< cached-begin (point)) (< (point) cached-end)) t)
- ((and cached-pos (= cached-pos (point))) nil)
- ((and cached-pos (> cached-pos (point))
- (< (abs (- cached-pos (point))) caml-lookback-limit))
- (let (end found (here (point)))
- ; go back to somewhere sure
- (goto-char cached-pos)
- (while (> (point) here)
- ; look for the end of a comment
- (while (and (if (search-backward comment-end (1- here) 'move)
- (setq end (match-end 0))
- (setq end nil))
- (caml-in-literal-p)))
- (if end (setq found (caml-backward-comment))))
- (if (and found (= (point) here)) (setq end nil))
- (if (not end)
- (setq caml-last-noncomment-pos here)
- (set-marker caml-last-comment-start (point))
- (set-marker caml-last-comment-end end))
- end))
- (t
- (let (begin found (here (point)))
- ; go back to somewhere sure (or far enough)
- (goto-char
- (if cached-pos cached-pos (- (point) caml-lookback-limit)))
- (while (< (point) here)
- ; look for the beginning of a comment
- (while (and (if (search-forward comment-start (1+ here) 'move)
- (setq begin (match-beginning 0))
- (setq begin nil))
- (caml-in-literal-p)))
- (if begin (setq found (caml-forward-comment))))
- (if (and found (= (point) here)) (setq begin nil))
- (if (not begin)
- (setq caml-last-noncomment-pos here)
- (set-marker caml-last-comment-start begin)
- (set-marker caml-last-comment-end (point)))
- begin))))))
+ (if caml-last-comment-start
+ (save-excursion
+ (let* ((cached-pos caml-last-noncomment-pos)
+ (cached-begin (marker-position caml-last-comment-start))
+ (cached-end (marker-position caml-last-comment-end)))
+ (cond
+ ((and cached-begin cached-end
+ (< cached-begin (point)) (< (point) cached-end)) t)
+ ((and cached-pos (= cached-pos (point))) nil)
+ ((and cached-pos (> cached-pos (point))
+ (< (abs (- cached-pos (point))) caml-lookback-limit))
+ (let (end found (here (point)))
+ ; go back to somewhere sure
+ (goto-char cached-pos)
+ (while (> (point) here)
+ ; look for the end of a comment
+ (while (and (if (search-backward comment-end (1- here) 'move)
+ (setq end (match-end 0))
+ (setq end nil))
+ (caml-in-literal-p)))
+ (if end (setq found (caml-backward-comment))))
+ (if (and found (= (point) here)) (setq end nil))
+ (if (not end)
+ (setq caml-last-noncomment-pos here)
+ (set-marker caml-last-comment-start (point))
+ (set-marker caml-last-comment-end end))
+ end))
+ (t
+ (let (begin found (here (point)))
+ ;; go back to somewhere sure (or far enough)
+ (goto-char
+ (if cached-pos cached-pos (- (point) caml-lookback-limit)))
+ (while (< (point) here)
+ ;; look for the beginning of a comment
+ (while (and (if (search-forward comment-start (1+ here) 'move)
+ (setq begin (match-beginning 0))
+ (setq begin nil))
+ (caml-in-literal-p)))
+ (if begin (setq found (caml-forward-comment))))
+ (if (and found (= (point) here)) (setq begin nil))
+ (if (not begin)
+ (setq caml-last-noncomment-pos here)
+ (set-marker caml-last-comment-start begin)
+ (set-marker caml-last-comment-end (point)))
+ begin)))))))
;; Various constants and regexps