This commit is contained in:
parent
aa2ff7b2f0
commit
2f3113f55d
11742 changed files with 1223940 additions and 0 deletions
7
unikernel/duniverse/dune_/editor-integration/emacs/dune
Normal file
7
unikernel/duniverse/dune_/editor-integration/emacs/dune
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
(install
|
||||
(package dune)
|
||||
(section share_root)
|
||||
(files
|
||||
(dune.el as emacs/site-lisp/dune.el)
|
||||
(dune-flymake.el as emacs/site-lisp/dune-flymake.el)
|
||||
(dune-watch.el as emacs/site-lisp/dune-watch.el)))
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
;;; dune-flymake.el --- Flymake support for dune files -*- coding: utf-8 -*-
|
||||
|
||||
;; Copyright 2017- Christophe Troestler
|
||||
;; URL: https://github.com/ocaml/dune
|
||||
;; Version: 1.0
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package complements the dune mode with on the fly tests to
|
||||
;; pinpoint errors.
|
||||
|
||||
;; Permission to use, copy, modify, and distribute this software for
|
||||
;; any purpose with or without fee is hereby granted, provided that
|
||||
;; the above copyright notice and this permission notice appear in
|
||||
;; all copies.
|
||||
;;
|
||||
;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
;; WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
;; WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
;; AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
;; CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
;; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
;; NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
;; CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
(require 'flymake)
|
||||
(require 'dune)
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defvar dune-flymake-temporary-file-directory
|
||||
(expand-file-name "dune" temporary-file-directory)
|
||||
"Directory where to duplicate the files for flymake.")
|
||||
|
||||
(defvar dune-flymake-program
|
||||
(expand-file-name "dune-lint" dune-flymake-temporary-file-directory)
|
||||
"Script to use to check the dune file.")
|
||||
|
||||
(defvar dune-flymake--allowed-file-name-masks
|
||||
'("\\(?:\\`\\|/\\)dune\\'" dune-flymake-init
|
||||
dune-flymake-cleanup)
|
||||
"Flymake entry for dune files. See `flymake-allowed-file-name-masks'.")
|
||||
|
||||
(defvar dune-flymake--err-line-patterns
|
||||
;; Beware that the path from the root will be reported by dune
|
||||
;; but flymake requires it to match the file name.
|
||||
'(("File \"[^\"]*\\(dune\\)\", line \\([0-9]+\\), \
|
||||
characters \\([0-9]+\\)-\\([0-9]+\\): +\\([^\n]*\\)$"
|
||||
1 2 3 5))
|
||||
"Value of `flymake-err-line-patterns' for dune files.")
|
||||
|
||||
(defun dune-flymake-create-lint-script ()
|
||||
"Create the lint script if it does not exist.
|
||||
This is needed as long as https://github.com/ocaml/dune/issues/241
|
||||
is not fixed."
|
||||
(unless (file-exists-p dune-flymake-program)
|
||||
(let ((dir (file-name-directory dune-program))
|
||||
(pgm "#!/usr/bin/env ocaml
|
||||
;;
|
||||
#load \"unix.cma\";;
|
||||
#load \"str.cma\";;
|
||||
|
||||
open Printf
|
||||
|
||||
let filename = Sys.argv.(1)
|
||||
let root = try Some(Sys.argv.(2)) with _ -> None
|
||||
|
||||
let read_all fh =
|
||||
let buf = Buffer.create 1024 in
|
||||
let b = Bytes.create 1024 in
|
||||
let len = ref 0 in
|
||||
while len := input fh b 0 1024; !len > 0 do
|
||||
Buffer.add_subbytes buf b 0 !len
|
||||
done;
|
||||
Buffer.contents buf
|
||||
|
||||
let errors =
|
||||
let root = match root with
|
||||
| None | Some \"\" -> \"\"
|
||||
| Some r -> \"--root=\" ^ Filename.quote r in
|
||||
let cmd = sprintf \"dune external-lib-deps %s %s\" root
|
||||
(Filename.quote (Filename.basename filename)) in
|
||||
let env = Unix.environment() in
|
||||
let (_,_,fh) as p = Unix.open_process_full cmd env in
|
||||
let out = read_all fh in
|
||||
match Unix.close_process_full p with
|
||||
| Unix.WEXITED (0|1) ->
|
||||
(* dune will normally exit with 1 as it will not be able to
|
||||
perform the requested action. *)
|
||||
out
|
||||
| Unix.WEXITED 127 -> printf \"dune not found in path.\\n\"; exit 1
|
||||
| Unix.WEXITED n -> printf \"dune exited with status %d.\\n\" n; exit 1
|
||||
| Unix.WSIGNALED n -> printf \"dune was killed by signal %d.\\n\" n;
|
||||
exit 1
|
||||
| Unix.WSTOPPED n -> printf \"dune was stopped by signal %d\\n.\" n;
|
||||
exit 1
|
||||
|
||||
|
||||
let () =
|
||||
let re = \"\\\\(:?\\\\)[\\r\\n]+\\\\([a-zA-Z]+\\\\)\" in
|
||||
let errors = Str.global_substitute (Str.regexp re)
|
||||
(fun s -> let colon = Str.matched_group 1 s = \":\" in
|
||||
let f = Str.matched_group 2 s in
|
||||
if f = \"File\" then \"\\n File\"
|
||||
else if colon then \": \" ^ f
|
||||
else \", \" ^ f)
|
||||
errors in
|
||||
print_string errors"))
|
||||
(make-directory dir t)
|
||||
(append-to-file pgm nil dune-program)
|
||||
(set-file-modes dune-program #o777)
|
||||
)))
|
||||
|
||||
(defun dune-flymake--temp-name (absolute-path)
|
||||
"Return the full path of the copy of ABSOLUTE-PATH in the temp dir.
|
||||
The temporary directory is given by `dune-flymake-temporary-file-directory'."
|
||||
(let ((slash-pos (string-match "/" absolute-path)))
|
||||
(file-truename (expand-file-name (substring absolute-path (1+ slash-pos))
|
||||
dune-flymake-temporary-file-directory))))
|
||||
|
||||
(defun dune-flymake--opam-files (dir)
|
||||
"Return all opam files in the directory DIR."
|
||||
(let ((files nil))
|
||||
(dolist (f (directory-files-and-attributes dir t ".*\\.opam\\'"))
|
||||
(when (null (cadr f))
|
||||
(push (car f) files)))
|
||||
files))
|
||||
|
||||
(defun dune-flymake--root (filename)
|
||||
"Return the Dune root for FILENAME.
|
||||
Create the temporary copy the necessary context files for dune."
|
||||
;; FIXME: the root depends on dune-project. If none is found,
|
||||
;; assume the commands are issued from the dir where opam files are found.
|
||||
(let* ((dir (locate-dominating-file (file-name-directory filename)
|
||||
#'dune-flymake--opam-files)))
|
||||
(when dir
|
||||
(setq dir (expand-file-name dir)); In case it is ~/...
|
||||
(make-directory (dune-flymake--temp-name dir) t)
|
||||
(dolist (f (dune-flymake--opam-files dir))
|
||||
(copy-file f (dune-flymake--temp-name f) t)))
|
||||
dir))
|
||||
|
||||
(defalias 'dune-flymake--safe-delete-file
|
||||
(if (fboundp 'flymake-proc--safe-delete-file)
|
||||
'flymake-proc--safe-delete-file
|
||||
'flymake-safe-delete-file))
|
||||
|
||||
(defun dune-flymake--delete-opam-files (dir)
|
||||
"Delete all opam files in the directory DIR."
|
||||
(dolist (f (dune-flymake--opam-files dir))
|
||||
(dune-flymake--safe-delete-file f)))
|
||||
|
||||
(defvaralias 'dune-flymake--temp-source-file-name
|
||||
(if (boundp 'flymake-proc--temp-source-file-name)
|
||||
'flymake-proc--temp-source-file-name
|
||||
'flymake-temp-source-file-name))
|
||||
|
||||
(defun dune-flymake-cleanup ()
|
||||
"Attempt to delete temp dir created by `dune-flymake-create-temp'.
|
||||
Do not fail on error."
|
||||
(let ((dir (file-name-directory dune-flymake--temp-source-file-name))
|
||||
(temp-dir (concat (directory-file-name
|
||||
dune-flymake-temporary-file-directory) "/")))
|
||||
(flymake-log 3 "Clean up %s" dune-flymake--temp-source-file-name)
|
||||
(dune-flymake--safe-delete-file dune-flymake--temp-source-file-name)
|
||||
(condition-case nil
|
||||
(delete-directory (expand-file-name "_build" dir) t)
|
||||
(error nil))
|
||||
;; Also delete parent dirs if empty or only contain opam files
|
||||
(while (and (not (string-equal dir temp-dir))
|
||||
(> (length dir) 0))
|
||||
(condition-case nil
|
||||
(progn
|
||||
(dune-flymake--delete-opam-files dir)
|
||||
(delete-directory dir)
|
||||
(setq dir (file-name-directory (directory-file-name dir))))
|
||||
(error ; then top the loop
|
||||
(setq dir ""))))))
|
||||
|
||||
(defalias 'dune-flymake--create-temp-buffer-copy
|
||||
(if (fboundp 'flymake-proc-init-create-temp-buffer-copy)
|
||||
'flymake-proc-init-create-temp-buffer-copy
|
||||
'flymake-init-create-temp-buffer-copy))
|
||||
|
||||
(defun dune-flymake-init ()
|
||||
"Set up dune-flymake."
|
||||
(dune-flymake-create-lint-script)
|
||||
(let ((fname (dune-flymake--create-temp-buffer-copy
|
||||
'dune-flymake-create-temp))
|
||||
(root (or (dune-flymake--root buffer-file-name) "")))
|
||||
(list dune-program (list fname root))))
|
||||
|
||||
(defun dune-flymake-dune-mode-hook ()
|
||||
"Hook to add to `dune-mode-hook' to enable lint tests."
|
||||
(push dune-flymake--allowed-file-name-masks
|
||||
(if (boundp 'flymake-proc-allowed-file-name-masks)
|
||||
flymake-proc-allowed-file-name-masks
|
||||
flymake-allowed-file-name-masks))
|
||||
(set (make-local-variable (if (boundp 'flymake-proc-err-line-patterns)
|
||||
'flymake-proc-err-line-patterns
|
||||
'flymake-err-line-patterns))
|
||||
dune-flymake--err-line-patterns))
|
||||
|
||||
(provide 'dune-flymake)
|
||||
|
||||
;;; dune-flymake.el ends here
|
||||
197
unikernel/duniverse/dune_/editor-integration/emacs/dune-watch.el
Normal file
197
unikernel/duniverse/dune_/editor-integration/emacs/dune-watch.el
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
;;; dune-watch.el --- provides integration with dune watch -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021 Kiran Gopinathan
|
||||
;; Author: Kiran Gopinathan <kirang@comp.nus.edu.sg>
|
||||
;; Keywords:
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package complements dune mode with integration with --watch
|
||||
;; tasks.
|
||||
|
||||
;; Permission to use, copy, modify, and distribute this software for
|
||||
;; any purpose with or without fee is hereby granted, provided that
|
||||
;; the above copyright notice and this permission notice appear in
|
||||
;; all copies.
|
||||
;;
|
||||
;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
;; WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
;; WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
;; AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
;; CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
;; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
;; NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
;; CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
;;; Code:
|
||||
(require 'subr-x)
|
||||
|
||||
;;;; Customisation
|
||||
(defgroup dune-watch nil
|
||||
"Customisation group for dune-watch-minor-mode."
|
||||
:prefix "dune-watch-mode-"
|
||||
:group 'applications)
|
||||
;;;;; Command configuration
|
||||
|
||||
(defcustom dune-watch-default-command "build"
|
||||
"Default command for dune-watch."
|
||||
:type 'string
|
||||
:group 'dune-watch)
|
||||
|
||||
(defcustom dune-watch-command-format
|
||||
"opam exec -- dune %s --watch"
|
||||
"Format of command to run to invoke dune watch mode."
|
||||
:type 'string
|
||||
:group 'dune-watch)
|
||||
|
||||
(defcustom dune-watch-delete-buffer-on-termination t
|
||||
"Whether to delete the dune watch buffer when the dune watch process terminates."
|
||||
:type 'bool
|
||||
:group 'dune-watch)
|
||||
|
||||
(defcustom dune-watch-popup-function #'display-buffer-pop-up-window
|
||||
"Function passed to `display-buffer' to present compilation output to user."
|
||||
:type 'function
|
||||
:group 'dune-watch)
|
||||
|
||||
(defcustom dune-watch-read-command t
|
||||
"Whether the user should be prompted to select a build task."
|
||||
:type 'bool
|
||||
:group 'dune-watch)
|
||||
|
||||
;;;; Constants
|
||||
|
||||
(defconst dune-watch-buffer-name "*dune-watch*"
|
||||
"Name of buffer used to track buffer outputs.")
|
||||
|
||||
(defconst dune-watch-process-name "*dune-watch-process*"
|
||||
"Name of process used to run dune watch.")
|
||||
|
||||
(defconst dune-watch-header "********** NEW BUILD"
|
||||
"Prefix of the header of dune watch output.")
|
||||
|
||||
(defconst dune-watch-read-prompt "dune task (default: %s): "
|
||||
"Prompt displayed to the user to dune task.")
|
||||
|
||||
(defconst dune-watch-supported-commands '("build" "test")
|
||||
"List of dune build tasks supported by dune-watch.")
|
||||
|
||||
;;;; Global variables
|
||||
|
||||
(defvar dune-watch-task-history nil
|
||||
"Variable used to track history of dune build tasks.")
|
||||
|
||||
;;;; Local variables
|
||||
(defvar-local dune-watch-buffer nil
|
||||
"Buffer corresponding to dune watch for current program")
|
||||
|
||||
(defvar-local dune-watch-process nil
|
||||
"Process corresponding to dune watch for current program")
|
||||
|
||||
(defvar-local dune-watch-header-start nil
|
||||
"Start of the header of the most recent dune watch output.")
|
||||
|
||||
;;;; Implementation
|
||||
|
||||
(defun dune-watch-generate-new-buffer ()
|
||||
"Return a new buffer to be used by dune watch."
|
||||
(let ((buffer (generate-new-buffer dune-watch-buffer-name)))
|
||||
(with-current-buffer buffer
|
||||
(compilation-mode)
|
||||
(setq dune-watch-buffer buffer)
|
||||
(setq dune-watch-process nil)
|
||||
(setq dune-watch-header-start nil))
|
||||
buffer))
|
||||
|
||||
(defun dune-watch-sentinel-function (watch-buffer process event)
|
||||
"Process sentinel used by dune-watch-minor-mode.
|
||||
|
||||
WATCH-BUFFER is the buffer used by dune watch.
|
||||
PROCESS is the dune-watch process name
|
||||
EVENT is the text output by the sentinel."
|
||||
(when (and dune-watch-delete-buffer-on-termination (buffer-live-p watch-buffer))
|
||||
(kill-buffer watch-buffer))
|
||||
(message "Dune watch process %s terminated with message \"%s\"" process (string-trim event)))
|
||||
|
||||
(defun dune-watch-update-header-start ()
|
||||
"Update the position of the header of the most recent dune watch output."
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(setq dune-watch-header-start
|
||||
(search-backward dune-watch-header nil t))))
|
||||
|
||||
(defun dune-watch-beautify-buffer ()
|
||||
"Cleans dune watch buffer by removing all but the last build output."
|
||||
(let ((inhibit-read-only t)
|
||||
(buffer-start (point-min)))
|
||||
(when (and dune-watch-header-start buffer-start)
|
||||
(delete-region buffer-start dune-watch-header-start)
|
||||
(setq dune-watch-header-start buffer-start))))
|
||||
|
||||
(defun dune-watch-contains-errors ()
|
||||
"Determines whether the dune watch buffer output contain any errors."
|
||||
(save-excursion
|
||||
(when dune-watch-header-start
|
||||
(goto-char dune-watch-header-start)
|
||||
(search-forward "File" nil t))))
|
||||
|
||||
(defun dune-watch-popup-buffer ()
|
||||
"Pops up compilation output to user."
|
||||
(when dune-watch-buffer
|
||||
(display-buffer dune-watch-buffer dune-watch-popup-function)))
|
||||
|
||||
(defun dune-watch-filter-function (watch-buffer process event)
|
||||
"Process filter function used by dune watch.
|
||||
|
||||
WATCH-BUFFER is the buffer corresponding to the process.
|
||||
PROCESS is the name of the process.
|
||||
EVENT is the string returned by the dune watch."
|
||||
(when (and watch-buffer (buffer-live-p watch-buffer))
|
||||
(with-current-buffer watch-buffer
|
||||
(let ((inhibit-read-only t)
|
||||
(buffer-end (point-max)))
|
||||
(goto-char buffer-end)
|
||||
(insert event)
|
||||
(dune-watch-update-header-start)
|
||||
(dune-watch-beautify-buffer)
|
||||
|
||||
(when (dune-watch-contains-errors)
|
||||
(dune-watch-popup-buffer))))))
|
||||
|
||||
(defun dune-watch-on-kill-buffer ()
|
||||
"Kill dune-watch process when main buffer is killed."
|
||||
(when (and dune-watch-minor-mode dune-watch-process (process-live-p dune-watch-process))
|
||||
(kill-process dune-watch-process)))
|
||||
|
||||
(defun start-dune-watch (command)
|
||||
"Start a subprocess to run the dune COMMAND using the --watch flag."
|
||||
(message "starting process to watch %s task..." command)
|
||||
(let* ((buffer (dune-watch-generate-new-buffer))
|
||||
(filter-process (apply-partially #'dune-watch-filter-function buffer))
|
||||
(sentinel-process (apply-partially #'dune-watch-sentinel-function buffer))
|
||||
(command (format dune-watch-command-format command)))
|
||||
(setq dune-watch-buffer buffer)
|
||||
(setq dune-watch-process (make-process
|
||||
:name dune-watch-process-name
|
||||
:buffer buffer
|
||||
:command (split-string command)
|
||||
:filter filter-process
|
||||
:sentinel sentinel-process))
|
||||
(add-hook 'kill-buffer-hook #'dune-watch-on-kill-buffer)))
|
||||
|
||||
(define-minor-mode dune-watch-minor-mode "A minor mode to run dune commands"
|
||||
:group 'dune-watch
|
||||
:init-value nil
|
||||
(let ((command dune-watch-default-command))
|
||||
(when dune-watch-read-command
|
||||
(setq command (or (completing-read
|
||||
(format dune-watch-read-prompt dune-watch-default-command)
|
||||
dune-watch-supported-commands
|
||||
nil nil dune-watch-default-command 'dune-watch-task-history)
|
||||
command)))
|
||||
(start-dune-watch command)))
|
||||
|
||||
(provide 'dune-watch)
|
||||
;;; dune-watch.el ends here
|
||||
463
unikernel/duniverse/dune_/editor-integration/emacs/dune.el
Normal file
463
unikernel/duniverse/dune_/editor-integration/emacs/dune.el
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
;;; dune.el --- Integration with the dune build system
|
||||
|
||||
;; Copyright 2018 Jane Street Group, LLC <opensource@janestreet.com>
|
||||
;; 2017- Christophe Troestler
|
||||
;; URL: https://github.com/ocaml/dune
|
||||
;; Version: 1.0
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This package provides helper functions for interacting with the
|
||||
;; dune build system from Emacs. It also prevides a mode to edit dune
|
||||
;; files.
|
||||
|
||||
;; Installation:
|
||||
;; You need to install the OCaml program ``dune''. The
|
||||
;; easiest way to do so is to install the opam package manager:
|
||||
;;
|
||||
;; https://opam.ocaml.org/doc/Install.html
|
||||
;;
|
||||
;; and then run "opam install dune".
|
||||
|
||||
;; This file is not part of GNU Emacs.
|
||||
|
||||
;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
;; WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
;; WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
;; AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
;; CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
;; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
;; NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
;; CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defgroup dune nil
|
||||
"Integration with the dune build system."
|
||||
:tag "Dune build system."
|
||||
:version "1.0"
|
||||
:group 'languages)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; Syntax highlighting of dune files
|
||||
|
||||
(defface dune-error-face
|
||||
'((t (:inherit error)))
|
||||
"Face for errors (e.g. obsolete constructs)."
|
||||
:group 'dune)
|
||||
|
||||
(defvar dune-error-face 'dune-error-face
|
||||
"Face for errors (e.g. obsolete constructs).")
|
||||
|
||||
(defface dune-separator-face
|
||||
'((t (:inherit default)))
|
||||
"Face for various kind of separators such as ':'."
|
||||
:group 'dune)
|
||||
(defvar dune-separator-face 'dune-separator-face
|
||||
"Face for various kind of separators such as ':'.")
|
||||
|
||||
(defconst dune-stanzas-regex
|
||||
(eval-when-compile
|
||||
(concat (regexp-opt
|
||||
'("library" "executable" "executables" "rule" "toplevel"
|
||||
"ocamllex" "ocamlyacc" "menhir" "alias" "install"
|
||||
"copy_files" "copy_files#" "include" "tests" "test" "dirs"
|
||||
"env" "ignored_subdirs" "include_subdirs" "data_only_dirs"
|
||||
"documentation" "cinaps" "coqlib" "coq.theory" "coq.pp"
|
||||
"foreign_library")
|
||||
) "\\(?:\\_>\\|[[:space:]]\\)"))
|
||||
"Stanzas in dune files.")
|
||||
|
||||
(defconst dune-fields-regex
|
||||
(eval-when-compile
|
||||
(regexp-opt
|
||||
'("name" "public_name" "synopsis" "modules" "libraries" "wrapped"
|
||||
"preprocess" "preprocessor_deps" "optional" "c_names" "cxx_names"
|
||||
"foreign_stubs" "foreign_archives" "install_c_headers" "modes"
|
||||
"no_dynlink" "kind" "ppx_runtime_libraries" "virtual_deps" "js_of_ocaml"
|
||||
"flags" "ocamlc_flags" "ocamlopt_flags" "library_flags" "c_flags"
|
||||
"cxx_flags" "c_library_flags" "self_build_stubs_archive" "inline_tests"
|
||||
"modules_without_implementation" "private_modules"
|
||||
;; + special_builtin_support
|
||||
"special_builtin_support" "build_info" "data_module" "api_version"
|
||||
;; +stdlib
|
||||
"stdlib" "modules_before_stdlib" "exit_module" "internal_modules"
|
||||
;; + virtual libraries
|
||||
"virtual_modules" "implements" "variant" "default_implementation"
|
||||
"allow_overlapping_dependencies"
|
||||
;; + for "executable" and "executables":
|
||||
"package" "link_flags" "link_deps" "names" "public_names" "variants"
|
||||
"forbidden_libraries"
|
||||
;; + for "foreign_library" and "foreign_stubs":
|
||||
"archive_name" "language" "names" "flags" "include_dirs" "extra_deps"
|
||||
;; + for "rule":
|
||||
"targets" "action" "deps" "mode" "fallback" "locks"
|
||||
;; + for "menhir":
|
||||
"merge_into"
|
||||
;; + for "cinaps":
|
||||
"files"
|
||||
;; + for "alias"
|
||||
"enabled_if"
|
||||
;; + for env
|
||||
"binaries"
|
||||
;; + for "install"
|
||||
"section" "files"
|
||||
;; Coq fields
|
||||
"theories" "modules_flags" "plugins")
|
||||
'symbols))
|
||||
"Field names allowed in dune files.")
|
||||
|
||||
(defconst dune-builtin-regex
|
||||
(eval-when-compile
|
||||
(concat (regexp-opt
|
||||
'(;; Linking modes
|
||||
"byte" "native" "best"
|
||||
;; modes
|
||||
"standard" "fallback" "promote" "promote-until-clean"
|
||||
;; Actions
|
||||
"run" "chdir" "setenv"
|
||||
"with-stdout-to" "with-stderr-to" "with-outputs-to"
|
||||
"ignore-stdout" "ignore-stderr" "ignore-outputs"
|
||||
"with-stdin-from" "with-exit-codes"
|
||||
"progn" "echo" "write-file" "cat" "copy" "copy#" "system"
|
||||
"bash" "diff" "diff?" "cmp"
|
||||
;; FIXME: "flags" is already a field and we do not have enough
|
||||
;; context to distinguishing both.
|
||||
"backend" "generate_runner" "runner_libraries" "flags"
|
||||
"extends"
|
||||
;; Dependency specification
|
||||
"file" "alias" "alias_rec" "glob_files" "files_recursively_in"
|
||||
"universe" "package" "source_tree" "env_var")
|
||||
t)
|
||||
"\\(?:\\_>\\|[[:space:]]\\)"))
|
||||
"Builtin sub-fields in dune.")
|
||||
|
||||
(defconst dune-builtin-labels-regex
|
||||
(regexp-opt '("standard" "include") 'words)
|
||||
"Builtin :labels in dune.")
|
||||
|
||||
(defvar dune-var-kind-regex
|
||||
(eval-when-compile
|
||||
(regexp-opt
|
||||
'("ocaml-config"
|
||||
"dep" "exe" "bin" "lib" "libexec" "lib-available"
|
||||
"version" "read" "read-lines" "read-strings")
|
||||
'words))
|
||||
"Optional prefix to variable names.")
|
||||
|
||||
(defmacro dune--field-vals (field &rest vals)
|
||||
"Build a `font-lock-keywords' rule for the dune FIELD accepting values VALS."
|
||||
`(list (concat "(" ,field "[[:space:]]+" ,(regexp-opt vals t))
|
||||
1 font-lock-constant-face))
|
||||
|
||||
(defvar dune-font-lock-keywords
|
||||
`((,(concat "(\\(" dune-stanzas-regex "\\)") 1 font-lock-keyword-face)
|
||||
("([^ ]+ +\\(as\\) +[^ ]+)" 1 font-lock-keyword-face)
|
||||
(,(concat "(" dune-fields-regex) 1 font-lock-function-name-face)
|
||||
(,(concat "%{" dune-var-kind-regex " *\\(\\:\\)[^{}:]*\\(\\(?::\\)?\\)")
|
||||
(1 font-lock-builtin-face)
|
||||
(2 dune-separator-face)
|
||||
(3 dune-separator-face))
|
||||
("%{\\([^{}]*\\)}" 1 font-lock-variable-name-face keep)
|
||||
(,(concat "\\(:" dune-builtin-labels-regex "\\)[[:space:]()\n]")
|
||||
1 font-lock-builtin-face)
|
||||
;; Named dependencies:
|
||||
("(\\(:[a-zA-Z]+\\)[[:space:]]+" 1 font-lock-variable-name-face)
|
||||
("\\(true\\|false\\)" 1 font-lock-constant-face)
|
||||
("(\\(select\\)[[:space:]]+[^[:space:]]+[[:space:]]+\\(from\\)\\>"
|
||||
(1 font-lock-constant-face)
|
||||
(2 font-lock-constant-face))
|
||||
,(eval-when-compile
|
||||
(dune--field-vals "kind" "normal" "ppx_rewriter" "ppx_deriver"))
|
||||
,(eval-when-compile
|
||||
(dune--field-vals "mode" "standard" "fallback" "promote"
|
||||
"promote-until-clean"))
|
||||
(,(concat "(" dune-builtin-regex) 1 font-lock-builtin-face)
|
||||
("(preprocess[[:space:]]+(\\(pps\\)" 1 font-lock-builtin-face)
|
||||
("(name +\\(runtest\\))" 1 font-lock-builtin-face)
|
||||
(,(eval-when-compile
|
||||
(concat "(" (regexp-opt '("fallback") t)))
|
||||
1 dune-error-face)))
|
||||
|
||||
(defvar dune-mode-syntax-table
|
||||
(let ((table (make-syntax-table)))
|
||||
(modify-syntax-entry ?\; "< b" table)
|
||||
(modify-syntax-entry ?\n "> b" 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)
|
||||
"Dune syntax table.")
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; SMIE
|
||||
|
||||
(require 'smie)
|
||||
|
||||
(defvar dune-smie-grammar
|
||||
(when (fboundp 'smie-prec2->grammar)
|
||||
(smie-prec2->grammar
|
||||
(smie-bnf->prec2 '()))))
|
||||
|
||||
(defun dune-smie-rules (kind token)
|
||||
"Rules for `smie-setup'.
|
||||
See `smie-rules-function' for the meaning of KIND and TOKEN."
|
||||
(cond
|
||||
((eq kind :close-all) '(column . 0))
|
||||
((and (eq kind :after) (equal token ")"))
|
||||
(save-excursion
|
||||
(goto-char (cadr (smie-indent--parent)))
|
||||
(if (looking-at-p dune-stanzas-regex)
|
||||
'(column . 0)
|
||||
1)))
|
||||
((eq kind :before)
|
||||
(if (smie-rule-parent-p "(")
|
||||
(save-excursion
|
||||
(goto-char (cadr (smie-indent--parent)))
|
||||
(cond
|
||||
((looking-at-p dune-stanzas-regex) 1)
|
||||
((looking-at-p dune-fields-regex)
|
||||
(smie-rule-parent 0))
|
||||
((smie-rule-sibling-p) (cons 'column (current-column)))
|
||||
(t (cons 'column (current-column)))))
|
||||
'(column . 0)))
|
||||
((eq kind :list-intro)
|
||||
nil)
|
||||
(t 1)))
|
||||
|
||||
(defun dune-smie-rules-verbose (kind token)
|
||||
"Same as `dune-smie-rules' but echoing information.
|
||||
See `smie-rules-function' for the meaning of KIND and TOKEN."
|
||||
(let ((value (dune-smie-rules kind token)))
|
||||
(message
|
||||
"%s '%s'; sibling-p:%s parent:%s hanging:%s = %s"
|
||||
kind token
|
||||
(ignore-errors (smie-rule-sibling-p))
|
||||
(ignore-errors smie--parent)
|
||||
(ignore-errors (smie-rule-hanging-p))
|
||||
value)
|
||||
value))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; Skeletons
|
||||
;; See Info node "Autotype".
|
||||
|
||||
(define-skeleton dune-insert-library-form
|
||||
"Insert a library stanza."
|
||||
nil
|
||||
"(library" > \n
|
||||
"(name " _ ")" > \n
|
||||
"(public_name " _ ")" > \n
|
||||
"(libraries " _ ")" > \n
|
||||
"(synopsis \"" _ "\"))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-executable-form
|
||||
"Insert an executable stanza."
|
||||
nil
|
||||
"(executable" > \n
|
||||
"(name " _ ")" > \n
|
||||
"(public_name " _ ")" > \n
|
||||
"(modules " _ ")" > \n
|
||||
"(libraries " _ "))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-executables-form
|
||||
"Insert an executables stanza."
|
||||
nil
|
||||
"(executables" > \n
|
||||
"(names " _ ")" > \n
|
||||
"(public_names " _ ")" > \n
|
||||
"(libraries " _ "))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-rule-form
|
||||
"Insert a rule stanza."
|
||||
nil
|
||||
"(rule" > \n
|
||||
"(targets " _ ")" > \n
|
||||
"(deps " _ ")" > \n
|
||||
"(action (" _ ")))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-ocamllex-form
|
||||
"Insert an ocamllex stanza."
|
||||
nil
|
||||
"(ocamllex (" _ "))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-ocamlyacc-form
|
||||
"Insert an ocamlyacc stanza."
|
||||
nil
|
||||
"(ocamlyacc (" _ "))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-menhir-form
|
||||
"Insert a menhir stanza."
|
||||
nil
|
||||
"(menhir" > \n
|
||||
"((modules (" _ "))))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-alias-form
|
||||
"Insert an alias stanza."
|
||||
nil
|
||||
"(alias" > \n
|
||||
"(name " _ ")" > \n
|
||||
"(deps " _ "))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-install-form
|
||||
"Insert an install stanza."
|
||||
nil
|
||||
"(install" > \n
|
||||
"(section " _ ")" > \n
|
||||
"(files " _ "))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-copyfiles-form
|
||||
"Insert a copy_files stanza."
|
||||
nil
|
||||
"(copy_files " _ ")" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-test-form
|
||||
"Insert a test stanza."
|
||||
nil
|
||||
"(test" > \n
|
||||
"(name " _ "))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-tests-form
|
||||
"Insert a tests stanza."
|
||||
nil
|
||||
"(tests" > \n
|
||||
"(names " _ "))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-env-form
|
||||
"Insert a env stanza."
|
||||
nil
|
||||
"(env" > \n
|
||||
"(" _ " " _ "))" > ?\n)
|
||||
|
||||
(define-skeleton dune-insert-ignored-subdirs-form
|
||||
"Insert a ignored_subdirs stanza."
|
||||
nil
|
||||
"(ignored_subdirs (" _ "))" > ?\n)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defvar dune-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map "\C-c\C-c" 'compile)
|
||||
(define-key map "\C-c.l" 'dune-insert-library-form)
|
||||
(define-key map "\C-c.e" 'dune-insert-executable-form)
|
||||
(define-key map "\C-c.x" 'dune-insert-executables-form)
|
||||
(define-key map "\C-c.r" 'dune-insert-rule-form)
|
||||
(define-key map "\C-c.p" 'dune-insert-ocamllex-form)
|
||||
(define-key map "\C-c.y" 'dune-insert-ocamlyacc-form)
|
||||
(define-key map "\C-c.m" 'dune-insert-menhir-form)
|
||||
(define-key map "\C-c.a" 'dune-insert-alias-form)
|
||||
(define-key map "\C-c.i" 'dune-insert-install-form)
|
||||
(define-key map "\C-c.c" 'dune-insert-copyfiles-form)
|
||||
(define-key map "\C-c.t" 'dune-insert-tests-form)
|
||||
(define-key map "\C-c.v" 'dune-insert-env-form)
|
||||
(define-key map "\C-c.d" 'dune-insert-ignored-subdirs-form)
|
||||
map)
|
||||
"Keymap used in dune mode.")
|
||||
|
||||
(defun dune-build-menu ()
|
||||
"Build the menu for `dune-mode'."
|
||||
(easy-menu-define
|
||||
dune-mode-menu (list dune-mode-map)
|
||||
"dune mode menu."
|
||||
'("Dune/jbuild"
|
||||
("Stanzas"
|
||||
["library" dune-insert-library-form t]
|
||||
["executable" dune-insert-executable-form t]
|
||||
["executables" dune-insert-executables-form t]
|
||||
["rule" dune-insert-rule-form t]
|
||||
["alias" dune-insert-alias-form t]
|
||||
["ocamllex" dune-insert-ocamllex-form t]
|
||||
["ocamlyacc" dune-insert-ocamlyacc-form t]
|
||||
["menhir" dune-insert-menhir-form t]
|
||||
["install" dune-insert-install-form t]
|
||||
["copy_files" dune-insert-copyfiles-form t]
|
||||
["test" dune-insert-test-form t]
|
||||
["env" dune-insert-env-form t]
|
||||
["ignored_subdirs" dune-insert-ignored-subdirs-form t]
|
||||
)))
|
||||
(easy-menu-add dune-mode-menu))
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(define-derived-mode dune-mode prog-mode "dune"
|
||||
"Major mode to edit dune files.
|
||||
For customization purposes, use `dune-mode-hook'."
|
||||
(set (make-local-variable 'font-lock-defaults) '(dune-font-lock-keywords))
|
||||
(set (make-local-variable 'comment-start) ";")
|
||||
(set (make-local-variable 'comment-end) "")
|
||||
(setq indent-tabs-mode nil)
|
||||
(set (make-local-variable 'require-final-newline) mode-require-final-newline)
|
||||
(smie-setup dune-smie-grammar #'dune-smie-rules)
|
||||
(dune-build-menu))
|
||||
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist
|
||||
'("\\(?:\\`\\|/\\)dune\\(?:\\.inc\\|\\-project\\|\\-workspace\\)?\\'" . dune-mode))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;; Interacting with dune
|
||||
|
||||
(defcustom dune-command "dune"
|
||||
"The dune command."
|
||||
:type 'string)
|
||||
|
||||
;;;###autoload
|
||||
(defun dune-promote ()
|
||||
"Promote the correction for the current file."
|
||||
(interactive)
|
||||
(if (buffer-modified-p)
|
||||
(error "Cannot promote as buffer is modified")
|
||||
(shell-command
|
||||
(format
|
||||
"%s promote %s" dune-command
|
||||
(shell-quote-argument (file-name-nondirectory (buffer-file-name)))))
|
||||
(revert-buffer nil t)))
|
||||
|
||||
;;;###autoload
|
||||
(defun dune-runtest-and-promote ()
|
||||
"Run tests in the current directory and promote the current buffer."
|
||||
(interactive)
|
||||
(compile (format "%s build @@runtest" dune-command))
|
||||
(dune-promote))
|
||||
|
||||
(defun dune-project-p (directory)
|
||||
"Return t if DIRECTORY is a dune project."
|
||||
(file-exists-p (expand-file-name "dune-project" directory)))
|
||||
|
||||
(defun dune-workspace-p (directory)
|
||||
"Return t if DIRECTORY is a dune workspace."
|
||||
(file-exists-p (expand-file-name "dune-workspace" directory)))
|
||||
|
||||
(defun dune-root (&optional directory)
|
||||
"Return the root directory of the dune project of DIRECTORY.
|
||||
|
||||
DIRECTORY defaults to `default-directory' if not provided."
|
||||
(let*
|
||||
(root
|
||||
workspace
|
||||
(dir (or directory default-directory))
|
||||
(project-p (lambda (dir)
|
||||
(cond
|
||||
((dune-workspace-p dir)
|
||||
(setq workspace t)
|
||||
t)
|
||||
((and
|
||||
(not workspace)
|
||||
(dune-project-p dir))
|
||||
t)))))
|
||||
(while dir
|
||||
(setq dir (locate-dominating-file dir project-p))
|
||||
(when dir
|
||||
(setq root dir
|
||||
dir (file-name-parent-directory dir))))
|
||||
root))
|
||||
|
||||
(provide 'dune)
|
||||
|
||||
;;; dune.el ends here
|
||||
Loading…
Add table
Add a link
Reference in a new issue