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:
Pouya Abbassi 2024-04-11 20:00:36 +03:30
parent 980566ea70
commit 209358c70d
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG Key ID: 591DF1A6C74C0CBC
17 changed files with 743 additions and 581 deletions

View File

@ -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";
};
};
};
}

View File

@ -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

View File

@ -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

39
lisp/fg42/eglot.el Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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}
'';
}

View File

@ -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' {

View File

@ -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; }

View File

@ -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;

View File

@ -18,7 +18,7 @@
lxsameer = {
email = "lxsameer@lxsameer.com";
github = "lxsameer";
git = "lxsameer";
git = "lxsameer";
matrix = "@lxsameer:matrix.org";
name = "Sameer Rahmani";
keys = [{

View File

@ -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))