nix: Add a basic flake and derivation for fg42
This commit is contained in:
parent
884ec253f1
commit
d4cf1e698d
|
@ -39,3 +39,4 @@ emacs.d/
|
|||
|
||||
docs/site/orgs/cubes/
|
||||
**/*/sitemap.inc
|
||||
result
|
10
core/fpkg.el
10
core/fpkg.el
|
@ -26,6 +26,7 @@
|
|||
;;; Code:
|
||||
;;(require 'use-package)
|
||||
|
||||
(defvar package-names ())
|
||||
|
||||
(defun inject-straight (args)
|
||||
"Inject `:straight t' to ARGS it the key was missing."
|
||||
|
@ -47,9 +48,12 @@
|
|||
|
||||
(if (and (listp details) (< 0 (length details)))
|
||||
(let ((params (inject-straight (inject-defer details))))
|
||||
(progn
|
||||
`(use-package ,pkg ,@params)))
|
||||
`(use-package ,pkg :straight t :defer t)))
|
||||
(progn
|
||||
(add-to-list 'package-names pkg)
|
||||
`(use-package ,pkg ,@params)))
|
||||
(progn
|
||||
(add-to-list 'package-names pkg)
|
||||
`(use-package ,pkg :straight t :defer t))))
|
||||
|
||||
|
||||
(defmacro fpkg/require (pkg)
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
;;; FG42 --- The mighty editor for the emacsians -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (C) 2010-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
;;
|
||||
;; Author: Sameer Rahmani <lxsameer@gnu.org>
|
||||
;; Keywords: lisp fg42 IDE package manager
|
||||
;; Version: 1.0.0
|
||||
;;
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
;;
|
||||
;;; Commentary:
|
||||
;; This file contains all the dependencies of FG42 v4 in no particular order.
|
||||
;; Whether we use a package during runtime or not we will build everything
|
||||
;; and decide what to load at runtime.
|
||||
;;
|
||||
;; We fetch these dependencies using Nix via the emacs-overlay.
|
||||
;;
|
||||
;;; Code:
|
||||
(defmacro depends-on (&rest _)
|
||||
"Just a placeholder."
|
||||
nil)
|
||||
|
||||
(depends-on
|
||||
bm
|
||||
command-log-mode
|
||||
dockerfile-mode
|
||||
lsp-ltex
|
||||
dracula-theme
|
||||
lsp-julia
|
||||
flycheck-julia
|
||||
julia-formatter
|
||||
julia-repl
|
||||
julia-mode
|
||||
nix-mode
|
||||
noether-mode
|
||||
ednc
|
||||
idris-mode
|
||||
company-coq
|
||||
proof-general
|
||||
msgpack
|
||||
gdscript-mode
|
||||
meson-mode
|
||||
lsp-scheme
|
||||
clojure-mode
|
||||
cider
|
||||
aggressive-indent-mode
|
||||
graphviz-dot-mode
|
||||
terraform-mode
|
||||
magit-todos
|
||||
magit
|
||||
diff-hl
|
||||
rustic
|
||||
yasnippet
|
||||
yasnippet-snippets
|
||||
ninja-mode
|
||||
eldoc-cmake
|
||||
cmake-mode
|
||||
pyenv-mode
|
||||
pyvenv
|
||||
poetry
|
||||
lsp-pyright
|
||||
python-black
|
||||
|
||||
lsp-java
|
||||
flycheck-gradle
|
||||
gradle-mode
|
||||
groovy-mode
|
||||
|
||||
vterm
|
||||
|
||||
projectile-ripgrep
|
||||
|
||||
go-mode
|
||||
|
||||
company-box
|
||||
company
|
||||
lsp-ui
|
||||
lsp-mode
|
||||
yaml-mode
|
||||
flycheck
|
||||
expand-region
|
||||
eros
|
||||
org-mode
|
||||
org-journal
|
||||
org-bullets
|
||||
org-sidebar
|
||||
org-super-agenda
|
||||
org-ql
|
||||
ctrlf
|
||||
selectrum-prescient
|
||||
selectrum
|
||||
badwolf-theme
|
||||
ace-window
|
||||
avy
|
||||
paredit
|
||||
rainbow-delimiters
|
||||
exec-path-from-shell
|
||||
discover
|
||||
emojify
|
||||
alert
|
||||
imenu-list
|
||||
pinentry
|
||||
helpful
|
||||
which-key
|
||||
dirvish
|
||||
origami
|
||||
f
|
||||
projectile
|
||||
mini-modeline
|
||||
smart-mode-line
|
||||
all-the-icons
|
||||
)
|
||||
|
||||
;;; deps.el ends here
|
|
@ -0,0 +1,148 @@
|
|||
{
|
||||
"nodes": {
|
||||
"emacs-overlay": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1707815184,
|
||||
"narHash": "sha256-WFoDXgaPdhjgQB3ut+ZN+VT7e60Yw+KUyvUkOSu5Wto=",
|
||||
"owner": "nix-community",
|
||||
"repo": "emacs-overlay",
|
||||
"rev": "0f7f3b39157419f3035a2dad39fbaf8a4ba0448d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "emacs-overlay",
|
||||
"rev": "0f7f3b39157419f3035a2dad39fbaf8a4ba0448d",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705309234,
|
||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705309234,
|
||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1707689078,
|
||||
"narHash": "sha256-UUGmRa84ZJHpGZ1WZEBEUOzaPOWG8LZ0yPg1pdDF/yM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f9d39fb9aff0efee4a3d5f4a6d7c17701d38a1d8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1707650010,
|
||||
"narHash": "sha256-dOhphIA4MGrH4ElNCy/OlwmN24MsnEqFjRR6+RY7jZw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "809cca784b9f72a5ad4b991e0e7bcf8890f9c3a6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1707451808,
|
||||
"narHash": "sha256-UwDBUNHNRsYKFJzyTMVMTF5qS4xeJlWoeyJf+6vvamU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "442d407992384ed9c0e6d352de75b69079904e4e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "442d407992384ed9c0e6d352de75b69079904e4e",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"emacs-overlay": "emacs-overlay",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
# Fg42 - Emacs Editor for advance users
|
||||
#
|
||||
# Copyright (c) 2010-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
{
|
||||
description = "FG42 - Emacs Editor for advance users";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/442d407992384ed9c0e6d352de75b69079904e4e";
|
||||
inputs.emacs-overlay.url = "github:nix-community/emacs-overlay/0f7f3b39157419f3035a2dad39fbaf8a4ba0448d"; inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
outputs = { self, nixpkgs, ... }@inputs:
|
||||
inputs.flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ inputs.emacs-overlay.overlays.package ];
|
||||
};
|
||||
elispDepsFile = ./deps.el;
|
||||
|
||||
elispPkgs = pkgs.callPackage ./nix/deps.nix {
|
||||
inherit elispDepsFile;
|
||||
};
|
||||
|
||||
ourPackages = pkgs.callPackage ./nix/packages.nix {};
|
||||
|
||||
fg42 = pkgs.callPackage ./nix/fg42.nix {
|
||||
inherit elispPkgs ourPackages;
|
||||
srcDir = ./.;
|
||||
};
|
||||
|
||||
in {
|
||||
inherit pkgs;
|
||||
|
||||
packages.default = pkgs.writeScriptBin "fg42" ''
|
||||
#!${pkgs.stdenv.shell}
|
||||
|
||||
export FG42_HOME=${fg42}/fg42
|
||||
LIBRARY_PATH="$(cc -print-file-name=libgccjit.so):$LIBRARY_PATH" FG42_WM=false emacs \
|
||||
--name FG42 \
|
||||
--no-site-file --no-site-lisp \
|
||||
--no-splash --title FG42 \
|
||||
-l $FG42_HOME/fg42-config.el "$@"
|
||||
'';
|
||||
|
||||
packages.wm = pkgs.writeScriptBin "fg42-wm" ''
|
||||
#!${pkgs.stdenv.shell}
|
||||
|
||||
# Disable access control for the current user.
|
||||
xhost +SI:localuser:$USER
|
||||
|
||||
# Make Java applications aware this is a non-reparenting window manager.
|
||||
export _JAVA_AWT_WM_NONREPARENTING=1
|
||||
|
||||
# Set default cursor.
|
||||
xsetroot -cursor_name left_ptr
|
||||
|
||||
# Set keyboard repeat rate.
|
||||
xset r rate 400 30
|
||||
|
||||
# Uncomment the following block to use the exwm-xim module.
|
||||
#export XMODIFIERS=@im=exwm-xim
|
||||
#export GTK_IM_MODULE=xim
|
||||
#export QT_IM_MODULE=xim
|
||||
#export CLUTTER_IM_MODULE=xim
|
||||
|
||||
export FG42_HOME=${fg42}/fg42
|
||||
LIBRARY_PATH=$(cc -print-file-name=libgccjit.so):$LIBRARY_PATH FG42_WM=true emacs \
|
||||
--name FG42 \
|
||||
--no-site-file --no-site-lisp \
|
||||
--no-splash --title FG42 \
|
||||
-l $FG42_HOME/fg42-config.el "$@"
|
||||
'';
|
||||
|
||||
# devShells.default = pkgs.mkShell {
|
||||
# nativeBuildInputs = deps ++ [ pkgs.fish ];
|
||||
# shellHook = ''
|
||||
# fish && exit
|
||||
# '';
|
||||
# };
|
||||
}
|
||||
);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
# Fg42 - Emacs Editor for advance users
|
||||
#
|
||||
# Copyright (c) 2010-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
{ pkgs, lib, elispDepsFile }:
|
||||
with builtins;
|
||||
let
|
||||
reader = import ./elisp_reader.nix { inherit lib; };
|
||||
elispAst = reader.fromElisp (builtins.readFile elispDepsFile);
|
||||
dependsOnForm = filter (x: head x == "depends-on") elispAst;
|
||||
elispPkgs = if length dependsOnForm == 0
|
||||
then throw "Can't find the form 'depends-on' on 'deps.el'"
|
||||
else tail (head dependsOnForm);
|
||||
in elispPkgs
|
|
@ -0,0 +1,752 @@
|
|||
# This file is copied from https://github.com/talyz/fromElisp which
|
||||
# distributes under the compatible license MIT.
|
||||
#
|
||||
# We have changed this according to our needs
|
||||
{ lib
|
||||
, commentMaxLength ? 300
|
||||
, stringMaxLength ? 3000
|
||||
, characterMaxLength ? 50
|
||||
, integerMaxLength ? 50
|
||||
, floatMaxLength ? 50
|
||||
, boolVectorMaxLength ? 50
|
||||
, symbolMaxLength ? 50
|
||||
, orgModeBabelCodeBlockHeaderMaxLength ? 200
|
||||
, orgModeBabelCodeBlockArgMaxLength ? 30
|
||||
}:
|
||||
|
||||
with lib;
|
||||
with builtins;
|
||||
|
||||
let
|
||||
|
||||
# Create a matcher from a regex string and maximum length. A
|
||||
# matcher takes a string and returns the first match produced by
|
||||
# running its regex on it, or null if the match is unsuccessful,
|
||||
# but only as far in as specified by maxLength.
|
||||
mkMatcher = regex: maxLength:
|
||||
string:
|
||||
let
|
||||
substr = substring 0 maxLength string;
|
||||
matched = match regex substr;
|
||||
in
|
||||
if matched != null then head matched else null;
|
||||
|
||||
removeStrings = stringsToRemove: string:
|
||||
let
|
||||
len = length stringsToRemove;
|
||||
listOfNullStrings = genList (const "") len;
|
||||
in
|
||||
replaceStrings stringsToRemove listOfNullStrings string;
|
||||
|
||||
# Split a string of elisp into individual tokens and add useful
|
||||
# metadata.
|
||||
tokenizeElisp' = { elisp, startLineNumber ? 1 }:
|
||||
let
|
||||
# These are the only characters that can not be unescaped in a
|
||||
# symbol name. We match the inverse of these to get the actual
|
||||
# symbol characters and use them to differentiate between
|
||||
# symbols and tokens that could potentially look like symbols,
|
||||
# such as numbers. Due to the leading bracket, this has to be
|
||||
# placed _first_ inside a bracket expression.
|
||||
notInSymbol = '']["'`,#;\\()[:space:][:cntrl:]'';
|
||||
|
||||
matchComment = mkMatcher "(;[^\n]*).*" commentMaxLength;
|
||||
|
||||
matchString = mkMatcher ''("([^"\\]|\\.)*").*'' stringMaxLength;
|
||||
|
||||
matchCharacter = mkMatcher ''([?]((\\[sSHMAC]-)|\\\^)*(([^][\\()]|\\[][\\()])|\\[^^SHMACNuUx0-7]|\\[uU][[:digit:]a-fA-F]+|\\x[[:digit:]a-fA-F]*|\\[0-7]{1,3}|\\N\{[^}]+}))([${notInSymbol}?]|$).*'' characterMaxLength;
|
||||
|
||||
matchNonBase10Integer = mkMatcher ''(#([BbOoXx]|[[:digit:]]{1,2}r)[[:digit:]a-fA-F]+)([${notInSymbol}]|$).*'' integerMaxLength;
|
||||
|
||||
matchInteger = mkMatcher ''([+-]?[[:digit:]]+[.]?)([${notInSymbol}]|$).*'' integerMaxLength;
|
||||
|
||||
matchBoolVector = mkMatcher ''(#&[[:digit:]]+"([^"\\]|\\.)*").*'' boolVectorMaxLength;
|
||||
|
||||
matchFloat = mkMatcher ''([+-]?([[:digit:]]*[.][[:digit:]]+|([[:digit:]]*[.])?[[:digit:]]+e([+-]?[[:digit:]]+|[+](INF|NaN))))([${notInSymbol}]|$).*'' floatMaxLength;
|
||||
|
||||
matchDot = mkMatcher ''([.])([${notInSymbol}]|$).*'' 2;
|
||||
|
||||
# Symbols can contain pretty much any characters - the general
|
||||
# rule is that if nothing else matches, it's a symbol, so we
|
||||
# should be pretty generous here and match for symbols last. See
|
||||
# https://www.gnu.org/software/emacs/manual/html_node/elisp/Symbol-Type.html
|
||||
matchSymbol =
|
||||
let
|
||||
symbolChar = ''([^${notInSymbol}]|\\.)'';
|
||||
in mkMatcher ''(${symbolChar}+)([${notInSymbol}]|$).*'' symbolMaxLength;
|
||||
|
||||
maxTokenLength = foldl' max 0 [
|
||||
commentMaxLength
|
||||
stringMaxLength
|
||||
characterMaxLength
|
||||
integerMaxLength
|
||||
floatMaxLength
|
||||
boolVectorMaxLength
|
||||
symbolMaxLength
|
||||
];
|
||||
|
||||
# Fold over all the characters in a string, checking for
|
||||
# matching tokens.
|
||||
#
|
||||
# The implementation is a bit obtuse, for optimization reasons:
|
||||
# nix doesn't have tail-call optimization, thus a strict fold,
|
||||
# which should essentially force a limited version of tco when
|
||||
# iterating a list, is our best alternative.
|
||||
#
|
||||
# The string read from is split into a list of its constituent
|
||||
# characters, which is then folded over. Each character is then
|
||||
# used to determine a likely matching regex "matcher" to run on
|
||||
# the string, starting at the position of the aforementioned
|
||||
# character. When an appropriate matcher has been found and run
|
||||
# successfully on the string, its result is added to
|
||||
# `state.acc`, a list of all matched tokens. The length of the
|
||||
# matched token is determined and passed on to the following
|
||||
# iteration through `state.skip`. If `state.skip` is positive,
|
||||
# nothing will be done in the current iteration, except
|
||||
# decrementing `state.skip` for the next one: this skips the
|
||||
# characters we've already matched. At each iteration,
|
||||
# `state.pos` is also incremented, to keep track of the current
|
||||
# string position.
|
||||
#
|
||||
# The order of the matches is significant - matchSymbol will,
|
||||
# for example, also match numbers and characters, so we check
|
||||
# for symbols last.
|
||||
readToken = state: char:
|
||||
let
|
||||
rest = substring state.pos maxTokenLength elisp;
|
||||
comment = matchComment rest;
|
||||
character = matchCharacter rest;
|
||||
nonBase10Integer = matchNonBase10Integer rest;
|
||||
integer = matchInteger rest;
|
||||
float = matchFloat rest;
|
||||
function = matchFunction rest;
|
||||
boolVector = matchBoolVector rest;
|
||||
string = matchString rest;
|
||||
dot = matchDot rest;
|
||||
symbol = matchSymbol rest;
|
||||
in
|
||||
if state.skip > 0 then
|
||||
state // {
|
||||
pos = state.pos + 1;
|
||||
skip = state.skip - 1;
|
||||
line = if char == "\n" then state.line + 1 else state.line;
|
||||
}
|
||||
else if char == "\n" then
|
||||
let
|
||||
mod = state.line / 1000;
|
||||
newState = {
|
||||
pos = state.pos + 1;
|
||||
line = state.line + 1;
|
||||
inherit mod;
|
||||
};
|
||||
in
|
||||
state // (
|
||||
# Force evaluation of old state every 1000 lines. Nix
|
||||
# doesn't have a modulo builtin, so we have to save
|
||||
# the result of an integer division and compare
|
||||
# between runs.
|
||||
if mod > state.mod then
|
||||
seq state.acc newState
|
||||
else
|
||||
newState
|
||||
)
|
||||
else if elem char [ " " "\t" "\r" ] then
|
||||
state // {
|
||||
pos = state.pos + 1;
|
||||
inherit (state) line;
|
||||
}
|
||||
else if char == ";" then
|
||||
if comment != null then
|
||||
state // {
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength comment) - 1;
|
||||
}
|
||||
else throw "Unrecognized token on line ${toString state.line}: ${rest}"
|
||||
else if char == "(" then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "openParen"; value = "("; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
}
|
||||
else if char == ")" then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "closeParen"; value = ")"; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
}
|
||||
else if char == "[" then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "openBracket"; value = "["; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
}
|
||||
else if char == "]" then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "closeBracket"; value = "]"; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
}
|
||||
else if char == "'" then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "quote"; value = "'"; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
}
|
||||
else if char == ''"'' then
|
||||
if string != null then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "string"; value = string; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength string) - 1;
|
||||
}
|
||||
else throw "Unrecognized token on line ${toString state.line}: ${rest}"
|
||||
else if char == "#" then
|
||||
let nextChar = substring 1 1 rest;
|
||||
in
|
||||
if nextChar == "'" then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "function"; value = "#'"; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = 1;
|
||||
}
|
||||
else if nextChar == "&" then
|
||||
if boolVector != null then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "boolVector"; value = boolVector; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength boolVector) - 1;
|
||||
}
|
||||
else throw "Unrecognized token on line ${toString state.line}: ${rest}"
|
||||
else if nextChar == "s" then
|
||||
if substring 2 1 rest == "(" then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "record"; value = "#s"; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = 1;
|
||||
}
|
||||
else throw "List must follow #s in record on line ${toString state.line}: ${rest}"
|
||||
else if nextChar == "[" then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "byteCode"; value = "#"; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
}
|
||||
else if nonBase10Integer != null then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "nonBase10Integer"; value = nonBase10Integer; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength nonBase10Integer) - 1;
|
||||
}
|
||||
else throw "Unrecognized token on line ${toString state.line}: ${rest}"
|
||||
else if elem char [ "+" "-" "." "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" ] then
|
||||
if integer != null then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "integer"; value = integer; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength integer) - 1;
|
||||
}
|
||||
else if float != null then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "float"; value = float; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength float) - 1;
|
||||
}
|
||||
else if dot != null then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "dot"; value = dot; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength dot) - 1;
|
||||
}
|
||||
else if symbol != null then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "symbol"; value = symbol; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength symbol) - 1;
|
||||
}
|
||||
else throw "Unrecognized token on line ${toString state.line}: ${rest}"
|
||||
else if char == "?" then
|
||||
if character != null then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "character"; value = character; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength character) - 1;
|
||||
}
|
||||
else throw "Unrecognized token on line ${toString state.line}: ${rest}"
|
||||
else if char == "`" then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "backquote"; value = "`"; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
}
|
||||
else if char == "," then
|
||||
if substring 1 1 rest == "@" then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "slice"; value = ",@"; inherit (state) line; }];
|
||||
skip = 1;
|
||||
pos = state.pos + 1;
|
||||
}
|
||||
else
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "expand"; value = ","; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
}
|
||||
else if symbol != null then
|
||||
state // {
|
||||
acc = state.acc ++ [{ type = "symbol"; value = symbol; inherit (state) line; }];
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength symbol) - 1;
|
||||
}
|
||||
else
|
||||
throw "Unrecognized token on line ${toString state.line}: ${rest}";
|
||||
in (builtins.foldl' readToken { acc = []; pos = 0; skip = 0; line = startLineNumber; mod = 0; } (stringToCharacters elisp)).acc;
|
||||
|
||||
tokenizeElisp = elisp:
|
||||
tokenizeElisp' { inherit elisp; };
|
||||
|
||||
# Produce an AST from a list of tokens produced by `tokenizeElisp`.
|
||||
parseElisp' = tokens:
|
||||
let
|
||||
# Convert literal value tokens in a flat list to their
|
||||
# corresponding nix representation.
|
||||
parseValues = tokens:
|
||||
map (token:
|
||||
if token.type == "string" then
|
||||
token // {
|
||||
value = substring 1 (stringLength token.value - 2) token.value;
|
||||
}
|
||||
else if token.type == "integer" then
|
||||
token // {
|
||||
value = fromJSON (removeStrings ["+" "."] token.value);
|
||||
}
|
||||
else if token.type == "symbol" && token.value == "t" then
|
||||
token // {
|
||||
value = true;
|
||||
}
|
||||
else if token.type == "float" then
|
||||
let
|
||||
initial = head (match "([+-]?([[:digit:]]*[.])?[[:digit:]]+(e([+-]?[[:digit:]]+|[+](INF|NaN)))?)" token.value);
|
||||
isSpecial = (match "(.+(e[+](INF|NaN)))" initial) != null;
|
||||
withoutPlus = removeStrings ["+"] initial;
|
||||
withPrefix =
|
||||
if substring 0 1 withoutPlus == "." then
|
||||
"0" + withoutPlus
|
||||
else if substring 0 2 withoutPlus == "-." then
|
||||
"-0" + removeStrings ["-"] withoutPlus
|
||||
else
|
||||
withoutPlus;
|
||||
in
|
||||
if !isSpecial && withPrefix != null then
|
||||
token // {
|
||||
value = fromJSON withPrefix;
|
||||
}
|
||||
else
|
||||
token
|
||||
else
|
||||
token
|
||||
) tokens;
|
||||
|
||||
# Convert pairs of opening and closing tokens to their
|
||||
# respective collection types, i.e. lists and vectors. Also,
|
||||
# normalize the forms of nil, which can be written as either
|
||||
# `nil` or `()`, to empty lists.
|
||||
#
|
||||
# For performance reasons, this is implemented as a fold over
|
||||
# the list of tokens, rather than as a recursive function. To
|
||||
# keep track of list depth when sublists are parsed, a list,
|
||||
# `state.acc`, is used as a stack. When entering a sublist, an
|
||||
# empty list is pushed to `state.acc`, and items in the sublist
|
||||
# are subsequently added to this list. When exiting the list,
|
||||
# `state.acc` is popped and the completed list is added to the
|
||||
# new head of `state.acc`, i.e. the outer list, which we were
|
||||
# parsing before entering the sublist.
|
||||
#
|
||||
# Evaluation of old state is forced with `seq` in a few places,
|
||||
# because nix otherwise keeps it around, eventually resulting in
|
||||
# a stack overflow.
|
||||
parseCollections = tokens:
|
||||
let
|
||||
parseToken = state: token:
|
||||
let
|
||||
openColl = if token.type == "openParen" then "list" else if token.type == "openBracket" then "vector" else null;
|
||||
closeColl = if token.type == "closeParen" then "list" else if token.type == "closeBracket" then "vector" else null;
|
||||
in
|
||||
if openColl != null then
|
||||
state // {
|
||||
acc = [ [] ] ++ seq (head state.acc) state.acc;
|
||||
inColl = [ openColl ] ++ state.inColl;
|
||||
depth = state.depth + 1;
|
||||
line = [ token.line ] ++ state.line;
|
||||
}
|
||||
else if closeColl != null then
|
||||
if (head state.inColl) == closeColl then
|
||||
let
|
||||
outerColl = elemAt state.acc 1;
|
||||
currColl = {
|
||||
type = closeColl;
|
||||
value = head state.acc;
|
||||
line = head state.line;
|
||||
inherit (state) depth;
|
||||
};
|
||||
rest = tail (tail state.acc);
|
||||
in
|
||||
state // seq state.acc {
|
||||
acc = [ (outerColl ++ [ currColl ]) ] ++ rest;
|
||||
inColl = tail state.inColl;
|
||||
depth = state.depth - 1;
|
||||
line = tail state.line;
|
||||
}
|
||||
else
|
||||
throw "Unmatched ${token.type} on line ${toString token.line}"
|
||||
else if token.type == "symbol" && token.value == "nil" then
|
||||
let
|
||||
currColl = head state.acc;
|
||||
rest = tail state.acc;
|
||||
emptyList = {
|
||||
type = "list";
|
||||
depth = state.depth + 1;
|
||||
value = [];
|
||||
};
|
||||
in
|
||||
state // seq currColl { acc = [ (currColl ++ [ emptyList ]) ] ++ rest; }
|
||||
else
|
||||
let
|
||||
currColl = head state.acc;
|
||||
rest = tail state.acc;
|
||||
in
|
||||
state // seq currColl { acc = [ (currColl ++ [ token ]) ] ++ rest; };
|
||||
in
|
||||
head (builtins.foldl' parseToken { acc = [ [] ]; inColl = [ null ]; depth = -1; line = []; } tokens).acc;
|
||||
|
||||
# Handle dotted pair notation, a syntax where the car and cdr
|
||||
# are represented explicitly. See
|
||||
# https://www.gnu.org/software/emacs/manual/html_node/elisp/Dotted-Pair-Notation.html#Dotted-Pair-Notation
|
||||
# for more info.
|
||||
#
|
||||
# This mainly entails handling lists that are the cdrs of a
|
||||
# dotted pairs, concatenating the lexically distinct lists into
|
||||
# the logical list they actually represent.
|
||||
#
|
||||
# For example:
|
||||
# (a . (b . (c . nil))) -> (a b c)
|
||||
parseDots = tokens:
|
||||
let
|
||||
parseToken = state: token:
|
||||
if token.type == "dot" then
|
||||
if state.inList then
|
||||
state // {
|
||||
dotted = true;
|
||||
depthReduction = state.depthReduction + 1;
|
||||
}
|
||||
else
|
||||
throw ''"Dotted pair notation"-dot outside list on line ${toString token.line}''
|
||||
else if isList token.value then
|
||||
let
|
||||
collectionContents = foldl' parseToken {
|
||||
acc = [];
|
||||
dotted = false;
|
||||
inList = token.type == "list";
|
||||
inherit (state) depthReduction;
|
||||
} token.value;
|
||||
in
|
||||
state // {
|
||||
acc = state.acc ++ (
|
||||
if state.dotted then
|
||||
collectionContents.acc
|
||||
else
|
||||
[
|
||||
(token // {
|
||||
value = collectionContents.acc;
|
||||
depth = token.depth - state.depthReduction;
|
||||
})
|
||||
]
|
||||
);
|
||||
dotted = false;
|
||||
}
|
||||
else
|
||||
state // {
|
||||
acc = state.acc ++ [token];
|
||||
};
|
||||
in
|
||||
(foldl' parseToken { acc = []; dotted = false; inList = false; depthReduction = 0; } tokens).acc;
|
||||
|
||||
parseQuotes = tokens:
|
||||
let
|
||||
parseToken = state: token':
|
||||
let
|
||||
token =
|
||||
if isList token'.value then
|
||||
token' // {
|
||||
value = (foldl' parseToken { acc = []; quotes = []; } token'.value).acc;
|
||||
}
|
||||
else
|
||||
token';
|
||||
in
|
||||
if elem token.type [ "quote" "expand" "slice" "backquote" "function" "record" "byteCode" ] then
|
||||
state // {
|
||||
quotes = [ token ] ++ state.quotes;
|
||||
}
|
||||
else if state.quotes != [] then
|
||||
let
|
||||
quote = value: token:
|
||||
token // {
|
||||
inherit value;
|
||||
};
|
||||
quotedValue = foldl' quote token state.quotes;
|
||||
in
|
||||
state // {
|
||||
acc = state.acc ++ [ quotedValue ];
|
||||
quotes = [];
|
||||
}
|
||||
else
|
||||
state // {
|
||||
acc = state.acc ++ [ token ];
|
||||
};
|
||||
in
|
||||
(foldl' parseToken { acc = []; quotes = []; } tokens).acc;
|
||||
in
|
||||
parseQuotes (parseDots (parseCollections (parseValues tokens)));
|
||||
|
||||
parseElisp = elisp:
|
||||
parseElisp' (tokenizeElisp elisp);
|
||||
|
||||
fromElisp' = ast:
|
||||
let
|
||||
readObject = object:
|
||||
if isList object.value then
|
||||
map readObject object.value
|
||||
else if object.type == "quote" then
|
||||
["quote" (readObject object.value)]
|
||||
else if object.type == "backquote" then
|
||||
["`" (readObject object.value)]
|
||||
else if object.type == "expand" then
|
||||
["," (readObject object.value)]
|
||||
else if object.type == "slice" then
|
||||
[",@" (readObject object.value)]
|
||||
else if object.type == "function" then
|
||||
["#'" (readObject object.value)]
|
||||
else if object.type == "byteCode" then
|
||||
["#"] ++ (readObject object.value)
|
||||
else if object.type == "record" then
|
||||
["#s"] ++ (readObject object.value)
|
||||
else
|
||||
object.value;
|
||||
in
|
||||
map readObject ast;
|
||||
|
||||
fromElisp = elisp:
|
||||
fromElisp' (parseElisp elisp);
|
||||
|
||||
# Parse an Org mode babel text and return a list of all code blocks
|
||||
# with metadata.
|
||||
#
|
||||
# The general operation is similar to tokenizeElisp', so check its
|
||||
# documentation for a more in-depth description.
|
||||
#
|
||||
# As in tokenizeElisp', the string read from is split into a list of
|
||||
# its constituent characters, which is then folded over. Each
|
||||
# character is then used to determine whether we should try to run a
|
||||
# match for a `#+begin_src` header or `#+end_src` footer, starting
|
||||
# at the position of the aforementioned character. These matches
|
||||
# should only be attempted if the current character is `#` and the
|
||||
# line has nothing but whitespace before it (noted by
|
||||
# `state.leadingWhitespace`).
|
||||
#
|
||||
# When an appropriate match for a header has been found, its
|
||||
# arguments are further parsed and the result is put into the code
|
||||
# block's `flags` attribute. The subsequent characters are added to
|
||||
# the code block's `body` attribute, until a footer is successfully
|
||||
# matched and the block is added to the list of parsed blocks,
|
||||
# `state.acc`.
|
||||
parseOrgModeBabel = text:
|
||||
let
|
||||
matchBeginCodeBlock = mkMatcher "(#[+][bB][eE][gG][iI][nN]_[sS][rR][cC])([[:space:]]+).*" orgModeBabelCodeBlockHeaderMaxLength;
|
||||
matchHeader = mkMatcher "(#[+][hH][eE][aA][dD][eE][rR][sS]?:)([[:space:]]+).*" orgModeBabelCodeBlockHeaderMaxLength;
|
||||
matchEndCodeBlock = mkMatcher "(#[+][eE][nN][dD]_[sS][rR][cC][^\n]*).*" orgModeBabelCodeBlockHeaderMaxLength;
|
||||
|
||||
matchBeginCodeBlockLang = match "([[:blank:]]*)([[:alnum:]][[:alnum:]-]*).*";
|
||||
matchBeginCodeBlockFlags = mkMatcher "([^\n]*[\n]).*" orgModeBabelCodeBlockHeaderMaxLength;
|
||||
|
||||
parseToken = state: char:
|
||||
let
|
||||
rest = substring state.pos orgModeBabelCodeBlockHeaderMaxLength text;
|
||||
beginCodeBlock = matchBeginCodeBlock rest;
|
||||
header = matchHeader rest;
|
||||
endCodeBlock = matchEndCodeBlock rest;
|
||||
language = matchBeginCodeBlockLang rest;
|
||||
flags = matchBeginCodeBlockFlags rest;
|
||||
|
||||
force = expr: seq state.pos (seq state.line expr);
|
||||
in
|
||||
if state.skip > 0 then
|
||||
state // force {
|
||||
pos = state.pos + 1;
|
||||
skip = state.skip - 1;
|
||||
line = if char == "\n" then state.line + 1 else state.line;
|
||||
leadingWhitespace = char == "\n" || (state.leadingWhitespace && elem char [ " " "\t" "\r" ]);
|
||||
}
|
||||
else if char == "#" && state.leadingWhitespace && !state.readBody && beginCodeBlock != null then
|
||||
state // {
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength beginCodeBlock) - 1;
|
||||
leadingWhitespace = false;
|
||||
readLanguage = true;
|
||||
}
|
||||
else if char == "#" && state.leadingWhitespace && !state.readBody && header != null then
|
||||
state // {
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength header) - 1;
|
||||
leadingWhitespace = false;
|
||||
readFlags = true;
|
||||
}
|
||||
else if state.readLanguage then
|
||||
if language != null then
|
||||
state // {
|
||||
block = state.block // {
|
||||
language = elemAt language 1;
|
||||
};
|
||||
pos = state.pos + 1;
|
||||
skip = (foldl' (total: string: total + (stringLength string)) 0 language) - 1;
|
||||
leadingWhitespace = false;
|
||||
readLanguage = false;
|
||||
readFlags = true;
|
||||
readBody = true;
|
||||
}
|
||||
else throw "Language missing or invalid for code block on line ${toString state.line}!"
|
||||
else if state.readFlags then
|
||||
if flags != null then
|
||||
let
|
||||
parseFlag = state: item:
|
||||
let
|
||||
prefix = if isString item then substring 0 1 item else null;
|
||||
in
|
||||
if elem prefix [ ":" "-" "+" ] then
|
||||
state // {
|
||||
acc = state.acc // { ${item} = true; };
|
||||
flag = item;
|
||||
}
|
||||
else if state.flag != null then
|
||||
state // {
|
||||
acc = state.acc // { ${state.flag} = item; };
|
||||
flag = null;
|
||||
}
|
||||
else
|
||||
state;
|
||||
in
|
||||
state // {
|
||||
block = state.block // {
|
||||
flags =
|
||||
(foldl'
|
||||
parseFlag
|
||||
{ acc = state.block.flags;
|
||||
flag = null;
|
||||
inherit (state) line;
|
||||
}
|
||||
(fromElisp flags)).acc;
|
||||
startLineNumber = state.line + 1;
|
||||
};
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength flags) - 1;
|
||||
line = if char == "\n" then state.line + 1 else state.line;
|
||||
leadingWhitespace = char == "\n";
|
||||
readFlags = false;
|
||||
}
|
||||
else throw "Arguments malformed for code block on line ${toString state.line}!"
|
||||
else if char == "#" && state.leadingWhitespace && endCodeBlock != null then
|
||||
state // {
|
||||
acc = state.acc ++ [ state.block ];
|
||||
block = {
|
||||
language = null;
|
||||
body = "";
|
||||
flags = {};
|
||||
};
|
||||
pos = state.pos + 1;
|
||||
skip = (stringLength endCodeBlock) - 1;
|
||||
leadingWhitespace = false;
|
||||
readBody = false;
|
||||
}
|
||||
else if state.readBody then
|
||||
let
|
||||
mod = state.pos / 100;
|
||||
newState = {
|
||||
block = state.block // {
|
||||
body = state.block.body + char;
|
||||
};
|
||||
inherit mod;
|
||||
pos = state.pos + 1;
|
||||
line = if char == "\n" then state.line + 1 else state.line;
|
||||
leadingWhitespace = char == "\n" || (state.leadingWhitespace && elem char [ " " "\t" "\r" ]);
|
||||
};
|
||||
in
|
||||
if mod > state.mod then
|
||||
state // seq state.block.body (force newState)
|
||||
else
|
||||
state // newState
|
||||
else
|
||||
state // force {
|
||||
pos = state.pos + 1;
|
||||
line = if char == "\n" then state.line + 1 else state.line;
|
||||
leadingWhitespace = char == "\n" || (state.leadingWhitespace && elem char [ " " "\t" "\r" ]);
|
||||
};
|
||||
in
|
||||
(foldl'
|
||||
parseToken
|
||||
{ acc = [];
|
||||
mod = 0;
|
||||
pos = 0;
|
||||
skip = 0;
|
||||
line = 1;
|
||||
block = {
|
||||
language = null;
|
||||
body = "";
|
||||
flags = {};
|
||||
};
|
||||
leadingWhitespace = true;
|
||||
readLanguage = false;
|
||||
readFlags = false;
|
||||
readBody = false;
|
||||
}
|
||||
(stringToCharacters text)).acc;
|
||||
|
||||
# Run tokenizeElisp' on all Elisp code blocks (with `:tangle yes`
|
||||
# set) from an Org mode babel text. If the block doesn't have a
|
||||
# `tangle` attribute, it's determined by `defaultArgs`.
|
||||
tokenizeOrgModeBabelElisp' = defaultArgs: text:
|
||||
let
|
||||
codeBlocks =
|
||||
filter
|
||||
(block:
|
||||
let
|
||||
tangle = toLower (block.flags.":tangle" or defaultArgs.":tangle" or "no");
|
||||
language = toLower block.language;
|
||||
in
|
||||
elem language [ "elisp" "emacs-lisp" ]
|
||||
&& elem tangle [ "yes" ''"yes"'' ])
|
||||
(parseOrgModeBabel text);
|
||||
in
|
||||
foldl'
|
||||
(result: codeBlock:
|
||||
result ++ (tokenizeElisp' {
|
||||
elisp = codeBlock.body;
|
||||
inherit (codeBlock) startLineNumber;
|
||||
})
|
||||
)
|
||||
[]
|
||||
codeBlocks;
|
||||
|
||||
tokenizeOrgModeBabelElisp =
|
||||
tokenizeOrgModeBabelElisp' {
|
||||
":tangle" = "no";
|
||||
};
|
||||
|
||||
parseOrgModeBabelElisp' = defaultArgs: text:
|
||||
parseElisp' (tokenizeOrgModeBabelElisp' defaultArgs text);
|
||||
|
||||
parseOrgModeBabelElisp = text:
|
||||
parseElisp' (tokenizeOrgModeBabelElisp text);
|
||||
|
||||
fromOrgModeBabelElisp' = defaultArgs: text:
|
||||
fromElisp' (parseOrgModeBabelElisp' defaultArgs text);
|
||||
|
||||
fromOrgModeBabelElisp = text:
|
||||
fromElisp' (parseOrgModeBabelElisp text);
|
||||
|
||||
in
|
||||
{
|
||||
inherit tokenizeElisp parseElisp fromElisp;
|
||||
inherit tokenizeElisp' parseElisp' fromElisp';
|
||||
inherit tokenizeOrgModeBabelElisp parseOrgModeBabelElisp fromOrgModeBabelElisp;
|
||||
inherit tokenizeOrgModeBabelElisp' parseOrgModeBabelElisp' fromOrgModeBabelElisp';
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
# Fg42 - Emacs Editor for advance users
|
||||
#
|
||||
# Copyright (c) 2010-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
{
|
||||
stdenv,
|
||||
elispPkgs,
|
||||
srcDir,
|
||||
emacsPackages,
|
||||
ourPackages,
|
||||
writeScriptBin,
|
||||
symlinkJoin,
|
||||
# This is a set of system tools required for FG42
|
||||
# to work.
|
||||
emacs,
|
||||
ripgrep,
|
||||
git,
|
||||
texinfo,
|
||||
vazir-fonts,
|
||||
fira-code,
|
||||
nerdfonts,
|
||||
}:
|
||||
with builtins;
|
||||
let
|
||||
getEpkg = (x: if hasAttr x emacsPackages
|
||||
then getAttr x emacsPackages
|
||||
else getAttr x ourPackages);
|
||||
|
||||
epkgs = (map getEpkg elispPkgs);
|
||||
|
||||
in stdenv.mkDerivation (final: rec{
|
||||
pname = "fg42";
|
||||
version = "4.0.0";
|
||||
|
||||
src = srcDir;
|
||||
outputs = [ "out" ];
|
||||
|
||||
FG42_USE_NIX = true;
|
||||
|
||||
buildPhase = ''
|
||||
mkdir -p $out/fg42
|
||||
mkdir -p $out/bin/
|
||||
|
||||
cp -rv ${src}/core $out/fg42/
|
||||
cp -rv ${src}/share $out/
|
||||
|
||||
runHook preBuild
|
||||
cd $out/fg42
|
||||
emacs -L . --batch -f batch-byte-compile *.el
|
||||
cd -
|
||||
cp -v ${src}/fg42-config.el $out/fg42/
|
||||
|
||||
runHook postBuild
|
||||
|
||||
'';
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
# LISPDIR=$out/share/emacs/site-lisp
|
||||
# install -d $LISPDIR
|
||||
# install *.el *.elc $LISPDIR
|
||||
emacs --batch -l package --eval "(package-generate-autoloads \"${pname}\" \"$out/fg42\")"
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
|
||||
# scripts = symlinkJoin {
|
||||
# name = "fg42_scripts";
|
||||
# paths = [
|
||||
# editor
|
||||
# wm
|
||||
# ];
|
||||
# };
|
||||
|
||||
#nativeBuildInputs = deps;
|
||||
buildInputs = epkgs ++ [
|
||||
ripgrep
|
||||
git
|
||||
texinfo
|
||||
vazir-fonts
|
||||
fira-code
|
||||
nerdfonts
|
||||
#scripts
|
||||
];
|
||||
# depsTargetTarget = [
|
||||
# pkgs.emacs
|
||||
# ];
|
||||
})
|
|
@ -0,0 +1,32 @@
|
|||
# Fg42 - Emacs Editor for advance users
|
||||
#
|
||||
# Copyright (c) 2010-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
{ lib, stdenv, emacsPackages, fetchFromGitea }:
|
||||
{
|
||||
noether-mode = emacsPackages.trivialBuild {
|
||||
pname = "noether-mode";
|
||||
version = "0.1.0";
|
||||
buildInputs = [ emacsPackages.posframe ];
|
||||
|
||||
src = fetchFromGitea {
|
||||
domain = "devheroes.codes";
|
||||
owner = "lxsameer";
|
||||
repo = "noether";
|
||||
rev = "849712fa91f097c69b00b2ffc9165b4baa852ee6";
|
||||
sha256 = "2ha/hiUZj+Ga1b9njhuoqV7QF2kCiocWtLQGPI3Yv58=";
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
# Fg42 - Emacs Editor for advance users
|
||||
#
|
||||
# Copyright (c) 2010-2023 Sameer Rahmani <lxsameer@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 2.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
{ pkgs, lib }:
|
||||
let
|
||||
isStrEmpty = s: (builtins.replaceStrings [ " " ] [ "" ] s) == "";
|
||||
|
||||
splitString = _sep: _s: builtins.filter
|
||||
(x: builtins.typeOf x == "string")
|
||||
(builtins.split _sep _s);
|
||||
|
||||
in {
|
||||
elispStr
|
||||
, alwaysEnsure ? false
|
||||
}:
|
||||
let
|
||||
inherit (import ./elisp_reader.nix { inherit lib; }) fromElisp;
|
||||
|
||||
readFunction = fromElisp;
|
||||
find = item: list:
|
||||
if list == [] then [] else
|
||||
if builtins.head list == item then
|
||||
list
|
||||
else
|
||||
find item (builtins.tail list);
|
||||
|
||||
getKeywordValue = keyword: list:
|
||||
let
|
||||
keywordList = find keyword list;
|
||||
in
|
||||
if keywordList != [] then
|
||||
let
|
||||
keywordValue = builtins.tail keywordList;
|
||||
in
|
||||
if keywordValue != [] then
|
||||
builtins.head keywordValue
|
||||
else
|
||||
true
|
||||
else
|
||||
null;
|
||||
|
||||
isDisabled = item:
|
||||
let
|
||||
disabledValue = getKeywordValue ":disabled" item;
|
||||
in
|
||||
if disabledValue == [] then
|
||||
false
|
||||
else if builtins.isBool disabledValue then
|
||||
disabledValue
|
||||
else if builtins.isString disabledValue then
|
||||
true
|
||||
else
|
||||
false;
|
||||
|
||||
getName = item:
|
||||
let
|
||||
ensureValue = getKeywordValue ":ensure" item;
|
||||
usePackageName = builtins.head (builtins.tail item);
|
||||
in
|
||||
if builtins.isString ensureValue then
|
||||
if lib.hasPrefix ":" ensureValue then
|
||||
usePackageName
|
||||
else
|
||||
ensureValue
|
||||
else if ensureValue == true || (ensureValue == null && alwaysEnsure) then
|
||||
usePackageName
|
||||
else
|
||||
[];
|
||||
|
||||
recurse = item:
|
||||
if builtins.isList item && item != [] then
|
||||
let
|
||||
packageManager = builtins.head item;
|
||||
in
|
||||
if builtins.elem packageManager [ "depends-on" ] then
|
||||
if !(isDisabled item) then
|
||||
[ packageManager (getName item) ] ++ map recurse item
|
||||
else
|
||||
[]
|
||||
else
|
||||
map recurse item
|
||||
else
|
||||
[];
|
||||
in lib.flatten (map recurse (readFunction elispStr))
|
Loading…
Reference in New Issue