Skip to content

Latest commit

 

History

History
227 lines (192 loc) · 7.49 KB

README.adoc

File metadata and controls

227 lines (192 loc) · 7.49 KB

company-mlton

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"*).

Screenshot

`company-mlton` screenshot

Dependencies

Install Package via Git

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)

Usage

Completion candidates for Standard ML (long) identifiers are loaded from a basis file created by mlton using -show-basis file or (*#showBasis "file"*).

Default Basis

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.

Custom Basis Workflows

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"*).

Using -show-basis 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 choose project-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.

Using (*#showBasis "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.