On this page:
4.1 Enigma API:   Metadata
name
version
license
4.2 Enigma API:   CLI Application
app-cli
4.3 Enigma API:   Compile Subcommand
compile-found-emacs-lisp-files
4.4 Enigma API:   Dependencies Subcommand
install-emacs-lisp-files-dependencies
4.5 Enigma API:   Install Subcommand
install-found-emacs-lisp-files
4.6 Enigma API:   Launch Subcommand
launch-with-found-emacs-lisp-files
4.7 Enigma API:   Test Subcommand
test-found-emacs-lisp-files
4.8 Enigma API:   Stamp
create-stamp
stamp-actual?
4.9 Enigma API:   Content Guess
guess-file-test-runner
guess-file-dependencies
guess-file-version
4.10 Enigma API:   File Context
similar-emacs-lisp-name?
find-likely-emacs-lisp-segments
guess-load-path
find-emacs-lisp-context
4.11 Enigma API:   Emacs Package Archives
parameter/  emacs-package-archives
get-emacs-package-archives
refresh-emacs-package-archives
4.12 Enigma API:   Load Path
make-emacs-cmdline-args
4.13 Enigma API:   Run Emacs
parameter/  emacs-executable
parameter/  buttercup-executable
parameter/  ert-runner-executable
run-emacs
run-buttercup
run-ert-runner
4.14 Enigma API:   Find System Utils
emacs-lisp-file?
find-emacs-lisp-files
4.15 Enigma API:   Path System Utilities
path-segments
make-paths-tree
path->directory
path->name
4.16 Enigma API:   Time Util
with-time-report
9.2

4 Racket API🔗

4.1 Enigma API: Metadata🔗

 (require enigma/metadata) package: enigma-app

value

name : string?

Package name.

Current value: enigma

value

version : string?

Package version.

Current value: 1.0.3

value

license : string?

Package license identifier.

Current value: MPL-2.0

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
 
(provide
 (contract-out
  [name string?]
  [version string?]
  [license string?]))
 
(define name "enigma")
 
(define version "1.0.3")
 
(define license "MPL-2.0")

4.2 Enigma API: CLI Application🔗

 (require enigma/app/cli) package: enigma-app

procedure

(app-cli argv)  void?

  argv : (listof string?)
Entry point for the Enigma command-line application.

The source code of this module:

#lang racket/base
 
(require json)
(require racket/cmdline)
(require racket/file)
(require racket/match)
 
(require threading)
 
(require "../metadata.rkt")
 
(require "../emacs/package/archives.rkt")
(require "../emacs/system/run-emacs.rkt")
 
(require "../subcommands/compile.rkt")
(require "../subcommands/dependencies.rkt")
(require "../subcommands/install.rkt")
(require "../subcommands/launch.rkt")
(require "../subcommands/test.rkt")
 
(require "../log/simplelog.rkt")
(require "../system/output/color.rkt")
(require "../system/utils/path.rkt")
 
(provide app-cli)
 
(define (program-parameter/execution-directory/guard path)
  (define actual-path
    (~> path
        expand-user-path
        path->complete-path
        simplify-path))
  (match actual-path
    [(? directory-exists? d) d]
    [(? file-exists? f) (path->directory f)]
    [_ (raise (exn:fail (format "Given path does not exist, given: ~v" actual-path)
                        (current-continuation-marks)))]))
 
(define (program-parameter/user-emacs-directory/guard path)
  (cond
    [path
     (~> path
         expand-user-path
         path->complete-path
         simplify-path
         path->string)]
    [else #false]))
 
(define program-pseudo-parameter/extra-emacs-flags
  (make-parameter '()))
 
(define program-parameter/execution-directory
  (make-parameter (current-directory)
                  program-parameter/execution-directory/guard))
 
(define program-parameter/user-emacs-directory
  (make-parameter #false
                  program-parameter/user-emacs-directory/guard))
 
(define (display-jsexpr jsexpr)
  (~>> jsexpr
       (jsexpr->string #:indent 2)
       displayln))
 
(define (call-for-exit procedure/arity2)
  (begin
    (define-values (result-code result-hash)
      (procedure/arity2 (program-parameter/execution-directory)
                        (program-pseudo-parameter/extra-emacs-flags)))
    (display-jsexpr result-hash)
    (match result-hash
      [(list) (simplelog-error "No matching Emacs lisp files were found")]
      [_ (void)])
    (exit result-code)))
 
(define (run-action action emacs-flags)
  (parameterize ([current-directory (program-parameter/execution-directory)]
                 [program-pseudo-parameter/extra-emacs-flags emacs-flags])
    (simplelog-debug "Called with: action: ~v and emacs-flags: ~v"
                     action
                     emacs-flags)
    (simplelog-debug "Working in directory: ~a" (current-directory))
    (define maybe-user-emacs-directory
      (program-parameter/user-emacs-directory))
    (cond
      [maybe-user-emacs-directory
       (define emacs-directory-setup-expr
         `(progn
           (setq user-emacs-directory ,maybe-user-emacs-directory)
           (setq package-user-dir
                 (expand-file-name "elpa" user-emacs-directory))))
       (simplelog-debug "User's Emacs directory: ~a" maybe-user-emacs-directory)
       (make-directory* maybe-user-emacs-directory)
       (program-pseudo-parameter/extra-emacs-flags
        `("--eval"
          ,(format "~s" emacs-directory-setup-expr)
          ,@(program-pseudo-parameter/extra-emacs-flags)))]
      [else (void)])
    (match action
      ['compile (call-for-exit compile-found-emacs-lisp-files)]
      ['dependencies (call-for-exit install-emacs-lisp-files-dependencies)]
      ['install (call-for-exit install-found-emacs-lisp-files)]
      ['launch (call-for-exit launch-with-found-emacs-lisp-files)]
      ['test (call-for-exit test-found-emacs-lisp-files)]
      [_ (raise (exn:fail (format "Unknown action, given: ~v" action)
                          (current-continuation-marks)))])))
 
(define (subcommand/dependencies argv)
  (command-line
   #:program "enigma dependencies"
   #:argv argv
   #:multi
   [("--clear-package-archives")
    "Clear the Emacs package-archives of any records"
    (parameter/emacs-package-archives '())]
   [("--add-package-archive")
    package-archive-url
    "Register a package-archive record"
    (parameter/emacs-package-archives
     (cons package-archive-url (parameter/emacs-package-archives)))]
   #:args emacs-flags
   (run-action 'dependencies emacs-flags)))
 
(define (subcommand/compile argv)
  (command-line
   #:program "enigma compile"
   #:argv argv
   #:args emacs-flags
   (run-action 'compile emacs-flags)))
 
(define (subcommand/test argv)
  (command-line
   #:program "enigma test"
   #:argv argv
   #:once-each
   [("--buttercup-exe")
    buttercup-exe
    "Set a custom Buttercup executable path"
    (parameter/buttercup-executable buttercup-exe)]
   [("--ert-runner-exe")
    ert-runner-exe
    "Set a custom ERT-Runner executable path"
    (parameter/ert-runner-executable ert-runner-exe)]
   #:args emacs-flags
   (run-action 'test emacs-flags)))
 
(define (subcommand/launch argv)
  (command-line
   #:program "enigma launch"
   #:argv argv
   #:args emacs-flags
   (run-action 'launch emacs-flags)))
 
(define (subcommand/install argv)
  (command-line
   #:program "enigma install"
   #:argv argv
   #:multi
   [("--clear-package-archives")
    "Clear the Emacs package-archives of any records"
    (parameter/emacs-package-archives '())]
   [("--add-package-archive")
    package-archive-url
    "Register a package-archive record"
    (parameter/emacs-package-archives
     (cons package-archive-url (parameter/emacs-package-archives)))]
   #:args emacs-flags
   (run-action 'install emacs-flags)))
 
(define (app-cli argv)
  (command-line
   #:program name
   #:argv argv
   #:usage-help "requires a subcommand, one of: compile, dependencies, install, launch or test"
   #:ps "
Available subcommands:
  - compile - compile found Emacs Lisp files,
  - dependencies - install Emacs Lisp files' dependencies (\"Package-Requires\"),
  - install - install (suitable) Emacs Lisp files as packages (their parent dir),
  - launch - launch GNU Emacs in a prepared environment,
  - test - run found Emacs Lisp tests (supports: ert, ert-runner or buttercup).
 
Original author: Maciej xgqt Barć. Licensed under the permissive
copyleft MPL-2.0 license (http://mozilla.org/MPL/2.0/). No warranty.
"
   #:once-each
   [("-V" "--version")
    "Show the program's currently used version"
    (begin
      (displayln version)
      (exit 0))]
   [("--license")
    "Show the program's license"
    (begin
      (displayln license)
      (exit 0))]
   [("-C" "--directory")
    directory
    "Change to directory directory before doing anything else"
    (program-parameter/execution-directory directory)]
   [("--no-color")
    "Suppress displaying colors by the program"
    (parameter/colored-output? #false)]
   [("--user-emacs-directory")
    directory
    "Overwrite the user's Emacs directory"
    (program-parameter/user-emacs-directory directory)]
   [("--emacs-exe")
    emacs-exe
    "Set a custom GNU Emacs executable path"
    (parameter/emacs-executable emacs-exe)]
   #:args (subcommand . subcommand-arguments)
   (match subcommand
     [(or "subcommands")
      (displayln "Available subcommands:")
      (for ([s '("compile" "dependencies" "install" "launch" "test")])
        (printf "  - ~a\n" s))]
     [(or "compile" "com" "c")
      (subcommand/compile subcommand-arguments)]
     [(or "dependencies" "deps" "d")
      (subcommand/dependencies subcommand-arguments)]
     [(or "install" "inst" "i")
      (subcommand/install subcommand-arguments)]
     [(or "launch" "l")
      (subcommand/launch subcommand-arguments)]
     [(or "test" "t")
      (subcommand/test subcommand-arguments)]
     [_ (raise (exn:fail (format "Unknown subcommand, given: ~v" subcommand)
                         (current-continuation-marks)))])))

4.3 Enigma API: Compile Subcommand🔗

 (require enigma/subcommands/compile) package: enigma-app

procedure

(compile-found-emacs-lisp-files start-path 
  extra-emacs-flags) 
  
exact-integer? (listof hash?)
  start-path : path-string?
  extra-emacs-flags : (listof string?)
Compile GNU Emacs Lisp found at the start-path. Pass extra-emacs-flags as extra compilation flags.

Returns the values that indicate: exact-integer? total compilation success and a list of objects that describe properties of compiled files.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/match)
 
(require "../emacs/file/emacs-context.rkt")
(require "../emacs/path/load-path.rkt")
(require "../emacs/system/run-emacs.rkt")
 
(require "../system/utils/path.rkt")
(require "../system/utils/time.rkt")
 
(require "../log/simplelog.rkt")
(require "../compiler/stamp/stamp.rkt")
 
(provide
 (contract-out
  [compile-found-emacs-lisp-files
   (-> path-string? (listof string?)
       (values exact-integer? (listof hash?)))]))
 
(define (compile-emacs-lisp-file/inner file-path load-path extra-emacs-flags)
  (define cmdline-args
    (append (make-emacs-cmdline-args load-path extra-emacs-flags)
            `("-batch" "-f" "batch-byte-compile" ,(path->string file-path))))
  (define exit-code (run-emacs cmdline-args))
  (match exit-code
    [0 (create-stamp file-path "compiled")]
    [_ (void)])
  exit-code)
 
(define (compile-emacs-lisp-file file-path load-path extra-emacs-flags)
  (cond
    [(stamp-actual? file-path "compiled")
     (simplelog-debug "Skipping up-to-date file: ~a" (path->name file-path))
     0]
    [else
     (define name (path->name file-path))
     (with-time-report
       #:announce
       (lambda ()
         (simplelog-info "Compiling ~a" name))
       #:report
       (lambda (elapsed)
         (simplelog-debug "Compilation of ~a took ~ams" name elapsed))
       #:thunk
       (lambda ()
         (compile-emacs-lisp-file/inner file-path load-path extra-emacs-flags)))]))
 
(define (compile-found-emacs-lisp-files start-path extra-emacs-flags)
  (define result-code (make-parameter 0))
  (define-values (emacs-lisp-files load-path)
    (find-emacs-lisp-context start-path))
  (define sources-count (length emacs-lisp-files))
  (define result-hash
    (for/list ([emacs-lisp-file emacs-lisp-files]
               [i (in-naturals 1)])
      (simplelog-status i sources-count "compile-emacs-lisp-file ~a" emacs-lisp-file)
      (define exit-code
        (compile-emacs-lisp-file emacs-lisp-file load-path extra-emacs-flags))
      (define successful? (= 0 exit-code))
      (define compiled-file
        (match successful?
          [#true
           (match (path-replace-extension emacs-lisp-file #".elc")
             [(? file-exists? f) (path->string f)]
             [_ #false])]
          [_ #false]))
      (define h
        (hash 'compiled-file compiled-file
              'exit-code exit-code
              'file-path (path->string emacs-lisp-file)
              'successful? successful?))
      (when (not successful?)
        (result-code exit-code))
      h))
  (values (result-code) result-hash))

4.4 Enigma API: Dependencies Subcommand🔗

 (require enigma/subcommands/dependencies)
  package: enigma-app

procedure

(install-emacs-lisp-files-dependencies start-path 
  extra-emacs-flags) 
  
exact-integer? (listof hash?)
  start-path : path-string?
  extra-emacs-flags : (listof string?)
Install dependencies of Emacs Lisp packages found under start-path. Pass extra-emacs-flags as extra flags.

Returns the values that indicate: exact-integer? total installation success and a list of objects that describe properties of installed dependencies.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
 
(require "../emacs/file/content-guess.rkt")
(require "../emacs/file/emacs-context.rkt")
(require "../emacs/package/archives.rkt")
(require "../emacs/path/load-path.rkt")
(require "../emacs/system/run-emacs.rkt")
 
(require "../log/simplelog.rkt")
(require "../system/utils/time.rkt")
 
(provide
 (contract-out
  [install-emacs-lisp-files-dependencies
   (-> path-string? (listof string?)
       (values exact-integer? (listof hash?)))]))
 
(define (install-remote-emacs-package package-name extra-emacs-flags)
  (define package-bootstrap-expr
    `(progn
      (setq package-archives (quote ,(get-emacs-package-archives)))
      (package-initialize)
      (package-install (quote ,package-name))))
  (define cmdline-args
    (append (make-emacs-cmdline-args '() extra-emacs-flags)
            `("-batch"
              "--eval" ,(format "~s" package-bootstrap-expr))))
  (define exit-code
    (with-time-report
      #:announce
      (lambda ()
        (simplelog-success "Installing dependency ~a" package-name))
      #:report
      (lambda (elapsed)
        (simplelog-debug "Installing dependency ~a took ~ams" package-name elapsed))
      #:thunk
      (lambda ()
        (run-emacs cmdline-args))))
  (when (= 0 exit-code)
    (simplelog-success "Install of remote package ~v succeeded" package-name))
  exit-code)
 
(define (install-emacs-lisp-files-dependencies start-path extra-emacs-flags)
  (define result-code (make-parameter 0))
  (define-values (emacs-lisp-files _load-path)
    (find-emacs-lisp-context start-path))
  (define refresh-result (refresh-emacs-package-archives extra-emacs-flags))
  (when (> refresh-result 0)
    (result-code refresh-result))
  (define sources
    (for/fold ([results '()])
              ([emacs-lisp-file emacs-lisp-files])
      (define version (guess-file-version emacs-lisp-file))
      (define dependencies (guess-file-dependencies emacs-lisp-file))
      (cond
        [(and version (not (null? dependencies)))
         (cons (cons emacs-lisp-file dependencies) results)]
        [else results])))
  (define sources-count (length sources))
  (define result-hash
    (for/list ([emacs-lisp-pair (reverse sources)]
               [i (in-naturals 1)])
      (define emacs-lisp-file (car emacs-lisp-pair))
      (define dependencies (cdr emacs-lisp-pair))
      (simplelog-status i sources-count "install-remote-emacs-package ~a" emacs-lisp-file)
      (simplelog-debug "Found dependencies: ~v in file ~a"
                       dependencies emacs-lisp-file)
      (define exit-codes
        (for/list ([package-name dependencies])
          (install-remote-emacs-package package-name extra-emacs-flags)))
      (define first-failure-exit-code
        (for/first ([exit-code exit-codes] #:when (> exit-code 0)) exit-code))
      (define successful? (not first-failure-exit-code))
      (when (not successful?)
        (result-code first-failure-exit-code))
      (hash 'dependencies (format "~s" dependencies)
            'file-path (path->string emacs-lisp-file)
            'successful? successful?)))
  (values (result-code) result-hash))

4.5 Enigma API: Install Subcommand🔗

 (require enigma/subcommands/install) package: enigma-app

procedure

(install-found-emacs-lisp-files start-path 
  extra-emacs-flags) 
  
exact-integer? (listof hash?)
  start-path : path-string?
  extra-emacs-flags : (listof string?)
Install Emacs Lisp packages found under start-path. Pass extra-emacs-flags as extra install flags.

Returns the values that indicate: exact-integer? total installation success and a list of objects that describe properties of installed files.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/match)
 
(require "../emacs/file/emacs-context.rkt")
(require "../emacs/path/load-path.rkt")
(require "../emacs/system/run-emacs.rkt")
 
(require "../system/utils/path.rkt")
(require "../system/utils/time.rkt")
 
(require "../log/simplelog.rkt")
(require "../compiler/stamp/stamp.rkt")
 
(provide
 (contract-out
  [compile-found-emacs-lisp-files
   (-> path-string? (listof string?)
       (values exact-integer? (listof hash?)))]))
 
(define (compile-emacs-lisp-file/inner file-path load-path extra-emacs-flags)
  (define cmdline-args
    (append (make-emacs-cmdline-args load-path extra-emacs-flags)
            `("-batch" "-f" "batch-byte-compile" ,(path->string file-path))))
  (define exit-code (run-emacs cmdline-args))
  (match exit-code
    [0 (create-stamp file-path "compiled")]
    [_ (void)])
  exit-code)
 
(define (compile-emacs-lisp-file file-path load-path extra-emacs-flags)
  (cond
    [(stamp-actual? file-path "compiled")
     (simplelog-debug "Skipping up-to-date file: ~a" (path->name file-path))
     0]
    [else
     (define name (path->name file-path))
     (with-time-report
       #:announce
       (lambda ()
         (simplelog-info "Compiling ~a" name))
       #:report
       (lambda (elapsed)
         (simplelog-debug "Compilation of ~a took ~ams" name elapsed))
       #:thunk
       (lambda ()
         (compile-emacs-lisp-file/inner file-path load-path extra-emacs-flags)))]))
 
(define (compile-found-emacs-lisp-files start-path extra-emacs-flags)
  (define result-code (make-parameter 0))
  (define-values (emacs-lisp-files load-path)
    (find-emacs-lisp-context start-path))
  (define sources-count (length emacs-lisp-files))
  (define result-hash
    (for/list ([emacs-lisp-file emacs-lisp-files]
               [i (in-naturals 1)])
      (simplelog-status i sources-count "compile-emacs-lisp-file ~a" emacs-lisp-file)
      (define exit-code
        (compile-emacs-lisp-file emacs-lisp-file load-path extra-emacs-flags))
      (define successful? (= 0 exit-code))
      (define compiled-file
        (match successful?
          [#true
           (match (path-replace-extension emacs-lisp-file #".elc")
             [(? file-exists? f) (path->string f)]
             [_ #false])]
          [_ #false]))
      (define h
        (hash 'compiled-file compiled-file
              'exit-code exit-code
              'file-path (path->string emacs-lisp-file)
              'successful? successful?))
      (when (not successful?)
        (result-code exit-code))
      h))
  (values (result-code) result-hash))

4.6 Enigma API: Launch Subcommand🔗

 (require enigma/subcommands/launch) package: enigma-app

procedure

(launch-with-found-emacs-lisp-files start-path 
  extra-emacs-flags) 
  
exact-integer? (listof hash?)
  start-path : path-string?
  extra-emacs-flags : (listof string?)
Launch GNU Emacs with load-paths under start-path Pass extra-emacs-flags as extra launch flags.

Returns the values that indicate: exact-integer? total launch success and a list of objects that describe properties of used files.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
 
(require "../emacs/file/emacs-context.rkt")
(require "../emacs/path/load-path.rkt")
(require "../emacs/system/run-emacs.rkt")
 
(require "../log/simplelog.rkt")
 
(provide
 (contract-out
  [launch-with-found-emacs-lisp-files
   (-> path-string? (listof string?)
       (values exact-integer? (listof hash?)))]))
 
(define (launch-with-found-emacs-lisp-files start-path extra-emacs-flags)
  (define-values (emacs-lisp-files load-path)
    (find-emacs-lisp-context start-path))
  (define cmdline-args (make-emacs-cmdline-args load-path extra-emacs-flags))
  (simplelog-debug "Launching from: ~a" start-path)
  (define exit-code (run-emacs cmdline-args))
  (define result-hash
    (for/list ([emacs-lisp-file emacs-lisp-files])
      (hash 'file-path (path->string emacs-lisp-file))))
  (values exit-code result-hash))

4.7 Enigma API: Test Subcommand🔗

 (require enigma/subcommands/test) package: enigma-app

procedure

(test-found-emacs-lisp-files start-path 
  extra-emacs-flags) 
  
exact-integer? (listof hash?)
  start-path : path-string?
  extra-emacs-flags : (listof string?)
Test Emacs Lisp files found under start-path. Pass extra-emacs-flags as extra test flags.

Returns the values that indicate: exact-integer? total test success and a list of objects that describe properties of tested files.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/match)
 
(require threading)
 
(require "../emacs/file/content-guess.rkt")
(require "../emacs/file/emacs-context.rkt")
(require "../emacs/path/load-path.rkt")
(require "../emacs/system/run-emacs.rkt")
 
(require "../system/utils/path.rkt")
(require "../system/utils/time.rkt")
 
(require "../log/simplelog.rkt")
(require "../compiler/stamp/stamp.rkt")
 
(provide
 (contract-out
  [test-found-emacs-lisp-files
   (-> path-string? (listof string?)
       (values exact-integer? (listof hash?)))]))
 
(define (test-emacs-lisp-file/inner file-path load-path extra-emacs-flags)
  (define test-runner (guess-file-test-runner file-path))
  (define exit-code
    (match test-runner
      ['ert
       (define cmdline-args
         (append (make-emacs-cmdline-args load-path extra-emacs-flags)
                 `("-batch"
                   ,(~> file-path path->directory path->string)
                   "-l" ,(path->string file-path)
                   "-f" "ert-run-tests-batch-and-exit")))
       (run-emacs cmdline-args)]
      ['ert-runner
       (define cmdline-args
         (append (make-emacs-cmdline-args load-path extra-emacs-flags)
                 (~> file-path path->string list)))
       (run-ert-runner cmdline-args)]
      ['buttercup
       (define cmdline-args
         (append (make-emacs-cmdline-args load-path extra-emacs-flags)
                 (~> file-path path->directory path->string list)))
       (run-buttercup cmdline-args)]
      [_ (raise (exn:fail (format "Unknown test runner, given: ~v" test-runner)
                          (current-continuation-marks)))]))
  (match exit-code
    [0 (create-stamp file-path "tested")]
    [_ (void)])
  exit-code)
 
(define (test-emacs-lisp-file file-path load-path extra-emacs-flags)
  (cond
    [(stamp-actual? file-path "tested")
     (simplelog-debug "Skipping up-to-date file: ~a" (path->name file-path))
     0]
    [else
     (define name (path->name file-path))
     (with-time-report
       #:announce
       (lambda ()
         (simplelog-info "Testing ~a" name))
       #:report
       (lambda (elapsed)
         (simplelog-debug "Test of ~a took ~ams" name elapsed))
       #:thunk
       (lambda ()
         (test-emacs-lisp-file/inner file-path load-path extra-emacs-flags)))]))
 
(define (emacs-lisp-test-file? file-path)
  (define name (path->name file-path))
  (and (not (regexp-match #rx"^test(s|)-helper\\.el$" name))
       (or (regexp-match #rx"^test(s|)-.*\\.el$" name)
           (regexp-match #rx".*-test(s|)\\.el$" name))))
 
(define (test-found-emacs-lisp-files start-path extra-emacs-flags)
  (define result-code (make-parameter 0))
  (define-values (emacs-lisp-files load-path)
    (find-emacs-lisp-context start-path))
  (define sources (filter emacs-lisp-test-file? emacs-lisp-files))
  (define sources-count (length sources))
  (define result-hash
    (for/list ([emacs-lisp-file sources]
               [i (in-naturals 1)])
      (simplelog-status i sources-count "test-emacs-lisp-file ~a" emacs-lisp-file)
      (define exit-code
        (test-emacs-lisp-file emacs-lisp-file load-path extra-emacs-flags))
      (define successful? (= 0 exit-code))
      (define h
        (hash 'exit-code exit-code
              'file-path (path->string emacs-lisp-file)
              'successful? successful?))
      (when (not successful?)
        (result-code exit-code))
      h))
  (values (result-code) result-hash))

4.8 Enigma API: Stamp🔗

 (require enigma/compiler/stamp/stamp) package: enigma-app

procedure

(create-stamp file-path stamp-type)  hash?

  file-path : path-string?
  stamp-type : string?
Create a stamp for file-path using the stamp-type.

Returns a hash with stamp data.

procedure

(stamp-actual? file-path stamp-type)  boolean?

  file-path : path-string?
  stamp-type : string?
Check whether the stamp for file-path of stamp-type matches the file, meaning it is up-to-date.

Returns #true when the stamp exists and its recorded file size and modification time match the file values.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/file)
 
(require json)
(require threading)
 
(require "../../log/simplelog.rkt")
(require "../../system/utils/path.rkt")
 
(provide
 (contract-out
  [create-stamp
   (-> path-string? string?
       hash?)]
  [stamp-actual?
   (-> path-string? string?
       boolean?)]))
 
(define (get-stamp-path file-path stamp-type)
  (define stamp-file-name
    (~> file-path
        path->name
        (path-replace-extension _ (format ".~a.stamp" stamp-type))))
  (define stamp-file-directory
    (~> file-path
        path->directory
        (build-path _ ".cache")))
  (define stamp-file-path
    (~> stamp-file-directory
        (build-path _ stamp-file-name)))
  (values stamp-file-path stamp-file-directory stamp-file-name))
 
(define (create-stamp file-path stamp-type)
  (define-values (stamp-path dir name) (get-stamp-path file-path stamp-type))
  (define source-size (file-size file-path))
  (define source-time (file-or-directory-modify-seconds file-path))
  (define creation-time (current-seconds))
  (define stamp-data
    (hash 'creation-time creation-time
          'source-size source-size
          'source-time source-time))
  (simplelog-debug "Creating stamp: ~a" name)
  (make-directory* dir)
  (call-with-output-file stamp-path
    #:exists 'replace
    #:mode 'text
    (lambda (out)
      (displayln (jsexpr->string stamp-data) out)))
  stamp-data)
 
(define read-stamp
  (lambda~> file->string
            string->jsexpr))
 
(define (stamp-actual? file-path stamp-type)
  (define-values (stamp-path _dir _name) (get-stamp-path file-path stamp-type))
  (cond
    [(file-exists? stamp-path)
     (define stamp-data (read-stamp stamp-path))
     (and stamp-data
          (= (hash-ref stamp-data 'source-size)
             (file-size file-path))
          (= (hash-ref stamp-data 'source-time)
             (file-or-directory-modify-seconds file-path)))]
    [else #false]))

4.9 Enigma API: Content Guess🔗

 (require enigma/emacs/file/content-guess)
  package: enigma-app

procedure

(guess-file-test-runner file-path)  symbol?

  file-path : path-string?
Guess the test runner used by the file at file-path.

Returns 'ert, 'ert-runner, 'buttercup or 'none.

procedure

(guess-file-dependencies file-path)  (listof symbol?)

  file-path : path-string?
Guess the package-requires dependencies of the file at file-path.

Returns a list of symbols representing package dependencies.

procedure

(guess-file-version file-path)  (or/c string? #false)

  file-path : path-string?
Guess the version of the file at file-path.

Returns a version string or #false.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/file)
(require racket/match)
(require racket/string)
 
(require threading)
 
(require "../../system/utils/path.rkt")
 
(provide
 (contract-out
  [guess-file-test-runner
   (-> path-string?
       symbol?)]
  [guess-file-dependencies
   (-> path-string?
       (listof symbol?))]
  [guess-file-version
   (-> path-string?
       (or/c string? #false))]))
 
(define (guess-file-test-runner file-path)
  (cond
    [(~> file-path
         path->directory
         (build-path "test-helper.el")
         file-exists?)
     'ert-runner]
    [else
     (define content (file->string file-path))
     (match content
       [(regexp #rx"\\(require 'ert") 'ert]
       [(regexp #rx"\\(ert-deftest ") 'ert]
       [(regexp #rx"\\(require 'buttercup") 'buttercup]
       [(regexp #rx"\\(describe ") 'buttercup]
       [_ 'none])]))
 
(define (guess-file-dependencies file-path)
  (define maybe-deps
    (for/or ([line (file->lines file-path)])
      (match line
        [(regexp #rx"^;; Package-Requires: \\(.+\\)" (list matched))
         ;; FIXME: A little fragile, maybe reimplement.
         (~> matched
             (regexp-replace ";; Package-Requires:" _ "")
             string-trim
             open-input-string      ; "((emacs \"26.3\") (s \"0.2\"))"
             read                   ; ((emacs "26.3") (s "0.2"))
             (map car _)            ; (emacs s)
             (filter (lambda (pkg-name)
                       (match pkg-name
                         [(or 'emacs 'seq) #false]
                         [_ #true])) _))]
        [_ #false])))
  (cond
    [maybe-deps]
    [else '()]))
 
(define (guess-file-version file-path)
  (for/or ([line (file->lines file-path)])
    (match line
      [(regexp #rx"^;; Version: .*" (list matched))
       (~> matched
           (regexp-replace ";; Version:" _ "")
           string-trim)]
      [(regexp #rx"^;; Package-Version: .*" (list matched))
       (~> matched
           (regexp-replace ";; Package-Version:" _ "")
           string-trim)]
      [_ #false])))

4.10 Enigma API: File Context🔗

 (require enigma/emacs/file/emacs-context)
  package: enigma-app

procedure

(similar-emacs-lisp-name? emacs-lisp-path    
  supposed-path)  boolean?
  emacs-lisp-path : path-string?
  supposed-path : path-string?
Check if supposed-path has a name similar to emacs-lisp-path.

procedure

(find-likely-emacs-lisp-segments file-path)  (listof path?)

  file-path : path-string?
Find paths in the tree of file-path with similar names.

procedure

(guess-load-path emacs-lisp-files)  (listof path?)

  emacs-lisp-files : (listof path?)
Guess the Emacs Lisp load-path from the given files.

procedure

(find-emacs-lisp-context start-path)

  
(listof path?) (listof path?)
  start-path : path-string?
Find Emacs Lisp files and their load-path under start-path.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/list)
(require racket/set)
 
(require threading)
 
(require "../../system/utils/find.rkt")
(require "../../system/utils/path.rkt")
 
(provide
 (contract-out
   [similar-emacs-lisp-name?
    (-> path-string?
        path-string?
        boolean?)]
   [find-likely-emacs-lisp-segments
    (-> path-string?
        (listof path?))]
   [guess-load-path
    (-> (listof path?)
        (listof path?))]
   [find-emacs-lisp-context
    (-> path-string?
        (values (listof path?) (listof path?)))]))
 
(define (similar-emacs-lisp-name? emacs-lisp-path supposed-path)
  (define lisp-name (path->name emacs-lisp-path))
  (define lispy-name (regexp-replace #rx"\\.el$" lisp-name ""))
  (define path-name (path->name supposed-path))
  (cond
    [(~> (string-append "^" lispy-name "(_|-).*")
         regexp
         (regexp-match path-name)
         (and #true))]
    [(~> (string-append ".*(_|-)" lispy-name "$")
         regexp
         (regexp-match path-name)
         (and #true))]
    [(~> (string-append ".*(_|-)" lisp-name "$")
         regexp
         (regexp-match path-name)
         (and #true))]
    [(equal? lispy-name path-name)]
    [(equal? lisp-name path-name)]
    [else #false]))
 
(define (find-likely-emacs-lisp-segments file-path)
  (for/list ([path (make-paths-tree file-path)]
             #:when (similar-emacs-lisp-name? file-path path))
    path))
 
(define (guess-load-path emacs-lisp-files)
  (~> emacs-lisp-files
      (map find-likely-emacs-lisp-segments _)
      flatten
      list->set
      set->list
      (filter directory-exists? _)))
 
(define (file-hidden-directory? file-path start-path)
  (define full-path-segments (path-segments file-path))
  (define start-path-segments (path-segments start-path))
  (define only-relative-segments
    (~> start-path-segments
        length
        (list-tail full-path-segments _)))
  (define relative-directory-segments (drop-right only-relative-segments 1))
  (ormap (lambda~> path->string
                   (regexp-match-exact? #rx"^\\..*" _))
         relative-directory-segments))
 
(define (find-emacs-lisp-context start-path)
  (define emacs-lisp-files (find-emacs-lisp-files start-path))
  (define load-path (guess-load-path emacs-lisp-files))
  (define non-hidden-files
    (filter (lambda~> (file-hidden-directory? _ start-path)
                      not)
            emacs-lisp-files))
  (values non-hidden-files load-path))

4.11 Enigma API: Emacs Package Archives🔗

 (require enigma/emacs/package/archives)
  package: enigma-app

parameter

(parameter/emacs-package-archives)

  (or/c #false (listof string?))
(parameter/emacs-package-archives archive-list)  void?
  archive-list : (or/c #false (listof string?))
A parameter that holds the list of Emacs package archive URLs.

When set to #false, the default archives list is used.

Get the Emacs package archives as a list of (cons name url) pairs.

Uses the value of parameter/emacs-package-archives if set, otherwise uses the default archives.

procedure

(refresh-emacs-package-archives extra-emacs-flags)

  exact-integer?
  extra-emacs-flags : (listof string?)
Refresh the Emacs package archives by running Emacs in batch mode. Pass extra-emacs-flags as extra flags.

Returns an exit code, 0 on success.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/match)
 
(require threading)
 
(require "../../log/simplelog.rkt")
 
(require "../path/load-path.rkt")
(require "../system/run-emacs.rkt")
 
(provide
 (contract-out
  [parameter/emacs-package-archives
   (parameter/c (or/c #false (listof string?)))]
  [get-emacs-package-archives
   (-> list?)]
  [refresh-emacs-package-archives
   (-> (listof string?)
       exact-integer?)]))
 
(define parameter/emacs-package-archives
  (make-parameter #false))
 
(define default-emacs-package-archives-list
  '("https://tromey.com/elpa/"
    "https://elpa.gnu.org/packages/"
    "https://melpa.org/packages/"))
 
(define make-archive-name
  (lambda~> (regexp-replace #rx"^http(s)://" _ "")
            (regexp-replace #rx"/.*$" _ "")
            (regexp-replace* #rx"\\." _ "-")))
 
(define (make-emacs-package-archives archives-list)
  (for/list ([archive-url archives-list])
    (define archive-name (make-archive-name archive-url))
    (cons archive-name archive-url)))
 
(define (get-emacs-package-archives)
  (match (parameter/emacs-package-archives)
    [#false
     (make-emacs-package-archives default-emacs-package-archives-list)]
    [emacs-package-archives
     (make-emacs-package-archives emacs-package-archives)]))
 
(define (refresh-emacs-package-archives-expr)
  `(progn
    (require 'package)
    (setq package-archives (quote ,(get-emacs-package-archives)))
    (message " Package user dir: %s" package-user-dir)
    (message " Initializing package database...")
    (package-initialize)
    (message " Refreshing package archives...")
    (package-refresh-contents)))
 
(define (refresh-emacs-package-archives extra-emacs-flags)
  (define cmdline-args
    (append (make-emacs-cmdline-args '() extra-emacs-flags)
            `("-batch"
              "--eval" ,(format "~s" (refresh-emacs-package-archives-expr)))))
  (simplelog-debug "Refreshing Emacs packages")
  (define exit-code (run-emacs cmdline-args))
  (when (= 0 exit-code)
    (simplelog-success "Refresh of Emacs packages succeeded"))
  exit-code)

4.12 Enigma API: Load Path🔗

 (require enigma/emacs/path/load-path) package: enigma-app

procedure

(make-emacs-cmdline-args load-path-dirs    
  extra-emacs-flags)  (listof string?)
  load-path-dirs : (listof path-string?)
  extra-emacs-flags : (listof string?)
Build command-line arguments for Emacs from load-path-dirs and extra-emacs-flags.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/match)
 
(provide
 (contract-out
  [make-emacs-cmdline-args
   (-> (listof path-string?) (listof string?)
       (listof string?))]))
 
(define (make-emacs-cmdline-args load-path-dirs extra-emacs-flags)
  (define load-flags
    (for/fold ([args '()])
              ([load-path-dir load-path-dirs])
      `(,@args "-L" ,(match load-path-dir
                       [(? string? s) s]
                       [p (path->string p)]))))
  (append load-flags extra-emacs-flags))

4.13 Enigma API: Run Emacs🔗

 (require enigma/emacs/system/run-emacs)
  package: enigma-app

parameter

(parameter/emacs-executable)  string?

(parameter/emacs-executable executable-name-or-path)  void?
  executable-name-or-path : string?
Parameter for the GNU Emacs executable.

Defaults to "emacs".

parameter

(parameter/buttercup-executable)  string?

(parameter/buttercup-executable executable-name-or-path)
  void?
  executable-name-or-path : string?
Parameter for the Buttercup executable.

Defaults to "buttercup".

parameter

(parameter/ert-runner-executable)  string?

(parameter/ert-runner-executable executable-name-or-path)
  void?
  executable-name-or-path : string?
Parameter for the ERT-Runner executable.

Defaults to "ert-runner".

procedure

(run-emacs args)  exact-integer?

  args : (listof string?)
Run GNU Emacs with args.

Returns the exit code.

procedure

(run-buttercup args)  exact-integer?

  args : (listof string?)
Run Buttercup with args.

Returns the exit code.

procedure

(run-ert-runner args)  exact-integer?

  args : (listof string?)
Run ERT-Runner with args.

Returns the exit code.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/string)
(require racket/system)
 
(require "../../log/simplelog.rkt")
 
(provide
 (contract-out
  [parameter/emacs-executable
   (parameter/c string?)]
  [parameter/buttercup-executable
   (parameter/c string?)]
  [parameter/ert-runner-executable
   (parameter/c string?)]
  [run-emacs
   (-> (listof string?)
       exact-integer?)]
  [run-buttercup
   (-> (listof string?)
       exact-integer?)]
  [run-ert-runner
   (-> (listof string?)
       exact-integer?)]))
 
(define parameter/emacs-executable
  (make-parameter "emacs"))
 
(define parameter/buttercup-executable
  (make-parameter "buttercup"))
 
(define parameter/ert-runner-executable
  (make-parameter "ert-runner"))
 
(define (run-emacs args)
  (define emacs-exe-path
    (find-executable-path (parameter/emacs-executable)))
  (when (not emacs-exe-path)
    (raise (exn:fail "Could not find any GNU Emacs executable"
                     (current-continuation-marks))))
  (define cmd-args (append (list "-q" "--no-site-file") args))
  (simplelog-debug "Executing: ~a ~a" emacs-exe-path (string-join cmd-args " "))
  (apply system*/exit-code emacs-exe-path cmd-args))
 
(define (run-buttercup args)
  (define buttercup-exe-path
    (find-executable-path (parameter/buttercup-executable)))
  (when (not buttercup-exe-path)
    (raise (exn:fail "Could not find buttercup executable"
                     (current-continuation-marks))))
  (define cmd-args (append '("--traceback" "full") args))
  (simplelog-debug "Executing: ~a ~a" buttercup-exe-path (string-join cmd-args " "))
  (apply system*/exit-code buttercup-exe-path cmd-args))
 
(define (run-ert-runner args)
  (define ert-runner-exe-path
    (find-executable-path (parameter/ert-runner-executable)))
  (when (not ert-runner-exe-path)
    (raise (exn:fail "Could not find ert-runner executable"
                     (current-continuation-marks))))
  (define cmd-args (append '("--reporter" "ert+duration" "--script") args))
  (simplelog-debug "Executing: ~a ~a" ert-runner-exe-path (string-join cmd-args " "))
  (apply system*/exit-code ert-runner-exe-path cmd-args))

4.14 Enigma API: Find System Utils🔗

 (require enigma/system/utils/find) package: enigma-app

procedure

(emacs-lisp-file? file-path)  boolean?

  file-path : path-string?
Checks whether file-path is an Emacs Lisp file.

procedure

(find-emacs-lisp-files start-path)  (listof path?)

  start-path : path-string?
Recursively finds all Emacs Lisp files under start-path.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/file)
 
(require threading)
 
(require "../../system/utils/path.rkt")
 
(provide
 (contract-out
  [emacs-lisp-file?
   (-> path-string?
       boolean?)]
  [find-emacs-lisp-files
   (-> path-string?
       (listof path?))]))
 
(define emacs-lisp-file?
  (lambda~> expand-user-path
            path->complete-path
            simplify-path
            path->string
            ((lambda (p)
               (and
                ;; Matches "*.el".
                (~> p
                    (regexp-match #rx"\\.el$" _)
                    (and #true))
                ;; Is not a dir.
                (not (directory-exists? p))
                ;; Is not hidden.
                (~> p
                    path->name
                    (regexp-match #rx"^\\." _)
                    not))) _)))
 
(define find-emacs-lisp-files
  (lambda~> expand-user-path
            path->complete-path
            (simplify-path #true)
            (find-files emacs-lisp-file? #:follow-links? #true _)))

4.15 Enigma API: Path System Utilities🔗

 (require enigma/system/utils/path) package: enigma-app

procedure

(path-segments file-path)  (listof path?)

  file-path : path-string?
Splits file-path into its individual path segments.

procedure

(make-paths-tree file-path)  (listof path?)

  file-path : path-string?
Builds a list of all ancestor paths of file-path.

procedure

(path->directory file-path)  path?

  file-path : path-string?
Returns the parent directory of file-path.

procedure

(path->name file-path)  string?

  file-path : path-string?
Returns the final path component of file-path.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
(require racket/list)
 
(require threading)
 
(provide
 (contract-out
  [path-segments
   (-> path-string?
       (listof path?))]
  [make-paths-tree
   (-> path-string?
       (listof path?))]
  [path->directory
   (-> path-string?
       path?)]
  [path->name
   (-> path-string?
       string?)]))
 
(define path-segments
  (lambda~> expand-user-path
            path->complete-path
            simplify-path
            explode-path))
 
(define (make-paths-tree file-path)
  (for/fold ([paths '()])
            ([path-segment (path-segments file-path)])
    (cond
      [(null? paths)
       (list path-segment)]
      [else
       (define last-used-path (car paths))
       (cons (build-path last-used-path path-segment) paths)])))
 
(define path->directory
  (lambda~> path-segments
            reverse
            cdr
            reverse
            (apply build-path _)))
 
(define path->name
  (lambda~> path-segments
            last
            path->string))

4.16 Enigma API: Time Util🔗

 (require enigma/system/utils/time) package: enigma-app

procedure

(with-time-report #:announce pre-report-function    
  #:report report-function    
  #:thunk thunk-function)  any?
  pre-report-function : (-> any)
  report-function : (-> exact-integer? any)
  thunk-function : (-> any)
Calls thunk-function, measuring its execution time.

Before calling the thunk, pre-report-function is invoked as an announcement.

After the thunk completes, the elapsed time in milliseconds is reported to report-function.

The return value is the result of thunk-function.

The source code of this module:

#lang racket/base
 
(require racket/contract/base)
 
(require threading)
 
(provide
 (contract-out
  [with-time-report
    (-> #:announce (-> any)
        #:report (-> exact-integer? any)
        #:thunk (-> any)
        any)]))
 
(define (with-time-report
          #:announce [pre-report-function void]
          #:report [report-function void]
          #:thunk [thunk-function void])
  (define start-time-milliseconds (current-milliseconds))
  (dynamic-wind
    (lambda ()
      (pre-report-function))
    (lambda ()
      (thunk-function))
    (lambda ()
      (define stop-time-milliseconds (current-milliseconds))
      (~> (- stop-time-milliseconds start-time-milliseconds)
          report-function))))