emacs-twist / elisp-helpers

A Nix library for scraping Emacs Lisp package metadata

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

nix-elisp-helpers

This repository provides some Nix functions for getting information of Emacs Lisp packages. It uses talyz/fromElisp parser to parse S expressions.

Features

This repository provides functions for parsing a Cask file, a MELPA-style recipe, and a package list on ELPA. Then you can use the parsing result to inspect dependencies, fetch the source repository, and generate a list of files of the package.

It also has good support for flake references. You can get a flake reference to the source repository of a package.

Requirements

The fetch functions in this package requires Nix 2.4 or later.

Installation

default.nix in this repository provides several functions for elisp development. You can import the repository directly:

with (import (builtins.fetchGit {
  url = "https://github.com/akirak/nix-elisp-helpers.git";
  ref = "master";
  ...
}));

Alternatively, you can use niv to add to your repository:

niv add akirak/nix-elisp-helpers
with (import (import ./nix/sources.nix).nix-elisp-helpers { inherit pkgs; });

Flake

You can also use the functions via flake, but they are available under lib.${system} due to an indirect dependency on nixpkgs.

Usage

Cask

Parsing a Cask file

parseCask function takes a Cask file content as an argument and returns the package data in an attribute set:

let
  packageInfo = parseCask (builtins.readFile ./Cask)
in
  ...

development.dependencies holds all depends-on items in development, e.g.:

assert (builtins.map builtins.head packageInfo.development.dependencies ==
  ["f" "s" "dash" "ansi" "ecukes" "servant" "ert-runner" "el-mock" "noflet" "ert-async" "shell-split-string"]);
...

MELPA package

Parsing a MELPA recipe

parseMelpaRecipe function takess a MELPA-style recipe string as an argument and returns its content as an attribute set:

# The file contains '(smex :repo "nonsequitur/smex" :fetcher github)'
let
  package = parseMelpaRecipe (builtins.readFile ./smex);
in
...

It’s handy because it returns an attribute set:

assert (package.ename == "smex");
assert (package.repo == "nonsequitur/smex");
assert (package.fetcher == "github");
assert (package.files == null);
...

Converting a MELPA recipe to a flake reference

fetchTreeFromMelpaRecipe function takes a string or attribute set for a MELPA-style recipe and fetches a snapshot of the source repository of the package. The snapshot is stored in Nix, and the store path is returned.

fetchTreeFromMelpaRecipe (builtins.readFile ./my-package-recipe)
let
  recipe = ./dash;
  src = fetchTreeFromMelpaRecipe (builtins.readFile recipe);
in
pkgs.emacsPackages.melpaBuild {
  pname = "dash";
  version = "2.15";
  # The remote source is used
  inherit src recipe;
  ...
}

Note that this function does not work in pure evaluation mode.

You can also use flakeRefAttrsFromMelpaRecipe function to retrieve an attribute set that can be passed to builtins.fetchTree function which is available since Nix 2.4.

Note: Nixpkgs includes an equivalent function in pkgs/applications/editors/emacs/elixp-packages/libgenerated.nix.

flakeRefUrlFromMelpaRecipe function takes a recipe string as an argument and returns a URL-like flake reference:

let
  recipe = ''
    (smex :repo "nonsequitur/smex" :fetcher github)
  '';
in
assert (flakeRefUrlFromMelpaRecipe recipe == "github:nonsequitur/smex");
...

Note that this function may not completely support all of the reference specs.

Expanding the files spec a MELPA recipe

expandMelpaRecipeFiles function expands :files spec in a recipe under a given directory. This is equivalent to package-build-expand-file-specs function in package-build, which is used to build packages on MELPA:

expandMelpaRecipeFiles ./. ["*.el" [":excludes" ".dir-locals.el" "*-test.el"]]

The first argument must be a path to a directory, and the second argument can be either a list or null. When null is given as a spec, the default spec of MELPA is used.

It returns an attribute set of matching files relative from the directory:

{
  "hello.el" = "hello.el";
  "hello-utils.el" = "hello-utils.el";
}

It can be combined with parseMelpaRecipe:

let
  package = parseMelpaRecipe (builtins.readFile ./awesome-package);
  files = expandMelpaRecipeFiles ./. package.files;
in
assert (files == {
  "awesome-package.el" = "awesome-package.el";
  "awesome-package-utils.el" = "awesome-package-utils.el";
});
...

Note: This function returned a result in the past, but it now returns an attribute set.

Notes on MELPA recipes

Supported recipe specs
This library does not support 100% of the recipe format supported by MELPA.
:fetcher

The following :fetcher types are generally supported: github, gitlab, and git. hg may not be supported.

:url

:url is supported when you use git fetcher.

:repo

:repo is supported when you use one of github and gitlab fetcher types.

:branch

:branch is supported.

:version-regexp

:version-regexp is not supported. Maybe coming soon.

:commit

:commit is supported.

:files

:files is supported.

ELPA packages

Parsing an ELPA package list

Parse an ELPA-style package list (example) and returns an attribute set.

packages = parseElpaPackages (builtins.readFile ./elpa-packages)

Each value in the attribute set (which should be originally a plist) is converted to an attribute set:

assert (packages.ztree.url == "https://github.com/fourier/ztree");
...

Converting an ELPA package entry to a flake reference

flakeRefAttrsFromElpaAttrs takes an attribute set from a value in the result of parseElpaPackages and returns an attribute set that can be passed to builtins.fetchTree:

let
  packages = parseElpaPackages (builtins.readFile ./elpa-packages);
in
builtins.fetchTree (flakeRefAttrsFromElpaAttrs {} packages.ztree)

The first argument is an attribute set which can consist of the following options:

  • If preferReleaseBranch is true, :release-branch is chosen as the branch if there is one.

Package descriptions

Parsing a package description

parsePkg parses the content of a *-pkg.el file.

let
  description = parsePkg ''
    (define-package "my-package" "0.5"
      "My first package"
      '((emacs "26.2")
        (dash "2.18")))
  '';
in
..
{
  ename = "my-package";
  version = "0.5";
  summary = "My first package";
  packageRequires = {
    emacs = "26.2";
    dash = "2.18";
  };
}

If the package description has no field for the requirements, the value of packageRequires attribute will become null.

Note the attribute names follow the conventions used by parseMelpaRecipe if applicable.

Other utility functions

Converting a flake reference to another representation

flakeRefUrlFromFlakeRefAttrs converts an attribute set to its equivalent URL-style representation.

Credits

This project uses talyz/fromElisp for parsing Emacs Lisp expressions in Nix.

About

A Nix library for scraping Emacs Lisp package metadata

License:GNU General Public License v3.0


Languages

Language:Nix 97.4%Language:Emacs Lisp 2.0%Language:Makefile 0.6%