diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51281904..46d159cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ jobs: test: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] emacs-version: diff --git a/CHANGELOG.md b/CHANGELOG.md index ae48a821..988cdfe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how * 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) ## 1.7.0 > Released Feb 21, 2020 diff --git a/Eask b/Eask index eb32c200..bfe2b6c2 100644 --- a/Eask +++ b/Eask @@ -6,9 +6,11 @@ (files "dashboard.el" "dashboard-widgets.el" - "*.org" + "*.org" "banners") +(depends-on "emacs" "26.1") + (development (depends-on "elisp-lint") (depends-on "page-break-lines")) diff --git a/dashboard-widgets.el b/dashboard-widgets.el index 01fe102e..e0ea9eb3 100644 --- a/dashboard-widgets.el +++ b/dashboard-widgets.el @@ -160,7 +160,8 @@ preserved." The format is: 'icon title help action face prefix suffix'. Example: -'((\"☆\" \"Star\" \"Show stars\" (lambda (&rest _) (show-stars)) 'warning \"[\" \"]\"))" +'((\"☆\" \"Star\" \"Show stars\" (lambda (&rest _) + (show-stars)) 'warning \"[\" \"]\"))" :type '(repeat (repeat (list string string string function symbol string string))) :group 'dashboard) @@ -1076,7 +1077,7 @@ It is the MATCH attribute for `org-map-entries'" When the dashboard-agenda is created this format is inserted into `org-agenda-prefix-format' as `dashboard-agenda' and compiled with `org-compile-prefix-format' previous calling `dashboard-agenda-entry-format' for - each agenda entry." +each agenda entry." :type 'string :group 'dashboard) diff --git a/dashboard.el b/dashboard.el index ac230942..c86caa8e 100644 --- a/dashboard.el +++ b/dashboard.el @@ -22,10 +22,17 @@ ;;; Code: +(require 'ffap) (require 'recentf) + (require 'dashboard-widgets) +(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") +(declare-function dashboard-ls--files "ext:dashboard-ls.el") (declare-function page-break-lines-mode "ext:page-break-lines.el") +(declare-function project-forget-projects-under "ext:project.el") (defgroup dashboard nil "Extensible startup screen." @@ -48,6 +55,9 @@ (define-key map (kbd "}") #'dashboard-next-section) (define-key map (kbd "{") #'dashboard-previous-section) + (define-key map (kbd "") #'dashboard-remove-item-under) + (define-key map (kbd "") #'dashboard-remove-item-under) + (define-key map (kbd "1") #'dashboard-section-1) (define-key map (kbd "2") #'dashboard-section-2) (define-key map (kbd "3") #'dashboard-section-3) @@ -94,6 +104,41 @@ (defvar dashboard--section-starts nil "List of section starting positions.") +;; +;; Util +;; +(defun dashboard--goto-line (line) + "Goto LINE." + (goto-char (point-min)) (forward-line (1- line))) + +(defmacro dashboard--save-excursion (&rest body) + "Execute BODY save window point." + (declare (indent 0) (debug t)) + `(let ((line (line-number-at-pos nil t)) + (column (current-column))) + ,@body + (dashboard--goto-line line) + (move-to-column column))) + +;; +;; Core +;; +(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")))) + (user-error "Failed searching dashboard section")))) + ;; ;; Navigation ;; @@ -122,26 +167,6 @@ (when next-section-start (goto-char next-section-start)))) -(defun dashboard--goto-line (ln) - "Goto LN." - (goto-char (point-min)) (forward-line (1- ln))) - -(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")))) - (user-error "Failed searching dashboard section")))) - (defun dashboard--section-lines () "Return a list of integer represent the starting line number of each section." (let (pb-lst) @@ -153,7 +178,7 @@ (setq pb-lst (reverse pb-lst)) pb-lst)) -(defun dashboard--goto-section (index) +(defun dashboard--goto-section-by-index (index) "Navigate to item section by INDEX." (let* ((pg-lst (dashboard--section-lines)) (items-id (1- index)) @@ -163,23 +188,23 @@ (dashboard--goto-line items-pg)))) (defun dashboard-section-1 () - "Navigate to section 1." (interactive) (dashboard--goto-section 1)) + "Navigate to section 1." (interactive) (dashboard--goto-section-by-index 1)) (defun dashboard-section-2 () - "Navigate to section 2." (interactive) (dashboard--goto-section 2)) + "Navigate to section 2." (interactive) (dashboard--goto-section-by-index 2)) (defun dashboard-section-3 () - "Navigate to section 3." (interactive) (dashboard--goto-section 3)) + "Navigate to section 3." (interactive) (dashboard--goto-section-by-index 3)) (defun dashboard-section-4 () - "Navigate to section 4." (interactive) (dashboard--goto-section 4)) + "Navigate to section 4." (interactive) (dashboard--goto-section-by-index 4)) (defun dashboard-section-5 () - "Navigate to section 5." (interactive) (dashboard--goto-section 5)) + "Navigate to section 5." (interactive) (dashboard--goto-section-by-index 5)) (defun dashboard-section-6 () - "Navigate to section 6." (interactive) (dashboard--goto-section 6)) + "Navigate to section 6." (interactive) (dashboard--goto-section-by-index 6)) (defun dashboard-section-7 () - "Navigate to section 7." (interactive) (dashboard--goto-section 7)) + "Navigate to section 7." (interactive) (dashboard--goto-section-by-index 7)) (defun dashboard-section-8 () - "Navigate to section 8." (interactive) (dashboard--goto-section 8)) + "Navigate to section 8." (interactive) (dashboard--goto-section-by-index 8)) (defun dashboard-section-9 () - "Navigate to section 9." (interactive) (dashboard--goto-section 9)) + "Navigate to section 9." (interactive) (dashboard--goto-section-by-index 9)) (defun dashboard-previous-line (arg) "Move point up and position it at that line’s item. @@ -201,6 +226,106 @@ Optional prefix ARG says how many lines to move; default is one line." (forward-char (if (and arg (< arg 0)) -1 1))) (beginning-of-line-text)) +;; +;; ffap +;; +(defun dashboard--goto-section (section) + "Move to SECTION declares in variable `dashboard-item-shortcuts'." + (let ((fnc (intern (format "dashboard-jump-to-%s" section)))) + (dashboard-funcall-fboundp fnc))) + +(defun dashboard--current-index (section &optional pos) + "Return the idex by SECTION from POS." + (let (target-ln section-line) + (save-excursion + (when pos (goto-char pos)) + (setq target-ln (line-number-at-pos)) + (dashboard--goto-section section) + (setq section-line (line-number-at-pos))) + (- target-ln section-line))) + +(defun dashboard--section-list (section) + "Return the list from SECTION." + (cl-case section + (`recents recentf-list) + (`bookmarks (bookmark-all-names)) + (`projects (dashboard-projects-backend-load-projects)) + (`ls-directories (dashboard-ls--dirs)) + (`ls-files (dashboard-ls--files)) + (t (user-error "Unknown section for search: %s" section)))) + +(defun dashboard--current-item-in-path () + "Return the path from current dashboard section in path." + (let ((section (dashboard--current-section)) path) + (cl-case section + (`bookmarks (setq path (bookmark-get-filename path))) + (t + (let ((lst (dashboard--section-list section)) + (index (dashboard--current-index section))) + (setq path (nth index lst))))) + path)) + +(defun dashboard--on-path-item-p () + "Return non-nil if current point is on the item path from dashboard." + (save-excursion + (when (= (point) (line-end-position)) (ignore-errors (forward-char -1))) + (eq (get-char-property (point) 'face) 'dashboard-items-face))) + +(defun dashboard--ffap-guesser--adv (fnc &rest args) + "Advice execution around function `ffap-guesser'. + +Argument FNC is the adviced function. +Optional argument ARGS adviced function arguments." + (cl-case major-mode + (`dashboard-mode + (or (and (dashboard--on-path-item-p) + (dashboard--current-item-in-path)) + (apply fnc args))) ; fallback + (t (apply fnc args)))) +(advice-add 'ffap-guesser :around #'dashboard--ffap-guesser--adv) + +;; +;; Removal +;; +(defun dashboard-remove-item-under () + "Remove a item from the current item section." + (interactive) + (cl-case (dashboard--current-section) + (`recents (dashboard-remove-item-recentf)) + (`bookmarks (dashboard-remove-item-bookmarks)) + (`projects (dashboard-remove-item-projects)) + (`agenda (dashboard-remove-item-agenda)) + (`registers (dashboard-remove-item-registers))) + (dashboard--save-excursion (dashboard-refresh-buffer))) + +(defun dashboard-remove-item-recentf () + "Remove a file from `recentf-list'." + (interactive) + (let ((path (save-excursion (end-of-line) (ffap-guesser)))) + (setq recentf-list (delete path recentf-list))) + (dashboard-mute-apply (recentf-save-list))) + +(defun dashboard-remove-item-projects () + "Remove a path from `project--list'." + (interactive) + (let ((path (save-excursion (end-of-line) (ffap-guesser)))) + (dashboard-mute-apply + (cl-case dashboard-projects-backend + (`projectile ) ; TODO: .. + (`project-el (project-forget-projects-under path)))))) + +(defun dashboard-remove-item-bookmarks () + "Remove a bookmarks from `bookmark-alist'." + (interactive)) ; TODO: .. + +(defun dashboard-remove-item-agenda () + "Remove an agenda from `org-agenda-files'." + (interactive)) ; TODO: .. + +(defun dashboard-remove-item-registers () + "Remove a registers from `register-alist'." + (interactive)) ; TODO: .. + ;; ;; Confirmation ;;