;;; FG42 --- The mighty editor for the emacsians -*- lexical-binding: t; -*- ;; ;; Copyright (c) 2010-2023 Sameer Rahmani ;; ;; Author: Sameer Rahmani ;; URL: https://devheroes.codes/FG42/FG42 ;; Version: 3.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 'fpkg) (require 'fg42/flags) (fpkg/require 'projectile) (fpkg/require 'f) (defflag fg42-project "Enable the support for project files in FG42." t) (defgroup fg42/project nil "The customization group for the fg42-project-global-mode." :group 'convenience) (defvar fg42/visited-projects nil "A plist of all the visited projects. It's mapping of project name to the project datastructure") (defmacro defproject (&rest props) "Define a project with given PROPS to be used with FG42's project system." (let* ((p (projectile-project-name)) (pr (projectile-project-root)) (f-name (intern (format "project/%s" p)))) (when (not p) (error "Can't detect the projectile project. Is it a git repo?")) `(progn (defun ,f-name () ,(format "Project '%s' configuration. Location: '%s'" p pr) (list ,@props)) (when (not (plist-member fg42/visited-projects ,(symbol-name f-name))) (plist-put fg42/visited-projects ,p #',f-name))))) (defun fg42/-project-get-or-load-file () "Return the project plist either from the cache or by loading the `.fg42.el' file." (let* ((p (projectile-project-name)) (f-name (intern (format "project/%s" p)))) (if (functionp f-name) (funcall f-name) (when (projectile-project-root) (let ((file (f-join (projectile-project-root) ".fg42.el"))) (if (file-exists-p file) (progn (load file) (if (functionp f-name) (funcall f-name) (progn (warn "'%s' does not define a project." (f-join (projectile-project-root) ".fg42.el")) nil))) nil)))))) (defun fg42/project-run-slot (n) "Run the slot number N of the current project." (let ((p (fg42/-project-get-or-load-file))) (funcall (or (plist-get p (intern (format ":slot-%s" n))) (lambda () (message "No task.")))))) (defmacro fg42/-project-define-slots (n) "Define N slots." `(progn ,@(mapcar (lambda (x) `(progn (defun ,(intern (format "fg42/project-slot-%s" x)) () (format "Run the slot number %s of the current project." ,x) (interactive) (fg42/project-run-slot ,x)) (define-key fg42-project-global-mode-map (kbd ,(format "C-c C-%s" x)) (function ,(intern (format "fg42/project-slot-%s" x)))))) (number-sequence 0 n)))) ;;;###autoload (define-minor-mode fg42-project-global-mode "A minor mode to activate FG42 project mode." :global t :lighter "Pj42" :group 'fg42/project :keymap (make-sparse-keymap) (if fg42-project-global-mode (fg42/-project-define-slots 9) (message "Fg42 project is disabled"))) (provide 'fg42/project) ;;; project.el ends here