FG42/core/fg42/project.el

119 lines
3.9 KiB
EmacsLisp

;;; FG42 --- The mighty editor for the emacsians -*- lexical-binding: t; -*-
;;
;; Copyright (c) 2010-2023 Sameer Rahmani <lxsameer@gnu.org>
;;
;; Author: Sameer Rahmani <lxsameer@gnu.org>
;; 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 <http://www.gnu.org/licenses/>.
;;
;;; 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