diff --git a/core/cubes/editor.el b/core/cubes/editor.el index 91a3aaa..ed50b32 100644 --- a/core/cubes/editor.el +++ b/core/cubes/editor.el @@ -28,6 +28,7 @@ (require 'fg42/modeline) (require 'cubes/modeline) (require 'fg42/themes) +(require 'fg42/project) (defflag font-icons "Enable the support for font icones in FG42." t) @@ -361,6 +362,8 @@ https://emacsthemes.com/themes/badwolf-theme.html" ;; Call the editor related cubes. They will be run only if ;; their flag is active otherwise they will be skipped + (when-flag fg42-project + (fg42-project-global-mode t)) (let ((mline (or (plist-get fg42/editor-cube-params :modeline) #'fg42/default-modeline))) diff --git a/core/fg42/project.el b/core/fg42/project.el new file mode 100644 index 0000000..7e6d9fe --- /dev/null +++ b/core/fg42/project.el @@ -0,0 +1,118 @@ +;;; 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 "s-s %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 diff --git a/core/fg42/ssh.el b/core/fg42/ssh.el new file mode 100644 index 0000000..c47522a --- /dev/null +++ b/core/fg42/ssh.el @@ -0,0 +1,58 @@ +;;; 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 'seq) +(require 'fg42/utils) + + +(defun ssh-run (host commands &rest opts) + "Run the give list of COMMANDS via ssh on HOST with the given OPTS." + (let ((sh (or (plist-get opts :shell) "/bin/bash")) + (cmds (format "'%s'" (mapconcat #'identity commands ";"))) + (output-buffer (get-buffer-create (format "*ssh[%s]*" host)))) + + (with-current-buffer output-buffer + (setq buffer-read-only nil) + (erase-buffer)) + + (let ((p (start-process-shell-command + (format "ssh-to-%s" host) + (buffer-name output-buffer) + (format "ssh -t %s %s -c %s" + host + sh + cmds)))) + (set-process-sentinel p + (lambda (_ event) + (when (string= event "finished\n") + (message "Commands finished. Closing SSH connection.") + ;;(kill-process process) + )))) + (message "Task started on %s -> %s" host (buffer-name output-buffer)))) + + +(provide 'fg42/ssh) +;;; ssh.el ends here diff --git a/core/fg42/utils.el b/core/fg42/utils.el index f35a1dc..cb45004 100644 --- a/core/fg42/utils.el +++ b/core/fg42/utils.el @@ -196,6 +196,19 @@ is non-nil value it means that the function can be called interactively." (fg42/log "INFO" args)) +(defmacro ->shell (&rest body) + "Return a list of shell commands in the BODY. + +Each element has to be list like `(cd \"..\")' the command is static but +the arguments get evaluated." + `(quote + ,(mapcar + (lambda (x) + (when (not (listp x)) + (error "Expect a list. Got %s" x)) + (format "%s %s" (car x) (mapconcat #'eval (cdr x) " "))) + body))) + (provide 'fg42/utils) ;;; utils.el ends here