From 3f4a0d5370ae6c34afe180df96add3b8522f4af1 Mon Sep 17 00:00:00 2001 From: mattkae Date: Wed, 11 May 2022 09:23:58 -0400 Subject: initial commit --- lisp/perfect-margin.el | 336 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 lisp/perfect-margin.el (limited to 'lisp/perfect-margin.el') diff --git a/lisp/perfect-margin.el b/lisp/perfect-margin.el new file mode 100644 index 0000000..4453f85 --- /dev/null +++ b/lisp/perfect-margin.el @@ -0,0 +1,336 @@ +;;; perfect-margin.el --- auto center windows, work with minimap and/or linum-mode +;; Copyright (C) 2014 Randall Wang + +;; Author: Randall Wang +;; Created: 19 Nov 2014 +;; Version: 0.1 +;; URL: https://github.com/mpwang/perfect-margin +;; Keywords: convenience, frames +;; Package-Requires: ((emacs "24.0") (cl-lib "0.5")) + +;; 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 2 +;; 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 +;; MERCHANT ABILITY 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: +;; +;; # Usage +;; +;; Put perfect-margin under your Emacs load path, and add this to your init.el +;; +;; (require 'perfect-margin) +;; +;; Use `M-x perfect-margin-mode` to turn on/off perfect-margin. +;; +;; To make it permanent add this to your init.el after require. +;; +;; (perfect-margin-mode 1) +;; +;; Note: when using together with minimap or linum, make sure you place config for perfect-margin *AFTER* minimap and linum. +;; +;; # Customization +;; +;; Via `M-x customize-group` and enter perfect-margin. +;; +;; Change `perfect-margin-visible-width` and `Apply and Save`. That's it. +;; +;; *Or* you can change the visible window width by setup `perfect-margin-visible-width` on the init.el. +;; +;; (setq perfect-margin-visible-width 128) +;; +;; # Additional binding on margin area +;; +;; You can place this in your init.el to make mouse wheel scroll on margin area just like it scroll on the visible window. +;; +;; (dolist (margin '(" " " ")) +;; (global-set-key (kbd (concat margin "")) 'ignore) +;; (global-set-key (kbd (concat margin "")) 'ignore) +;; (dolist (multiple '("" "double-" "triple-")) +;; (global-set-key (kbd (concat margin "<" multiple "wheel-up>")) 'mwheel-scroll) +;; (global-set-key (kbd (concat margin "<" multiple "wheel-down>")) 'mwheel-scroll))) + +;;; Code: + +(require 'linum) +(require 'cl-lib) + +(defvar minimap-width-fraction) +(defvar minimap-buffer-name) +(declare-function minimap-get-window "minimap") + +(defgroup perfect-margin nil + "Auto center windows, work with minimap and/or linum-mode." + :group 'emacs) + +(defcustom perfect-margin-lighter " \u24c2" + "Mode-line indicator for symbol `perfect-margin-mode'." + :type '(choice (const :tag "No lighter" "") string) + :safe 'stringp + :group 'perfect-margin) + +(defcustom perfect-margin-visible-width 128 + "The visible width of main window to be kept at center." + :group 'perfect-margin + :type 'number) + +(defcustom perfect-margin-ignore-regexps + '("^minibuf" "^[*]") + "List of strings to determine if window is ignored. +Each string is used as regular expression to match the window buffer name." + :group 'perfect-margin + :type '(repeat regexp)) + +(defcustom perfect-margin-ignore-filters + '(window-minibuffer-p) + "List of functions to determine if window is ignored. +Each function is called with window as its sole arguemnt, returning a non-nil value indicate to ignore the window." + :group 'perfect-margin + :type '(list function)) + +(defcustom perfect-margin-ignore-modes + '(exwm-mode + doc-view-mode + nov-mode) + "List of symbols of ignored major modes." + :type '(repeat symbol) + :group 'perfect-margin) + +;;---------------------------------------------------------------------------- +;; env predictors +;;---------------------------------------------------------------------------- +(defun perfect-margin-with-linum-p () + "Whether linum is found and turn on." + (bound-and-true-p linum-mode)) + +(defun perfect-margin-with-minimap-p () + "Whether minimap is found." + (bound-and-true-p minimap-mode)) + +;;---------------------------------------------------------------------------- +;; Private functions +;;---------------------------------------------------------------------------- +(defun perfect-margin--width-with-margins (win) + "Calculate size of window(WIN) with it's margins." + (let ((margins (window-margins win))) + (+ (window-width win) + (or (car margins) 0) + (or (cdr margins) 0)))) + +(defun perfect-margin--minimap-window-p (win) + "Judge if the window(WIN) is the minimap window itself, when it's live." + (when (and (perfect-margin-with-minimap-p) + (minimap-get-window) + (window-live-p (minimap-get-window))) + (let ((minimap-edges (window-edges (minimap-get-window))) + (current-edges (window-edges win))) + (and (= (nth 0 minimap-edges) (nth 0 current-edges)) + (= (nth 1 minimap-edges) (nth 1 current-edges)) + (= (nth 2 minimap-edges) (nth 2 current-edges)))))) + +(defun perfect-margin--minimap-left-adjacent-covered-p (win) + "Judge if the window(WIN) is left adjacent to minimap window." + (when (and (perfect-margin-with-minimap-p) + (minimap-get-window) + (window-live-p (minimap-get-window))) + (let ((minimap-edges (window-edges (minimap-get-window))) + (current-edges (window-edges win))) + (and (= (nth 2 minimap-edges) (nth 0 current-edges)) + (<= (nth 1 minimap-edges) (nth 1 current-edges)) + (>= (nth 3 minimap-edges) (nth 3 current-edges)))))) + +(defun perfect-margin--init-window-margins () + "Calculate target window margins as if there is only one window on frame." + (let ((init-margin-width (round (max 0 (/ (- (frame-width) perfect-margin-visible-width) 2))))) + (cons init-margin-width init-margin-width))) + +(defun perfect-margin--auto-margin-ignore-p (win) + "Conditions for filtering window (WIN) to setup margin." + (let* ((buffer (window-buffer win)) + (name (buffer-name buffer))) + (or (with-current-buffer buffer + (apply #'derived-mode-p perfect-margin-ignore-modes)) + (cl-some #'identity + (nconc (mapcar (lambda (regexp) (string-match-p regexp name)) perfect-margin-ignore-regexps) + (mapcar (lambda (func) (funcall func win)) perfect-margin-ignore-filters)))))) + +;;---------------------------------------------------------------------------- +;; Minimap +;;---------------------------------------------------------------------------- +(defun perfect-margin-minimap-margin-window (win) + "Setup window margins with minimap at different stage. +WIN will be any visible window, including the minimap window." + ;; Hint: do not reply on (window-width (minimap-get-window)) + (let ((init-window-margins (perfect-margin--init-window-margins)) + (win-edges (window-edges win))) + (cond + ((perfect-margin--auto-margin-ignore-p win) + (set-window-margins win (if (perfect-margin-with-linum-p) 3 0) 0)) + ((not (minimap-get-window)) + ;; minimap-window is not available + (cond + ((= (frame-width) (perfect-margin--width-with-margins win)) + (set-window-margins win (car init-window-margins) (cdr init-window-margins))) + ;; When the window is first splited and minimap-window is not set, + ;; the minimap has the same buffer name with it's target window. + ;; Distinguish the minimap and target window base on edge and size. + ;; + ;; the left hand side window when minimap window is created by + ;; split-window-horizontally the first time. + ;; catch and don't set minimap window + ((and (= (nth 0 win-edges) 0) + (= (nth 2 win-edges) (round (* perfect-margin-visible-width minimap-width-fraction))))) + ((and (= (nth 0 win-edges) (round (* perfect-margin-visible-width minimap-width-fraction))) + (= (or (car (window-margins win)) 0) (car init-window-margins)) + (= (or (cdr (window-margins win)) 0) (cdr init-window-margins))) + ;; the newly split-off window on the right hand side, which carries init-window-margins + (set-window-margins win + (max (if (perfect-margin-with-linum-p) 3 0) + (- (car init-window-margins) + (round (* perfect-margin-visible-width minimap-width-fraction)))) + (cdr init-window-margins))) + (t + (set-window-margins win (if (perfect-margin-with-linum-p) 3 0) 0)))) + ;; catch and don't set minimap window + ((string-match minimap-buffer-name (buffer-name (window-buffer win)))) + ((not (window-live-p (minimap-get-window))) + ;; minimap-window is not live yet + (cond + ;; catch and don't set minimap window + ((and (= (nth 0 win-edges) 0) + (= (nth 2 win-edges) (round (* perfect-margin-visible-width minimap-width-fraction))))) + ((and (= (nth 0 win-edges) (round (* perfect-margin-visible-width minimap-width-fraction))) + ;; the splited target window carries original margins + (= (or (car (window-margins win)) 0) (car init-window-margins)) + (= (or (cdr (window-margins win)) 0) (cdr init-window-margins))) + (set-window-margins win + (max (if (perfect-margin-with-linum-p) 3 0) + (- (car init-window-margins) + (round (* perfect-margin-visible-width minimap-width-fraction)))) + (cdr init-window-margins))) + ((= (frame-width) (perfect-margin--width-with-margins win)) + ;; when switch window, the minimap window is kill first, set it's left adjacent window margins to nil. + ;; left edge of win extends to frame's left-most edge, it's width increased by width of minimap window. + (set-window-margins win (car init-window-margins) (cdr init-window-margins))) + (t + (set-window-margins win (if (perfect-margin-with-linum-p) 3 0) 0)))) + ;; minimap window is created, but it has the same name with it's target window + ;; catch and don't set minimap window + ((perfect-margin--minimap-window-p win)) + ((perfect-margin--minimap-left-adjacent-covered-p win) + (cond + ((not (>= (nth 2 win-edges) (frame-width))) + (set-window-margins win (if (perfect-margin-with-linum-p) 3 0) 0)) + (t + (set-window-margins win + (max (if (perfect-margin-with-linum-p) 3 0) + (- (car init-window-margins) + (round (* perfect-margin-visible-width minimap-width-fraction)))) + (cdr init-window-margins))))) + ((= (frame-width) (perfect-margin--width-with-margins win)) + (set-window-margins win (car init-window-margins) (cdr init-window-margins))) + (t + (set-window-margins win (if (perfect-margin-with-linum-p) 3 0) 0))))) + +;;---------------------------------------------------------------------------- +;; Main +;;---------------------------------------------------------------------------- +(defun perfect-margin-margin-windows () + "Main logic to setup window's margin, keep the visible main window always at center." + (dolist (win (window-list)) + (cond + ((perfect-margin-with-minimap-p) (perfect-margin-minimap-margin-window win)) + ((and (not (perfect-margin--auto-margin-ignore-p win)) + (<= (frame-width) (perfect-margin--width-with-margins win))) + (let ((init-window-margins (perfect-margin--init-window-margins))) + (set-window-margins win (car init-window-margins) (cdr init-window-margins)))) + (t (set-window-margins win (if (perfect-margin-with-linum-p) 3 0) 0))) + (set-window-fringes win 0 0))) + +(defun perfect-margin-margin-frame (&optional _) + "Hook to resize window when frame size change." + (when (frame-size-changed-p) + (perfect-margin-margin-windows))) + +;;---------------------------------------------------------------------------- +;; Advice +;;---------------------------------------------------------------------------- +(defun perfect-margin--linum-format (line) + "Function for `linum-format' to set left margin for LINE to be 3 as maximum." + (propertize + (format (concat "%" (number-to-string (max 3 (length (number-to-string line)))) "d") line) + 'face + 'linum)) + +(defvar perfect-margin--linum-update-win-left-margin nil + "Variable to store original window marings before `linum-update-window'.") + +(defadvice linum-update-window (before perfect-margin-linum-update-before (win)) + "Save window's original left margin." + (setq perfect-margin--linum-update-win-left-margin (or (car (window-margins win)) 0))) + +(defadvice linum-update-window (after perfect-margin-linum-update-after (win)) + "Restore windonw's original left margin, as `linum-update-window' always reset left margin." + (set-window-margins win perfect-margin--linum-update-win-left-margin (cdr (window-margins win)))) + +(defadvice minimap-update (after minimap-update-no-fringe nil) + "Prevent fringe overlay of target buffer from drawing on `minimap-window'." + (when (and (minimap-get-window) + (window-live-p (minimap-get-window)) + minimap-hide-fringes) + (set-window-fringes (minimap-get-window) 0 0))) + +(defadvice split-window (before perfect-margin--disable-margins nil) + (dolist (win (window-list)) + (set-window-margins win 0 0) + (set-window-fringes win 0 0))) + +;;---------------------------------------------------------------------------- +;; MINOR mode definition +;;---------------------------------------------------------------------------- +;;;###autoload +(define-minor-mode perfect-margin-mode + "Auto center windows." + :init-value nil + :lighter perfect-margin-lighter + :global t + (if perfect-margin-mode + ;; add hook and activate + (progn + (when (perfect-margin-with-linum-p) + (ad-activate 'linum-update-window) + (when (eq linum-format 'dynamic) + (setq linum-format 'perfect-margin--linum-format))) + (when (perfect-margin-with-minimap-p) + (ad-activate 'minimap-update)) + (ad-activate 'split-window) + (add-hook 'window-configuration-change-hook 'perfect-margin-margin-windows) + (add-hook 'window-size-change-functions 'perfect-margin-margin-frame) + (perfect-margin-margin-windows)) + ;; remove hook and restore margin + (when (perfect-margin-with-linum-p) + (ad-deactivate 'linum-update-window) + (when (eq linum-format 'perfect-margin--linum-format) + (setq linum-format 'dynamic)) + (linum-update-current)) + (when (perfect-margin-with-minimap-p) + (ad-deactivate 'minimap-update)) + (ad-deactivate 'split-window) + (remove-hook 'window-configuration-change-hook 'perfect-margin-margin-windows) + (remove-hook 'window-size-change-functions 'perfect-margin-margin-frame) + (dolist (window (window-list)) + (set-window-margins window 0 0)))) + +(provide 'perfect-margin) + +;;; perfect-margin.el ends here -- cgit v1.2.1