From 209358c70d56033ef1709beffee3214e95995e77 Mon Sep 17 00:00:00 2001 From: Pouya Abbassi Date: Thu, 11 Apr 2024 20:00:36 +0330 Subject: [PATCH] clojure-mode with clojure-lsp as eglot backend Add eglot-format to before-save-hook Reformat all the nix files using nixpkgs-fmt Redesign the WM mode modeline Disable exwm mode. Posframe issue Disable noether mode on WM until we fix the issue Add the time unit to the modeline Make eglot work with clojure-lsp --- flake.nix | 23 +- lisp/fg42/deps.el | 8 +- lisp/fg42/editor.el | 10 +- lisp/fg42/eglot.el | 39 ++ lisp/fg42/langs/clojure.el | 57 +++ lisp/fg42/langs/nix.el | 15 +- lisp/fg42/minibuffer.el | 9 + lisp/fg42/modeline.el | 10 +- lisp/fg42/modeline/units.el | 5 +- lisp/fg42/modeline/views.el | 49 ++- nix/deps.nix | 11 +- nix/desktop.nix | 33 -- nix/elisp_reader.nix | 830 ++++++++++++++++++------------------ nix/factory.nix | 18 +- nix/fg42.nix | 72 +++- nix/maintainers.nix | 2 +- nix/parse.nix | 133 +++--- 17 files changed, 743 insertions(+), 581 deletions(-) create mode 100644 lisp/fg42/eglot.el create mode 100644 lisp/fg42/langs/clojure.el delete mode 100644 nix/desktop.nix diff --git a/flake.nix b/flake.nix index 20fee02..35ae0d1 100644 --- a/flake.nix +++ b/flake.nix @@ -36,13 +36,14 @@ runtimeInputs = [ pkgs.xorg.xorgserver ]; text = '' - ${pkgs.xorg.xorgserver.out}/bin/Xephyr -br -ac -noreset -screen 800x600 :1 + ${pkgs.xorg.xorgserver.out}/bin/Xephyr -br -ac -noreset -screen 800x600 :1 ''; }; noether = inputs.noether.outputs.packages.${system}.default; factory = params: pkgs.callPackage ./nix/factory.nix ({ inherit noether; } // params); - default = (factory {}); - in { + default = (factory { }); + in + { packages = { default = default.fg42; } // (pkgs.lib.optionalAttrs (system == "x86_64-linux") { @@ -58,7 +59,21 @@ nativeBuildInputs = [ default.fg42 pkgs.fish test-x default.run-test-wm ]; buildInputs = [ default.fg42 ]; }; - }; + apps.wm = { + type = "app"; + program = "${default.run-test-wm}/bin/run-test-wm"; + }; + + apps.x = { + type = "app"; + program = "${test-x}/bin/test-x"; + }; + apps.default = { + type = "app"; + program = "${default.fg42}/bin/fg42"; + }; + + }; }; } diff --git a/lisp/fg42/deps.el b/lisp/fg42/deps.el index 0bc5647..e3552cb 100644 --- a/lisp/fg42/deps.el +++ b/lisp/fg42/deps.el @@ -52,7 +52,6 @@ gdscript-mode meson-mode lsp-scheme - clojure-mode cider aggressive-indent graphviz-dot-mode @@ -90,6 +89,10 @@ org-ql ctrlf badwolf-theme + clojure-mode + clojure-ts-mode + cider + flycheck-clj-kondo ace-window avy paredit @@ -122,7 +125,8 @@ forge svg-tag-mode base16-theme - consult) + consult + nerd-icons-completion) (provide 'fg42/deps) ;;; deps.el ends here diff --git a/lisp/fg42/editor.el b/lisp/fg42/editor.el index 8c31207..6d7eda1 100644 --- a/lisp/fg42/editor.el +++ b/lisp/fg42/editor.el @@ -26,11 +26,13 @@ ;; Language support (require 'fg42/autocomplete) (require 'fg42/langs/langs) + (require 'fg42/eglot) (require 'fg42/langs/cpp) (require 'fg42/langs/verilog) (require 'fg42/langs/python) (require 'fg42/langs/elisp) (require 'fg42/langs/nix) + (require 'fg42/langs/clojure) (require 'fg42/git) (require 'fg42/wm) @@ -197,12 +199,6 @@ contextual information." "Yasnippet's snippets." :after yasnippet) - ;; Language Servers and friends - (use! eglot - "Eglot is a minimalistic yet powerful LSP replacement -shipped with Emacs." - :commands eglot - :autoload eglot-ensure) (use! flycheck "Flycheck is a modern on-the-fly syntax checking extension for GNU Emacs." @@ -270,7 +266,7 @@ shipped with Emacs." ;; git changes in the fringe (global-diff-hl-mode) - ;; Modeline replacement + ;; Modeline replacement (noether-global-mode) ;; Rectangular select diff --git a/lisp/fg42/eglot.el b/lisp/fg42/eglot.el new file mode 100644 index 0000000..b91977f --- /dev/null +++ b/lisp/fg42/eglot.el @@ -0,0 +1,39 @@ +;;; FG42 --- The mighty editor for the emacsians -*- lexical-binding: t; -*- +;; +;; Copyright (c) 2010-2024 Sameer Rahmani & Contributors +;; +;; Author: Sameer Rahmani +;; URL: https://devheroes.codes/FG42/FG42 +;; Version: 4.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 . +;; +;;; Commentary: +;;; Code: +(eval-when-compile + (require 'fpkg)) + + +;; Language Servers and friends +(use! eglot + "Eglot is a minimalistic yet powerful LSP replacement +shipped with Emacs." + :hook + (prog-mode . eglot-ensure) + (prog-mode . (lambda () + (add-hook 'before-save-hook 'eglot-format nil t)))) + + +(provide 'fg42/eglot) +;;; eglot.el ends here diff --git a/lisp/fg42/langs/clojure.el b/lisp/fg42/langs/clojure.el new file mode 100644 index 0000000..6233a89 --- /dev/null +++ b/lisp/fg42/langs/clojure.el @@ -0,0 +1,57 @@ +;;; FG42 --- The mighty editor for the emacsians -*- lexical-binding: t; -*- +;; +;; Copyright (c) 2010-2024 Sameer Rahmani & Contributors +;; +;; Author: Sameer Rahmani +;; URL: https://devheroes.codes/FG42/FG42 +;; Version: 4.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 . +;; +;;; Commentary: +;;; Code: +(eval-when-compile + (require 'fpkg)) + + +(use! flycheck-clj-kondo + "flycheck linter for clojure using `clj-konda'" + :defer t) + +;; (defun fg42/clojure-ts-mode-setup () +;; "A hook handler to setup cpp related configurations." +;; ;; We set eglot's autoload command to `eglot-ensure' +;; (eglot-ensure)) + +(use! clojure-ts-mode + "Clojure mode" + ;; :init + ;; (add-to-list 'major-mode-remap-alist '(clojure-mode . clojure-ts-mode)) + :mode (("\\.clj\\'" . clojure-ts-mode) + ("\\.cljs\\'" . clojure-ts-mode) + ("\\.cljc\\'" . clojure-ts-mode)) + :config + (with-eval-after-load 'eglot + ;; Force nil to use nixpkgs-fmt for formatting + (let ((clojure-lsp '(clojure-ts-mode . clojure-mode))) + (require 'flycheck-clj-kondo) + (add-to-list 'eglot-server-programs clojure-lsp))) + + :hook + (clojure-ts-mode . company-mode) + (clojure-ts-mode . cider) + (clojure-ts-mode . eglot-ensure)) + +(provide 'fg42/langs/clojure) +;;; clojure.el ends here diff --git a/lisp/fg42/langs/nix.el b/lisp/fg42/langs/nix.el index 4fcbddd..b9ed6b8 100644 --- a/lisp/fg42/langs/nix.el +++ b/lisp/fg42/langs/nix.el @@ -24,13 +24,26 @@ (eval-when-compile (require 'fpkg)) + + (use! nix-mode "Nix language support for Emacs." :mode "\\.nix\\'" + :hook (nix-mode . eglot-ensure) (nix-mode . company-mode) - (nix-mode . flyspell-prog-mode)) + (nix-mode . flyspell-prog-mode) + + :init + :config + (with-eval-after-load 'eglot + ;; Force nil to use nixpkgs-fmt for formatting + (let ((nil-lsp '(nix-mode . ("nil" + :initializationOptions + (:formatting (:command ["nixpkgs-fmt"])))))) + (add-to-list 'eglot-server-programs nil-lsp)))) + (provide 'fg42/langs/nix) ;;; nix.el ends here diff --git a/lisp/fg42/minibuffer.el b/lisp/fg42/minibuffer.el index 2bdef3e..16f87c7 100644 --- a/lisp/fg42/minibuffer.el +++ b/lisp/fg42/minibuffer.el @@ -196,5 +196,14 @@ match all of the components in any order." :bind (("M-g e" . consult-compile-error))) +(use! nerd-icons-completion + "Nerd icons in marginalia" + :after marginalia + :config + (nerd-icons-completion-mode) + (add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup)) + + + (provide 'fg42/minibuffer) ;;; minibuffer.el ends here diff --git a/lisp/fg42/modeline.el b/lisp/fg42/modeline.el index 14221be..08ca2be 100644 --- a/lisp/fg42/modeline.el +++ b/lisp/fg42/modeline.el @@ -58,6 +58,7 @@ to Emacs modeline." :commands noether-global-mode :config (require 'noether-views) + (require 'fg42/modeline/views) (let ((active-border (get-base16-color-or :base0A "#bd93f9")) (inactive-border (get-base16-color-or :base03 "#44475a"))) @@ -73,14 +74,11 @@ to Emacs modeline." (if fg42/modeline-inactive-face fg42/modeline-inactive-face 'fg42/-disabled-modeline-dective-border) (default-value 'face-remapping-alist) face-remaps)) + ;; Setup modelines (when-not-wm - (require 'fg42/modeline/views) - - (when-not-wm - (setq-default noether-views (list fg42/modeline)))) - + (setq-default noether-views (list fg42/modeline))) (when-wm - (setq-default noether-views (list fg42/minimal-exwm)))) + (setq-default noether-views nil))) (provide 'fg42/modeline) ;;; modeline.el ends here diff --git a/lisp/fg42/modeline/units.el b/lisp/fg42/modeline/units.el index 3b4ea13..7900167 100644 --- a/lisp/fg42/modeline/units.el +++ b/lisp/fg42/modeline/units.el @@ -64,9 +64,12 @@ "Set the EXWM input mode for the current buffer." (setq fg42/-exwm-input-mode (format "%s" exwm--input-mode))) + (defun fg42/-format-exwm-input-mode (_ v _ _) "Just return the input mode name V." - v) + (if (=string v "line") + (propertize "L" 'font-lock-face `(:foreground ,(get-base16-color-or :base07 "eeeeec"))) + (propertize "C" 'font-lock-face `(:foreground ,(get-base16-color-or :base0A "eeeeec"))))) (noether-defunit fg42/exwm-input-mode-unit diff --git a/lisp/fg42/modeline/views.el b/lisp/fg42/modeline/views.el index 54c7466..7662162 100644 --- a/lisp/fg42/modeline/views.el +++ b/lisp/fg42/modeline/views.el @@ -31,6 +31,10 @@ "Keep the modeline at bottom right by using the data from INFO." (cons -1 -1)) +(defun fg42/--bottom-right-padded (info) + "Keep the modeline at bottom right by using the data from INFO." + (cons -70 -1)) + (defun fg42/adjust-modeline (view) "Adjust the VIEW after parent frame resize." @@ -58,7 +62,6 @@ (buffer-name-unit :label (format "%s " (nerd-icons-codicon "nf-cod-layers")) :len 20) - ;; (mode-name-unit :label " " :len 4) (projectile-project-unit :label (format "%s " (nerd-icons-octicon "nf-oct-project")) :len 20) @@ -66,31 +69,33 @@ :label (format "%s " (nerd-icons-devicon "nf-dev-git_branch")) :len 20) (fg42/mode-icon) - (line-unit :label (format "%s " (nerd-icons-codicon "nf-cod-location"))))) + (line-unit :label (format "%s " (nerd-icons-codicon "nf-cod-location"))) + (time-unit :label (format " %s " (nerd-icons-mdicon "nf-md-clock_time_three"))))) -(when-wm - (noether-defview fg42/minimal-exwm - "A super simple bar containing the line number and column number that +(noether-defview fg42/minimal-exwm + "A super simple bar containing the line number and column number that Appears on the center of the current window." - :managed? t - :buffer "*exwm-status*" - :binding (kbd "C-c 3") - :separator "|" - :frame - (list - ;; Such a big numbers for X and Y will cause the frame to appear on the - ;; bottom right corner and covering the minibuffer - :position '(-30 . -1) - :border-width 0 - :timeout 5 - :border-color "#bd93f9") + :managed? t + :buffer "*exwm-status*" + :binding (kbd "C-c 1") + :separator " | " - :units - (list - (fg42/exwm-input-mode-unit :label "") - (buffer-name-unit :label "") - (time-unit :label "")))) + :timeout 10 + :frame + (list + :poshandler #'fg42/--bottom-right-padded + :border-width 0 + :border-color "#bd93f9") + + :units + (list + (fg42/exwm-input-mode-unit :label (format "%s " (nerd-icons-faicon "nf-fa-linux"))) + (buffer-name-unit + :label (format "%s " (nerd-icons-codicon "nf-cod-layers")) + :len 30) + + (time-unit :label (format "%s " (nerd-icons-mdicon "nf-md-clock_time_three"))))) (provide 'fg42/modeline/views) diff --git a/nix/deps.nix b/nix/deps.nix index dcbdf3a..6d65e0e 100644 --- a/nix/deps.nix +++ b/nix/deps.nix @@ -13,14 +13,15 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - { 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 + elispPkgs = + if length dependsOnForm == 0 + then throw "Can't find the form 'depends-on' on 'deps.el'" + else tail (head dependsOnForm); +in +elispPkgs diff --git a/nix/desktop.nix b/nix/desktop.nix deleted file mode 100644 index 1fca586..0000000 --- a/nix/desktop.nix +++ /dev/null @@ -1,33 +0,0 @@ -# Fg42 - Emacs Editor for advance users -# -# Copyright (c) 2010-2024 Sameer Rahmani -# -# 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 . -{ pkgs, fg42, version }: -{ - desktop = pkgs.writeText "FG42.desktop" '' -[Desktop Entry] -Encoding=UTF-8 -Name=FG42 -GenericName=FG42 -Comment=Emacs Editor for advance users -MimeType=text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-java;application/x-shellscript;text/x-c;text/x-c++;text/x-ruby;text/x-python;text/x-clojure;text/css;text/html;text/x-javascript; -Type=Application -Terminal=false -Categories=Development;TextEditor; -StartupWMClass=FG42 -Exec=${fg42} %F -Icon=fg42 -Version=${version} - ''; -} diff --git a/nix/elisp_reader.nix b/nix/elisp_reader.nix index f41cf8e..a1958c3 100644 --- a/nix/elisp_reader.nix +++ b/nix/elisp_reader.nix @@ -25,18 +25,18 @@ let # 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; + 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; + replaceStrings stringsToRemove listOfNullStrings string; # Split a string of elisp into individual tokens and add useful # metadata. @@ -73,7 +73,8 @@ let matchSymbol = let symbolChar = ''([^${notInSymbol}]|\\.)''; - in mkMatcher ''(${symbolChar}+)([${notInSymbol}]|$).*'' symbolMaxLength; + in + mkMatcher ''(${symbolChar}+)([${notInSymbol}]|$).*'' symbolMaxLength; maxTokenLength = foldl' max 0 [ commentMaxLength @@ -125,173 +126,174 @@ let dot = matchDot rest; symbol = matchSymbol rest; in - if state.skip > 0 then - state // { + 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; - 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; - } + 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 = "expand"; value = ","; inherit (state) line; }]; + 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 - 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; + 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; }; @@ -302,41 +304,43 @@ 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 + 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; + else + token + ) + tokens; # Convert pairs of opening and closing tokens to their # respective collection types, i.e. lists and vectors. Also, @@ -363,52 +367,52 @@ 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 + 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 - currColl = head state.acc; - rest = tail state.acc; - emptyList = { - type = "list"; - depth = state.depth + 1; - value = []; + 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 currColl { acc = [ (currColl ++ [ emptyList ]) ] ++ rest; } + state // seq state.acc { + acc = [ (outerColl ++ [ currColl ]) ] ++ rest; + inColl = tail state.inColl; + depth = state.depth - 1; + line = tail state.line; + } else - let - currColl = head state.acc; - rest = tail state.acc; - in - state // seq currColl { acc = [ (currColl ++ [ token ]) ] ++ rest; }; + 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; + 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 @@ -434,33 +438,35 @@ let 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; + 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; - } + 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]; + acc = state.acc ++ [ token ]; }; in - (foldl' parseToken { acc = []; dotted = false; inList = false; depthReduction = 0; } tokens).acc; + (foldl' parseToken { acc = [ ]; dotted = false; inList = false; depthReduction = 0; } tokens).acc; parseQuotes = tokens: let @@ -469,35 +475,35 @@ let token = if isList token'.value then token' // { - value = (foldl' parseToken { acc = []; quotes = []; } token'.value).acc; + 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 ]; - }; + 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; + (foldl' parseToken { acc = [ ]; quotes = [ ]; } tokens).acc; in - parseQuotes (parseDots (parseCollections (parseValues tokens))); + parseQuotes (parseDots (parseCollections (parseValues tokens))); parseElisp = elisp: parseElisp' (tokenizeElisp elisp); @@ -508,23 +514,23 @@ let if isList object.value then map readObject object.value else if object.type == "quote" then - ["quote" (readObject object.value)] + [ "quote" (readObject object.value) ] else if object.type == "backquote" then - ["`" (readObject object.value)] + [ "`" (readObject object.value) ] else if object.type == "expand" then - ["," (readObject object.value)] + [ "," (readObject object.value) ] else if object.type == "slice" then - [",@" (readObject object.value)] + [ ",@" (readObject object.value) ] else if object.type == "function" then - ["#'" (readObject object.value)] + [ "#'" (readObject object.value) ] else if object.type == "byteCode" then - ["#"] ++ (readObject object.value) + [ "#" ] ++ (readObject object.value) else if object.type == "record" then - ["#s"] ++ (readObject object.value) + [ "#s" ] ++ (readObject object.value) else object.value; in - map readObject ast; + map readObject ast; fromElisp = elisp: fromElisp' (parseElisp elisp); @@ -570,135 +576,137 @@ let 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 + 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 = (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; + skip = (foldl' (total: string: total + (stringLength string)) 0 language) - 1; leadingWhitespace = false; + readLanguage = false; readFlags = true; + readBody = 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 + 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 // { - acc = state.acc ++ [ state.block ]; - block = { - language = null; - body = ""; - flags = {}; + 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 endCodeBlock) - 1; - leadingWhitespace = false; - readBody = false; + skip = (stringLength flags) - 1; + line = if char == "\n" then state.line + 1 else state.line; + leadingWhitespace = char == "\n"; + readFlags = 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" ]); + 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; }; - in - if mod > state.mod then - state // seq state.block.body (force newState) - else - state // newState - else - state // force { + 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 - (foldl' - parseToken - { acc = []; - mod = 0; - pos = 0; - skip = 0; - line = 1; - block = { - language = null; - body = ""; - flags = {}; + 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" ]); }; - leadingWhitespace = true; - readLanguage = false; - readFlags = false; - readBody = false; - } - (stringToCharacters text)).acc; + 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 @@ -712,19 +720,19 @@ 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"'' ]) + elem language [ "elisp" "emacs-lisp" ] + && elem tangle [ "yes" ''"yes"'' ]) (parseOrgModeBabel text); - in - foldl' - (result: codeBlock: - result ++ (tokenizeElisp' { - elisp = codeBlock.body; - inherit (codeBlock) startLineNumber; - }) - ) - [] - codeBlocks; + in + foldl' + (result: codeBlock: + result ++ (tokenizeElisp' { + elisp = codeBlock.body; + inherit (codeBlock) startLineNumber; + }) + ) + [ ] + codeBlocks; tokenizeOrgModeBabelElisp = tokenizeOrgModeBabelElisp' { diff --git a/nix/factory.nix b/nix/factory.nix index 34ddef5..d72e13f 100644 --- a/nix/factory.nix +++ b/nix/factory.nix @@ -13,10 +13,15 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -{ lib, stdenv, emacs29, callPackage, writeShellApplication, noether -, emacsParams ? {}, - fg42Params ? {} -} : +{ lib +, stdenv +, emacs29 +, callPackage +, writeShellApplication +, noether +, emacsParams ? { } +, fg42Params ? { } +}: let lemacs = emacs29.override ({ withTreeSitter = true; @@ -44,7 +49,8 @@ let runtimeInputs = [ fg42 ]; text = '' - DISPLAY=:1 ${fg42}/bin/fg42-wm + DISPLAY=:1 ${fg42}/bin/fg42-wm ''; }; -in { inherit fg42 run-test-wm; } +in +{ inherit fg42 run-test-wm; } diff --git a/nix/fg42.nix b/nix/fg42.nix index 874b6c2..c583d2e 100644 --- a/nix/fg42.nix +++ b/nix/fg42.nix @@ -13,22 +13,51 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -{ lib, stdenv, elispPkgs, srcDir, emacsPackagesFor, ourPackages, direnv -, makeFontsConf, nix - -, nil, # nix lsp server +{ lib +, stdenv +, elispPkgs +, srcDir +, emacsPackagesFor +, ourPackages +, direnv +, makeFontsConf +, nix +, nixpkgs-fmt +, nil +, # nix lsp server # python deps - python311, python3Packages, - # This is a set of system tools required for FG42 + python311 +, python3Packages +, # This is a set of system tools required for FG42 # to work. - pyright, emacs, ripgrep, git, texinfo, vazir-fonts, fira-code, nerdfonts -, fira-mono, noto-fonts, gcc, ltex-ls, bash, tree-sitter, fd -, aspellWithDicts, - - supportWM ? true, xorg, slock, - - supportPython ? true, supportVerilog ? true, svls, verilator, }: + pyright +, emacs +, ripgrep +, git +, texinfo +, vazir-fonts +, fira-code +, nerdfonts +, fira-mono +, noto-fonts +, gcc +, ltex-ls +, bash +, tree-sitter +, fd +, aspellWithDicts +, supportWM ? true +, xorg +, slock +, supportPython ? true +, supportVerilog ? true +, supportClojure ? true +, svls +, verilator +, clojure-lsp +, +}: with builtins; let version = "4.0.0"; @@ -44,7 +73,15 @@ let dicts = aspellWithDicts (dicts: with dicts; [ en en-computers en-science ]); runtimeBins = [ - ripgrep git tree-sitter direnv nix nil dicts fd + ripgrep + git + tree-sitter + direnv + nix + nil + dicts + fd + nixpkgs-fmt ] ++ (lib.optional (!stdenv.buildPlatform.isRiscV) [ # Not supported on Risc-V @@ -64,12 +101,14 @@ let # SystemC is required by verilator that at the # moment is only available on Linux verilator - ]) ++ (lib.optional (supportWM && stdenv.isLinux) [ + ]) ++ (lib.optional (supportClojure) [ clojure-lsp ]) + ++ (lib.optional (supportWM && stdenv.isLinux) [ # Window manager supports works on Linux only xorg.xhost slock ]); + paths = map (x: "${x}/bin/") (lib.lists.flatten runtimeBins); pathsStr = lib.strings.concatStrings (lib.strings.intersperse ":" paths); @@ -84,7 +123,8 @@ let ]; }; -in stdenv.mkDerivation (final: rec { +in +stdenv.mkDerivation (final: rec { inherit version; pname = "fg42"; src = srcDir; diff --git a/nix/maintainers.nix b/nix/maintainers.nix index 35f0561..28f8ced 100644 --- a/nix/maintainers.nix +++ b/nix/maintainers.nix @@ -18,7 +18,7 @@ lxsameer = { email = "lxsameer@lxsameer.com"; github = "lxsameer"; - git = "lxsameer"; + git = "lxsameer"; matrix = "@lxsameer:matrix.org"; name = "Sameer Rahmani"; keys = [{ diff --git a/nix/parse.nix b/nix/parse.nix index 0edb263..30db3ff 100644 --- a/nix/parse.nix +++ b/nix/parse.nix @@ -22,76 +22,77 @@ let (x: builtins.typeOf x == "string") (builtins.split _sep _s); -in { - elispStr +in +{ elispStr , alwaysEnsure ? false }: - let - inherit (import ./elisp_reader.nix { inherit lib; }) fromElisp; +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); + 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: + getKeywordValue = keyword: list: + let + keywordList = find keyword list; + in + if keywordList != [ ] then let - keywordList = find keyword list; + keywordValue = builtins.tail keywordList; 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 + if keywordValue != [ ] then + builtins.head keywordValue else - []; - in lib.flatten (map recurse (readFunction elispStr)) + 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))