emacs.d/elpa/dashboard-20200217.1153/dashboard.el

257 lines
9.2 KiB
EmacsLisp
Raw Normal View History

2019-12-12 17:30:29 +01:00
;;; dashboard.el --- A startup screen extracted from Spacemacs -*- lexical-binding: t -*-
;; Copyright (c) 2016-2019 Rakan Al-Hneiti & Contributors
;;
;; Author: Rakan Al-Hneiti
;; URL: https://github.com/emacs-dashboard/emacs-dashboard
;;
;; This file is not part of GNU Emacs.
;;
;;; License: GPLv3
;;
;; Created: October 05, 2016
;; Package-Version: 1.7.0-SNAPSHOT
;; Keywords: startup, screen, tools, dashboard
;; Package-Requires: ((emacs "25.3") (page-break-lines "0.11"))
;;; Commentary:
;; An extensible Emacs dashboard, with sections for
;; bookmarks, projectile projects, org-agenda and more.
;;; Code:
(require 'seq)
(require 'page-break-lines)
(require 'recentf)
(require 'dashboard-widgets)
;; Custom splash screen
(defvar dashboard-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-p") 'dashboard-previous-line)
(define-key map (kbd "C-n") 'dashboard-next-line)
(define-key map (kbd "<up>") 'dashboard-previous-line)
(define-key map (kbd "<down>") 'dashboard-next-line)
(define-key map (kbd "k") 'dashboard-previous-line)
(define-key map (kbd "j") 'dashboard-next-line)
(define-key map [tab] 'widget-forward)
(define-key map (kbd "C-i") 'widget-forward)
(define-key map [backtab] 'widget-backward)
(define-key map (kbd "RET") 'dashboard-return)
(define-key map [down-mouse-1] 'widget-button-click)
(define-key map (kbd "g") #'dashboard-refresh-buffer)
(define-key map (kbd "}") #'dashboard-next-section)
(define-key map (kbd "{") #'dashboard-previous-section)
map)
"Keymap for dashboard mode.")
(define-derived-mode dashboard-mode special-mode "Dashboard"
"Dashboard major mode for startup screen.
\\<dashboard-mode-map>
"
:group 'dashboard
:syntax-table nil
:abbrev-table nil
2020-01-29 18:18:31 +01:00
(buffer-disable-undo)
2019-12-12 17:30:29 +01:00
(whitespace-mode -1)
(linum-mode -1)
(page-break-lines-mode 1)
(setq inhibit-startup-screen t)
(setq buffer-read-only t
truncate-lines t))
(defgroup dashboard nil
"Extensible startup screen."
:group 'applications)
(defcustom dashboard-center-content nil
"Whether to center content within the window."
:type 'boolean
:group 'dashboard)
(defconst dashboard-buffer-name "*dashboard*"
"Dashboard's buffer name.")
(defvar dashboard--section-starts nil
"List of section starting positions.")
(defun dashboard-previous-section ()
"Navigate back to previous section."
(interactive)
(let ((current-section-start nil)
(current-position (point))
(previous-section-start nil))
(dolist (elt dashboard--section-starts)
(when (and current-section-start
(not previous-section-start))
(setq previous-section-start elt))
(when (and (not current-section-start)
(< elt current-position))
(setq current-section-start elt)))
(goto-char (if (eq current-position current-section-start)
previous-section-start
current-section-start))))
(defun dashboard-next-section ()
"Navigate forward to next section."
(interactive)
(let ((current-position (point))
(next-section-start nil)
(section-starts (reverse dashboard--section-starts)))
(dolist (elt section-starts)
(when (and (not next-section-start)
(> elt current-position))
(setq next-section-start elt)))
(when next-section-start
(goto-char next-section-start))))
(defun dashboard-previous-line (arg)
"Move point up and position it at that lines item.
Optional prefix ARG says how many lines to move; default is one line."
(interactive "^p")
(dashboard-next-line (- arg)))
(defun dashboard-next-line (arg)
"Move point down and position it at that lines item.
Optional prefix ARG says how many lines to move; default is one line."
;; code heavily inspired by `dired-next-line'
(interactive "^p")
(let ((line-move-visual nil)
(goal-column nil))
(line-move arg t))
;; We never want to move point into an invisible line. Dashboard doesnt
;; use invisible text currently but when it does were ready!
(while (and (invisible-p (point))
(not (if (and arg (< arg 0)) (bobp) (eobp))))
(forward-char (if (and arg (< arg 0)) -1 1)))
(beginning-of-line-text))
(defun dashboard-return ()
"Hit return key in dashboard buffer."
(interactive)
(let ((start-ln (line-number-at-pos))
(fd-cnt 0)
(diff-line nil)
(entry-pt nil))
(save-excursion
(while (and (not diff-line)
(not (= (point) (point-min)))
(not (get-char-property (point) 'button))
(not (= (point) (point-max))))
(forward-char 1)
(setq fd-cnt (1+ fd-cnt))
(unless (= start-ln (line-number-at-pos))
(setq diff-line t)))
(unless (= (point) (point-max))
(setq entry-pt (point))))
(when (= fd-cnt 1)
(setq entry-pt (1- (point))))
(if entry-pt
(widget-button-press entry-pt)
(call-interactively #'widget-button-press))))
(defun dashboard-maximum-section-length ()
"For the just-inserted section, calculate the length of the longest line."
(let ((max-line-length 0))
(save-excursion
(dashboard-previous-section)
(while (not (eobp))
(setq max-line-length
(max max-line-length
(- (line-end-position) (line-beginning-position))))
(forward-line)))
max-line-length))
(defun dashboard-insert-startupify-lists ()
"Insert the list of widgets into the buffer."
(interactive)
(let ((buffer-exists (buffer-live-p (get-buffer dashboard-buffer-name)))
(recentf-is-on (recentf-enabled-p))
(origial-recentf-list recentf-list)
(dashboard-num-recents (or (cdr (assoc 'recents dashboard-items)) 0))
(max-line-length 0))
;; disable recentf mode,
;; so we don't flood the recent files list with org mode files
;; do this by making a copy of the part of the list we'll use
;; let dashboard widgets change that
;; then restore the orginal list afterwards
;; (this avoids many saves/loads that would result from
;; disabling/enabling recentf-mode)
(if recentf-is-on
(setq recentf-list (seq-take recentf-list dashboard-num-recents)))
(when (or (not (eq dashboard-buffer-last-width (window-width)))
(not buffer-exists))
(setq dashboard-banner-length (window-width)
dashboard-buffer-last-width dashboard-banner-length)
(with-current-buffer (get-buffer-create dashboard-buffer-name)
(let ((buffer-read-only nil))
(erase-buffer)
(dashboard-insert-banner)
(dashboard-insert-page-break)
(setq dashboard--section-starts nil)
(mapc (lambda (els)
(let* ((el (or (car-safe els) els))
(list-size
(or (cdr-safe els)
dashboard-items-default-length))
(item-generator
(cdr-safe (assoc el dashboard-item-generators))))
(add-to-list 'dashboard--section-starts (point))
(funcall item-generator list-size)
(setq max-line-length
(max max-line-length (dashboard-maximum-section-length)))
(dashboard-insert-page-break)))
dashboard-items)
(when dashboard-center-content
2020-02-17 23:07:13 +01:00
(when dashboard--section-starts
(goto-char (car (last dashboard--section-starts))))
2019-12-12 17:30:29 +01:00
(let ((margin (floor (/ (max (- (window-width) max-line-length) 0) 2))))
(while (not (eobp))
(and (not (eq ? (char-after)))
(insert (make-string margin ?\ )))
(forward-line 1))))
(dashboard-insert-footer))
(dashboard-mode)
(goto-char (point-min))))
(if recentf-is-on
(setq recentf-list origial-recentf-list))))
(add-hook 'window-setup-hook
(lambda ()
(add-hook 'window-size-change-functions 'dashboard-resize-on-hook)
(dashboard-resize-on-hook)))
(defun dashboard-refresh-buffer ()
"Refresh buffer."
(interactive)
(kill-buffer dashboard-buffer-name)
(dashboard-insert-startupify-lists)
(switch-to-buffer dashboard-buffer-name))
(defun dashboard-resize-on-hook (&optional _)
"Re-render dashboard on window size change."
(let ((space-win (get-buffer-window dashboard-buffer-name))
(frame-win (frame-selected-window)))
(when (and space-win
(not (window-minibuffer-p frame-win)))
(with-selected-window space-win
(dashboard-insert-startupify-lists)))))
;;;###autoload
(defun dashboard-setup-startup-hook ()
"Setup post initialization hooks.
If a command line argument is provided,
assume a filename and skip displaying Dashboard."
(when (< (length command-line-args) 2 )
(add-hook 'after-init-hook (lambda ()
;; Display useful lists of items
(dashboard-insert-startupify-lists)))
(add-hook 'emacs-startup-hook '(lambda ()
(switch-to-buffer "*dashboard*")
(goto-char (point-min))
(redisplay)))))
(provide 'dashboard)
;;; dashboard.el ends here