company-mlton
is a
company-mode
completion back-end for
MLton/Standard ML. It provides completion for
Standard ML keywords and for Standard ML (long) identifiers.
Candidate completion identifiers for the latter are loaded from a
basis file created by mlton
using -show-basis file
or
(*#showBasis "file"*)
.
Clone repository:
cd ~/.emacs.d git clone https://github.com/MatthewFluet/company-mlton
Add to .emacs
or init.el
:
(add-to-list 'load-path "~/.emacs.d/company-mlton") (require 'company-mlton) (add-hook 'sml-mode-hook #'company-mlton-init)
Completion candidates for Standard ML (long) identifiers are
loaded from a basis file created by mlton
using
-show-basis file
or
(*#showBasis "file"*)
.
company-mlton
ships with a default basis file that corresponds to
MLton’s default environment (implicitly used by mlton
when compiling
a .sml
file). It includes the
Standard ML Basis Library,
structure Unsafe: UNSAFE
, structure SMLofNJ: SML_OF_NJ
, and
structure MLton: MLTON
(plus supporting signatures). This default
basis is automatically used for sml-mode
buffers that do not set the
buffer-local variable company-mlton-basis-file
. Thus, it provides
useful completion for single-file .sml
programs.
For larger Standard ML programs, it can be more useful to load a
custom basis file created by mlton
using
-show-basis file
or
(*#showBasis "file"*)
.
In some projects, a common set of utility libraries are used by many
source .sml
files. For example, consider a project described as
follows:
-
project.mlb
:$(SML_LIB)/basis/basis.mlb $(SML_LIB)/smlnj-lib/Util/smlnj-lib.mlb $(SML_LIB)/smlnj-lib/Controls/controls-lib.mlb ../lib/PrettyPrint/PrettyPrint.mlb ../lib/ParserCombinators/ParserCombinators.mlb src1.sml src2.sml main.sml
Within src1.sml
, src2.sml
, and main.sml
, it would be useful to
complete with the common set of libraries. To do so, extract the
"imports" of project.mlb
to project-imports.mlb
:
-
project-imports.mlb
:$(SML_LIB)/basis/basis.mlb $(SML_LIB)/smlnj-lib/Util/smlnj-lib.mlb $(SML_LIB)/smlnj-lib/Controls/controls-lib.mlb ../lib/PrettyPrint/PrettyPrint.mlb ../lib/ParserCombinators/ParserCombinators.mlb
-
project.mlb
:project-imports.mlb main.sml
Now, save the environment described by project-imports.mlb
:
mlton -show-basis project-imports.basis -stop tc project-imports.mlb
Finally, arrange for the buffer-local variable
company-mlton-basis-file
to be set to project-imports.basis
for
each source .sml
file. This can be accomplished by any of the
following:
-
Execute
M-x company-mlton-basis-load
after loading a source.sml
file and chooseproject-imports.basis
at the prompt. -
Add a file-local variables
-*-
line to each of the source.sml
files:(* -*- company-mlton-basis-file: "project-imports.basis"; -*- *)
A file-local variables
-*-
line must be the first line of the file. -
Add a file-local variables
Local Variables:
block to each of the source.sml
files:(* Local Variables: *) (* company-mlton-basis-file: "project-imports.basis" *) (* End: *)
A file-local variables
Local Variables:
block is typically placed at the end of the file. -
Add a
.dir-locals.el
file to the directory:((sml-mode . ((company-mlton-basis-file . "project-imports.basis"))))
The advantage of the -show-basis file
workflow is that
the custom basis file need only be created once (or whenever the
common set of libraries changes) and can be shared among many source
.sml
files. The disadvantage of the -show-basis file
workflow is that the environment used for completion is not
specialized to each source .sml
file.
More specialized completions for a particular source .sml
file can
be provided by using (*#showBasis "file"*)
directives.
A comment of the form (*#showBasis "file"*)
in a
source .sml
file is recognized by mlton
as a directive to save the
environment at that point to file
. The file
is
interpreted relative to the source .sml
file in which it appears.
The comment is lexed as a distinct token and is parsed as a
structure-level declaration.
Via company-mlton-init
added to sml-mode-hook
, comments of the
form (*#showBasis "file"*)
are recognized when a
source .sml
file is loaded and the buffer-local variable
company-mlton-basis-file
is set to file
. Similarly,
executing M-x company-mlton-basis-autodetect
(or M-x
company-mlton-init
) will scan the current buffer for comments of the
form (*#showBasis "file"*)
and set the
buffer-local variable company-mlton-basis-file
accordingly; this can
be used if the (*#showBasis "file"*)
comment is
added after the source .sml
file is loaded.
A (*#showBasis "file"*)
directive can be used to
capture an environment that includes functor arguments, local
structure aliases, and local structure declarations. For example,
consider writing a type-checker module as a functor, parameterized by
an abstract-syntax-tree represenation and a core representation and
defining an environment module by applying a functor:
functor TypeCheck (S: sig structure Ast: AST_IR structure Core: CORE_IR end): sig val typeCheck: Ast.Prog.t -> Core.Prog.t option end = struct open S structure A = Ast structure C = Core structure E = MkEnv (structure Dom = A.Var structure Rng = struct type t = C.Var.t * C.Type.t end) (*#showBasis "type-check.basis"*) fun typeCheck p = raise Fail "typeCheck" end
Compile (or at least type check) the whole project (or at least the
portion of the project that includes type-check.fun
) as usual. The
environment saved to type-check.basis
will include structure A
,
structure C
, and structure E
, in addition to all identifiers in
scope at the start of the functor declaration.
The advantage of the (*#showBasis "file"*)
workflow is that the custom basis file can be specialized to each
source .sml
file. It should also fit naturally into a workflow that
frequently compiles the current work-in-progress source .sml
file to
check for type errors.