;;; elogt.el --- Portage Emerge log browser -*- lexical-binding: t -*- ;; Copyright 2023 Gentoo Authors ;; This file 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 2 of the License, or ;; (at your option) any later version. ;; This file 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 GNU Emacs. If not, see . ;; Authors: Maciej Barć ;; Maintainer: Gentoo Emacs project ;; Created: 03 Feb 2023 ;; Version: 0.0.0 ;; Keywords: convenience ;; Homepage: https://gitweb.gentoo.org/proj/emacs-elogt.git ;; Package-Requires: ((emacs "25")) ;; SPDX-License-Identifier: GPL-2.0-or-later ;;; Commentary: ;; Portage Emerge log browser for GNU Emacs. ;; ElogT displays the Portage logs in a convenient table and allows log file ;; browsing and deletion. ;;; Code: (defconst elogt-version "0.0.0" "ElogT version.") (defconst elogt--portage-log-stars '((" \e[31;01m*\e[0m" . error) (" \e[32m*\e[0m" . info) (" \e[33;01m*\e[0m" . warn))) (defconst elogt--priority-level-indicators `((error . ,(propertize "Error" 'font-lock-face '(:foreground "red"))) (info . ,(propertize "Info" 'font-lock-face '(:foreground "green"))) (warn . ,(propertize "Warn" 'font-lock-face '(:foreground "orange"))) (none . ,(propertize "None" 'font-lock-face '(:foreground "purple"))))) ;; Customization (defgroup elogt nil "Customization for ElogT, Portage Emerge log browser." :group 'ebuild) (defcustom elogt-portage-log-dir "/var/log/portage" "Portage log directory location." :safe 'stringp :type 'file :group 'elogt) (defcustom elogt-check-priority t "Check priority of each logfile. If set to nil entries will have a phony Info level." :type 'boolean :group 'elogt) ;; Log file processing (defun elogt--gather-portage-logs () "Gather logs from ‘elogt-log-dir’." (directory-files elogt-portage-log-dir t "\\.log")) (defun elogt--logfile-find-stars (file-name) "Find special info indicators in FILE-NAME." (let ((buffer (find-file-noselect file-name 'nowarn 'rawfile)) (found-stars nil)) (with-current-buffer buffer (dolist (star elogt--portage-log-stars) (save-excursion (goto-char (point-min)) (let ((found (search-forward (car star) nil 'noerror))) (when found (push (cdr star) found-stars)))))) (kill-buffer buffer) found-stars)) (defun elogt--priority-level-property (key) "Rerun a value referenced by KEY of ‘elogt--priority-level-indicators’." (cdr (assoc key elogt--priority-level-indicators))) (defun elogt--logfile-priority-level (file-name) "Return a FILE-NAME priority level. A logfile priority level is one of: None, Info, Warn, Error." (let ((found-stars (elogt--logfile-find-stars file-name))) (cond ((not elogt-check-priority) (elogt--priority-level-property 'info)) ((null found-stars) (elogt--priority-level-property 'none)) ((member 'error found-stars) (elogt--priority-level-property 'error)) ((member 'warn found-stars) (elogt--priority-level-property 'warn)) (t (elogt--priority-level-property 'info))))) (defun elogt--logfile-properties (file-name) "Extract the properties form given FILE-NAME." (apply #'vector (cons (elogt--logfile-priority-level file-name) (split-string (file-name-base file-name) ":")))) (defun elogt--make-log-table-contents () "Make ElogT table contents." (let ((index 0)) (mapcar (lambda (log-file-path) (let ((entry (list index (elogt--logfile-properties log-file-path)))) (setq index (+ index 1)) entry)) (elogt--gather-portage-logs)))) ;; Table interaction (defun elogt--entry-logfile-path (table-entry) "Return a logfile path of a given ElogT TABLE-ENTRY." (format "%s/%s:%s:%s.log" elogt-portage-log-dir (aref table-entry 1) (aref table-entry 2) (aref table-entry 3))) (defun elogt--get-table-entry-logfile-path () "Return a logfile path of current ElogT table entry." (elogt--entry-logfile-path (tabulated-list-get-entry))) (defun elogt--open-entry-file () "Open a ElogT table entry at point. Return opened buffer (done via `find-file')." (find-file (elogt--get-table-entry-logfile-path))) (defun elogt-open-entry () "Open specified ElogT table entry and put it in a mode for viewing only." (interactive) (let ((buffer (elogt--open-entry-file))) (with-current-buffer buffer (fundamental-mode) (view-mode)))) (defun elogt--delete-entry-file () "Delete a logfile of a entry at point." (delete-file (elogt--get-table-entry-logfile-path))) (defun elogt-delete-entry () "Delete specified ElogT table entry." (interactive) (elogt--delete-entry-file) (tabulated-list-delete-entry)) (defun elogt-refresh-table () "Refresh the ElogT table." (interactive) (message "Refreshing the ElogT table, please wait...") (setq tabulated-list-entries (elogt--make-log-table-contents)) (tabulated-list-init-header) (tabulated-list-print t) (message "The ElogT table is ready.")) ;; Major mode (defvar elogt-mode-hook nil "Hook for `elogt' major mode.") (defvar elogt-mode-map (let ((elogt-mode-map (make-keymap))) (define-key elogt-mode-map (kbd "/") #'isearch-forward) (define-key elogt-mode-map (kbd "o") #'elogt-open-entry) (define-key elogt-mode-map (kbd "d") #'elogt-delete-entry) (define-key elogt-mode-map (kbd "g") #'elogt-refresh-table) elogt-mode-map) "Key map for `elogt' major mode.") (define-derived-mode elogt-mode tabulated-list-mode "ElogT" "Major mode for browsing Portage log files." (setq tabulated-list-format [("Priority" 10 t) ("Category" 20 t) ("Package" 30 t) ("Time" 20 t)]) (setq tabulated-list-sort-key (cons "Time" t)) (run-hooks 'elogt-mode-hook) (use-local-map elogt-mode-map)) ;; Main provided features ;;;###autoload (defun elogt () "Browse Portage log files." (interactive) (let ((buffer (get-buffer-create "*ElogT*"))) (with-current-buffer buffer (elogt-mode) (elogt-refresh-table)) (display-buffer buffer))) (provide 'elogt) ;;; elogt.el ends here