diff --git a/nix/fg42/#derivation.nix# b/nix/fg42/#derivation.nix# new file mode 100644 index 0000000..eefa5fd --- /dev/null +++ b/nix/fg42/#derivation.nix# @@ -0,0 +1,219 @@ +# Fg42 - Emacs Editor for advance users +# +# Copyright (c) 2010-2024 Sameer Rahmani +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +{ lib +, config +, stdenv +, git +, texinfo +, gcc +, bash +, utils +, emacsPackagesFor +, makeFontsConf +, xorg +, maintainers +, writeText +, writeShellApplication +, ... +}: + +with builtins; +let + cfg = config.fg42; + emacs = cfg.emacs; + version = cfg.version; + + emacsBundle = (emacsPackagesFor emacs).withPackages (_: cfg.elispPackages); + + paths = map (x: "${x}/bin/") cfg.paths; + pathsStr = lib.strings.concatStrings (lib.strings.intersperse ":" paths); + + mimeTypes = builtins.concatStringsSep ";" cfg.mimeTypes; + + fontsConf = makeFontsConf { fontDirectories = cfg.fonts; }; + + # Whatever that needs to be in the PATH is considered a runtime + # dependency. + # Fonts are obviously runtime dependency as well + runtimeDependencies = cfg.paths ++ cfg.fonts ++ cfg.elispPackages; + + buildConfig = generateBuildConfig cfg.buildConfig; + + addToList = epkg: ''(add-to-list 'directories-to-autogen "${epkg}")''; + + sexprs = [ + '' + (require 'loaddefs-gen) + (defvar directories-to-autogen '()) + (defvar output-file (getenv "LOADEF_OUTPUT")) + '' + ] ++ (map addToList cfg.elispPackages) ++ [ + '' + (message ">> %s ----- %s" directories-to-autogen output-file) + (loaddefs-generate directories-to-autogen + output-file) + (provide 'loaddef-generator) + '' + ]; + loaddefGenerator = writeText "loaddef-generator.el" (concatStringsSep "\n" sexprs); + loaddefScript = writeShellApplication { + name = "loaddef"; + + text = '' + #!${stdenv.shell} + set +x + LOADEF_OUTPUT="$1" ${emacsBundle}/bin/emacs -Q -q --batch -l ${loaddefGenerator} + ''; + }; + + startupPackage = cfg.startUp; + + + # elsipFiles = map (file: ) cfg.elispPackages; + # nativeCompiler = epkgs: '' + # emacs -L . --batch -f batch-native-compile ${elispFiles} + # ''; +in + +stdenv.mkDerivation rec { + inherit version; + pname = "FG42"; + + src = ../../.; + + buildPhase = '' + runHook preBuild + + LISPDIR=$out/share/fg42/ + mkdir -p $out/bin + mkdir -p $out/share/applications/ + + install -d $LISPDIR + + cp -rv ${src}/share $out/ + cp -rv ${src}/snippets $LISPDIR/snippets + cp "${fontsConf}" $LISPDIR/fonts.conf + + chmod 755 $LISPDIR -R + + export FONTCONFIG_FILE="$LISPDIR/fonts.conf" + + cat >> $out/share/applications/FG42.desktop << EOF + [Desktop Entry] + Encoding=UTF-8 + Name=FG42 + GenericName=FG42 + Comment=The nix base Emacs bundle for advance users + MimeType=${mimeTypes} + Type=Application + Terminal=false + Categories=Development;TextEditor; + StartupWMClass=FG42 + Exec=${placeholder "out"}/bin/fg42 + Icon=fg42 + Version=${version} + EOF + + cat >> $out/share/runtime_deps << EOF + ${lib.strings.concatLines runtimeDependencies} + ${emacsBundle} + EOF + + cd $LISPDIR + #compile stuff + cd - + + cat >> $out/bin/fg42 << EOF + #!${stdenv.shell} + + export PATH=${pathsStr}:$PATH + export FONTCONFIG_FILE="$LISPDIR/fonts.conf" + + LIBRARY_PATH="\$(${stdenv.cc}/bin/cc -print-file-name=libgccjit.so):\$LIBRARY_PATH" \ + FG42_WM=fales ${emacsBundle}/bin/emacs \ + --name FG42 \ + -q --no-splash --title FG42 \ + -l ${cfg.startUp} "\$@" + EOF + chmod +x $out/bin/fg42 + + + LISPDIR=$out/share/fg42/lisp/ + mkdir -p $LISPDIR + #emacs --batch -l package --eval "(package-generate-autoloads \"${pname}\" \"$LISPDIR\")" + # ${loaddefScript}/bin/loaddef ${placeholder "out"}/share/fg42/lisp/loaddefs.el + + runHook postBuild + + cat >> $out/bin/fg42-wm << EOF + #!${stdenv.shell} + + export FG42_HOME=${placeholder "out"}/share/fg42/ + export FG42_EMACSD=~/.fg42/v4/emacs.d + export FG42_USE_NIX=true; + export PATH=${pathsStr}:\$PATH + export FONTCONFIG_FILE="$LISPDIR/fonts.conf" + + # Disable access control for the current user. + ${xorg.xhost}/bin/xhost +SI:localuser:\$USER + + # Make Java applications aware this is a non-reparenting window manager. + export _JAVA_AWT_WM_NONREPARENTING=1 + + # Set default cursor. + xsetroot -cursor_name left_ptr + + # Set keyboard repeat rate. + xset r rate 400 30 + + # Uncomment the following block to use the exwm-xim module. + # export XMODIFIERS=@im=exwm-xim + # export GTK_IM_MODULE=xim + # export QT_IM_MODULE=xim + # export CLUTTER_IM_MODULE=xim + + LIBRARY_PATH="\$(${stdenv.cc}/bin/cc -print-file-name=libgccjit.so):\$LIBRARY_PATH" \ + FG42_WM=true ${emacsBundle}/bin/emacs \ + --name FG42 \ + -q --no-splash --title FG42 \ + -l \$FG42_HOME/lisp/fg42/fg42.el "\$@" + EOF + chmod +x $out/bin/fg42-wm + + + runHook postBuild + ''; + + buildInputs = [ emacs emacsBundle git texinfo gcc bash ]; + + addEmacsNativeLoadPath = true; + + meta = { + broken = false; + platforms = emacs.meta.platforms; + homepage = "https://fg42.org/"; + maintainers = [ maintainers.lxsameer ]; + description = "The mighty editor for the Emacsians"; + longDescription = '' + FG42 is a framework to create and editor and window manager based on GNU/Emacs. + It has a pre-defined setup as well which can be installed out of the box. But the + goal of this project is to provide the API necessary to create an integrated editor. + + So you need to know about Emacs in advance. + ''; + license = lib.licenses.gpl3Plus; + }; +} diff --git a/nix/fg42/.#derivation.nix b/nix/fg42/.#derivation.nix new file mode 120000 index 0000000..766c032 --- /dev/null +++ b/nix/fg42/.#derivation.nix @@ -0,0 +1 @@ +lxsameer@majin.69922:1712871165 \ No newline at end of file diff --git a/nix/fg42/default.nix b/nix/fg42/default.nix index 8f5bf10..e175639 100644 --- a/nix/fg42/default.nix +++ b/nix/fg42/default.nix @@ -30,14 +30,20 @@ let lib = lib; }; + + utils = pkgs.callPackage ./utils.nix { }; + unitModule = import ./unit.nix { inherit pkgs lib; }; final = lib.evalModules { modules = [ unitModule ] ++ fg42Modules ++ extraModules; + specialArgs = { + inherit utils; + inherit (utils) makeFG42Drv; + }; }; - in pkgs.callPackage ./derivation.nix { inherit (final) config options; + inherit utils; maintainers = import ../maintainers.nix; - utils = pkgs.callPackage ./utils.nix { }; } diff --git a/nix/fg42/derivation.nix b/nix/fg42/derivation.nix index 2a785ff..9c47979 100644 --- a/nix/fg42/derivation.nix +++ b/nix/fg42/derivation.nix @@ -25,8 +25,11 @@ , makeFontsConf , xorg , maintainers +, writeText +, writeShellApplication , ... }: + with builtins; let cfg = config.fg42; @@ -42,8 +45,49 @@ let fontsConf = makeFontsConf { fontDirectories = cfg.fonts; }; + # Whatever that needs to be in the PATH is considered a runtime + # dependency. + # Fonts are obviously runtime dependency as well + runtimeDependencies = cfg.paths ++ cfg.fonts ++ cfg.elispPackages; + buildConfig = generateBuildConfig cfg.buildConfig; + + addToList = epkg: ''(add-to-list 'directories-to-autogen "${epkg}")''; + + sexprs = [ + '' + (require 'loaddefs-gen) + (defvar directories-to-autogen '()) + (defvar output-file (getenv "LOADEF_OUTPUT")) + '' + ] ++ (map addToList cfg.elispPackages) ++ [ + '' + (message ">> %s ----- %s" directories-to-autogen output-file) + (loaddefs-generate directories-to-autogen + output-file) + (provide 'loaddef-generator) + '' + ]; + loaddefGenerator = writeText "loaddef-generator.el" (concatStringsSep "\n" sexprs); + loaddefScript = writeShellApplication { + name = "loaddef"; + + text = '' + #!${stdenv.shell} + set +x + LOADEF_OUTPUT="$1" ${emacsBundle}/bin/emacs -Q -q --batch -l ${loaddefGenerator} + ''; + }; + + startupPackage = cfg.startUp; + + + # elsipFiles = map (file: ) cfg.elispPackages; + # nativeCompiler = epkgs: '' + # emacs -L . --batch -f batch-native-compile ${elispFiles} + # ''; in + stdenv.mkDerivation rec { inherit version; pname = "FG42"; @@ -51,13 +95,15 @@ stdenv.mkDerivation rec { src = ../../.; buildPhase = '' + runHook preBuild + LISPDIR=$out/share/fg42/ mkdir -p $out/bin mkdir -p $out/share/applications/ install -d $LISPDIR - cp -rv ${src}/lisp/ $LISPDIR + # cp -rv ${src}/lisp/ $LISPDIR cp -rv ${src}/share $out/ cp -rv ${src}/snippets $LISPDIR/snippets @@ -83,18 +129,18 @@ stdenv.mkDerivation rec { Version=${version} EOF - runHook preBuild + cat >> $out/share/runtime_deps << EOF + ${lib.strings.concatLines runtimeDependencies} + ${emacsBundle} + EOF cd $LISPDIR - emacs -L . --batch -f batch-byte-compile *.el + #compile stuff cd - cat >> $out/bin/fg42 << EOF #!${stdenv.shell} - export FG42_HOME=${placeholder "out"}/share/fg42/ - export FG42_EMACSD=~/.fg42/v4/emacs.d - export FG42_USE_NIX=true; export PATH=${pathsStr}:$PATH export FONTCONFIG_FILE="$LISPDIR/fonts.conf" @@ -102,9 +148,16 @@ stdenv.mkDerivation rec { FG42_WM=fales ${emacsBundle}/bin/emacs \ --name FG42 \ -q --no-splash --title FG42 \ - -l \$FG42_HOME/lisp/fg42/init.el "\$@" + -l ${cfg.startUp} "\$@" EOF chmod +x $out/bin/fg42 + + + LISPDIR=$out/share/fg42/lisp/ + mkdir -p $LISPDIR + #emacs --batch -l package --eval "(package-generate-autoloads \"${pname}\" \"$LISPDIR\")" + # ${loaddefScript}/bin/loaddef ${placeholder "out"}/share/fg42/lisp/loaddefs.el + runHook postBuild cat >> $out/bin/fg42-wm << EOF @@ -137,23 +190,15 @@ stdenv.mkDerivation rec { LIBRARY_PATH="\$(${stdenv.cc}/bin/cc -print-file-name=libgccjit.so):\$LIBRARY_PATH" \ FG42_WM=true ${emacsBundle}/bin/emacs \ --name FG42 \ - -q --no-splash --title FG42 \ - -l \$FG42_HOME/lisp/fg42/init.el "\$@" + -q --no-splash --title FG42 \ + -l \$FG42_HOME/lisp/fg42/fg42.el "\$@" EOF chmod +x $out/bin/fg42-wm - LISPDIR=$out/share/fg42/lisp/ - emacs --batch -l package --eval "(package-generate-autoloads \"${pname}\" \"$LISPDIR\")" runHook postBuild ''; - # installPhase = '' - # runHook preInstall - - # runHook postInstall - # ''; - buildInputs = [ emacs emacsBundle git texinfo gcc bash ]; addEmacsNativeLoadPath = true; diff --git a/nix/fg42/unit.nix b/nix/fg42/unit.nix index c6168d0..1b08e15 100644 --- a/nix/fg42/unit.nix +++ b/nix/fg42/unit.nix @@ -59,7 +59,6 @@ with lib; A list of mineType strings that FG42 should handle via desktop file. ''; - }; fg42.fonts = mkOption { @@ -69,6 +68,12 @@ with lib; A list of font packages that should be included in FG42 ''; }; + + fg42.startUp = mkOption { + type = types.uniq types.path; + description = "The main startup file to load first. This is usually fg42.el"; + }; + }; diff --git a/nix/fg42/utils.nix b/nix/fg42/utils.nix index 55d7a27..b38b19a 100644 --- a/nix/fg42/utils.nix +++ b/nix/fg42/utils.nix @@ -13,7 +13,26 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -{ ... }: +{ emacsPackages, writeTextFile, ... }: { makeDesktopFile = { }; + + makeFG42Drv = { pname, version, ... }@args: + (emacsPackages.trivialBuild args).overrideAttrs (old: { + pname = "fg42-${pname}"; + + installPhase = '' + runHook preInstall + + LISPDIR=$out/share/emacs/ + install -d $LISPDIR + cp -rv lisp/ $LISPDIR/site-lisp + + emacs --batch -l package --eval "(package-generate-autoloads \"${args.pname}\" \"$LISPDIR/site-lisp\")" + + runHook postInstall + ''; + + }); + } diff --git a/nix/modules/build_config/default.nix b/nix/modules/build_config/default.nix new file mode 100644 index 0000000..5ad98be --- /dev/null +++ b/nix/modules/build_config/default.nix @@ -0,0 +1,122 @@ +# Fg42 - Emacs Editor for advance users +# +# Copyright (c) 2010-2024 Sameer Rahmani +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This is the home manager module that exposes FG42. It differs +# from FG42 modules that are structurally the same but used in +# different context + +# A list of default FG42 modules to build FG42 with. +{ lib, config, pkgs, ... }: +with lib; +let + generateBuildConfig = configs: + let + defineConfig = cs: name: + let + config = builtins.getAttr name cs; + form = if config.const then "defconst" else "defvar"; + in + ''(${form} ${name} ${config.value} "${config.docs}")''; + + configsList = builtins.map (defineConfig configs) (builtins.attrNames configs); + + in + pkgs.writeTextFile { + name = "build-config"; + text = '' + ${builtins.concatStringsSep "\n" configsList} + (provide 'fg42/build-config) + ''; + executable = false; + }; + + buildConfig = generateBuildConfig (config.fg42.buildConfig); + drv = pkgs.emacsPackages.trivialBuild rec { + pname = "fg42-build-config"; + version = config.fg42.version; + src = ./.; + + buildPhase = '' + runHook preBuild + + LISPDIR=$out/share/emacs/site-lisp/fg42 + install -d $LISPDIR + + cp -v ${buildConfig} $LISPDIR/build-config.el + + emacs -L . --batch -f batch-byte-compile *.el + + runHook postBuild + ''; + installPhase = '' + runHook preInstall + + LISPDIR=$out/share/emacs/site-lisp/fg42/ + emacs --batch -l package --eval "(package-generate-autoloads \"${pname}\" \"$LISPDIR\")" + + runHook postInstall + ''; + + }; +in +{ + options = { + fg42.buildConfig = mkOption { + type = types.attrsOf + (types.submodule { + options = { + value = mkOption { + type = types.str; + default = "nil"; + description = '' + The value should be quoted. For example the string "nil" + means the actual nil in elisp and the string "\"foo\"" + means elisp string "foo". + ''; + }; + docs = mkOption { + type = types.str; + description = "The docstring for the variable or constant (mandatory)"; + }; + const = mkOption { + type = types.bool; + default = false; + description = "defvar vs defconst"; + }; + }; + }); + default = { }; + example = '' + fg42.config = { + "fg42/some-var" = { + value = "foo"; + docs = "The docstring"; + const = false; + }; + }; + ''; + description = '' + An attrset of configuration variables and their values + that should end up in `fg42/config` elisp module. + ''; + }; + + }; + + config = { + fg42.elispPackages = [ drv ]; + }; +} diff --git a/nix/modules/default.nix b/nix/modules/default.nix index 9fd4e4e..74bf1f9 100644 --- a/nix/modules/default.nix +++ b/nix/modules/default.nix @@ -22,7 +22,8 @@ { pkgs, lib }: let modules = [ - ./editor.nix + ./build_config + ./editor ]; pkgsModule = { config, ... }: { diff --git a/nix/modules/editor.nix b/nix/modules/editor.nix deleted file mode 100644 index a03a636..0000000 --- a/nix/modules/editor.nix +++ /dev/null @@ -1,33 +0,0 @@ -# Fg42 - Emacs Editor for advance users -# -# Copyright (c) 2010-2024 Sameer Rahmani -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 2. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# This is the home manager module that exposes FG42. It differs -# from FG42 modules that are structurally the same but used in -# different context - -# A list of default FG42 modules to build FG42 with. -{ lib, ... }: -{ - options = { - fg42.editor = lib.mkOption { - type = lib.types.str; - default = "blah"; - description = '' - FG42's version1. - ''; - }; - }; -} diff --git a/nix/modules/editor/default.nix b/nix/modules/editor/default.nix new file mode 100644 index 0000000..2a49bfa --- /dev/null +++ b/nix/modules/editor/default.nix @@ -0,0 +1,79 @@ +# Fg42 - Emacs Editor for advance users +# +# Copyright (c) 2010-2024 Sameer Rahmani +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This is the home manager module that exposes FG42. It differs +# from FG42 modules that are structurally the same but used in +# different context + +# A list of default FG42 modules to build FG42 with. +{ lib, config, pkgs, makeFG42Drv, ... }: +let + deps = (with pkgs.emacsPackages; [ + origami + which-key + projectile + projectile-ripgrep + pkg-info + expand-region + helpful + envrc + pinentry + discover + exec-path-from-shell + avy + ace-window + yasnippet + yasnippet-snippets + flycheck + ]); + drv = makeFG42Drv { + pname = "fg42-editor"; + version = config.fg42.version; + buildInputs = deps; + src = ./.; + }; + + dicts = pkgs.aspellWithDicts (dicts: with dicts; [ en en-computers en-science ]); +in +{ + + config = { + fg42.elispPackages = [ drv ] ++ deps; + fg42.startUp = lib.mkDefault "${drv}/share/emacs/site-lisp/fg42.el"; + + fg42.fonts = (with pkgs;[ + vazir-fonts + fira-code + fira-mono + noto-fonts + ]); + + fg42.paths = (with pkgs;[ + ripgrep + dicts + ]); + + fg42.buildConfig = { + "fg42/font" = { + value = '''("Fira Mono" 11)''; + docs = '' + The default font for FG42. You can override it + in your configuration file. + ''; + }; + }; + }; +} diff --git a/nix/modules/editor/lisp/fg42.el b/nix/modules/editor/lisp/fg42.el new file mode 100644 index 0000000..a6088e9 --- /dev/null +++ b/nix/modules/editor/lisp/fg42.el @@ -0,0 +1,166 @@ +;;; FG42 --- The mighty editor for the emacsians -*- lexical-binding: t; -*- +;; +;; Copyright (c) 2010-2024 Sameer Rahmani +;; +;; Author: Sameer Rahmani +;; URL: https://devheroes.codes/FG42/FG42 +;; Version: 4.0.0 +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;;; Commentary: +;;; Code: +(require 'fg42/utils) +(eval-when-compile + (defvar package-archives) + (defvar use-package-ensure-function)) + +;; ============================================================================ +;; Basic inital configuration for FG42's operations +;; ============================================================================ +(defgroup fg42 nil + "Customize your FG42 instance via this group of configurations." + :group :editor) + +(defvar fg42/disabled-features nil + "List of features to disable.") + +(defvar fg42/after-init-hook nil + "The hook tha runs when FG42 finished running the user configuration.") + +(defvar fg42/debug? nil + "It is t If FG42 is running in debug mode and nil otherwise.") + +(defvar fg42/-gc-cons-threshold 16777216 + "Value of GC threshold of FG42.") + +(defvar fg42/initialized nil + "A variable that indicates whether FG42 is passed initialization.") + + +(defconst fg42/cache-dir (or (getenv "FG42_CACHE_DIR") + (path-join (or (getenv "XDG_CACHE_HOME") "~/.cache") + "fg42"))) + + +(defconst fg42/config-dir (or (getenv "FG42_CONFIG_DIR") + (path-join (or (getenv "XDG_CONFIG_HOME") "~/.config") + "fg42"))) + + +(defun defer-garbage-collection () + "Disable garbage collection." + (setq gc-cons-threshold fg42/-gc-cons-threshold)) + + +(defun restore-garbage-collection () + "Restore garbage collection to it's default value." + (run-at-time + 1 nil (lambda () (setq gc-cons-threshold most-positive-fixnum)))) + + +(defun fg42/-startup-optimization () + "Optimized FG42 startup." + ;; by setting gc threshold to the largest number + ;; Emacs can understand we are basically disabling it :). + (setq gc-cons-threshold most-positive-fixnum + gc-cons-percentage 0.6) + ;; after initilization phase restore cons threshold to normal + (add-hook 'emacs-startup-hook + (lambda () + (setq gc-cons-threshold fg42/-gc-cons-threshold ; 16mb + gc-cons-percentage 0.1))) + + ;; disable gc when we are in minibuffer using the same method we + ;; use for initilization time + (add-hook 'minibuffer-setup-hook #'defer-garbage-collection) + ;; just enable gc when exiting minibuffer + (add-hook 'minibuffer-exit-hook #'restore-garbage-collection) + ;; we dont need Emacs to check every file type and look for file + ;; handlers when we are initializing so we backup the original + ;; value and set it to nil + (setq --file-name-handler-alist file-name-handler-alist) + + (setq file-name-handler-alist nil) + ;; after initialization we can restore that file-name-handler-alist + ;; to original value. + (add-hook 'emacs-startup-hook + (lambda () + (setq file-name-handler-alist + --file-name-handler-alist))) + ;; initial mode for emacs can be fundamental mode we have nothing to lose + (setq initial-major-mode 'fundamental-mode)) + + +(defun fg42/initialize () + "Initialize FG42 after the Emacs window is rendered by loading the user init file." + ;; (fg42/-startup-optimization) + (require 'fg42/editor) + + (when (file-exists-p user-init-file) + (load user-init-file)) + + (fg42/setup-editor) + ;; ;; If user didn't select a theme, load the default stuff + ;; (when (not (featurep 'fg42/themes)) + ;; (require 'fg42/themes)) + + (add-hook 'emacs-startup-hook + (lambda () + (run-hooks 'fg42/after-init-hook) + (run-hooks 'fg42/ui-hook) + (setq fg42/initialized t)))) + + +(defun fg42/main () + "The entry point of FG42." + (when (string= (getenv "FG42_DEBUG") "1") + (setq debug-on-error t + fg42/debug? t)) + + ;; We don't use any Emacs package manager, so + ;; prevent package.el to install anything at startup + (setq package-enable-at-startup nil + package-archives nil + use-package-ensure-function 'ignore + + ;; Setup the path for the custom.el file, We don't want + ;; to touch user's Emacs configuration. + custom-file (path-join fg42/config-dir "custom.el") + + ;; Change the config dir to `~/.config/fg42/' by default + ;; again, we don't want to temper with user's Emacs + user-emacs-directory fg42/config-dir + user-init-file + (or (getenv "FG42_CONFIG_FILE") + (path-join fg42/config-dir "fg42.el"))) + + ;; Change the eln file cache to leave Emacs alone. + (startup-redirect-eln-cache + (or (getenv "FG42_ELN_DIR") + (path-join fg42/cache-dir "fg42.el"))) + + ;; Load the customization file. In FG42 it is different than + ;; the default `user-init-file' + (if (file-exists-p custom-file) + (load custom-file)) + + ;; From point forward we can use normal Elisp stuff without + ;; interfering with user's Emacs configuration + (fg42/initialize)) + +(fg42/main) + +(provide 'fg42) +;;; fg42.el ends here diff --git a/nix/modules/editor/lisp/fg42/editor.el b/nix/modules/editor/lisp/fg42/editor.el new file mode 100644 index 0000000..c67996c --- /dev/null +++ b/nix/modules/editor/lisp/fg42/editor.el @@ -0,0 +1,293 @@ +;;; FG42 --- The mighty editor for the emacsians -*- lexical-binding: t; -*- +;; +;; Copyright (c) 2010-2024 Sameer Rahmani & Contributors +;; +;; Author: Sameer Rahmani +;; URL: https://devheroes.codes/FG42/FG42 +;; Version: 4.0.0 +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;;; Commentary: +;;; Code: +(eval-when-compile + (require 'fpkg) + ;; We build this file via the main FG42's Nix derivation. It + ;; contains the final Nix configuration of FG42. + (require 'fg42/build-config) + ;; ;; 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/git) + ;; (require 'fg42/wm) + ;; (require 'fg42/org) + ;; (require 'fg42/minibuffer) + ;; (require 'fg42/graphics) + ;; (require 'fg42/modeline) + ) + +(require 'server) +(require 'fg42/utils) + + +(defun fg42/setup-font () + "Set the default font of `FG42' to FONT-NAME and FONT-SIZE." + (let ((name (car fg42/font)) + (size (cadr fg42/font))) + (add-to-list 'default-frame-alist + (cons 'font (format "%s-%d" name size))) + + (set-face-attribute 'default t :font name))) + + + +(defun fg42/setup-editor () + "Setup the overall functionality of FG42." + + (use! origami + "A text folding minor mode for Emacs." + :bind + (("C-c TAB" . origami-toggle-node)) + :config + (global-origami-mode t)) + + (use! which-key + "which-key is a minor mode for Emacs that displays the key bindings following +your currently entered incomplete command (a prefix) in a popup. For example, +after enabling the minor mode if you enter ~C-x~ and wait for the default of +1 second the minibuffer will expand with all of the available key bindings +that follow ~C-x~ (or as many as space allows given your settings). +This includes prefixes like ~C-x 8~ which are shown in a different face." + :config + (which-key-setup-side-window-bottom) + (which-key-mode)) + + (use! projectile + "Projectile is a project interaction library for Emacs. Its goal is to provide +a nice set of features operating on a project level without introducing +external dependencies." + :hook (emacs-startup . projectile-mode) + :config + ;; We don't want the auto discovery on startup + (setq projectile-auto-discover nil) + (setq projectile-enable-caching t) + + :bind (:map projectile-mode-map + ("s-p" . projectile-command-map) + ("C-c p" . projectile-command-map))) + + + (use! projectile-ripgrep + "Use ripgrep with projectile" + :after projectile) + + + (use! pkg-info + "`pkg-info' integration.") + + + (use! expand-region + "Expand region increases the selected region by semantic units. +Just keep pressing the key until it selects what you want." + :bind ("C-=" . er/expand-region)) + + + (use! helpful + "Helpful is an alternative to the built-in Emacs help that provides much more +contextual information." + :bind + (("C-h f" . helpful-callable) + ("C-h v" . helpful-variable) + ("C-h k" . helpful-key) + ("C-h x" . helpful-command) + ("C-c C-d" . helpful-at-point))) + + (use! envrc + "Activate direnv whenever encounter a `.envrc' file" + :config + (envrc-global-mode)) + + (use! pinentry + "Pinentry cube with setup the =pinentry= program to be used within FG42." + :commands pinentry-start + :init + (setq epa-pinentry-mode 'loopback)) + + (use! imenu-list + "his Emacs minor-mode creates an automatically updated buffer + called `Ilist' that is populated with the current buffer's imenu entries. + The `Ilist' buffer is typically shown as a sidebar (Emacs vertically splits the window)." + :bind (("C-'" . imenu-list-smart-toggle))) + + (use! discover + "Adds support for the discover.el `https://github.com/mickeynp/discover.el'.") + + (use! exec-path-from-shell + "This package fixes the =exec-path-from-shell= issue on MacOS." + :config + (when (memq window-system '(mac ns x)) + (exec-path-from-shell-initialize))) + + (use! hl-line + "Highlights the current line." + :hook (emacs-startup . global-hl-line-mode)) + + (use! avy + "This cube controls the different aspect of buffer navigation" + :bind ("M-1" . avy-goto-word-1)) + + (use! ace-window + "This cube controls the different aspect of buffer navigation" + :bind ("C-" . ace-window)) + + (when-not-wm + + (use! flyspell + "Spell checking on the fly" + :commands (flyspell-mode flyspell-prog-mode) + :init + (setq-default ispell-program-name "aspell") + (setq-default ispell-extra-args '("--sug-mode=ultra" + "--lang=en_US" + "--camel-case"))) + + (use! savehist + "Persist history over Emacs restarts. Vertico sorts by history position." + :init + (savehist-mode)) + + (use! display-line-numbers + "The builtin replacement of linum. It's is pretty fast." + :config + (global-display-line-numbers-mode 1))) + + + (use! yasnippet + "A Snippet is a template system for Emacs. " + :config + (let* ((snippet-home + (path-join + (file-name-directory + (locate-library "yasnippet-snippets")) "snippets")) + (local-snippet (path-join (file-name-directory + (locate-library "fg42")) "../snippets")) + (user-snippets (path-join fg42/config-dir "snippets"))) + + (setq yas-snippet-dirs `(,user-snippets ,local-snippet ,snippet-home)) + (yas-global-mode 1))) + + (use! yasnippet-snippets + "Yasnippet's snippets." + :after yasnippet) + + + (use! flycheck + "Flycheck is a modern on-the-fly syntax checking extension for GNU Emacs." + :config (global-flycheck-mode)) + + ;; In the following section we're setting some default behavior of FG42. + ;; Most of these configuration are opiniated and I think most of people + ;; shared the same opinion or don't care at all. + (fg42/setup-font) + (add-hook 'fg42/after-init-hook + (lambda () + (set-default 'cursor-type 'bar) + (set-cursor-color "#eeeeec"))) + + ;; Remove splash screen + (setq inhibit-splash-screen t) + ;; scratch should be scratch + (setq initial-scratch-message nil) + ;; Don't allow tab as indent + (setq-default indent-tabs-mode nil) + ;; Default indent width + (setq tab-width 2) + ;; Share the clipboard with X applications + (setq x-select-enable-clipboard t) + ;; Automatically removed excess backups of the file + (setq delete-old-versions t) + ;; Emacs 28 and newer: Hide commands in M-x which do not work in the current + ;; mode. Vertico commands are hidden in normal buffers. This setting is + ;; useful beyond Vertico. + (setq read-extended-command-predicate #'command-completion-default-include-p) + + ;; Support opening new minibuffers from inside existing minibuffers. + (setq enable-recursive-minibuffers t) + + (setq tooltip-use-echo-area t) + (setq x-gtk-use-system-tooltips nil) + + ;; Global configurations + (tool-bar-mode -1) + (tooltip-mode nil) + (menu-bar-mode -1) + + (when (display-graphic-p) + ;; Smoother scrolling + (pixel-scroll-precision-mode) + (scroll-bar-mode -1)) + + (defalias 'yes-or-no-p 'y-or-n-p) + + ;; Hooks --- + (add-hook + 'emacs-startup-hook + (lambda () + ;; It only applies to toolkit=no + (set-mouse-color (get-base16-color-or :base07 "#eeeeec")) + + ;; Switch from `dabbrev-expand' to `hippie-expand' + (global-set-key [remap dabbrev-expand] 'hippie-expand) + + (column-number-mode t) + (show-paren-mode t) + (electric-pair-mode 1) + + ;; Rectangular select + (cua-selection-mode t) + + ;; Yank the region on type + (delete-selection-mode 1))) + + ;; Deletel extra trailing white spaces on save + (add-hook 'before-save-hook 'delete-trailing-whitespace) + + ;; Enable rainbow-delimiters for programming + (add-hook 'prog-mode-hook #'rainbow-delimiters-mode) + + ;; With this section we will have two emacs server running + ;; this way we can interact with the wm via emacsclient as well + (when (not (server-running-p)) + (when-wm + (setq server-name "fg42-wm")) + (server-start) + (require 'org-protocol)) + + (when-wm + ;; Activating the WM mode + (exwm-enable) + (exwm-systemtray-enable) + (exwm-randr-enable)) + (message "[FG42]: Use `fg42-help' to get help.")) + + +(provide 'fg42/editor) +;;; editor.el ends here diff --git a/nix/modules/editor/lisp/fg42/utils.el b/nix/modules/editor/lisp/fg42/utils.el new file mode 100644 index 0000000..1f8d8e8 --- /dev/null +++ b/nix/modules/editor/lisp/fg42/utils.el @@ -0,0 +1,161 @@ +;;; Utils --- Utils library of FG42 -*- lexical-binding: t; -*- +;; +;; Copyright (c) 2010-2024 Sameer Rahmani +;; +;; Author: Sameer Rahmani +;; URL: https://devheroes.codes/FG42/FG42 +;; Version: 4.0.0 +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;;; Commentary: +;; `System' is just a state monad which holds the state of the editor. +;; Each system has to have a `start' function to start the setup process. +;; +;;; Code: +(require 'cl-lib) + +(autoload 'seq-partition "seq") +(autoload 'cl-reduce "cl-seq") + + +(defun buffer-mode (buffer-or-string) + "Return the major mode associated with a the given BUFFER-OR-STRING." + (with-current-buffer buffer-or-string + major-mode)) + + +(defun ->buffer (buffer-name data &optional fn) + "Insert the given DATA into the given buffer provided by BUFFER-NAME. + +It will create a the buffer if it doesn't exist. It will call the given FN +at the end in context of the buffer. This function accepts only one argument +with is the buffer." + (let ((buf (get-buffer-create buffer-name))) + (with-current-buffer buf + (insert data) + (when fn + (funcall fn buf))))) + + +(defun ->str (&rest args) + "Convert the given ARGS into string." + (funcall #'pp-to-string args)) + + +(defmacro inspect-expression (&rest body) + "Pretty prints the result of the given BODY." + `(pp-display-expression ,@body (get-buffer-create fg42/inspect-buffer))) + + +(defun inspect-data-append (data) + "Append the given DATA to the inspection buffer with padding." + ;; TODO: Move 'fg42/inspect-buffer' to the somewhere propriate + ;; possiblly the system. + (->buffer + "fg42/inspect-buffer" + (format + "\n;; START ======================================================\n%s%s" + (pp-to-string data) + ";; END.\n"))) + + +(defun apply-face (face-symbol text) + "Apply the given FACE-SYMBOL to the given TEXT." + (put-text-property 0 (length text) 'face face-symbol text)) + + +(defmacro comment (&rest _body) + "A macro similar to Clojure's comment macro that ignore the BODY." + (declare (indent 0)) + `nil) + + +(defmacro debug-message (&rest params) + "Print out the given PARAMS only if debug mode is on." + (if debug-on-error + `(message ,@params) + nil)) + + +(defmacro deprecated (msg &rest form) + "Mark the given FORM as deprecated with the given MSG." + (declare (indent 0)) + `(progn + (warn (format "[DEPRECATED]: %s" ,msg)) + ,@form)) + + +(defun path-join (&rest paths) + "Join the given PATHS." + (apply #'concat + (append + (mapcar #'file-name-as-directory (butlast paths)) + (last paths)))) + + +(defmacro -> (x &optional form &rest more) + "Thread the expr through the forms FORM and rest of form in MORE. +Insert X as the second item in the first form, making a list of +it if it is not a list already. If there are more forms, insert +the first form as the second item in second form, etc." + (declare (debug (form &rest [&or symbolp (sexp &rest form)]))) + (cond + ((null form) x) + ((null more) (if (listp form) + `(,(car form) ,x ,@(cdr form)) + (list form x))) + (:else `(-> (-> ,x ,form) ,@more)))) + + +(defmacro ->> (x &optional form &rest more) + "Thread the expr through the forms FORM and the rest at MORE. +Insert X as the last item in the first form, making a list of +it if it is not a list already. If there are more forms, insert +the first form as the +last item in second form, etc." + (declare (debug ->)) + (cond + ((null form) x) + ((null more) (if (listp form) + `(,@form ,x) + (list form x))) + (:else `(->> (->> ,x ,form) ,@more)))) + + +(defmacro when-wm (&rest body) + "Run the BODY only if in wm mode." + (if (string= (getenv "FG42_WM") "true") + `(progn ,@body) + nil)) + +(defmacro when-not-wm (&rest body) + "Run the BODY only if not in the wm mode." + (if (not (string= (getenv "FG42_WM") "true")) + `(progn ,@body) + nil)) + + +(defun get-base16-color-or (color-name default) + "Return the color for COLOR-NAME if a base16 theme is loade otherwise DEFAULT." + (let* ((theme (car custom-enabled-themes)) + (theme-sym (intern (format "%s-theme-colors" theme)))) + + (if (boundp theme-sym) + (or (eval `(plist-get ,theme-sym ,color-name)) default) + default))) + + +(provide 'fg42/utils) +;;; utils.el ends here diff --git a/nix/modules/editor/lisp/fpkg.el b/nix/modules/editor/lisp/fpkg.el new file mode 100644 index 0000000..0bc5bf6 --- /dev/null +++ b/nix/modules/editor/lisp/fpkg.el @@ -0,0 +1,69 @@ +;;; fpkg --- a simple package manager for FG42 -*- lexical-binding: t; -*- +;; +;; Copyright (C) 2010-2024 Sameer Rahmani +;; +;; Author: Sameer Rahmani +;; Keywords: lisp fg42 IDE package manager +;; Version: 1.0.0 +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;;; Commentary: +;; +;; Simple package manager for FG42 +;; +;;; Code: +(require 'map) + +(defvar package-names ()) + +(eval-when-compile + (defvar fg42/disabled-features '()) + (require 'use-package)) + + +(defun inject-params (args) + "Inject required `use-package' params to ARGS if the key is missing." + ;; (if (member :defer args) + ;; (append '(:ensure nil) args) + (append args '(:ensure nil))) + + +(defmacro fpkg/use (pkg &rest details) + "Install the given package DETAILS PKG via `use-package' and straight." + (declare (indent defun)) + (if (and (listp details) (< 0 (length details))) + (let ((p (inject-params details))) + `(progn + (use-package ,pkg ,@p))) + `(progn + (use-package ,pkg :defer t :ensure nil)))) + + +(defmacro use! (pkg docs &rest details) + "Loading the given package DETAILS PKG via `use-package'. + +DOCS is the documentation of the package." + (declare (indent defun) (doc-string 2)) + + (when (not (stringp docs)) + (error "Missing docstring for '%s' package" pkg)) + + (let ((disabled (or (member pkg fg42/disabled-features) nil))) + (when (not disabled) + `(use-package ,pkg ,@details)))) + + +(provide 'fpkg) +;;; fpkg.el ends here