forked from FG42/FG42
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
This commit is contained in:
parent
980566ea70
commit
209358c70d
23
flake.nix
23
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";
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
;;; FG42 --- The mighty editor for the emacsians -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (c) 2010-2024 Sameer Rahmani & Contributors
|
||||
;;
|
||||
;; Author: Sameer Rahmani <lxsameer@gnu.org>
|
||||
;; 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 <http://www.gnu.org/licenses/>.
|
||||
;;
|
||||
;;; 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
|
|
@ -0,0 +1,57 @@
|
|||
;;; FG42 --- The mighty editor for the emacsians -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (c) 2010-2024 Sameer Rahmani & Contributors
|
||||
;;
|
||||
;; Author: Sameer Rahmani <lxsameer@gnu.org>
|
||||
;; 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 <http://www.gnu.org/licenses/>.
|
||||
;;
|
||||
;;; 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
11
nix/deps.nix
11
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 <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
|
||||
elispPkgs =
|
||||
if length dependsOnForm == 0
|
||||
then throw "Can't find the form 'depends-on' on 'deps.el'"
|
||||
else tail (head dependsOnForm);
|
||||
in
|
||||
elispPkgs
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
# Fg42 - Emacs Editor for advance users
|
||||
#
|
||||
# Copyright (c) 2010-2024 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, 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}
|
||||
'';
|
||||
}
|
|
@ -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' {
|
||||
|
|
|
@ -13,10 +13,15 @@
|
|||
#
|
||||
# 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, 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; }
|
||||
|
|
72
nix/fg42.nix
72
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 <http://www.gnu.org/licenses/>.
|
||||
{ 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;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
lxsameer = {
|
||||
email = "lxsameer@lxsameer.com";
|
||||
github = "lxsameer";
|
||||
git = "lxsameer";
|
||||
git = "lxsameer";
|
||||
matrix = "@lxsameer:matrix.org";
|
||||
name = "Sameer Rahmani";
|
||||
keys = [{
|
||||
|
|
133
nix/parse.nix
133
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))
|
||||
|
|
Loading…
Reference in New Issue