This commit is contained in:
parent
aa2ff7b2f0
commit
2f3113f55d
11742 changed files with 1223940 additions and 0 deletions
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