.emacs.d

the most bloated Emacs config on this planet
git clone https://git.trogloxene.org/.emacs.d.git
Log | Files | Refs

init-package.el (21534B)


      1 ;;; init-package.el --- some other packages -*- lexical-binding: t -*-
      2 
      3 ;;; Commentary:
      4 
      5 ;;; Code:
      6 
      7 ;; keycast
      8 (my-use-package keycast my-enable-unessentials)
      9 
     10 ;; spell checker
     11 (my-use-package jinx (not *is-windows*)
     12   :delight
     13   (jinx-mode " Jinx" jinx)
     14   :config
     15   (setq jinx-languages "en_US")
     16   (add-to-list 'jinx-exclude-regexps '(t "\\cc"))
     17   (dolist (range '((#x4E00 . #x9FFF)
     18                    (#x3400 . #x4DBF)
     19                    (#x3000 . #x303F)
     20                    (#xFF00 . #xFFEF)
     21                    (#x20000 . #x2A6DF)))
     22     (let ((start (car range))
     23           (end (cdr range)))
     24       (dotimes (i (- end start))
     25         (modify-syntax-entry (+ start i) "_" jinx--syntax-table)))))
     26 
     27 (my-use-package consult-jinx (not *is-windows*)
     28   :straight
     29   (consult-jinx :type git
     30                 :repo "https://git.trogloxene.org/consult-jinx.git"))
     31 
     32 ;; grammar checkers
     33 (autoload #'eglot-managed-p "eglot")
     34 (defun my-toggle-grammar-checker ()
     35   "Toggle grammar checker depend on `my-grammar-checker-backend'."
     36   (interactive)
     37   (if text-mode-variant
     38       (pcase my-grammar-checker-backend
     39         ('harper (if (eglot-managed-p)
     40                      (call-interactively #'eglot-shutdown)
     41                    (call-interactively #'eglot)))
     42         ('languagetool (if (eglot-managed-p)
     43                            (call-interactively #'eglot-shutdown)
     44                          (call-interactively #'eglot)))
     45         ('nsspell (nsspell-grammar-mode))
     46         ('nil (message "No grammar checker backend")))
     47     (message "Not in a text-mode buffer")))
     48 
     49 (my-use-package nsspell
     50   (and *is-mac* (equal my-grammar-checker-backend 'nsspell))
     51   :straight (nsspell
     52              :type git
     53              :host github
     54              :repo "LdBeth/nsspell"
     55              :file ("lisp/"))
     56   :init
     57   (setq ispell-program-name "nsspell")
     58   (setq ispell-dictionary "en"))
     59 
     60 (my-use-package eglot-ltex
     61   (equal my-grammar-checker-backend 'languagetool)
     62   :straight
     63   (eglot-ltex :type git :host github :repo "emacs-languagetool/eglot-ltex")
     64   :hook
     65   (text-mode . (lambda () (require 'eglot-ltex)))
     66   :init
     67   (setq eglot-ltex-server-path "ltex-ls-plus"
     68         eglot-ltex-communication-channel 'stdio))
     69 
     70 (my-use-package harper
     71   (equal my-grammar-checker-backend 'harper)
     72   :no-require t
     73   :straight nil
     74   :init
     75   (with-eval-after-load 'eglot
     76     (add-to-list 'eglot-server-programs
     77                  '((markdown-mode :language-id "markdown") . ("harper-ls" "--stdio")))
     78     (add-to-list 'eglot-server-programs
     79                  '((org-mode :language-id "org") . ("harper-ls" "--stdio")))
     80     (setq-default eglot-workspace-configuration
     81                   '(:harper-ls (:linters (:SpellCheck :json-false :Spaces :json-false))))))
     82 
     83 ;; terminal emulator
     84 (my-use-package ghostel
     85   :hook
     86   (eshell-load . ghostel-eshell-visual-command-mode)
     87   (ghostel-mode . (lambda () (cns-mode -1)))
     88   :init
     89   (defun my-ghostel-send-C-k-and-kill ()
     90     "Send `C-k' to ghostel.
     91 Like normal Emacs `C-k'.  Kill to end of line and put content in kill-ring."
     92     (interactive)
     93     (kill-ring-save (point) (line-end-position))
     94     (ghostel-send-key "k" "ctrl"))
     95 
     96   (defun my-scroll-other-ghostel-window (fun lines)
     97     (let* ((buffer (window-buffer (other-window-for-scrolling)))
     98            (mode (buffer-local-value 'major-mode buffer))
     99            (running-p (buffer-local-value 'ghostel--command-running buffer)))
    100       (if (and (equal mode 'ghostel-mode)
    101                running-p)
    102           (with-current-buffer buffer
    103             (ghostel-send-key "next"))
    104         (funcall fun))))
    105 
    106   (defun my-scroll-other-ghostel-window-down (fun lines)
    107     (let* ((buffer (window-buffer (other-window-for-scrolling)))
    108            (mode (buffer-local-value 'major-mode buffer))
    109            (running-p (buffer-local-value 'ghostel--command-running buffer)))
    110       (if (and (equal mode 'ghostel-mode)
    111                running-p)
    112           (with-current-buffer buffer
    113             (ghostel-send-key "prior"))
    114         (funcall fun))))
    115 
    116   :config
    117   (setq eshell-destroy-buffer-when-process-dies t
    118         ghostel-ssh-install-terminfo t)
    119   (defalias 'eshell/v 'eshell/ghostel)
    120   (advice-add #'scroll-other-window :around #'my-scroll-other-ghostel-window)
    121   (advice-add #'scroll-other-window-down :around #'my-scroll-other-ghostel-window-down))
    122 
    123 (my-use-package consult-ghostel
    124   :straight
    125   (consult-ghostel :type git
    126                    :host github
    127                    :repo "dakra/ghostel"
    128                    :files ("extensions/consult-ghostel/")))
    129 
    130 ;; shell
    131 (my-use-package shell
    132   :straight nil
    133   :init
    134   (defvar my-shell-setenv
    135     '(("PAGER" . "cat")
    136       ("GIT_PAGER" . "cat")
    137       ("MANPAGER" . "cat")
    138       ("SYSTEMD_PAGER" . "cat")
    139       ("APT_PAGER" . "cat")))
    140 
    141   (defun my-setenv-local (var val buffer)
    142     (with-current-buffer buffer
    143       (unless (local-variable-p 'process-environment)
    144         (setq-local process-environment (copy-sequence process-environment)))
    145       (setenv var val)))
    146 
    147   (defun my-shell-setenv (buffer name &rest _)
    148     (when (string= name "shell")
    149       (dolist (pair my-shell-setenv)
    150         (my-setenv-local (car pair) (cdr pair) buffer))))
    151   (advice-add #'comint-exec :before #'my-shell-setenv)
    152 
    153   ;; from Protesilaos's Emacs configuration
    154   (defun my-shell--build-input-history ()
    155     "Return `comint-input-ring' as a list."
    156     (when (and (ring-p comint-input-ring)
    157                (not (ring-empty-p comint-input-ring)))
    158       (let (history)
    159         (dotimes (index (ring-length comint-input-ring))
    160           (push (ring-ref comint-input-ring index) history))
    161         (delete-dups history))))
    162 
    163   (defvar my-shell--input-history-completion-history nil
    164     "Minibuffer history of `my-shell--input-history-prompt'.
    165 Not to be confused with the shell input history, which is stored
    166 in the `comint-input-ring' (see `my-shell--build-input-history').")
    167 
    168   (defun my-shell--input-history-prompt ()
    169     "Prompt for completion against `my-shell--build-input-history'."
    170     (let* ((history (my-shell--build-input-history))
    171            (default (car history)))
    172       (completing-read
    173        (format-prompt "Insert input from history" default)
    174        history nil :require-match nil
    175        'my-shell--input-history-completion-history
    176        default)))
    177 
    178   (defun my-shell-input-from-history ()
    179     "Insert command from shell input history.
    180 Only account for the history Emacs knows about, ignoring
    181 `comint-input-ring-file-name' (e.g. ~/.bash_history)."
    182     (declare (interactive-only t))
    183     (interactive)
    184     (insert (my-shell--input-history-prompt))))
    185 
    186 ;; eshell-syntax-highlighting
    187 (my-use-package eshell-syntax-highlighting
    188   :hook
    189   (eshell-mode . eshell-syntax-highlighting-global-mode)
    190   (eshell-mode . (lambda () (setenv "TERM" "xterm-256color"))))
    191 
    192 ;; environment sync
    193 (my-use-package exec-path-from-shell (not *is-windows*)
    194   :hook
    195   (after-init . exec-path-from-shell-initialize))
    196 
    197 ;; workspace
    198 (my-use-package perspective
    199   :hook
    200   (after-init . persp-mode)
    201   :init
    202   (defun my-persp-scratch-buffer (arg)
    203     "Switch to the current perspective's scratch buffer.
    204 With a prefix ARG create a new *scratch* buffer."
    205     (interactive "P")
    206     (let ((default-directory (expand-file-name "~/")))
    207       (if arg
    208           (let* ((name (persp-current-name))
    209                  (initial-persp (equal name persp-initial-frame-name))
    210                  (buf (generate-new-buffer (concat "*scratch*"
    211                                                    (unless initial-persp
    212                                                      (format " (%s)" name))))))
    213             (switch-to-buffer buf)
    214             (funcall initial-major-mode))
    215         (persp-switch-to-scratch-buffer))))
    216 
    217   (defun my-persp-scratch-set-default-dir (fun &rest name)
    218     (let ((default-directory (expand-file-name "~/")))
    219       (apply fun name)))
    220   (advice-add #'persp-get-scratch-buffer :around #'my-persp-scratch-set-default-dir)
    221 
    222   (defvar my-persp-ibuffer-predicates
    223     (list (lambda (buf) (persp-buffer-filter buf t))
    224           (lambda (buf) (persp--ignore-buffer-p (buffer-name buf)))))
    225 
    226   (with-eval-after-load 'ibuffer
    227     (setq ibuffer-maybe-show-predicates
    228           (append ibuffer-maybe-show-predicates
    229                   my-persp-ibuffer-predicates)))
    230 
    231   (defun my-persp-ibuffer (arg)
    232     "Like `persp-ibuffer' but better."
    233     (interactive "P")
    234     (unless (featurep 'ibuffer)
    235       (require 'ibuffer))
    236     (ibuffer)
    237     (setq-local ibuffer-maybe-show-predicates
    238                 (if (and persp-mode (null arg))
    239                     (append ibuffer-maybe-show-predicates
    240                             my-persp-ibuffer-predicates)
    241                   '(ibuffer-hidden-buffer-p)))
    242     (ibuffer-update nil t))
    243 
    244   (defun my-persp-remove-marked-ibuffers ()
    245     "Remove marked buffers from perspective."
    246     (interactive)
    247     (let ((buffers (ibuffer-get-marked-buffers)))
    248       (dolist (buf buffers)
    249         (persp-remove-buffer buf)))
    250     (ibuffer-update nil t))
    251 
    252   (defun my-persp-add-marked-ibuffers (persp-name)
    253     "Add marked buffers to perspective."
    254     (interactive
    255      (list (completing-read "Perspective: "
    256                             (persp-names)
    257                             nil nil)))
    258     (let ((buffers (ibuffer-get-marked-buffers)))
    259       (persp-switch persp-name)
    260       (dolist (buf buffers)
    261         (persp-add-buffer buf))))
    262 
    263   (defun my-persp-set-marked-ibuffers (persp-name)
    264     "Set marked buffers to perspective."
    265     (interactive
    266      (list (completing-read "Perspective: "
    267                             (persp-names)
    268                             nil nil)))
    269     (let ((buffers (ibuffer-get-marked-buffers)))
    270       (persp-switch persp-name)
    271       (dolist (buf buffers)
    272         (persp-set-buffer buf))))
    273 
    274   :config
    275   (setopt persp-mode-prefix-key (kbd "C-c p"))
    276   (general-def perspective-map "d" #'persp-kill)
    277   (setq persp-switch-to-buffer-behavior 'switch
    278         persp-sort 'oldest)
    279   (with-eval-after-load 'consult
    280     (consult-customize consult-source-buffer :hidden t :default nil)
    281     (add-to-list 'consult-buffer-sources persp-consult-source)
    282     (setq switch-to-prev-buffer-skip
    283           (lambda (win buff bury-or-kill)
    284             (or (not (persp-is-current-buffer buff))
    285                 (cl-some (lambda (re) (string-match-p re (buffer-name buff)))
    286                          consult-buffer-filter)))))
    287   (setq persp-initial-frame-name "main"))
    288 
    289 ;; emms
    290 (my-use-package emms my-enable-unessentials
    291   :config
    292   (add-to-list 'recentf-exclude "~/.emacs.d/emms/history")
    293   (emms-all)
    294   (setq emms-player-list '(emms-player-vlc)
    295         emms-info-functions '(emms-info-native))
    296   (emms-history-load))
    297 
    298 ;; tokei
    299 (my-use-package tokei my-enable-unessentials)
    300 
    301 ;; helpful
    302 (my-use-package helpful my-enable-unessentials
    303   :straight
    304   (helpful :fork (:type git :host nil :repo "https://git.trogloxene.org/helpful.git"))
    305   :init
    306   (defun remove-hook-at-point ()
    307     "Remove the hook at the point in the *Help* buffer."
    308     (interactive)
    309     (unless (memq major-mode '(help-mode helpful-mode))
    310       (error "Only for help-mode or helpful-mode"))
    311     (let ((orig-point (point)))
    312       (save-excursion
    313         (when-let*
    314             ((hook (progn (goto-char (point-min)) (symbol-at-point)))
    315              (func (when (and
    316                           (or (re-search-forward (format "^Value:?[\s|\n]") nil t)
    317                               (goto-char orig-point))
    318                           (thing-at-point 'sexp))
    319                      (thing-at-point--end-of-sexp)
    320                      (backward-char 1)
    321                      (catch 'break
    322                        (while t
    323                          (condition-case _err
    324                              (backward-sexp)
    325                            (scan-error (throw 'break nil)))
    326                          (let ((bounds (bounds-of-thing-at-point 'sexp)))
    327                            (when (<= (car bounds) orig-point (cdr bounds))
    328                              (throw 'break (thing-at-point 'sexp)))))))))
    329           (when (yes-or-no-p (format "Remove %s from %s? " func hook))
    330             (remove-hook hook (intern func))
    331             (if (memq major-mode '(helpful-mode))
    332                 (helpful-update)))))))
    333 
    334   (defun my-helpful-view-source ()
    335     "View the source of the current symbol."
    336     (interactive)
    337     (let* ((find-file-visit-truename t)
    338            (location
    339             (helpful--definition helpful--sym helpful--callable-p))
    340            (buffer (car location))
    341            (position (cadr location)))
    342       (pop-to-buffer buffer)
    343       (set-auto-mode t)
    344       (if position
    345           (progn
    346             (when (or (< position (point-min))
    347                       (> position (point-max)))
    348               (widen))
    349             (unless (= (point) position)
    350               (push-mark nil t))
    351             (goto-char position))
    352         (message "Unable to find location in file")))))
    353 
    354 ;; typing game
    355 (my-use-package typit my-enable-unessentials)
    356 
    357 ;; z-machine interpreter
    358 (my-use-package malyon my-enable-unessentials
    359   :straight
    360   (malyon :fork (:host github :repo "cjennings/malyon")))
    361 
    362 ;; guix
    363 (my-use-package guix my-enable-guix)
    364 
    365 ;; process manager
    366 (my-use-package proced
    367   :straight nil
    368   :init
    369   (defun proced-sort-rss (&optional arg)
    370     "Sort Proced buffer by time the command rss.
    371 Prefix ARG controls sort order, see `proced-sort-interactive'."
    372     (interactive (list (or current-prefix-arg 'no-arg)) proced-mode)
    373     (proced-sort-interactive 'rss arg))
    374   (general-def proced-mode-map "s r" #'proced-sort-rss)
    375 
    376   :config
    377   (setopt proced-auto-update-flag (if *is-windows* nil t)
    378           proced-goal-attribute nil
    379           proced-show-remote-processes t
    380           proced-enable-color-flag t
    381           proced-format 'custom)
    382 
    383   (add-to-list
    384    'proced-format-alist
    385    (if *is-mac*
    386        '(custom user pid ppid rss start (args comm))
    387      '(custom user pid ppid sess tree pcpu pmem rss start time state (args comm))))
    388   (setq proced-low-memory-usage-threshold 0.025
    389         proced-medium-memory-usage-threshold 0.125))
    390 
    391 ;; syncthing
    392 (my-use-package syncthing my-enable-unessentials
    393   :hook
    394   (syncthing-mode . enable-truncate-lines))
    395 
    396 ;; pinentry
    397 (my-use-package pinentry (and *is-linux* my-enable-unessentials)
    398   :hook
    399   (after-init . pinentry-start))
    400 
    401 ;; pass
    402 (my-use-package pass my-enable-unessentials
    403   :init
    404   (defvar my-pass-meta-fields '("url" "username" "email")
    405     "List of pass metadata fields for completion.")
    406 
    407   (defun my-password-store-append-field (entry field value)
    408     "Append metadata to ENTRY."
    409     (interactive
    410      (let* ((entry (password-store--completing-read t))
    411             (field (completing-read "Field: " my-pass-meta-fields))
    412             (value (read-string (format "%s: " field))))
    413        (list entry field value)))
    414     (let* ((old (string-trim-right (password-store--run-show entry)))
    415            (new (format "%s\n%s: %s" old field value))
    416            (code (process-file-shell-command
    417                   (format "printf '%%s' %s | %s insert --multiline --force %s"
    418                           (shell-quote-argument new)
    419                           password-store-executable
    420                           (shell-quote-argument entry)))))
    421       (unless (zerop code) (user-error "Update %s failed" entry))
    422       (message "Add %s: %s → %s" field value entry)))
    423 
    424   (defun my-pass-append-field ()
    425     "Append metadata to the entry at point."
    426     (interactive)
    427     (pass--with-closest-entry
    428         entry
    429       (call-interactively
    430        (lambda (field value)
    431          (interactive
    432           (let* ((f (completing-read "Field: " my-pass-meta-fields))
    433                  (v (read-string (format "%s: " f))))
    434             (list f v)))
    435          (my-password-store-append-field entry field value)))))
    436 
    437   (defun my-pass-copy-email ()
    438     "Copy email of entry at point to kill ring."
    439     (interactive)
    440     (pass-copy-field "email"))
    441 
    442   (general-def pass-mode-map
    443     "a" #'my-pass-append-field
    444     "E" #'pass-edit
    445     "e" #'my-pass-copy-email))
    446 
    447 ;; hyperbole
    448 (my-use-package hyperbole
    449   :commands hui-search-web
    450   :straight
    451   (hyperbole :type git :host github :repo "rswgnu/hyperbole")
    452   :delight
    453   (hywiki-mode nil hywiki)
    454   (hyperbole-mode nil hyperbole)
    455   :init
    456   (defun my-hkey-either-lazy (&optional arg)
    457     (interactive "P")
    458     (require 'hyperbole)
    459     (hyperbole-mode 1)
    460     (keymap-global-unset "M-RET")
    461     (hkey-either arg))
    462 
    463   ;; a very interesting part
    464   ;; not as trusty as original expand-region though
    465   (defvar my-hui-er/history nil)
    466 
    467   (defvar my-hui-er/original-point nil)
    468 
    469   (defun my-hui-er/snapshot ()
    470     "Return a copy of current hui-select state, or nil."
    471     (when hui-select-region
    472       (cons hui-select-previous
    473             (cons (car hui-select-region) (cdr hui-select-region)))))
    474 
    475   (defun my-hui-er/select (start end type)
    476     "Select START to END as thing TYPE and update hui-select state."
    477     (setq hui-select-previous type)
    478     (hui-select-set-region start end)
    479     (goto-char start)
    480     (set-mark end)
    481     (activate-mark))
    482 
    483   (defun my-hui-er/expand ()
    484     "Select the syntactical thing at point."
    485     (interactive)
    486     (require 'hyperbole)
    487     (when (not (use-region-p))
    488       (setq my-hui-er/history nil
    489             my-hui-er/original-point (point))
    490       (hui-select-reset))
    491     (let ((snap (my-hui-er/snapshot)))
    492       (hui-select-thing)
    493       (if (not (equal snap (my-hui-er/snapshot)))
    494           (progn
    495             (when snap (push snap my-hui-er/history))
    496             (goto-char (car hui-select-region))
    497             (set-mark (cdr hui-select-region))
    498             (activate-mark)
    499             (message "%s" hui-select-previous))
    500         (message "(hui-er): `%s' is the largest selectable region" hui-select-previous))))
    501 
    502   (defun my-hui-er/contract ()
    503     "Shrink region back to the previous selection."
    504     (interactive)
    505     (if-let* ((top (pop my-hui-er/history))
    506               (start (cadr top))
    507               (end (cddr top))
    508               (type (car top)))
    509         (progn (my-hui-er/select start end type)
    510                (message "%s" hui-select-previous))
    511       (my-hui-er/reset t)
    512       (message "(hui-er): `%s' is the smallest selectable region" hui-select-previous)))
    513 
    514   (defun my-hui-er/reset (&optional silent)
    515     "Restore original point, clear history."
    516     (interactive)
    517     (setq my-hui-er/history nil)
    518     (when my-hui-er/original-point
    519       (goto-char my-hui-er/original-point))
    520     (deactivate-mark)
    521     (hui-select-reset)
    522     (unless silent
    523       (message "(hui-er): selection reset")))
    524 
    525   (defun my-hui-er/quit-hack ()
    526     (when (and (region-active-p)
    527                (or (eq last-command #'my-hui-er/expand)
    528                    (eq last-command #'my-hui-er/contract)))
    529       (my-hui-er/reset t)))
    530   (advice-add #'keyboard-quit :before #'my-hui-er/quit-hack)
    531 
    532   (defvar-keymap my-hui-er-repeat-map
    533     :repeat t
    534     "e" #'my-hui-er/expand
    535     "c" #'my-hui-er/contract
    536     "r" #'my-hui-er/reset)
    537   (put 'my-hui-er/expand 'repeat-map 'my-hui-er-repeat-map)
    538   (put 'my-hui-er/contract 'repeat-map 'my-hui-er-repeat-map)
    539   (put 'my-hui-er/reset 'repeat-map 'my-hui-er-repeat-map)
    540 
    541   :config
    542   (setq hsys-org-enable-smart-keys :buttons)
    543   (hyperbole-mode 1)
    544   (repeat-mode 1))
    545 
    546 ;; elfeed
    547 (my-use-package elfeed my-enable-unessentials
    548   :init
    549   (defun my-elfeed-search-preview-other-window ()
    550     "Preview currently selected elfeed entry in another window."
    551     (interactive)
    552     (let ((display-buffer-alist '(("\\*elfeed-entry\\*"
    553                                    (display-buffer-reuse-window
    554                                     display-buffer-pop-up-window))))
    555           (current-prefix-arg '(4)))
    556       (call-interactively #'elfeed-search-show-entry)))
    557   :config
    558   (setq elfeed-curl-timeout 600)
    559   (setq-default elfeed-search-filter ""))
    560 
    561 ;; posframe
    562 (my-use-package posframe my-enable-unessentials)
    563 
    564 ;; translator
    565 (my-use-package gt my-enable-unessentials
    566   :config
    567   (setq gt-default-translator
    568         (gt-translator
    569          :engines (gt-youdao-dict-engine)
    570          :taker (gt-taker :text 'word :langs '(en zh))
    571          :render (gt-posframe-pin-render))))
    572 
    573 ;; thesaurus
    574 (my-use-package synosaurus my-enable-unessentials
    575   :init
    576   (defun my-synosaurus-lookup (arg)
    577     "Lookup word in thesaurus.
    578 With a prefix ARG replace the word under the cursor by a synonym."
    579     (interactive "P")
    580     (if arg
    581         (call-interactively #'synosaurus-choose-and-replace)
    582       (call-interactively #'synosaurus-lookup)))
    583   :config
    584   (setq synosaurus-prefix nil
    585         synosaurus-choose-method 'default))
    586 
    587 ;; dashboard
    588 (my-use-package dashboard my-enable-dashboard
    589   :autoload dashboard-setup-startup-hook
    590   :hook after-init
    591   :config
    592   (setq dashboard-banner-logo-title (format "%s" emacs-version)
    593         dashboard-banner-ascii
    594         "
    595  #####  #     # #     #    ####### #     #    #     #####   #####
    596 #     # ##    # #     #    #       ##   ##   # #   #     # #     #
    597 #       # #   # #     #    #       # # # #  #   #  #       #
    598 #  #### #  #  # #     #    #####   #  #  # #     # #        #####
    599 #     # #   # # #     #    #       #     # ####### #             #
    600 #     # #    ## #     #    #       #     # #     # #     # #     #
    601  #####  #     #  #####     ####### #     # #     #  #####   #####
    602 "
    603         dashboard-startup-banner 'ascii
    604         dashboard-footer-messages '("hello")
    605         dashboard-center-content t
    606         dashboard-navigation-cycle t
    607         dashboard-items '((recents . 8)
    608                           (bookmarks . 5)
    609                           (projects . 5))
    610         dashboard-item-shortcuts '((recents . "r") (bookmarks . "b") (projects . "p") (agenda . "a") (registers . "e")))
    611   (dashboard-setup-startup-hook))
    612 
    613 (provide 'init-package)
    614 ;;; init-package.el ends here