From ed7319bd09c4fd862c09143f466eb79e228b3eac Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Thu, 29 Apr 2021 12:08:52 +0200 Subject: [PATCH] Initial commit, most code is already written --- .gitignore | 1 + eshell-info-banner.el | 228 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 .gitignore create mode 100644 eshell-info-banner.el diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b25c15b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/eshell-info-banner.el b/eshell-info-banner.el new file mode 100644 index 0000000..82cb37d --- /dev/null +++ b/eshell-info-banner.el @@ -0,0 +1,228 @@ +;;; eshell-info-banner.el --- System information as your Eshell banner -*- lexical-binding: t -*- + +;; Author: Lucien Cartier-Tilet +;; Maintainer: Lucien Cartier-Tilet +;; Version: 0.1.0 +;; Package-Requires: ((emacs "24") (dash "2") (f "0.20") (s "1)) +;; Homepage: https://labs.phundrak.com/phundrak/eshell-info-banner.el + +;; This file is not part of GNU Emacs + +;; 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: + +;; commentary + +;;; Code: + +(require 'cl-lib) +(require 'dash) +(require 'f) + + ; Groups ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defgroup eshell-info-banner nil + "System information as your Eshell banner." + :prefix "eshell-info-banner-" + :link '(url-link :tag "Gitea" "https://labs.phundrak.com/phundrak/eshell-info-banner.el")) + + ; Structs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(cl-defstruct eshell-info-banner--mounted-partitions + "Object representing a mounted partition found in the system." + path size used percent) + + ; Custom variables ;;;;;;;;;;;;;;;;;;;; + +(defcustom eshell-info-banner--max-length-part 13 + "Maximum length of a partition’s ruler." + :group 'eshell-info-banner + :type 'integer) + +(defcustom eshell-info-banner--percentage-critical 90 + "When a percentage becomes critical." + :group 'eshell-info-banner + :type 'float) + +(defcustom eshell-info-banner--percentage-warning 75 + "When to warn about a percentage." + :group 'eshell-info-banner + :type 'float) + +(defcustom eshell-info-banner--progress-bar-char "=" + "Character to fill the progress bars with." + :group 'eshell-info-banner + :type 'char) + + ; Faces ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defface eshell-info-banner-background-face + '((t :inherit font-lock-comment-face)) + "Face for \"empty\" part of progress bars." + :group 'eshell-info-banner) + +(defface eshell-info-banner-normal-face + '((t :inherit font-lock-string-face)) + "Face for eshell-info-banner progress bars displaying acceptable levels." + :group 'eshell-info-banner) + +(defface eshell-info-banner-warning-face + '((t :inherit warning)) + "Face for eshell-info-banner progress bars displaying high levels." + :group 'eshell-info-banner) + +(defface eshell-info-banner-critical-face + '((t :inherit error)) + "Face for eshell-info-banner progress bars displaying critical levels." + :group 'eshell-info-banner) + + ; Macros ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defmacro eshell-info-banner--with-face (str &rest properties) + "Helper macro for applying face `PROPERTIES' to `STR'." + `(propertize ,str 'face (list ,@properties))) + + ; Internal functions ;;;;;;;;;;;;;;;;;; + +(defun eshell-info-banner--abbr-path (path &optional abbr) + "Remove `$HOME' from `PATH', abbreviate parent dirs if `ABBR' non nil. + +Abbreviate `PATH' by removing the value of `HOME' if it is +present in the former, and if `ABBR' is t then all parent +directories of the current `PATH' are abbreviated to only one +character. If an abbreviated directory starts with a dot, then +include it before the abbreviated name of the directory, +e.g. \".config\" -> \".c\". + +For public use, `PATH' should be a string representing a UNIX +path. For internal use, `PATH' cna also be a list. If `PATH' is +neither of these, an error will be thrown by the function." + (cond + ((stringp path) (f-short + (if abbr + (eshell-info-banner--abbr-path (f-split (eshell-info-banner--abbr-path path))) + path))) + ((null path) "") + ((listp path) + (f-join + (let* ((dir (car path)) + (first-char (substring dir 0 1))) + (if (equal "." first-char) + (substring dir 0 2) + first-char)) + (eshell-info-banner--abbr-path (cdr path)))) + (t (error "Invalid argument %s, neither stringp or listp" path)))) + +(defun eshell-info-banner--get-mounted-partitions () + "Detect mounted partitions on the system. + +Return detected partitions as a list of structs." + (let ((partitions (split-string (shell-command-to-string "df -lH") (regexp-quote "\n") t))) + (-keep (lambda (partition) + (let* ((partition (split-string partition " " t)) + (filesystem (nth 0 partition)) + (size (nth 1 partition)) + (used (nth 2 partition)) + (percent (nth 4 partition)) + (mount (nth 5 partition))) + (when (string-prefix-p "/dev" filesystem t) + (make-eshell-info-banner--mounted-partitions + :path (if (length> mount eshell-info-banner--max-length-part) + mount + (eshell-info-banner--abbr-path mount t)) + :size size + :used used + :percent (string-to-number + (string-trim-left percent (regexp-quote "%"))))))) + partitions))) + +(defun eshell-info-banner--get-left-pad (initial-pad partitions) + "Get left padding for the various rulers. + +If `PARTITIONS' have a name short enough, then return +`INITIAL-PAD', otherwise return enough length to display the +shortened name of the partitions with a long name." + (if partitions + (let ((part-length (length (eshell-info-banner--mounted-partitions-path (car partitions))))) + (eshell-info-banner--get-left-pad (if (> part-length initial-pad) + part-length + initial-pad) + (cdr partitions))) + initial-pad)) + +(defun eshell-info-banner--get-color-percentage (percentage) + "Display a `PERCENTAGE' with its according face." + (let ((percentage (if (stringp percentage) + (string-to-number percentage) + percentage))) + (cond + ((> percentage eshell-info-banner--percentage-critical) + 'eshell-info-banner-critical-face) + ((> percentage eshell-info-banner--percentage-warning) + 'eshell-info-banner-warning-face) + (t 'eshell-info-banner-normal-face)))) + +(defun eshell-info-banner--progress-bar (length percentage) + "Display a progress bar `LENGTH' long and `PERCENTAGE' full. +The full path will be displayed filled with the character +specified by `eshell-info-banner--progress-bar-char' up to +`PERCENTAGE' percents. The rest will be empty." + (let* ((length-filled (if (= 0 percentage) + 0 + (/ (* length percentage) 100))) + (length-empty (- length length-filled))) + (concat + (eshell-info-banner--with-face "[" :weight 'bold) + (eshell-info-banner--with-face (s-repeat length-filled eshell-info-banner--progress-bar-char) + :weight 'bold + :inherit (eshell-info-banner--get-color-percentage percentage)) + (eshell-info-banner--with-face (s-repeat length-empty eshell-info-banner--progress-bar-char) + :weight 'bold :inherit 'eshell-info-banner-background-face) + (eshell-info-banner--with-face "]" :weight 'bold)))) + +(defun eshell-info-banner--display-memory (type used total text-padding bar-length) + "Display a memory’s usage with a progress bar. +Displayed progress bars will have this appearance: + +TYPE......: [=========] XXG / XXG (XX%) + +The `TYPE' of memory will be the text on the far left, while +`USED' and `TOTAL' will be displayed on the right of the progress +bar. From them, a percentage will be computed which will be used +to display a colored percentage of the progress bar and it will +be displayed on the far right. + +`TEXT-PADDING' will determine how many dots are necessary between +`TYPE' and the colon. + +`BAR-LENGTH' determines the length of the progress bar to be +displayed." + (let ((percentage (if (= used 0) + 0 + (/ (* 100 used) total)))) + (concat (s-pad-right text-padding "." type) + ": " + (eshell-info-banner--progress-bar bar-length percentage) + (format " %6s / %-5s (" + (file-size-human-readable used) + (file-size-human-readable total)) + (eshell-info-banner--with-face (number-to-string percentage) + :inherit (eshell-info-banner--get-color-percentage percentage)) + "%)\n"))) + +(provide 'eshell-info-banner) + +;;; eshell-info-banner.el ends here