diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..253bcb7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily diff --git a/.github/workflows/activate.yml b/.github/workflows/activate.yml index c8bfe28..278e8dd 100644 --- a/.github/workflows/activate.yml +++ b/.github/workflows/activate.yml @@ -20,9 +20,10 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] emacs-version: - - 26.3 - 27.2 - 28.2 + - 29.4 + - 30.1 experimental: [false] include: - os: ubuntu-latest @@ -34,9 +35,12 @@ jobs: - os: windows-latest emacs-version: snapshot experimental: true + exclude: + - os: macos-latest + emacs-version: 27.2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: jcs090218/setup-emacs@master with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 81af0e6..a9a9e42 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,9 +20,10 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, windows-latest] emacs-version: - - 26.3 - 27.2 - 28.2 + - 29.4 + - 30.1 experimental: [false] include: - os: ubuntu-latest @@ -34,9 +35,12 @@ jobs: - os: windows-latest emacs-version: snapshot experimental: true + exclude: + - os: macos-latest + emacs-version: 27.2 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: jcs090218/setup-emacs@master with: diff --git a/.gitignore b/.gitignore index fd1e6a7..c7e292e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ dist/ # packaging *-autoloads.el *-pkg.el + +# OS genrated +.DS_Store \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index dc00223..a39abdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,135 +8,144 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## 1.9.0 (Unreleased) > Released N/A -- N/A +* fix: Make remove entry optional ([#480](../../pull/480)) +* Add dashboard icon face ([#483](../../pull/483)) +* Add customization option to hide the cursor on the dashboard ([#492](../../pull/492)) +* Add option to vertically center dashboard ([#318](../../pull/318) and [#493](../../pull/493)) +* fix: Adding animation to webp image ([#502](../../pull/502)) +* feat: Support for insert startupify list in order ([#506](../../pull/506)) +* Support for random banners ([#510](../../pull/510)) +* Support for random footer icon ([#519](../../pull/519)) +* feat: Parameters for file icon `height` and `v-adjust` ([#546](../../pull/546)) +* fix: Show `installed packages` instead of `loaded packages`. ([#570](../../pull/570)) ## 1.8.0 > Released Jul 26, 2023 -* Disable display-line-numbers-mode (#182) -* Mute no project removed message (#212) -* Align the file icons horizontally (#221) -* Fix package count when using package.el and straight.el simultaneously (#233) -* Fix fitler agenda by time (#232) -* Respect to custom items face instead of widget-button face (#273) -* Support for image transforms (#278) -* Allow moving point during `dashboard-mode-hook` (#227) -* Add truncate style for path (#279) -* Add agenda highlight headlines (#281) -* Fix mosue click error (#285) -* Add flag to refresh instead to kill the dashboard buffer (#290) -* Fix wrong recent files when first starting the dashboard buffer (#294) -* Add license file (#296) -* Fix icon for week agenda items (#300) -* Make `page-break-lines` as optional dependency (#315) -* Add feature to customize section headings (#316) -* Add gif support (#327) -* Fix init info reporting with wrong time (#330) -* Add new ASCII GNU Emacs logo, `banners/4.txt`. (#337) -* Add feature to sort agenda (#335) -* Update the CI process uasing Cask (#341) -* Add a changelog (#342) -* Make shortcut function name respect to shortcut id (#348) -* Add capability to custom align agenda widget (#350) -* Correct straight.el package count (#354) -* Lazy load modules specify in dashboard buffer (#359) -* Clean up zombie project for project.el (#363) -* Add capability to navigate each section (#365) -* Add functionality to delete items (#366) -* Fix issue dashboard-banners-directory resolves with two slashes ("dashboard//banners") (GNU only?) (#367) -* Add capability to remove item for agenda (#372) -* Add CI, activation test (#381) -* Fix dashboard not showing up in daemon mode (#382) -* Calculate truncate path length in pixel (#402) -* Center banner with properties and combine text with image (#407) -* Caculate line length in pixel width (#427) -* Add ascii option to dashboard-startup-banner (#436) -* Agenda tags format (#441) -* Make icon display predicate customizeable (#442) -* Add support for nerd-icons (#451) -* Add custom variables for icon's arguments (#461) +* Disable display-line-numbers-mode ([#182](../../pull/182)) +* Mute no project removed message ([#212](../../pull/212)) +* Align the file icons horizontally ([#221](../../pull/221)) +* Fix package count when using package.el and straight.el simultaneously ([#233](../../pull/233)) +* Fix fitler agenda by time ([#232](../../pull/232)) +* Respect to custom items face instead of widget-button face ([#273](../../pull/273)) +* Support for image transforms ([#278](../../pull/278)) +* Allow moving point during `dashboard-mode-hook` ([#227](../../pull/227)) +* Add truncate style for path ([#279](../../pull/279)) +* Add agenda highlight headlines ([#281](../../pull/281)) +* Fix mosue click error ([#285](../../pull/285)) +* Add flag to refresh instead to kill the dashboard buffer ([#290](../../pull/290)) +* Fix wrong recent files when first starting the dashboard buffer ([#294](../../pull/294)) +* Add license file ([#296](../../pull/296)) +* Fix icon for week agenda items ([#300](../../pull/300)) +* Make `page-break-lines` as optional dependency ([#315](../../pull/315)) +* Add feature to customize section headings ([#316](../../pull/316)) +* Add gif support ([#327](../../pull/327)) +* Fix init info reporting with wrong time ([#330](../../pull/330)) +* Add new ASCII GNU Emacs logo, `banners/4.txt`. ([#337](../../pull/337)) +* Add feature to sort agenda ([#335](../../pull/335)) +* Update the CI process uasing Cask ([#341](../../pull/341)) +* Add a changelog ([#342](../../pull/342)) +* Make shortcut function name respect to shortcut id ([#348](../../pull/348)) +* Add capability to custom align agenda widget ([#350](../../pull/350)) +* Correct straight.el package count ([#354](../../pull/354)) +* Lazy load modules specify in dashboard buffer ([#359](../../pull/359)) +* Clean up zombie project for project.el ([#363](../../pull/363)) +* Add capability to navigate each section ([#365](../../pull/365)) +* Add functionality to delete items ([#366](../../pull/366)) +* Fix issue dashboard-banners-directory resolves with two slashes ("dashboard//banners") (GNU only?) ([#367](../../pull/367)) +* Add capability to remove item for agenda ([#372](../../pull/372)) +* Add CI, activation test ([#381](../../pull/381)) +* Fix dashboard not showing up in daemon mode ([#382](../../pull/382)) +* Calculate truncate path length in pixel ([#402](../../pull/402)) +* Center banner with properties and combine text with image ([#407](../../pull/407)) +* Caculate line length in pixel width ([#427](../../pull/427)) +* Add ascii option to `dashboard-startup-banner` ([#436](../../pull/436)) +* Agenda tags format ([#441](../../pull/441)) +* Make icon display predicate customizeable ([#442](../../pull/442)) +* Add support for nerd-icons ([#451](../../pull/451)) +* Add custom variables for icon's arguments ([#461](../../pull/461)) ## 1.7.0 > Released Feb 21, 2020 * Update year in Copyright -* Fix CI and include emacs 27 (#218) +* Fix CI and include emacs 27 ([#218](../../pull/218)) * Disable package-lint checks for now -* Fix error when `dashboard--section-starts' is nil (#204) -* Smaller icon size for remote files (#211) -* Create a custom variable for dashboard-footer (#186) -* Replace defvar with defcustom, making customize work properly (#205) -* Disable undo history for dashboard buffer (#207) -* Avoid loading `all-the-icons` prematurely (#209) +* Fix error when `dashboard--section-starts' is nil ([#204](../../pull/204)) +* Smaller icon size for remote files ([#211](../../pull/211)) +* Create a custom variable for dashboard-footer ([#186](../../pull/186)) +* Replace defvar with defcustom, making customize work properly ([#205](../../pull/205)) +* Disable undo history for dashboard buffer ([#207](../../pull/207)) +* Avoid loading `all-the-icons` prematurely ([#209](../../pull/209)) * Get elisp-lint path using find -* Refactor: use all-the-icons-icon-for-dir (#198) +* Refactor: use `all-the-icons-icon-for-dir` ([#198](../../pull/198)) * Update Emacs 26 to 26.3 * Update elisp-lint package * Update circe to 2.1 -* Replace if with when (#200) -* Fix space after comma for phrase in dashboard-footer (#194) -* Fix face description typo (#189) -* Fix widget face bug. Fixed some linting issues (#177) -* Dashboard return button (#171) -* Fix icons for weekly agenda and add an icon final condition (#173) -* Added filtering by category for org mode (#167) -* Revert "Remove `(require 'seq)` (#165)" -* Remove `(require 'seq)` (#165) +* Replace if with when ([#200](../../pull/200)) +* Fix space after comma for phrase in dashboard-footer ([#194](../../pull/194)) +* Fix face description typo ([#189](../../pull/189)) +* Fix widget face bug. Fixed some linting issues ([#177](../../pull/177)) +* Dashboard return button ([#171](../../pull/171)) +* Fix icons for weekly agenda and add an icon final condition ([#173](../../pull/173)) +* Added filtering by category for org mode ([#167](../../pull/167)) +* Revert "Remove `(require 'seq)` ([#165](../../pull/165))" +* Remove `(require 'seq)` ([#165](../../pull/165)) * Restore seq-take properly -* Replace `seq-take` with its code (#161) -* Reduce initial requires (#162) +* Replace `seq-take` with its code ([#161](../../pull/161)) +* Reduce initial requires ([#162](../../pull/162)) * Update screenshot -* Vimish bindings (#139) +* Vimish bindings ([#139](../../pull/139)) ## 1.6.0 > Released Jun 07, 2019 -* Added foot note plus several enhancements and code improvements (#137) @romatthe -* Do not require all-the-icons for footer icon, use it only if it is available (#141) @JesusMtnez -* Properly use footer icon fallback text (#142) @JesusMtnez -* Add face: dashboard-footer (#145) @seagle0128 -* Clean up project list before loading it (#144) @seagle0128 -* Add the navigator below the banner with the customized buttons. (#143) @seagle0128 -* Support `straight.el` properly in `dashboard-init-info` (#147) @JesusMtnez -* Support multiple lines for the navigator. (#150) @JesusMtnez -* Add newline at the end of insert footer. (#149) @jcs090218 -* Update metadata in comments @JesusMtnez -* Drop 24.4 support, from now on checking from emacs 25.3 @JesusMtnez +* Added foot note plus several enhancements and code improvements ([#137](../../pull/137)) **@romatthe** +* Do not require all-the-icons for footer icon, use it only if it is available ([#141](../../pull/141)) **@JesusMtnez** +* Properly use footer icon fallback text ([#142](../../pull/142)) **@JesusMtnez** +* Add face: dashboard-footer ([#145](../../pull/145)) **@seagle0128** +* Clean up project list before loading it ([#144](../../pull/144)) **@seagle0128** +* Add the navigator below the banner with the customized buttons. ([#143](../../pull/143)) **@seagle0128** +* Support `straight.el` properly in `dashboard-init-info` ([#147](../../pull/147)) **@JesusMtnez** +* Support multiple lines for the navigator. ([#150](../../pull/150)) **@JesusMtnez** +* Add newline at the end of insert footer. ([#149](../../pull/149)) **@jcs090218** +* Update metadata in comments **@JesusMtnez** +* Drop 24.4 support, from now on checking from emacs 25.3 **@JesusMtnez** ## 1.5.0 > Released May 27, 2019 -* Support bookmark/agenda/register/project actions. (#131) -* Made heading icons a customizable variable (#135) +* Support bookmark/agenda/register/project actions. ([#131](../../pull/131)) +* Made heading icons a customizable variable ([#135](../../pull/135)) ## 1.4.0 > Released May 22, 2019 -* Update emacs versions used in CircleCI (#124) +* Update emacs versions used in CircleCI ([#124](../../pull/124)) - Emacs 25.3 - Emacs 26.2 - Emacs master -* Added heading and file icons (#123) -* Added new init info functionality (#126) +* Added heading and file icons ([#123](../../pull/123)) +* Added new init info functionality ([#126](../../pull/126)) ## 1.3.1 > Released Mar 20, 2019 -* No need to launch 'projectile-mode (#113) -* Use "Applications" group and give a proper description (#114) -* Fix error in `dashboard-next-section` (#118) -* Make line moving commands behave like `dired` (#117) +* No need to launch 'projectile-mode ([#113](../../pull/113)) +* Use "Applications" group and give a proper description ([#114](../../pull/114)) +* Fix error in `dashboard-next-section` ([#118](../../pull/118)) +* Make line moving commands behave like `dired` ([#117](../../pull/117)) ## 1.3.0 > Released Mar 03, 2019 -* New configuration `dashboard-center-content` to center content (#88) -* Handle the case where dashboard lines longer than win width (#101) -* Fix shortcut regression bug (#102) -* Fix opening org mode "links" (#106) -* Add shortcut indicators to sections (#103) -* Indicate when a section has no items (#107) -* Give section headings a face for easy visual scanning (#109) +* New configuration `dashboard-center-content` to center content ([#88](../../pull/88)) +* Handle the case where dashboard lines longer than win width ([#101](../../pull/101)) +* Fix shortcut regression bug ([#102](../../pull/102)) +* Fix opening org mode "links" ([#106](../../pull/106)) +* Add shortcut indicators to sections ([#103](../../pull/103)) +* Indicate when a section has no items ([#107](../../pull/107)) +* Give section headings a face for easy visual scanning ([#109](../../pull/109)) ## 1.2.5 > Released Feb 25, 2019 diff --git a/Eask b/Eask index ebac3d1..b49e48e 100644 --- a/Eask +++ b/Eask @@ -1,5 +1,7 @@ +;; -*- mode: eask; lexical-binding: t -*- + (package "dashboard" - "1.8.0snapshot" + "1.9.0snapshot" "A startup screen extracted from Spacemacs") (website-url "https://github.com/emacs-dashboard/emacs-dashboard") @@ -13,10 +15,14 @@ (source "gnu") (source "melpa") -(depends-on "emacs" "26.1") +(depends-on "emacs" "27.1") (development (depends-on "elisp-lint") (depends-on "page-break-lines")) (setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432 + +(add-hook 'eask-before-compile-hook + (lambda (&rest _) + (setq byte-compile-error-on-warn t))) diff --git a/README.org b/README.org index 1c179a6..60a7ac8 100644 --- a/README.org +++ b/README.org @@ -11,43 +11,57 @@ An extensible emacs startup screen showing you what's most important. * Features - 1. Displays an awesome Emacs banner! - 2. Recent files - 3. Bookmarks list - 4. Recent projects list (Depends on `projectile` or `project.el` package) - 5. Org mode agenda - 6. Register list - 7. Supports both [[https://github.com/domtronn/all-the-icons.el][all-the-icons]] and [[https://github.com/rainstormstudio/nerd-icons.el][nerd-icons]] +1. Displays an awesome Emacs banner! +2. Recent files +3. Bookmarks list +4. Recent projects list (Depends on `projectile` or `project.el` package) +5. Org mode agenda +6. Register list +7. Supports both [[https://github.com/domtronn/all-the-icons.el][all-the-icons]] and [[https://github.com/rainstormstudio/nerd-icons.el][nerd-icons]] * Screenshot [[./etc/screenshot.png]] * Dependencies -You will need the following packages which are all available on Melpa: +You will need the following packages which are all available on MELPA: -1. (optional) page-break-lines - [[https://github.com/purcell/page-break-lines]] -2. (optional) projectile - [[https://github.com/bbatsov/projectile]] -3. (optional) all-the-icons - [[https://github.com/domtronn/all-the-icons.el]] -4. (optional) nerd-icons - [[https://github.com/rainstormstudio/nerd-icons.el]] +1. [[https://github.com/purcell/page-break-lines][page-break-lines]] (optional) +2. [[https://github.com/bbatsov/projectile][projectile]] (optional) +3. [[https://github.com/domtronn/all-the-icons.el][all-the-icons]] (optional) +4. [[https://github.com/rainstormstudio/nerd-icons.el][nerd-icons]] (optional) * Usage #+BEGIN_SRC shell -M-x package-install RET dashboard + M-x package-install RET dashboard #+END_SRC ** Open the Dashboard You can set up the dashboard to open automatically at startup using =dashboard-setup-startup-hook=: - #+BEGIN_SRC elisp -(require 'dashboard) -(dashboard-setup-startup-hook) -;; Or if you use use-package -(use-package dashboard - :ensure t - :config - (dashboard-setup-startup-hook)) - #+END_SRC +#+BEGIN_SRC elisp + (require 'dashboard) + (dashboard-setup-startup-hook) +#+END_SRC + +Or with use-package: +#+BEGIN_SRC elisp + ;; use-package with package.el: + (use-package dashboard + :ensure t + :config + (dashboard-setup-startup-hook)) +#+END_SRC + +#+BEGIN_SRC elisp + ;; use-package with Elpaca: + (use-package dashboard + :elpaca t + :config + (add-hook 'elpaca-after-init-hook #'dashboard-insert-startupify-lists) + (add-hook 'elpaca-after-init-hook #'dashboard-initialize) + (dashboard-setup-startup-hook)) +#+END_SRC Alternatively, if you don't want the dashboard to open by default, you can use the interactive function =dashboard-open= to open it when you do want it. @@ -68,7 +82,7 @@ In addition to the above, configure =initial-buffer-choice= to show Dashboard in frames created with =emacsclient -c= as follows: #+BEGIN_SRC elisp -(setq initial-buffer-choice (lambda () (get-buffer-create "*dashboard*"))) + (setq initial-buffer-choice (lambda () (get-buffer-create dashboard-buffer-name))) #+END_SRC * Configuration @@ -76,72 +90,92 @@ Dashboard in frames created with =emacsclient -c= as follows: To update the banner or banner title #+BEGIN_SRC elisp -;; Set the title -(setq dashboard-banner-logo-title "Welcome to Emacs Dashboard") -;; Set the banner -(setq dashboard-startup-banner [VALUE]) -;; Value can be -;; - nil to display no banner -;; - 'official which displays the official emacs logo -;; - 'logo which displays an alternative emacs logo -;; - 1, 2 or 3 which displays one of the text banners -;; - "path/to/your/image.gif", "path/to/your/image.png" or "path/to/your/text.txt" which displays whatever gif/image/text you would prefer -;; - a cons of '("path/to/your/image.png" . "path/to/your/text.txt") - -;; Content is not centered by default. To center, set -(setq dashboard-center-content t) - -;; To disable shortcut "jump" indicators for each section, set -(setq dashboard-show-shortcuts nil) + ;; Set the title + (setq dashboard-banner-logo-title "Welcome to Emacs Dashboard") + ;; Set the banner + (setq dashboard-startup-banner [VALUE]) + ;; Value can be: + ;; - 'official which displays the official emacs logo. + ;; - 'logo which displays an alternative emacs logo. + ;; - an integer which displays one of the text banners + ;; (see dashboard-banners-directory files). + ;; - a string that specifies a path for a custom banner + ;; currently supported types are gif/image/text/xbm. + ;; - a cons of 2 strings which specifies the path of an image to use + ;; and other path of a text file to use if image isn't supported. + ;; (cons "path/to/image/file/image.png" "path/to/text/file/text.txt"). + ;; - a list that can display an random banner, + ;; supported values are: string (filepath), 'official, 'logo and integers. + + ;; Content is not centered by default. To center, set + (setq dashboard-center-content t) + ;; vertically center content + (setq dashboard-vertically-center-content t) + + ;; To disable shortcut "jump" indicators for each section, set + (setq dashboard-show-shortcuts nil) #+END_SRC -To customize which widgets are displayed, you can use the following snippet +To customize which items are displayed, you can use the following snippet #+BEGIN_SRC elisp -(setq dashboard-items '((recents . 5) - (bookmarks . 5) - (projects . 5) - (agenda . 5) - (registers . 5))) - #+END_SRC + (setq dashboard-items '((recents . 5) + (bookmarks . 5) + (projects . 5) + (agenda . 5) + (registers . 5))) +#+END_SRC This will add the recent files, bookmarks, projects, org-agenda and registers widgets to your dashboard each displaying 5 items. -To add your own custom widget is pretty easy, define your widget's callback function and add it to `dashboard-items` as such: -#+BEGIN_SRC elisp -(defun dashboard-insert-custom (list-size) - (insert "Custom text")) -(add-to-list 'dashboard-item-generators '(custom . dashboard-insert-custom)) -(add-to-list 'dashboard-items '(custom) t) - #+END_SRC +To customize which widgets to display in order (example: Banner, footer message ...): +#+begin_src emacs-lisp + (setq dashboard-startupify-list '(dashboard-insert-banner + dashboard-insert-newline + dashboard-insert-banner-title + dashboard-insert-newline + dashboard-insert-navigator + dashboard-insert-newline + dashboard-insert-init-info + dashboard-insert-items + dashboard-insert-newline + dashboard-insert-footer)) +#+end_src +See dashboard-startupify-list for all the widgets avalaibles. + +To enable cycle navigation between each section: +#+begin_src emacs-lisp + (setq dashboard-navigation-cycle t) +#+end_src -To add an icon to a custom widget, insert it with `dashboard-insert-heading` in your custom function. In this example, there is an icon but no shortcut. +To customize string format in shortcuts: +#+begin_src emacs-lisp + (setq dashboard-heading-shorcut-format " [%s]") +#+end_src + +To customize item shortcuts: #+BEGIN_SRC elisp -(defun dashboard-insert-custom (list-size) - (dashboard-insert-heading "News:" - nil - (all-the-icons-faicon "newspaper-o" - :height 1.2 - :v-adjust 0.0 - :face 'dashboard-heading)) - (insert "\n") - (insert " Custom text")) - #+END_SRC + (setq dashboard-item-shortcuts '((recents . "r") + (bookmarks . "m") + (projects . "p") + (agenda . "a") + (registers . "e"))) +#+END_SRC To modify the widget heading name: #+BEGIN_SRC elisp - (setq dashboard-item-names '(("Recent Files:" . "Recently opened files:") - ("Agenda for today:" . "Today's agenda:") - ("Agenda for the coming week:" . "Agenda:")) + (setq dashboard-item-names '(("Recent Files:" . "Recently opened files:") + ("Agenda for today:" . "Today's agenda:") + ("Agenda for the coming week:" . "Agenda:"))) #+END_SRC To use ~all-the-icons~ package: #+BEGIN_SRC emacs-lisp - (setq dashboard-icon-type 'all-the-icons) ;; use `all-the-icons' package + (setq dashboard-icon-type 'all-the-icons) ; use `all-the-icons' package #+END_SRC To use ~nerd-icons~ package: #+BEGIN_SRC emacs-lisp - (setq dashboard-display-icons-p t) ;; display icons on both GUI and terminal - (setq dashboard-icon-type 'nerd-icons) ;; use `nerd-icons' package + (setq dashboard-display-icons-p t) ; display icons on both GUI and terminal + (setq dashboard-icon-type 'nerd-icons) ; use `nerd-icons' package #+END_SRC To add icons to the widget headings and their items: @@ -152,88 +186,67 @@ To add icons to the widget headings and their items: To modify heading icons with another icon from all-the-icons octicons: #+BEGIN_SRC elisp - (dashboard-modify-heading-icons '((recents . "file-text") + (dashboard-modify-heading-icons '((recents . "file-text") (bookmarks . "book"))) #+END_SRC To modify heading icons with another icon from nerd-icons octicons: #+BEGIN_SRC emacs-lisp - (dashboard-modify-heading-icons '((recents . "nf-oct-file_text") + (dashboard-modify-heading-icons '((recents . "nf-oct-file_text") (bookmarks . "nf-oct-book"))) #+END_SRC -To show navigator below the banner: +To modify the icon height or vertical adjust: #+BEGIN_SRC emacs-lisp -(setq dashboard-set-navigator t) + (setq dashboard-icon-file-height 1.75) + (setq dashboard-icon-file-v-adjust -0.125) + (setq dashboard-heading-icon-height 1.75) + (setq dashboard-heading-icon-v-adjust -0.125) #+END_SRC To customize the buttons of the navigator like this: #+BEGIN_SRC emacs-lisp -;; Format: "(icon title help action face prefix suffix)" -(setq dashboard-navigator-buttons - `(;; line1 - ((,(all-the-icons-octicon "mark-github" :height 1.1 :v-adjust 0.0) - "Homepage" - "Browse homepage" - (lambda (&rest _) (browse-url "homepage"))) - ("★" "Star" "Show stars" (lambda (&rest _) (show-stars)) warning) - ("?" "" "?/h" #'show-help nil "<" ">")) - ;; line 2 - ((,(all-the-icons-faicon "linkedin" :height 1.1 :v-adjust 0.0) - "Linkedin" - "" - (lambda (&rest _) (browse-url "homepage"))) - ("⚑" nil "Show flags" (lambda (&rest _) (message "flag")) error)))) -#+END_SRC - -To show info about the packages loaded and the init time: -#+BEGIN_SRC elisp -(setq dashboard-set-init-info t) -#+END_SRC - -Also, the message can be customized like this: -#+BEGIN_SRC elisp -(setq dashboard-init-info "This is an init message!") -#+END_SRC - -A randomly selected footnote will be displayed. To disable it: -#+BEGIN_SRC elisp -(setq dashboard-set-footer nil) -#+END_SRC - -To customize it and customize its icon; - -#+BEGIN_SRC elisp -(setq dashboard-footer-messages '("Dashboard is pretty cool!")) -(setq dashboard-footer-icon (all-the-icons-octicon "dashboard" - :height 1.1 - :v-adjust -0.05 - :face 'font-lock-keyword-face)) + ;; Format: "(icon title help action face prefix suffix)" + (setq dashboard-navigator-buttons + `(;; line1 + ((,(all-the-icons-octicon "mark-github" :height 1.1 :v-adjust 0.0) + "Homepage" + "Browse homepage" + (lambda (&rest _) (browse-url "homepage"))) + ("★" "Star" "Show stars" (lambda (&rest _) (show-stars)) warning) + ("?" "" "?/h" #'show-help nil "<" ">")) + ;; line 2 + ((,(all-the-icons-faicon "linkedin" :height 1.1 :v-adjust 0.0) + "Linkedin" + "" + (lambda (&rest _) (browse-url "homepage"))) + ("⚑" nil "Show flags" (lambda (&rest _) (message "flag")) error)))) #+END_SRC To use it with [[https://github.com/ericdanan/counsel-projectile][counsel-projectile]] or [[https://github.com/bbatsov/persp-projectile][persp-projectile]] #+begin_src elisp -(setq dashboard-projects-switch-function 'counsel-projectile-switch-project-by-name) + (setq dashboard-projects-switch-function 'counsel-projectile-switch-project-by-name) #+end_src Or #+begin_src elisp -(setq dashboard-projects-switch-function 'projectile-persp-switch-project) + (setq dashboard-projects-switch-function 'projectile-persp-switch-project) #+end_src +Please check the [[./docs/variables-and-functions.org][complete list of variables and functions]]. ** Org mode’s agenda - To display today’s agenda items on the dashboard, add ~agenda~ to ~dashboard-items~: +To display today’s agenda items on the dashboard, add ~agenda~ to ~dashboard-items~: #+BEGIN_SRC elisp -(add-to-list 'dashboard-items '(agenda) t) + (add-to-list 'dashboard-items '(agenda) t) #+END_SRC To show agenda for the upcoming seven days set the variable ~dashboard-week-agenda~ to ~t~. #+BEGIN_SRC elisp -(setq dashboard-week-agenda t) + (setq dashboard-week-agenda t) #+END_SRC By default org-agenda entries are filter by time, only showing those @@ -241,7 +254,7 @@ task with ~DEADLINE~, ~SCHEDULE-TIME~ or ~TIMESTAMP~ . To show all agenda entrie (except ~DONE~) #+begin_src elisp -(setq dashboard-filter-agenda-entry 'dashboard-no-filter-agenda) + (setq dashboard-filter-agenda-entry 'dashboard-no-filter-agenda) #+end_src To have an extra filter, ~MATCH~ parameter is exposed as @@ -286,18 +299,22 @@ receives the tags directly from ~org-get-tags~. By default tags set the variable to ~ignore~: ~(setq dashboard-agenda-tags-format 'ignore)~ or to ~nil~. +** FAQ + +Check out our [[./docs/FAQ.org][Frequently Asked Questions]]. + ** Faces It is possible to customize Dashboard's appearance using the following faces: - ~dashboard-banner-logo-title~ :: - Highlights the banner title. + Highlights the banner title. - ~dashboard-text-banner~ :: - Highlights text banners. + Highlights text banners. - ~dashboard-heading~ :: - Highlights widget headings. + Highlights widget headings. - ~dashboard-items-face~ :: - Highlights widget items. + Highlights widget items. * Shortcuts @@ -320,11 +337,11 @@ You can use any of the following shortcuts inside Dashboard |----------------------------+------------------| * Wish List - 1. [X] Center content - 2. [X] More banner options - 3. [X] Customizing the list of widgets to display - 4. [X] Integrate Org-mode's agenda - 5. [ ] Listing Perspectives +1. [X] Center content +2. [X] More banner options +3. [X] Customizing the list of widgets to display +4. [X] Integrate Org-mode's agenda +5. [ ] Listing Perspectives * Contributions @@ -341,15 +358,49 @@ develop your changes and then install this as "development version". This is accomplished with the following steps: #+BEGIN_SRC shell -# In emacs: -M-x package-delete dashboard- RET + # In emacs: + M-x package-delete dashboard- RET +#+END_SRC + +#+BEGIN_SRC shell + make package + make install +#+END_SRC + +Or, you can use [[https://github.com/emacs-eask/cli][Eask]] to set up +the development environment, so there's no need to delete your local ~dashboard~. + +Install all dependencies and development dependencies: + +#+BEGIN_SRC shell + eask install-deps --dev #+END_SRC +To test the package's installation: + #+BEGIN_SRC shell -make package -make install + eask package + eask install #+END_SRC -** Prerequisites +To test compilation: + +#+BEGIN_SRC shell + eask compile +#+END_SRC + +🪧 The following steps are optional, but we recommend you follow these lint results! + +The built-in ~checkdoc~ linter: + +#+BEGIN_SRC shell + eask lint checkdoc +#+END_SRC + +The standard ~package~ linter: + +#+BEGIN_SRC shell + eask lint package +#+END_SRC - * [[https://github.com/emacs-eask/cli][Eask]] +/📝 P.S. For more information, find the Eask manual at https://emacs-eask.github.io/. diff --git a/dashboard-widgets.el b/dashboard-widgets.el index 2e2f8bc..6d34105 100644 --- a/dashboard-widgets.el +++ b/dashboard-widgets.el @@ -1,20 +1,11 @@ ;;; dashboard-widgets.el --- A startup screen extracted from Spacemacs -*- lexical-binding: t -*- -;; Copyright (c) 2016-2023 emacs-dashboard maintainers -;; -;; Author : Rakan Al-Hneiti -;; Maintainer : Jesús Martínez -;; Shen, Jen-Chieh -;; URL : https://github.com/emacs-dashboard/emacs-dashboard -;; +;; Copyright (c) 2016-2025 emacs-dashboard maintainers + ;; This file is not part of GNU Emacs. ;; ;;; License: GPLv3 ;; -;; Created: October 05, 2016 -;; Package-Version: 1.8.0 -;; Keywords: startup, screen, tools, dashboard -;; Package-Requires: ((emacs "26.1")) ;;; Commentary: ;; An extensible Emacs dashboard, with sections for @@ -24,8 +15,13 @@ (require 'cl-lib) (require 'image) +(require 'mule-util) +(require 'rect) (require 'subr-x) +;; +;;; Externals + ;; Compiler pacifier (declare-function all-the-icons-icon-for-dir "ext:all-the-icons.el") (declare-function all-the-icons-icon-for-file "ext:all-the-icons.el") @@ -42,7 +38,6 @@ (declare-function projectile-cleanup-known-projects "ext:projectile.el") (declare-function projectile-load-known-projects "ext:projectile.el") (declare-function projectile-mode "ext:projectile.el") -(declare-function projectile-relevant-known-projects "ext:projectile.el") ;;; project.el in Emacs 26 does not contain this function (declare-function project-known-project-roots "ext:project.el" nil t) (declare-function project-forget-zombie-projects "ext:project.el" nil t) @@ -60,13 +55,13 @@ (declare-function org-get-todo-face "ext:org.el") (declare-function org-get-todo-state "ext:org.el") (declare-function org-in-archived-heading-p "ext:org.el") +(declare-function org-link-display-format "ext:org.el") (declare-function org-map-entries "ext:org.el") (declare-function org-outline-level "ext:org.el") (declare-function org-release-buffers "ext:org.el") (declare-function org-time-string-to-time "ext:org.el") (declare-function org-today "ext:org.el") (declare-function recentf-cleanup "ext:recentf.el") -(defalias 'org-time-less-p 'time-less-p) (defvar org-level-faces) (defvar org-agenda-new-buffers) (defvar org-agenda-prefix-format) @@ -78,9 +73,21 @@ (declare-function string-pixel-width "subr-x.el") ; TODO: remove this after 29.1 (declare-function shr-string-pixel-width "shr.el") ; TODO: remove this after 29.1 -(defcustom dashboard-page-separator "\n\n" +(defvar truncate-string-ellipsis) +(declare-function truncate-string-ellipsis "mule-util.el") ; TODO: remove this after 28.1 +(defvar recentf-list nil) +(defvar dashboard-buffer-name) + +;; +;;; Customization + +(defcustom dashboard-page-separator "\n" "Separator to use between the different pages." - :type 'string + :type '(choice + (const :tag "Default" "\n") + (const :tag "Use Page indicator (requires page-break-lines)" + "\n\f\n") + (string :tag "Use Custom String")) :group 'dashboard) (defcustom dashboard-image-banner-max-height 0 @@ -103,6 +110,14 @@ preserved." :type 'integer :group 'dashboard) +(defcustom dashboard-image-extra-props nil + "Additional image attributes to assign to the image. +This could be useful for displaying images with transparency, +for example, by setting the `:mask' property to `heuristic'. +See `create-image' and Info node `(elisp)Image Descriptors'." + :type 'plist + :group 'dashboard) + (defcustom dashboard-set-heading-icons nil "When non nil, heading sections will have icons." :type 'boolean @@ -117,16 +132,15 @@ preserved." "When non nil, a navigator will be displayed under the banner." :type 'boolean :group 'dashboard) - -(defcustom dashboard-set-init-info t - "When non nil, init info will be displayed under the banner." - :type 'boolean - :group 'dashboard) +(make-obsolete-variable 'dashboard-set-navigator + 'dashboard-startupify-list "1.9.0") (defcustom dashboard-set-footer t "When non nil, a footer will be displayed at the bottom." :type 'boolean :group 'dashboard) +(make-obsolete-variable 'dashboard-set-footer + 'dashboard-startupify-list "1.9.0") (defcustom dashboard-footer-messages '("The one true editor, Emacs!" @@ -138,7 +152,7 @@ preserved." "While any text editor can save your files, only Emacs can save your soul" "I showed you my source code, pls respond") "A list of messages, one of which dashboard chooses to display." - :type 'list + :type '(repeat string) :group 'dashboard) (defcustom dashboard-icon-type (and (or dashboard-set-heading-icons @@ -173,10 +187,14 @@ The value can be one of: `all-the-icons', `nerd-icons'." (projects . "nf-oct-rocket") (registers . "nf-oct-database")))) "Association list for the icons of the heading sections. -Will be of the form `(list-type . icon-name-string)`. -If nil it is disabled. Possible values for list-type are: -`recents' `bookmarks' `projects' `agenda' `registers'" - :type '(repeat (alist :key-type symbol :value-type string)) +Will be of the form `(SECTION . ICON)`, where SECTION could be any dashboard +section, for example: `recents' `bookmarks' `projects' `agenda' `registers'. + +ICON could be the name of the icon belonging to `octicon' family +or (ICON-FUNCTION ICON-NAME), for example: \"nf-oct-file\" using +nerd-icons or (all-the-icons-faicon \"newspaper-o\") using all-the-icons." + :type '(alist :key-type symbol + :value-type (choice string (cons function string))) :group 'dashboard) (defcustom dashboard-heading-icon-height 1.2 @@ -189,6 +207,16 @@ If nil it is disabled. Possible values for list-type are: :type 'float :group 'dashboard) +(defcustom dashboard-icon-file-height 1.0 + "The height of the file icons." + :type 'float + :group 'dashboard) + +(defcustom dashboard-icon-file-v-adjust -0.05 + "The v-adjust of the file icons." + :type 'float + :group 'dashboard) + (defcustom dashboard-agenda-item-icon (pcase dashboard-icon-type ('all-the-icons (all-the-icons-octicon "primitive-dot" :height 1.0 :v-adjust 0.01)) @@ -197,6 +225,11 @@ If nil it is disabled. Possible values for list-type are: :type 'string :group 'dashboard) +(defcustom dashboard-agenda-action 'dashboard-agenda--visit-file-other-window + "Function to call when dashboard make an action over agenda item." + :type 'function + :group 'dashboard) + (defcustom dashboard-remote-path-icon (pcase dashboard-icon-type ('all-the-icons (all-the-icons-octicon "radio-tower" :height 1.0 :v-adjust 0.01)) @@ -240,38 +273,61 @@ The format is: `icon title help action face prefix suffix`. Example: `((\"☆\" \"Star\" \"Show stars\" (lambda (&rest _) (show-stars)) warning \"[\" \"]\"))" - :type '(repeat (repeat (list string string string function symbol string string))) + :type '(repeat (repeat (list string + string + string + function + (choice face + (repeat :tag "Anonymous face" sexp)) + (choice string + (const nil)) + (choice string + (const nil))))) :group 'dashboard) -(defcustom dashboard-init-info - (lambda () - (let ((package-count 0) (time (emacs-init-time))) - (when (bound-and-true-p package-alist) - (setq package-count (length package-activated-list))) - (when (boundp 'straight--profile-cache) - (setq package-count (+ (hash-table-count straight--profile-cache) package-count))) - (when (fboundp 'elpaca--queued) - (setq time (format "%f seconds" (float-time (time-subtract elpaca-after-init-time - before-init-time)))) - (setq package-count (length (elpaca--queued)))) - (if (zerop package-count) - (format "Emacs started in %s" time) - (format "%d packages loaded in %s" package-count time)))) - "Init info with packages loaded and init time." - :type '(function string) +(defcustom dashboard-init-info #'dashboard-init--info + "Custom function that must return a string to place instead of init-info." + :type 'function :group 'dashboard) -(defcustom dashboard-footer - (nth (random (1- (1+ (length dashboard-footer-messages)))) dashboard-footer-messages) - "A footer with some short message." - :type 'string - :group 'dashboard) +(defun dashboard-init--time () + "Return Emacs starting time in string including seconds ending." + (if (fboundp 'elpaca--queued) + (format "%s seconds" + (float-time (time-subtract elpaca-after-init-time + before-init-time))) + (emacs-init-time))) + +(defun dashboard-init--packages-count () + "Get the intalled package count depending on package manager. +Supported package managers are: package.el, straight.el and elpaca.el." + (let* ((package-count (if (bound-and-true-p package-alist) + (length package-activated-list) + 0)) + (straight-count (if (boundp 'straight--profile-cache) + (hash-table-count straight--profile-cache) + 0)) + (elpaca-count (if (fboundp 'elpaca--queued) + (length (elpaca--queued)) + 0))) + (+ package-count straight-count elpaca-count))) + + +(defun dashboard-init--info () + "Format init message. +Use `dashboard-init--time' and `dashboard-init--package-count' to generate +init message." + (let ((init-time (dashboard-init--time)) + (packages-count (dashboard-init--packages-count))) + (if (zerop packages-count) + (format "Emacs started in %s" init-time) + (format "%d packages installed. Emacs started in %s." + packages-count init-time)))) (defcustom dashboard-display-icons-p #'display-graphic-p "Predicate to determine whether dashboard should show icons. -Can be nil to not show icons and any truthy value to show them. When set -to a function the result of the function will be interpreted as the -predicate value." +Can be nil to not show icons and any truthy value to show them. When set to a +function the result of the function will be interpreted as the predicate value." :type '(choice (function :tag "Predicate function") (boolean :tag "Predicate value")) :group 'dashboard) @@ -316,7 +372,7 @@ ARGS should be a plist containing `:height', `:v-adjust', or `:face' properties. ('nerd-icons (apply #'nerd-icons-icon-for-file file args))))) (defun dashboard-octicon (name &rest args) - "Get the formatted octicon. + "Get the formatted octicon by NAME. ARGS should be a plist containing `:height', `:v-adjust', or `:face' properties." (dashboard-replace-displayable (pcase dashboard-icon-type @@ -330,33 +386,50 @@ ARGS should be a plist containing `:height', `:v-adjust', or `:face' properties. (all-the-icons-fileicon "emacs" :height 1.1 :v-adjust -0.05 - :face 'font-lock-keyword-face)) + :face 'dashboard-footer-icon-face)) ('nerd-icons (nerd-icons-sucicon "nf-custom-emacs" :height 1.1 :v-adjust -0.05 - :face 'font-lock-keyword-face))) - (propertize ">" 'face 'dashboard-footer)) - "Footer's icon." + :face 'dashboard-footer-icon-face))) + (propertize ">" 'face 'dashboard-footer-icon-face)) + "Footer's icon. +It can be a string or a string list for display random icons." + :type '(choice string + (repeat string)) + :group 'dashboard) + +(defcustom dashboard-heading-shorcut-format " (%s)" + "String for display key used in headings." :type 'string :group 'dashboard) (defcustom dashboard-startup-banner 'official - "Specify the startup banner. -Default value is `official', it displays the Emacs logo. `logo' displays Emacs -alternative logo. If set to `ascii', the value of `dashboard-banner-ascii' -will be used as the banner. An integer value is the index of text banner. -A string value must be a path to a .PNG or .TXT file. If the value is -nil then no banner is displayed." - :type '(choice (const :tag "no banner" nil) - (const :tag "offical" official) + "Specify the banner type to use. +Value can be + - \\='official displays the official Emacs logo. + - \\='logo displays an alternative Emacs logo. + - an integer which displays one of the text banners. + - a string that specifies the path of an custom banner + supported files types are gif/image/text/xbm. + - a cons of 2 strings which specifies the path of an image to use + and other path of a text file to use if image isn't supported. + - a list that can display an random banner, supported values are: + string (filepath), \\='official, \\='logo and integers." + :type '(choice (const :tag "official" official) (const :tag "logo" logo) (const :tag "ascii" ascii) (integer :tag "index of a text banner") - (string :tag "a path to an image or text banner") - (cons :tag "an image and text banner" + (string :tag "path to an image or text banner") + (cons :tag "image and text banner" (string :tag "image banner path") - (string :tag "text banner path"))) + (string :tag "text banner path")) + (repeat :tag "random banners" + (choice (string :tag "a path to an image or text banner") + (const :tag "official" official) + (const :tag "logo" logo) + (const :tag "ascii" ascii) + (integer :tag "index of a text banner")))) :group 'dashboard) (defcustom dashboard-item-generators @@ -369,10 +442,10 @@ nil then no banner is displayed." Will be of the form `(list-type . list-function)'. Possible values for list-type are: `recents', `bookmarks', `projects', `agenda' ,`registers'." - :type '(repeat (alist :key-type symbol :value-type function)) + :type '(alist :key-type symbol :value-type function) :group 'dashboard) -(defcustom dashboard-projects-backend 'projectile +(defcustom dashboard-projects-backend 'project-el "The package that supplies the list of recent projects. With the value `projectile', the projects widget uses the package projectile (available in MELPA). With the value `project-el', @@ -385,6 +458,11 @@ installed." (const :tag "Use project.el" project-el)) :group 'dashboard) +(defcustom dashboard-remove-missing-entry nil + "If non-nil, try to remove missing entries." + :type 'boolean + :group 'dashboard) + (defcustom dashboard-items '((recents . 5) (bookmarks . 5) @@ -393,7 +471,9 @@ installed." Will be of the form `(list-type . list-size)'. If nil it is disabled. Possible values for list-type are: `recents' `bookmarks' `projects' `agenda' `registers'." - :type '(repeat (alist :key-type symbol :value-type integer)) + :type '(repeat (choice + symbol + (cons symbol integer))) :group 'dashboard) (defcustom dashboard-item-shortcuts @@ -405,8 +485,8 @@ If nil it is disabled. Possible values for list-type are: "Association list of items and their corresponding shortcuts. Will be of the form `(list-type . keys)' as understood by `(kbd keys)'. If nil, shortcuts are disabled. If an entry's value is nil, that item's -shortcut is disbaled. See `dashboard-items' for possible values of list-type.'" - :type '(repeat (alist :key-type symbol :value-type string)) +shortcut is disabled. See `dashboard-items' for possible values of list-type.'" + :type '(alist :key-type symbol :value-type string) :group 'dashboard) (defcustom dashboard-item-names nil @@ -414,8 +494,12 @@ shortcut is disbaled. See `dashboard-items' for possible values of list-type.'" When an item is nil or not present, the default name is used. Will be of the form `(default-name . new-name)'." :type '(alist :key-type string :value-type string) - :options '("Recent Files:" "Bookmarks:" "Agenda for today:" - "Agenda for the coming week:" "Registers:" "Projects:") + :options '("Recent Files:" + "Bookmarks:" + "Agenda for today:" + "Agenda for the coming week:" + "Registers:" + "Projects:") :group 'dashboard) (defcustom dashboard-items-default-length 20 @@ -438,18 +522,9 @@ Set to nil for unbounded." :type 'integer :group 'dashboard) -(defcustom dashboard-path-shorten-string "..." - "String the that displays in the center of the path." - :type 'string - :group 'dashboard) - -(defvar recentf-list nil) - -(defvar dashboard-buffer-name) - -;; -;; Faces ;; +;;; Faces + (defface dashboard-text-banner '((t (:inherit font-lock-keyword-face))) "Face used for text banners." @@ -480,9 +555,14 @@ Set to nil for unbounded." "Face used for no items." :group 'dashboard) -(defface dashboard-footer +(defface dashboard-footer-face '((t (:inherit font-lock-doc-face))) - "Face used for widget headings." + "Face used for footer text." + :group 'dashboard) + +(defface dashboard-footer-icon-face + '((t (:inherit dashboard-footer-face))) + "Face used for icon in footer." :group 'dashboard) (define-obsolete-face-alias @@ -493,8 +573,8 @@ Set to nil for unbounded." 'dashboard-heading-face 'dashboard-heading "1.2.6") ;; -;; Util -;; +;;; Util + (defmacro dashboard-mute-apply (&rest body) "Execute BODY without message." (declare (indent 0) (debug t)) @@ -522,8 +602,8 @@ Set to nil for unbounded." (if (zerop (% len width)) 0 1)))) ; add one if exceeed ;; -;; Generic widget helpers -;; +;;; Widget helpers + (defun dashboard-subseq (seq end) "Return the subsequence of SEQ from 0 to END." (let ((len (length seq))) (butlast seq (- len (min len end))))) @@ -543,6 +623,9 @@ Set to nil for unbounded." search-label &optional no-next-line) "Insert a shortcut SHORTCUT-CHAR for a given SEARCH-LABEL. + +SHORTCUT-ID is the section identifier. + Optionally, provide NO-NEXT-LINE to move the cursor forward a line." (let* (;; Ensure punctuation and upper case in search string is not ;; used to construct the `defun' @@ -552,7 +635,8 @@ Optionally, provide NO-NEXT-LINE to move the cursor forward a line." `(progn (eval-when-compile (defvar dashboard-mode-map)) (defun ,sym nil - ,(concat "Jump to " name ". This code is dynamically generated in `dashboard-insert-shortcut'.") + ,(concat "Jump to " name ". +This code is dynamically generated in `dashboard-insert-shortcut'.") (interactive) (unless (search-forward ,search-label (point-max) t) (search-backward ,search-label (point-min) t)) @@ -577,32 +661,18 @@ If MESSAGEBUF is not nil then MSG is also written in message buffer." "Insert a page break line in dashboard buffer." (dashboard-append dashboard-page-separator)) +(defun dashboard-insert-newline (&optional times) + "When called without an argument, insert a newline. +When called with TIMES return a function that insert TIMES number of newlines." + (if times + (lambda () + (insert (make-string times (string-to-char "\n") t))) + (insert "\n"))) + (defun dashboard-insert-heading (heading &optional shortcut icon) "Insert a widget HEADING in dashboard buffer, adding SHORTCUT, ICON if provided." - (when (and (dashboard-display-icons-p) dashboard-set-heading-icons) - (let ((args `( :height ,dashboard-heading-icon-height - :v-adjust ,dashboard-heading-icon-v-adjust - :face dashboard-heading))) - (insert - (pcase heading - ("Recent Files:" - (apply #'dashboard-octicon (cdr (assoc 'recents dashboard-heading-icons)) args)) - ("Bookmarks:" - (apply #'dashboard-octicon (cdr (assoc 'bookmarks dashboard-heading-icons)) args)) - ((or "Agenda for today:" - "Agenda for the coming week:") - (apply #'dashboard-octicon (cdr (assoc 'agenda dashboard-heading-icons)) args)) - ("Registers:" - (apply #'dashboard-octicon (cdr (assoc 'registers dashboard-heading-icons)) args)) - ("Projects:" - (apply #'dashboard-octicon (cdr (assoc 'projects dashboard-heading-icons)) args)) - ("List Directories:" - (apply #'dashboard-octicon (cdr (assoc 'ls-directories dashboard-heading-icons)) args)) - ("List Files:" - (apply #'dashboard-octicon (cdr (assoc 'ls-files dashboard-heading-icons)) args)) - (_ - (if (null icon) " " icon)))) - (insert " "))) + (when (and (dashboard-display-icons-p) dashboard-set-heading-icons icon) + (insert icon " ")) (insert (propertize heading 'face 'dashboard-heading)) @@ -615,10 +685,10 @@ If MESSAGEBUF is not nil then MSG is also written in message buffer." (let ((ov (make-overlay (- (point) (length heading)) (point) nil t))) (overlay-put ov 'display (or (cdr (assoc heading dashboard-item-names)) heading)) (overlay-put ov 'face 'dashboard-heading)) - (when shortcut (insert (format " (%s)" shortcut)))) + (when shortcut (insert (format dashboard-heading-shorcut-format shortcut)))) -(defun dashboard-center-text (start end) - "Center the text between START and END." +(defun dashboard--find-max-width (start end) + "Return the max width within the region START and END." (save-excursion (goto-char start) (let ((width 0)) @@ -627,8 +697,13 @@ If MESSAGEBUF is not nil then MSG is also written in message buffer." (line-length (dashboard-str-len line-str))) (setq width (max width line-length))) (forward-line 1)) - (let ((prefix (propertize " " 'display `(space . (:align-to (- center ,(/ width 2))))))) - (add-text-properties start end `(line-prefix ,prefix indent-prefix ,prefix)))))) + width))) + +(defun dashboard-center-text (start end) + "Center the text between START and END." + (let* ((width (dashboard--find-max-width start end)) + (prefix (propertize " " 'display `(space . (:align-to (- center ,(/ (float width) 2))))))) + (add-text-properties start end `(line-prefix ,prefix indent-prefix ,prefix)))) (defun dashboard-insert-center (&rest strings) "Insert STRINGS in the center of the buffer." @@ -637,8 +712,7 @@ If MESSAGEBUF is not nil then MSG is also written in message buffer." (dashboard-center-text start (point)))) ;; -;; BANNER -;; +;;; Banner (defun dashboard-get-banner-path (index) "Return the full path to banner with index INDEX." @@ -651,10 +725,9 @@ If MESSAGEBUF is not nil then MSG is also written in message buffer." ;; - That function will only look at filenames, this one will inspect the file data itself. (and (file-exists-p img) (ignore-errors (image-type-available-p (image-type img))))) -(defun dashboard-choose-banner () - "Return a plist specifying the chosen banner based on `dashboard-startup-banner'." - (pcase dashboard-startup-banner - ('nil nil) +(defun dashboard-choose-banner (banner) + "Return a plist specifying the chosen banner based on BANNER." + (pcase banner ('official (append (when (image-type-available-p 'png) (list :image dashboard-banner-official-png)) @@ -666,24 +739,29 @@ If MESSAGEBUF is not nil then MSG is also written in message buffer." ('ascii (append (list :text dashboard-banner-ascii))) ((pred integerp) - (list :text (dashboard-get-banner-path dashboard-startup-banner))) + (list :text (dashboard-get-banner-path banner))) ((pred stringp) - (pcase dashboard-startup-banner + (pcase banner ((pred (lambda (f) (not (file-exists-p f)))) - (message "could not find banner %s, use default instead" dashboard-startup-banner) + (message "could not find banner %s, use default instead" banner) (list :text (dashboard-get-banner-path 1))) ((pred (string-suffix-p ".txt")) - (list :text (if (file-exists-p dashboard-startup-banner) - dashboard-startup-banner - (message "could not find banner %s, use default instead" dashboard-startup-banner) + (list :text (if (file-exists-p banner) + banner + (message "could not find banner %s, use default instead" banner) (dashboard-get-banner-path 1)))) ((pred dashboard--image-supported-p) - (list :image dashboard-startup-banner + (list :image banner :text (dashboard-get-banner-path 1))) (_ - (message "unsupported file type %s" (file-name-nondirectory dashboard-startup-banner)) + (message "unsupported file type %s" (file-name-nondirectory banner)) (list :text (dashboard-get-banner-path 1))))) - (`(,img . ,txt) + ((and + (pred listp) + (pred (lambda (c) + (and (not (proper-list-p c)) + (not (null c))))) + `(,img . ,txt)) (list :image (if (dashboard--image-supported-p img) img (message "could not find banner %s, use default instead" img) @@ -692,57 +770,72 @@ If MESSAGEBUF is not nil then MSG is also written in message buffer." txt (message "could not find banner %s, use default instead" txt) (dashboard-get-banner-path 1)))) + ((and + (pred proper-list-p) + (pred (lambda (l) (not (null l))))) + + (let* ((max (length banner)) + (choose (nth (random max) banner))) + (dashboard-choose-banner choose))) (_ - (message "unsupported banner config %s" dashboard-startup-banner)))) + (user-error "Unsupported banner type: `%s'" banner) + nil))) -(defun dashboard--type-is-gif-p (image-path) - "Return if image is a gif. +(defun dashboard--image-animated-p (image-path) + "Return if image is a gif or webp. String -> bool. Argument IMAGE-PATH path to the image." - (eq 'gif (image-type image-path))) + (memq (image-type image-path) '(gif webp))) + +(defun dashboard--type-is-xbm-p (image-path) + "Return if image is a xbm. +String -> bool. +Argument IMAGE-PATH path to the image." + (eq 'xbm (image-type image-path))) (defun dashboard-insert-banner () "Insert the banner at the top of the dashboard." (goto-char (point-max)) - (when-let (banner (dashboard-choose-banner)) + (when-let* ((banner (dashboard-choose-banner dashboard-startup-banner))) (insert "\n") + (when (display-graphic-p) (insert "\n")) (let ((start (point)) buffer-read-only text-width image-spec) - (insert "\n") ;; If specified, insert a text banner. - (when-let (txt (plist-get banner :text)) - (if (eq dashboard-startup-banner 'ascii) - (save-excursion (insert txt)) - (insert-file-contents txt)) - (put-text-property (point) (point-max) 'face 'dashboard-text-banner) - (setq text-width 0) - (while (not (eobp)) - (let ((line-length (- (line-end-position) (line-beginning-position)))) - (if (< text-width line-length) - (setq text-width line-length))) - (forward-line 1))) + (when-let* ((txt (plist-get banner :text))) + (save-excursion + (if (file-exists-p txt) + (insert-file-contents txt) + (insert txt))) + (put-text-property start (point-max) 'face 'dashboard-text-banner) + (setq text-width (dashboard--find-max-width start (point-max))) + (goto-char (point-max))) ;; If specified, insert an image banner. When displayed in a graphical frame, this will ;; replace the text banner. - (when-let (img (plist-get banner :image)) - (let ((size-props + (when-let* ((img (plist-get banner :image))) + (let ((img-props (append (when (> dashboard-image-banner-max-width 0) (list :max-width dashboard-image-banner-max-width)) (when (> dashboard-image-banner-max-height 0) - (list :max-height dashboard-image-banner-max-height))))) + (list :max-height dashboard-image-banner-max-height)) + dashboard-image-extra-props))) (setq image-spec - (cond ((dashboard--type-is-gif-p img) + (cond ((dashboard--image-animated-p img) + (create-image img)) + ((dashboard--type-is-xbm-p img) (create-image img)) ((image-type-available-p 'imagemagick) - (apply 'create-image img 'imagemagick nil size-props)) + (apply 'create-image img 'imagemagick nil img-props)) (t (apply 'create-image img nil nil (when (and (fboundp 'image-transforms-p) (memq 'scale (funcall 'image-transforms-p))) - size-props)))))) + img-props)))))) (add-text-properties start (point) `(display ,image-spec)) - (when (dashboard--type-is-gif-p img) (image-animate image-spec 0 t))) + (when (ignore-errors (image-multi-frame-p image-spec)) (image-animate image-spec 0 t))) + ;; Finally, center the banner (if any). (when-let* ((text-align-spec `(space . (:align-to (- center ,(/ text-width 2))))) (image-align-spec `(space . (:align-to (- center (0.5 . ,image-spec))))) @@ -761,28 +854,32 @@ Argument IMAGE-PATH path to the image." (t nil))) (prefix (propertize " " 'display prop))) (add-text-properties start (point) `(line-prefix ,prefix wrap-prefix ,prefix))) - (insert "\n\n") - (add-text-properties start (point) '(cursor-intangible t inhibit-isearch t)))) + (insert "\n") + (add-text-properties start (point) '(cursor-intangible t inhibit-isearch t))))) + +(defun dashboard-insert-banner-title () + "Insert `dashboard-banner-logo-title' if it's non-nil." (when dashboard-banner-logo-title (dashboard-insert-center (propertize dashboard-banner-logo-title 'face 'dashboard-banner-logo-title)) - (insert "\n\n")) - (dashboard-insert-navigator) - (dashboard-insert-init-info)) + (insert "\n"))) ;; -;; INIT INFO -;; +;;; Initialize info (defun dashboard-insert-init-info () - "Insert init info when `dashboard-set-init-info' is t." - (when dashboard-set-init-info - (let ((init-info (if (functionp dashboard-init-info) - (funcall dashboard-init-info) - dashboard-init-info))) - (dashboard-insert-center (propertize init-info 'face 'font-lock-comment-face))))) + "Insert init info." + (let ((init-info (cond ((stringp dashboard-init-info) + dashboard-init-info) + ((functionp dashboard-init-info) + (funcall dashboard-init-info)) + (t + (user-error "Unknown init info type (%s): %s" + (type-of dashboard-init-info) dashboard-init-info))))) + (dashboard-insert-center + (propertize init-info 'face 'font-lock-comment-face)))) (defun dashboard-insert-navigator () "Insert Navigator of the dashboard." - (when (and dashboard-set-navigator dashboard-navigator-buttons) + (when dashboard-navigator-buttons (dolist (line dashboard-navigator-buttons) (dolist (btn line) (let* ((icon (car btn)) @@ -803,7 +900,8 @@ Argument IMAGE-PATH path to the image." (when (and icon title (not (string-equal icon "")) (not (string-equal title ""))) - (propertize " " 'face 'variable-pitch)) + (propertize " " 'face `(:inherit (variable-pitch + ,face)))) (when title (propertize title 'face face))) :help-echo help :action action @@ -814,18 +912,22 @@ Argument IMAGE-PATH path to the image." :format "%[%t%]") (insert " "))) (dashboard-center-text (line-beginning-position) (line-end-position)) - (insert "\n")) - (insert "\n"))) + (insert "\n")))) (defmacro dashboard-insert-section (section-name list list-size shortcut-id shortcut-char action &rest widget-params) "Add a section with SECTION-NAME and LIST of LIST-SIZE items to the dashboard. +SHORTCUT-ID is the section identifier. SHORTCUT-CHAR is the keyboard shortcut used to access the section. ACTION is theaction taken when the user activates the widget button. WIDGET-PARAMS are passed to the \"widget-create\" function." `(progn (dashboard-insert-heading ,section-name - (if (and ,list ,shortcut-char dashboard-show-shortcuts) ,shortcut-char)) + (when (and ,list + ,shortcut-char + dashboard-show-shortcuts) + ,shortcut-char) + (dashboard-heading-icon ,shortcut-id)) (if ,list (when (and (dashboard-insert-section-list ,section-name @@ -836,9 +938,22 @@ WIDGET-PARAMS are passed to the \"widget-create\" function." (dashboard-insert-shortcut ,shortcut-id ,shortcut-char ,section-name)) (insert (propertize "\n --- No items ---" 'face 'dashboard-no-items-face))))) +(defun dashboard-heading-icon (section) + "Get the icon for SECTION from `dashboard-heading-icons'. +Return a space if icon is not found." + (let ((args (list :height dashboard-heading-icon-height + :v-adjust dashboard-heading-icon-v-adjust + :face 'dashboard-heading)) + (icon (assoc section dashboard-heading-icons))) + (if icon (cond + ((stringp (cdr icon)) (apply #'dashboard-octicon (cdr icon) args)) + ((listp (cdr icon)) (apply (cadr icon) (caddr icon) args)) + (t (error "Bad value %s in `dashboard-heading-icons'" icon))) + " "))) + ;; -;; Section list -;; +;;; Section list + (defmacro dashboard-insert-section-list (section-name list action &rest rest) "Insert into SECTION-NAME a LIST of items, expanding ACTION and passing REST to widget creation." @@ -846,14 +961,17 @@ to widget creation." (mapc (lambda (el) (let ((tag ,@rest)) - (insert "\n ") + (insert "\n") + (insert (spaces-string (or standard-indent tab-width 4))) (when (and (dashboard-display-icons-p) dashboard-set-file-icons) (let* ((path (car (last (split-string ,@rest " - ")))) (icon (if (and (not (file-remote-p path)) (file-directory-p path)) - (dashboard-icon-for-dir path nil "") + (dashboard-icon-for-dir path + :height dashboard-icon-file-height + :v-adjust dashboard-icon-file-v-adjust) (cond ((or (string-equal ,section-name "Agenda for today:") (string-equal ,section-name "Agenda for the coming week:")) @@ -861,7 +979,8 @@ to widget creation." ((file-remote-p path) dashboard-remote-path-icon) (t (dashboard-icon-for-file (file-name-nondirectory path) - :v-adjust -0.05)))))) + :height dashboard-icon-file-height + :v-adjust dashboard-icon-file-v-adjust)))))) (setq tag (concat icon " " ,@rest)))) (widget-create 'item @@ -874,27 +993,35 @@ to widget creation." :format "%[%t%]"))) ,list))) -;; Footer +;; +;;; Footer + (defun dashboard-random-footer () "Return a random footer from `dashboard-footer-messages'." (nth (random (length dashboard-footer-messages)) dashboard-footer-messages)) +(defun dashboard-footer-icon () + "Return footer icon or a random icon if `dashboard-footer-messages' is a list." + (if (and (not (null dashboard-footer-icon)) + (listp dashboard-footer-icon)) + (dashboard-replace-displayable + (nth (random (length dashboard-footer-icon)) + dashboard-footer-icon)) + (dashboard-replace-displayable dashboard-footer-icon))) + (defun dashboard-insert-footer () "Insert footer of dashboard." - (when-let ((footer (and dashboard-set-footer (dashboard-random-footer)))) - (insert "\n") + (when-let* ((footer (dashboard-random-footer)) + (footer-icon (dashboard-footer-icon))) (dashboard-insert-center - (dashboard-replace-displayable dashboard-footer-icon) - (if (and (stringp dashboard-footer-icon) - (not (string-empty-p dashboard-footer-icon))) - " " - "") - (propertize footer 'face 'dashboard-footer) + (if (string-empty-p footer-icon) footer-icon + (concat footer-icon " ")) + (propertize footer 'face 'dashboard-footer-face) "\n"))) ;; -;; Truncate -;; +;;; Truncate + (defcustom dashboard-shorten-by-window-width nil "Shorten path by window edges." :type 'boolean @@ -913,22 +1040,29 @@ to widget creation." "Return directory name from PATH." (file-name-nondirectory (directory-file-name (file-name-directory path)))) +(defun dashboard-truncate-string-ellipsis () + "Return the string used to indicate truncation." + (if (fboundp 'truncate-string-ellipsis) + (truncate-string-ellipsis) + (or truncate-string-ellipsis + "..."))) + (defun dashboard-shorten-path-beginning (path) "Shorten PATH from beginning if exceeding maximum length." (let* ((len-path (length path)) (slen-path (dashboard-str-len path)) - (len-rep (dashboard-str-len dashboard-path-shorten-string)) + (len-rep (dashboard-str-len (dashboard-truncate-string-ellipsis))) (len-total (- dashboard-path-max-length len-rep)) front) (if (<= slen-path dashboard-path-max-length) path (setq front (ignore-errors (substring path (- slen-path len-total) len-path))) - (if front (concat dashboard-path-shorten-string front) "")))) + (if front (concat (dashboard-truncate-string-ellipsis) front) "")))) (defun dashboard-shorten-path-middle (path) "Shorten PATH from middle if exceeding maximum length." (let* ((len-path (length path)) (slen-path (dashboard-str-len path)) - (len-rep (dashboard-str-len dashboard-path-shorten-string)) + (len-rep (dashboard-str-len (dashboard-truncate-string-ellipsis))) (len-total (- dashboard-path-max-length len-rep)) (center (/ len-total 2)) (end-back center) @@ -937,20 +1071,20 @@ to widget creation." (if (<= slen-path dashboard-path-max-length) path (setq back (substring path 0 end-back) front (ignore-errors (substring path start-front len-path))) - (if front (concat back dashboard-path-shorten-string front) "")))) + (if front (concat back (dashboard-truncate-string-ellipsis) front) "")))) (defun dashboard-shorten-path-end (path) "Shorten PATH from end if exceeding maximum length." (let* ((len-path (length path)) (slen-path (dashboard-str-len path)) - (len-rep (dashboard-str-len dashboard-path-shorten-string)) + (len-rep (dashboard-str-len (dashboard-truncate-string-ellipsis))) (diff (- slen-path len-path)) (len-total (- dashboard-path-max-length len-rep diff)) back) (if (<= slen-path dashboard-path-max-length) path (setq back (ignore-errors (substring path 0 len-total))) (if (and back (< 0 dashboard-path-max-length)) - (concat back dashboard-path-shorten-string) "")))) + (concat back (dashboard-truncate-string-ellipsis)) "")))) (defun dashboard--get-base-length (path type) "Return the length of the base from the PATH by TYPE." @@ -1043,8 +1177,8 @@ to widget creation." align-length)) ;; -;; Recentf -;; +;;; Recentf + (defcustom dashboard-recentf-show-base nil "Show the base file name infront of it's path." :type '(choice @@ -1067,7 +1201,10 @@ to widget creation." (defun dashboard-insert-recents (list-size) "Add the list of LIST-SIZE items from recently edited files." (setq dashboard--recentf-cache-item-format nil) - (dashboard-mute-apply (recentf-mode 1) (recentf-cleanup)) + (dashboard-mute-apply + (recentf-mode 1) + (when dashboard-remove-missing-entry + (ignore-errors (recentf-cleanup)))) (dashboard-insert-section "Recent Files:" (dashboard-shorten-paths recentf-list 'dashboard-recentf-alist 'recents) @@ -1091,8 +1228,8 @@ to widget creation." (t (format dashboard-recentf-item-format filename path)))))) ;; -;; Bookmarks -;; +;;; Bookmarks + (defcustom dashboard-bookmarks-show-base t "Show the base file name infront of it's path." :type '(choice @@ -1135,8 +1272,8 @@ to widget creation." el))) ;; -;; Projects -;; +;;; Projects + (defcustom dashboard-projects-switch-function nil "Custom function to switch to projects from dashboard. @@ -1198,11 +1335,16 @@ Return function that returns a list of projects." (cl-case dashboard-projects-backend (`projectile (require 'projectile) - (dashboard-mute-apply (projectile-cleanup-known-projects)) + (when dashboard-remove-missing-entry + (dashboard-mute-apply + (ignore-errors (projectile-cleanup-known-projects)))) (projectile-load-known-projects)) (`project-el (require 'project) - (dashboard-mute-apply (dashboard-funcall-fboundp #'project-forget-zombie-projects)) + (when dashboard-remove-missing-entry + (dashboard-mute-apply + (ignore-errors + (dashboard-funcall-fboundp #'project-forget-zombie-projects)))) (project-known-project-roots)) (t (display-warning '(dashboard) @@ -1227,8 +1369,8 @@ over custom backends." :error))))) ;; -;; Org Agenda -;; +;;; Org Agenda + (defcustom dashboard-week-agenda t "Show agenda weekly if its not nil." :type 'boolean @@ -1286,7 +1428,9 @@ Any custom function would receives the tags from `org-get-tags'" (defun dashboard-agenda-entry-format () "Format agenda entry to show it on dashboard. -Also,it set text properties that latter are used to sort entries and perform different actions." + +Also,it set text properties that latter are used to sort entries and perform +different actions." (let* ((scheduled-time (org-get-scheduled-time (point))) (deadline-time (org-get-deadline-time (point))) (entry-timestamp (dashboard-agenda--entry-timestamp (point))) @@ -1298,7 +1442,7 @@ Also,it set text properties that latter are used to sort entries and perform dif (org-get-category) (dashboard-agenda--formatted-tags))) (todo-state (org-get-todo-state)) - (item-priority (org-get-priority (org-get-heading t t t t))) + (item-priority (org-get-priority (org-get-heading t t nil t))) (todo-index (and todo-state (length (member todo-state org-todo-keywords-1)))) (entry-data (list 'dashboard-agenda-file (buffer-file-name) @@ -1311,12 +1455,12 @@ Also,it set text properties that latter are used to sort entries and perform dif (defun dashboard-agenda--entry-timestamp (point) "Get the timestamp from an entry at POINT." - (when-let ((timestamp (org-entry-get point "TIMESTAMP"))) + (when-let* ((timestamp (org-entry-get point "TIMESTAMP"))) (org-time-string-to-time timestamp))) (defun dashboard-agenda--formatted-headline () "Set agenda faces to `HEADLINE' when face text property is nil." - (let* ((headline (org-get-heading t t t t)) + (let* ((headline (org-link-display-format (org-get-heading t t t t))) (todo (or (org-get-todo-state) "")) (org-level-face (nth (- (org-outline-level) 1) org-level-faces)) (todo-state (format org-agenda-todo-keyword-format todo))) @@ -1332,8 +1476,8 @@ If not height is found on FACE or `dashboard-items-face' use `default'." (defun dashboard-agenda--formatted-time () "Get the scheduled or dead time of an entry. If no time is found return nil." - (when-let ((time (or (org-get-scheduled-time (point)) (org-get-deadline-time (point)) - (dashboard-agenda--entry-timestamp (point))))) + (when-let* ((time (or (org-get-scheduled-time (point)) (org-get-deadline-time (point)) + (dashboard-agenda--entry-timestamp (point))))) (format-time-string dashboard-agenda-time-string-format time))) (defun dashboard-agenda--formatted-tags () @@ -1359,12 +1503,12 @@ point." (unless (and (not (org-entry-is-done-p)) (not (org-in-archived-heading-p)) (or (and scheduled-time - (org-time-less-p scheduled-time due-date)) + (time-less-p scheduled-time due-date)) (and deadline-time - (org-time-less-p deadline-time due-date)) + (time-less-p deadline-time due-date)) (and entry-timestamp - (org-time-less-p now entry-timestamp) - (org-time-less-p entry-timestamp due-date)))) + (time-less-p now entry-timestamp) + (time-less-p entry-timestamp due-date)))) (point)))) (defun dashboard-filter-agenda-by-todo () @@ -1382,7 +1526,7 @@ if returns a point." (defun dashboard-get-agenda () "Get agenda items for today or for a week from now." - (if-let ((prefix-format (assoc 'dashboard-agenda org-agenda-prefix-format))) + (if-let* ((prefix-format (assoc 'dashboard-agenda org-agenda-prefix-format))) (setcdr prefix-format dashboard-agenda-prefix-format) (push (cons 'dashboard-agenda dashboard-agenda-prefix-format) org-agenda-prefix-format)) (org-compile-prefix-format 'dashboard-agenda) @@ -1437,8 +1581,8 @@ found for the strategy it uses nil predicate." (cl-case strategy (`priority-up '>) (`priority-down '<) - (`time-up 'org-time-less-p) - (`time-down (lambda (a b) (org-time-less-p b a))) + (`time-up 'time-less-p) + (`time-down (lambda (a b) (time-less-p b a))) (`todo-state-up '>) (`todo-state-down '<))) @@ -1463,6 +1607,19 @@ to compare." ((null arg2) t) (t (apply predicate (list arg1 arg2)))))) +(defun dashboard-agenda--visit-file (file point) + "Action on agenda-entry that visit a FILE at POINT." + (let ((buffer (find-file-noselect file))) + (with-current-buffer buffer + (goto-char point) + (switch-to-buffer buffer) + (recenter-top-bottom)))) + +(defun dashboard-agenda--visit-file-other-window (file point) + "Visit FILE at POINT of an agenda item in other window." + (let ((buffer (find-file-other-window file))) + (with-current-buffer buffer (goto-char point) (recenter-top-bottom)))) + (defun dashboard-insert-agenda (list-size) "Add the list of LIST-SIZE items of agenda." (require 'org-agenda) @@ -1475,15 +1632,14 @@ to compare." 'agenda (dashboard-get-shortcut 'agenda) `(lambda (&rest _) - (let ((buffer (find-file-other-window (get-text-property 0 'dashboard-agenda-file ,el)))) - (with-current-buffer buffer - (goto-char (get-text-property 0 'dashboard-agenda-loc ,el)) - (switch-to-buffer buffer)))) + (let ((file (get-text-property 0 'dashboard-agenda-file ,el)) + (point (get-text-property 0 'dashboard-agenda-loc ,el))) + (funcall dashboard-agenda-action file point))) (format "%s" el))) ;; -;; Registers -;; +;;; Registers + (defun dashboard-insert-registers (list-size) "Add the list of LIST-SIZE items of registers." (require 'register) diff --git a/dashboard.el b/dashboard.el index 5518a82..2d6cb75 100644 --- a/dashboard.el +++ b/dashboard.el @@ -1,10 +1,10 @@ ;;; dashboard.el --- A startup screen extracted from Spacemacs -*- lexical-binding: t -*- -;; Copyright (c) 2016-2023 emacs-dashboard maintainers +;; Copyright (c) 2016-2025 emacs-dashboard maintainers ;; ;; Author : Rakan Al-Hneiti -;; Maintainer : Jesús Martínez -;; Shen, Jen-Chieh +;; Maintainer : Shen, Jen-Chieh +;; Ricardo Arredondo ;; URL : https://github.com/emacs-dashboard/emacs-dashboard ;; ;; This file is not part of GNU Emacs. @@ -12,9 +12,10 @@ ;;; License: GPLv3 ;; ;; Created: October 05, 2016 -;; Package-Version: 1.8.0 +;; Package-Version: 1.9.0-SNAPSHOT ;; Keywords: startup, screen, tools, dashboard -;; Package-Requires: ((emacs "26.1")) +;; Package-Requires: ((emacs "27.1")) + ;;; Commentary: ;; An extensible Emacs dashboard, with sections for @@ -27,6 +28,9 @@ (require 'dashboard-widgets) +;; +;;; Externals + (declare-function bookmark-get-filename "ext:bookmark.el") (declare-function bookmark-all-names "ext:bookmark.el") (declare-function dashboard-ls--dirs "ext:dashboard-ls.el") @@ -38,6 +42,9 @@ (declare-function dashboard-refresh-buffer "dashboard.el") +;; +;;; Customization + (defgroup dashboard nil "Extensible startup screen." :group 'applications) @@ -45,17 +52,18 @@ ;; 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 "") 'dashboard-previous-line) - (define-key map (kbd "") '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 [mouse-1] 'dashboard-mouse-1) + (define-key map (kbd "C-p") #'dashboard-previous-line) + (define-key map (kbd "C-n") #'dashboard-next-line) + (define-key map (kbd "") #'dashboard-previous-line) + (define-key map (kbd "") #'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 (kbd "") #'widget-button-click) + (define-key map [mouse-1] #'dashboard-mouse-1) (define-key map (kbd "}") #'dashboard-next-section) (define-key map (kbd "{") #'dashboard-previous-section) @@ -75,11 +83,21 @@ map) "Keymap for dashboard mode.") +(defcustom dashboard-before-initialize-hook nil + "Hook that is run before dashboard buffer is initialized." + :group 'dashboard + :type 'hook) + (defcustom dashboard-after-initialize-hook nil "Hook that is run after dashboard buffer is initialized." :group 'dashboard :type 'hook) +(defcustom dashboard-hide-cursor nil + "Whether to hide the cursor in the dashboard." + :type 'boolean + :group 'dashboard) + (define-derived-mode dashboard-mode special-mode "Dashboard" "Dashboard major mode for startup screen." :group 'dashboard @@ -91,6 +109,8 @@ (when (featurep 'display-line-numbers) (display-line-numbers-mode -1)) (when (featurep 'page-break-lines) (page-break-lines-mode 1)) (setq-local revert-buffer-function #'dashboard-refresh-buffer) + (when dashboard-hide-cursor + (setq-local cursor-type nil)) (setq inhibit-startup-screen t buffer-read-only t truncate-lines t)) @@ -100,18 +120,58 @@ :type 'boolean :group 'dashboard) -(defconst dashboard-buffer-name "*dashboard*" - "Dashboard's buffer name.") +(defcustom dashboard-vertically-center-content nil + "Whether to vertically center content within the window." + :type 'boolean + :group 'dashboard) + +(defcustom dashboard-startupify-list + '(dashboard-insert-banner + dashboard-insert-newline + dashboard-insert-banner-title + dashboard-insert-newline + dashboard-insert-init-info + dashboard-insert-items + dashboard-insert-newline + dashboard-insert-footer) + "List of dashboard widgets (in order) to insert in dashboard buffer. +Avalaible functions: + `dashboard-insert-newline' + `dashboard-insert-page-break' + `dashboard-insert-banner' + `dashboard-insert-banner-title' + `dashboard-insert-navigator' + `dashboard-insert-init-info' + `dashboard-insert-items' + `dashboard-insert-footer' + +It must be a function or a cons cell where specify function and +its arg. + +Also you can add your custom function or a lambda to the list. +example: + (lambda () (delete-char -1))" + :type '(repeat (choice + function + (cons function sexp))) + :group 'dashboard) + +(defcustom dashboard-navigation-cycle nil + "Non-nil cycle the section navigation." + :type 'boolean + :group 'dashboard) -(defvar dashboard-force-refresh nil - "If non-nil, force refresh dashboard buffer.") +(defcustom dashboard-buffer-name "*dashboard*" + "Dashboard's buffer name." + :type 'string + :group 'dashboard) (defvar dashboard--section-starts nil "List of section starting positions.") ;; -;; Util -;; +;;; Util + (defun dashboard--goto-line (line) "Goto LINE." (goto-char (point-min)) (forward-line (1- line))) @@ -126,58 +186,75 @@ (move-to-column column))) ;; -;; Core -;; +;;; Core + +(defun dashboard--separator () + "Return separator used to search." + (concat "\n" dashboard-page-separator)) + (defun dashboard--current-section () "Return section symbol in dashboard." (save-excursion - (if (and (search-backward dashboard-page-separator nil t) - (search-forward dashboard-page-separator nil t)) - (let ((ln (thing-at-point 'line))) - (cond ((string-match-p "Recent Files:" ln) 'recents) - ((string-match-p "Bookmarks:" ln) 'bookmarks) - ((string-match-p "Projects:" ln) 'projects) - ((string-match-p "Agenda for " ln) 'agenda) - ((string-match-p "Registers:" ln) 'registers) - ((string-match-p "List Directories:" ln) 'ls-directories) - ((string-match-p "List Files:" ln) 'ls-files) - (t (user-error "Unknown section from dashboard")))) + (if-let* ((sep (dashboard--separator)) + ((and (search-backward sep nil t) + (search-forward sep nil t))) + (ln (thing-at-point 'line t))) + (cond ((string-match-p "Recent Files:" ln) 'recents) + ((string-match-p "Bookmarks:" ln) 'bookmarks) + ((string-match-p "Projects:" ln) 'projects) + ((string-match-p "Agenda for " ln) 'agenda) + ((string-match-p "Registers:" ln) 'registers) + ((string-match-p "List Directories:" ln) 'ls-directories) + ((string-match-p "List Files:" ln) 'ls-files) + (t (user-error "Unknown section from dashboard"))) (user-error "Failed searching dashboard section")))) ;; -;; Navigation -;; +;;; Navigation + (defun dashboard-previous-section () - "Navigate back to previous section." + "Navigate backwards to previous section." (interactive) - (let ((current-position (point)) current-section-start previous-section-start) - (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)))) + (let* ((items-len (1- (length dashboard-items))) + (first-item (car (nth 0 dashboard-items))) + (current (or (ignore-errors (dashboard--current-section)) + first-item)) + (items (mapcar #'car dashboard-items)) + (find (cl-position current items :test #'equal)) + (prev-index (1- find)) + (prev (cond (dashboard-navigation-cycle + (if (< prev-index 0) (nth items-len items) + (nth prev-index items))) + (t + (if (< prev-index 0) (nth 0 items) + (nth prev-index items)))))) + (dashboard--goto-section prev))) (defun dashboard-next-section () "Navigate forward to next section." (interactive) - (let ((current-position (point)) next-section-start - (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)))) + (let* ((items-len (1- (length dashboard-items))) + (last-item (car (nth items-len dashboard-items))) + (current (or (ignore-errors (dashboard--current-section)) + last-item)) + (items (mapcar #'car dashboard-items)) + (find (cl-position current items :test #'equal)) + (next-index (1+ find)) + (next (cond (dashboard-navigation-cycle + (or (nth next-index items) + (nth 0 items))) + (t + (if (< items-len next-index) + (nth (min items-len next-index) items) + (nth next-index items)))))) + (dashboard--goto-section next))) (defun dashboard--section-lines () "Return a list of integer represent the starting line number of each section." (let (pb-lst) (save-excursion (goto-char (point-min)) - (while (search-forward dashboard-page-separator nil t) + (while (search-forward (dashboard--separator) nil t) (when (ignore-errors (dashboard--current-section)) (push (line-number-at-pos) pb-lst)))) (setq pb-lst (reverse pb-lst)) @@ -192,6 +269,36 @@ (when (and items-pg (< items-id items-len)) (dashboard--goto-line items-pg)))) +(defun dashboard-cycle-section-forward (&optional section) + "Cycle forward through the entries in SECTION. +If SECTION is nil, cycle in the current section." + (let ((target-section (or section (dashboard--current-section)))) + (if target-section + (condition-case nil + (progn + (widget-forward 1) + (unless (eq target-section (dashboard--current-section)) + (dashboard--goto-section target-section))) + (widget-forward 1)) + (widget-forward 1)))) + +(defun dashboard-cycle-section-backward (&optional section) + "Cycle backward through the entries in SECTION. +If SECTION is nil, cycle in the current section." + (let ((target-section (or section (dashboard--current-section)))) + (if target-section + (condition-case nil + (progn + (widget-backward 1) + (unless (eq target-section (dashboard--current-section)) + (progn + (dashboard--goto-section target-section) + (while (eq target-section (dashboard--current-section)) + (widget-forward 1)) + (widget-backward 1)))) + (widget-backward 1)) + (widget-backward 1)))) + (defun dashboard-section-1 () "Navigate to section 1." (interactive) (dashboard--goto-section-by-index 1)) (defun dashboard-section-2 () @@ -232,8 +339,8 @@ Optional prefix ARG says how many lines to move; default is one line." (beginning-of-line-text)) ;; -;; ffap -;; +;;; ffap + (defun dashboard--goto-section (section) "Move to SECTION declares in variable `dashboard-item-shortcuts'." (let ((fnc (intern (format "dashboard-jump-to-%s" section)))) @@ -290,8 +397,8 @@ Optional argument ARGS adviced function arguments." (advice-add 'ffap-guesser :around #'dashboard--ffap-guesser--adv) ;; -;; Removal -;; +;;; Removal + (defun dashboard-remove-item-under () "Remove a item from the current item section." (interactive) @@ -337,8 +444,8 @@ Optional argument ARGS adviced function arguments." (interactive)) ; TODO: .. ;; -;; Confirmation -;; +;;; Confirmation + (defun dashboard-return () "Hit return key in dashboard buffer." (interactive) @@ -368,8 +475,8 @@ Optional argument ARGS adviced function arguments." (setq track-mouse old-track-mouse)))) ;; -;; Insertion -;; +;;; Insertion + (defmacro dashboard--with-buffer (&rest body) "Execute BODY in dashboard buffer." (declare (indent 0)) @@ -377,79 +484,90 @@ Optional argument ARGS adviced function arguments." (let ((inhibit-read-only t)) ,@body) (current-buffer))) -(defun dashboard-maximum-section-length () - "For the just-inserted section, calculate the length of the longest line." - (let ((max-line-length 0)) +(defun dashboard-insert-items () + "Function to insert dashboard items. +See `dashboard-item-generators' for all items available." + (let ((recentf-is-on (recentf-enabled-p)) + (origial-recentf-list recentf-list)) + (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)))) + + (insert "\n") + (push (point) dashboard--section-starts) + (funcall item-generator list-size) + (goto-char (point-max)) + + (when recentf-is-on + (setq recentf-list origial-recentf-list)))) + dashboard-items) + + (when dashboard-center-content + (dashboard-center-text + (if dashboard--section-starts + (car (last dashboard--section-starts)) + (point)) + (point-max))) + (save-excursion - (dashboard-previous-section) - (while (not (eobp)) - (setq max-line-length - (max max-line-length - (- (line-end-position) (line-beginning-position)))) - (forward-line 1))) - max-line-length)) - -(defun dashboard-insert-startupify-lists () - "Insert the list of widgets into the buffer." + (dolist (start dashboard--section-starts) + (goto-char start) + (insert dashboard-page-separator))) + + (insert "\n") + (insert dashboard-page-separator))) + +(defun dashboard-insert-startupify-lists (&optional force-refresh) + "Insert the list of widgets into the buffer, FORCE-REFRESH is optional." (interactive) (let ((inhibit-redisplay t) (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)) + (dashboard-num-recents (or (cdr (assoc 'recents dashboard-items)) 0))) (when recentf-is-on (setq recentf-list (dashboard-subseq recentf-list dashboard-num-recents))) (dashboard--with-buffer - (when (or dashboard-force-refresh (not (eq major-mode 'dashboard-mode))) + (when (or force-refresh (not (eq major-mode 'dashboard-mode))) + (run-hooks 'dashboard-before-initialize-hook) (erase-buffer) - (dashboard-insert-banner) - (insert "\n") (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)))) - (push (point) dashboard--section-starts) - (funcall item-generator list-size) - (goto-char (point-max)) - ;; add a newline so the next section-name doesn't get include - ;; on the same line. - (insert "\n") - (when recentf-is-on - (setq recentf-list origial-recentf-list)) - (setq max-line-length - (max max-line-length (dashboard-maximum-section-length))))) - dashboard-items) - (when dashboard-center-content - (dashboard-center-text - (if dashboard--section-starts - (car (last dashboard--section-starts)) - (point)) - (point-max))) - (save-excursion - (dolist (start dashboard--section-starts) - (goto-char start) - (delete-char -1) ; delete the newline we added previously - (insert dashboard-page-separator))) - (progn - (delete-char -1) - (insert dashboard-page-separator)) - (dashboard-insert-footer) - (goto-char (point-min)) + + (mapc (lambda (entry) + (if (and (listp entry) + (not (functionp entry))) + (apply (car entry) `(,(cdr entry))) + (funcall entry))) + dashboard-startupify-list) + (dashboard-vertically-center) (dashboard-mode))) (when recentf-is-on (setq recentf-list origial-recentf-list)))) +(defun dashboard-vertically-center () + "Center vertically the content of dashboard. Always go to point-min char." + (when-let* (dashboard-vertically-center-content + (start-height (cdr (window-absolute-pixel-position (point-min)))) + (end-height (cdr (window-absolute-pixel-position (point-max)))) + (content-height (- end-height start-height)) + (vertical-padding (floor (/ (- (window-pixel-height) content-height) 2))) + ((> vertical-padding 0)) + (vertical-lines (1- (floor (/ vertical-padding (line-pixel-height))))) + ((> vertical-lines 0))) + (goto-char (point-min)) + (insert (make-string vertical-lines ?\n))) + (goto-char (point-min))) ;;;###autoload (defun dashboard-open (&rest _) "Open (or refresh) the *dashboard* buffer." (interactive) - (let ((dashboard-force-refresh t)) (dashboard-insert-startupify-lists)) - (switch-to-buffer dashboard-buffer-name)) + (dashboard--with-buffer + (switch-to-buffer (current-buffer)) + (dashboard-insert-startupify-lists t))) (defalias #'dashboard-refresh-buffer #'dashboard-open) @@ -462,24 +580,21 @@ Optional argument ARGS adviced function arguments." (with-selected-window space-win (dashboard-insert-startupify-lists))))) +(defun dashboard-initialize () + "Switch to dashboard and run `dashboard-after-initialize-hook'." + (switch-to-buffer dashboard-buffer-name) + (goto-char (point-min)) + (redisplay) + (run-hooks 'dashboard-after-initialize-hook)) + ;;;###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 'window-setup-hook (lambda () - ;; 100 means `dashboard-resize-on-hook' will run last - (add-hook 'window-size-change-functions 'dashboard-resize-on-hook 100) - (dashboard-resize-on-hook))) - (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-buffer-name) - (goto-char (point-min)) - (redisplay) - (run-hooks 'dashboard-after-initialize-hook))))) + "Setup post initialization hooks unless a command line argument is provided." + (when (< (length command-line-args) 2) ;; Assume no file name passed + (add-hook 'window-size-change-functions #'dashboard-resize-on-hook 100) + (add-hook 'window-setup-hook #'dashboard-resize-on-hook) + (add-hook 'after-init-hook #'dashboard-insert-startupify-lists) + (add-hook 'emacs-startup-hook #'dashboard-initialize))) (provide 'dashboard) ;;; dashboard.el ends here diff --git a/docs/FAQ.org b/docs/FAQ.org new file mode 100644 index 0000000..bca1318 --- /dev/null +++ b/docs/FAQ.org @@ -0,0 +1,115 @@ +#+title: Frequently Asked Questions + +* /How can I hide modeline in dashboard?/ + +You can add this to your config: + #+begin_src emacs-lisp + (add-hook 'dashboard-mode-hook (lambda () (setq-local mode-line-format nil))) + #+end_src + You can also use [[https://github.com/hlissner/emacs-hide-mode-line][hide-mode-line]]: +#+begin_src elisp + (add-hook 'dashboard-mode-hook #'hide-mode-line-mode) + ;; Or for use-package + (use-package hide-mode-line + :hook (dashboard-mode . hide-mode-line-mode) + ...) +#+end_src +For doom-modeline users you may use this: +#+begin_src elisp + (add-to-list 'doom-modeline-mode-alist '(dashboard-mode)) +#+end_src + +* /Icons doesn't display in Windows GUI, How can I fix it?/ +Emacs Windows port doesn't handle well icons. + +For *nerd-icons* you can use *patched nerd fonts* found in their official +website [[https://www.nerdfonts.com/font-downloads]]. + +Otherwise you can use *UTF-8* coding system, putting this line into +your =early-init.el= +#+begin_src emacs-lisp + (if (eq system-type 'windows-nt) + (prefer-coding-system 'utf-8)) +#+end_src + +* /How can I use [[https://github.com/hlissner/emacs-solaire-mode][solaire-mode]] with dashboard?/ + +Add this to your config: +#+begin_src emacs-lisp + (add-hook 'dashboard-before-initialize-hook #'solaire-mode) + ;; Or if you prefer use-package + (use-package solaire-mode + :hook (dashboard-before-initialize . solaire-mode) + ...) +#+end_src + + +* /How can I add my custom widget?/ + +To add your own custom widget is pretty easy, define your widget's callback function and add it to `dashboard-items` as such: +#+BEGIN_SRC elisp + (defun dashboard-insert-custom (list-size) + (insert "Custom text")) + (add-to-list 'dashboard-item-generators '(custom . dashboard-insert-custom)) + (add-to-list 'dashboard-items '(custom) t) +#+END_SRC + +If you want to add an icon to a custom widget, insert it with `dashboard-insert-heading` in your custom function: +#+BEGIN_SRC elisp + ;; In this example, there is an icon but no shortcut + ;; see `dashboard-insert-heading' for more details. + (defun dashboard-insert-custom (list-size) + (dashboard-insert-heading "News:" + nil + (all-the-icons-faicon "newspaper-o" + :height 1.2 + :v-adjust 0.0 + :face 'dashboard-heading)) + (insert "\n") + (insert " Custom text")) +#+END_SRC + + +* /Items length is too high, How can I change it?/ +You can change dashboard-items-default-length amount: +#+BEGIN_SRC elisp + (setq dashboard-items-default-length 20) +#+END_SRC + +* /How can I change dashboard buffer name?/ +You can change it with ~dashboard-buffer-name~ +#+begin_src emacs-lisp + (setq dashboard-buffer-name "*my dashboard*") +#+end_src + +* /I don't like banner, init info or even items, How can I delete them from dashboard?/ + +You can delete them from ~dashboard-startupify-list~. +#+begin_src emacs-lisp + (setq dashboard-startupify-list '(dashboard-insert-banner-title + dashboard-insert-navigator + ,(dashboard-insert-newline 2) + dashboard-insert-footer)) +#+end_src + +* /How can I change my init message?/ + +You can customize it like this: +#+BEGIN_SRC elisp + (setq dashboard-init-info "This is an init message!") +#+END_SRC + +* /How can I change footer messages?/ + +You can customize it like this: +#+BEGIN_SRC elisp + (setq dashboard-footer-messages '("Dashboard is pretty cool!")) +#+END_SRC +If you want to change its icon you may use this: +#+BEGIN_SRC elisp + (setq dashboard-footer-icon (all-the-icons-octicon "dashboard" + :height 1.1 + :v-adjust -0.05 + :face 'font-lock-keyword-face)) +Also it can be a string list for display random footer icons. +#+END_SRC diff --git a/docs/variables-and-functions.org b/docs/variables-and-functions.org new file mode 100644 index 0000000..9df381d --- /dev/null +++ b/docs/variables-and-functions.org @@ -0,0 +1,128 @@ +** Variables +These are the variables available to users out of the box: +| **Variable** | **Description** | **Possible States** | +|------------------------------------|----------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------| +| `dashboard-navigation-cycle` | Enable cycling through sections in navigation. | `t`: Enable, `nil`: Disable | +| `dashboard-items` | Specify sections to display and their lengths. | List of `(section . count)` pairs (e.g., `((recents . 5) (bookmarks . 5) (projects . 5))`) | +| `dashboard-startup-banner` | Define the banner at the top of the dashboard. | `'official`, path to an image, or `nil` | +| `dashboard-banner-logo-title` | Text to display below the banner. | Any string (e.g., `"Welcome to Emacs!"`) | +| `dashboard-center-content` | Center the content horizontally. | `t`: Enable, `nil`: Disable | +| `dashboard-init-info` | Custom variable, could be a string or a function that returns a string | default: `dashboard-init--info` | +| `dashboard-vertically-center-content` | Center the content vertically. | `t`: Enable, `nil`: Disable | +| `dashboard-page-separator` | String used to separate sections in the buffer. | Any string (default: `"\n\n"`) | +| `dashboard-set-heading-icons` | Show icons next to section headings. | `t`: Enable, `nil`: Disable | +| `dashboard-set-file-icons` | Show icons next to items. | `t`: Enable, `nil`: Disable | +| `dashboard-heading-icons` | Icons to use for section headings. | Alist of `(section . icon-spec)` pairs, where icon-spec could be a name or a function-family and name (e.g., `((recents . "history") (bookmarks . "bookmark") (my-news . (all-the-icons-faicon "newspaper-o")) (agenda . (nerd-icons-codicon "nf-cod-calendar"))`) | +| `dashboard-icon-type` | Icon style used for headings and items. | `'all-the-icons`, `'ascii`, `nil` | +| `dashboard-recentf-show-base` | Control how recentf entries are displayed. | `'Respect format`, `'hide`, `'absolute`, `'expand`, `'truncate-middle` | +| `dashboard-recentf-item-format` | Format string for displaying recentf items. | Any valid format string (e.g., `"%s"`) | +| `dashboard-bookmarks-item-format` | Format string for displaying bookmarks items. | Any valid format string (e.g., `"%s"`) | +| `dashboard-path-style` | How to truncate paths. | `'truncate-beginning`, `'truncate-middle`, `'truncate-end`, `nil` | +| `dashboard-path-max-length` | Maximum length of displayed paths. | Integer (default: `70`) | +| `dashboard-footer` | Footer text to display at the bottom of the dashboard. | Any string (default: `nil`) | +| `dashboard-set-footer` | Enable or disable the footer. | `t`: Enable, `nil`: Disable | +| `dashboard-footer-messages` | List of footer messages to display randomly. | List of strings (e.g., `("Enjoy your Emacs session!" "Type M-x help for help.")`) | +| `dashboard-projects-backend` | Backend used for project integration. | `'projectile`, `'project-el`, or `nil` | +| `dashboard-projects-switch-function` | Function to call when switching projects. | Function (e.g., `projectile-persp-switch-project`) | +| `dashboard-agenda-prefix-format` | Custom format string for agenda items. | Any valid Org-mode prefix format | +| `dashboard-agenda-show-all` | Whether to show all agenda items. | `t`: Enable, `nil`: Disable | +| `dashboard-agenda-files` | Files to include in the agenda section. | List of file paths | +| `dashboard-week-agenda` | Whether to show the weekly agenda. | `t`: Enable, `nil`: Disable | +| `dashboard-projects-switch-project-action` | Action to take when switching projects. | Function (e.g., `'magit-status`) | +| `dashboard-buffer-name` | Name of the dashboard buffer. | Any string (default: `*dashboard*`) | +| `dashboard-before-initialize-hook` | Hook run before initializing the dashboard buffer. | Hook (list of functions) | +| `dashboard-after-initialize-hook` | Hook run after initializing the dashboard buffer. | Hook (list of functions) | + + +Example usage: + +- In plain e-lisp: +#+begin_src emacs-lisp + ;; ... + (setq dashboard-startup-banner 'official) + (setq dashboard-center-content t) + (setq dashboard-banner-logo-title "Welcome back") + (dashboard-setup-startup-hook) +#+end_src + +- With use-package: +#+begin_src emacs-lisp + (use-package dashboard + :ensure t + :init + (setq initial-buffer-choice 'dashboard-open) + :config + (dashboard-setup-startup-hook) + :custom + (dashboard-startup-banner 'official) + (dashboard-center-content t) + (dashboard-banner-logo-title "Welcome back")) +#+end_src +** Functions +These are the variables available to users out of the box: +| **Function** | **Description** | **Arguments** | +|-------------------------------------|-----------------------------------------------------------------------|-------------------------| +| `dashboard-open` | Opens or refreshes the dashboard buffer. | None | +| `dashboard-refresh-buffer` | Alias for `dashboard-open`. | None | +| `dashboard-setup-startup-hook` | Sets up hooks for initializing the dashboard. | None | +| `dashboard-initialize` | Switch to the dashboard buffer and run initialization hooks. | None | +| `dashboard-insert-startupify-lists` | Inserts the dashboard widgets into the buffer. | Optional `force-refresh` | +| `dashboard-next-section` | Navigate forward to the next section in the dashboard. | None | +| `dashboard-previous-section` | Navigate backward to the previous section in the dashboard. | None | +| `dashboard-cycle-section-forward` | Cycle forward through the entries in a specific section. | `section` (optional) | +| `dashboard-cycle-section-backward` | Cycle backward through the entries in a specific section. | `section` (optional) | +| `dashboard-cycle-current-section-forward` | Cycle forward through entries in the current section. | None | +| `dashboard-cycle-current-section-backward` | Cycle backward through entries in the current section. | None | +| `dashboard-remove-item-under` | Remove the current item from the dashboard. | None | +| `dashboard-init--info` | Build a string with the init-time and package-count | | +| `dashboard-init--time` | Same as `emacs-init-time` and support elpaca init time | | +| `dashboard-init--package-count | Return the number of installed packages | | +| `dashboard-insert-items` | Insert all configured dashboard items into the buffer. | None | +| `dashboard-insert-banner` | Insert the banner into the dashboard buffer. | None | +| `dashboard-insert-newline` | Insert a newline in the dashboard buffer. | None | +| `dashboard-insert-page-break` | Insert a page break in the dashboard buffer. | None | +| `dashboard-insert-footer` | Insert the footer into the dashboard buffer. | None | +| `dashboard-insert-navigator` | Insert the navigator into the dashboard buffer. | None | +| `dashboard-insert-banner-title` | Insert the banner title into the dashboard buffer. | None | +| `dashboard-insert-init-info` | Insert initialization information into the dashboard buffer. | None | +| `dashboard--current-section` | Return the symbol of the current section in the dashboard. | None | +| `dashboard--goto-section` | Move to a specified section in the dashboard. | `section` | +| `dashboard--goto-section-by-index` | Navigate to a section by its index. | `index` | +| `dashboard--section-lines` | Return a list of line numbers where each section starts. | None | +| `dashboard--current-index` | Return the index of the current section. | `section`, `pos` (optional) | +| `dashboard--section-list` | Return the list of items in a section. | `section` | +| `dashboard--on-path-item-p` | Check if the point is on an item in a path section. | None | +| `dashboard--current-item-in-path` | Return the file path of the current item under point. | None | +| `dashboard--ffap-guesser--adv` | Advice around `ffap-guesser` to guess items under point in the dashboard. | `fnc`, `args` | +| `dashboard-remove-item-recentf` | Remove an item from the recentf section. | None | +| `dashboard-remove-item-bookmarks` | Remove an item from the bookmarks section. | None | +| `dashboard-remove-item-projects` | Remove an item from the projects section. | None | +| `dashboard-remove-item-agenda` | Remove an item from the agenda section. | None | +| `dashboard-remove-item-registers` | Remove an item from the registers section. | None | +| `dashboard-return` | Press the return key on an item in the dashboard. | None | +| `dashboard-mouse-1` | Click an item in the dashboard using mouse-1. | None | +| `dashboard-resize-on-hook` | Re-render the dashboard on window size changes. | Optional `_` | +| `dashboard-vertically-center` | Vertically center the content of the dashboard buffer. | None | + + +Example usage (bindings in this case): + +- In plain e-lisp: +#+begin_src emacs-lisp + ;; Navigate to the next section programmatically + (dashboard-next-section) + + ;; Bind it to a key in the global keymap + (global-set-key (kbd "C-c n") 'dashboard-next-section) +#+end_src + +- With use-package: +#+begin_src emacs-lisp +(use-package dashboard + :ensure t + :config + (dashboard-setup-startup-hook) + ;; Bind in `dashboard-mode-map` for use only in the dashboard - to not disrupt keybidnings in other modes + :bind (:map dashboard-mode-map + ("n" . dashboard-next-section))) +#+end_src