From 7b990e5275bd15104d8c17b33e65fbc4e260ff6a Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Wed, 30 Jun 2021 18:01:49 -0500 Subject: [PATCH 01/14] Fix for issue with initial-input in org-ql-view--complete-buffers-files --- org-ql-view.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index 773c47f5..436f3e22 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -1054,8 +1054,9 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." "Return value for `org-ql-view-buffers-files' using completion." (cl-labels ((initial-input () (when org-ql-view-buffers-files - (org-ql-view--contract-buffers-files - org-ql-view-buffers-files)))) + (cons (car (org-ql-view--contract-buffers-files + org-ql-view-buffers-files)) + 0)))) (if (and org-ql-view-buffers-files (bufferp org-ql-view-buffers-files)) ;; Buffers can't be input by name, so if the default value is a buffer, just use it. From 7120b6334da5ce241e2738d0cb7fd387df2604a6 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Sat, 3 Jul 2021 02:17:33 -0500 Subject: [PATCH 02/14] Refactor of org-ql-view--complete-buffers-files Handles the different values `org-ql-view-buffers-files` can hold. Use completing-read only if `org-ql-view-buffers-files` is nil or the contracted form of `org-ql-view-buffers-files` is a string. --- org-ql-view.el | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index 436f3e22..14035841 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -1051,21 +1051,23 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." (_ buffers-files)))) (defun org-ql-view--complete-buffers-files () - "Return value for `org-ql-view-buffers-files' using completion." - (cl-labels ((initial-input - () (when org-ql-view-buffers-files - (cons (car (org-ql-view--contract-buffers-files - org-ql-view-buffers-files)) - 0)))) - (if (and org-ql-view-buffers-files - (bufferp org-ql-view-buffers-files)) - ;; Buffers can't be input by name, so if the default value is a buffer, just use it. - ;; TODO: Find a way to fix this. - org-ql-view-buffers-files - (org-ql-view--expand-buffers-files - (completing-read "Buffers/Files: " - (list 'buffer 'org-agenda-files 'org-directory 'all) - nil nil (initial-input)))))) + "Return value for `org-ql-view-buffers-files' using completion. +When `org-ql-view-buffers-files' cannot be contracted to a string +representation `org-ql-view-buffers-files' is returned." + (if (and org-ql-view-buffers-files + (bufferp org-ql-view-buffers-files)) + ;; Buffers can't be input by name, so if the default value is a buffer, just use it. + ;; TODO: Find a way to fix this. + org-ql-view-buffers-files + (let ((initial-input (when org-ql-view-buffers-files + (org-ql-view--contract-buffers-files + org-ql-view-buffers-files)))) + (if (or (not initial-input) (stringp initial-input)) + (org-ql-view--expand-buffers-files + (completing-read "Buffers/Files: " + (list 'buffer 'org-agenda-files 'org-directory 'all) + nil nil initial-input)) + org-ql-view-buffers-files)))) (defun org-ql-view--expand-buffers-files (buffers-files) "Return BUFFERS-FILES expanded to a list of files or buffers. From 89b84768ad4c451bd43d17d072ef081f3dd521e1 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Sat, 3 Jul 2021 21:25:10 -0500 Subject: [PATCH 03/14] Test cases for all org-ql-view--*-buffers-files functions --- tests/test-org-ql.el | 79 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/test-org-ql.el b/tests/test-org-ql.el index cd0a846a..aa564f55 100644 --- a/tests/test-org-ql.el +++ b/tests/test-org-ql.el @@ -2111,7 +2111,84 @@ with keyword arg NOW in PLIST." (it "Refuses to link to non-file-backed buffer" (expect (var-after-link-save-open 'org-ql-view-buffers-files link-buffer query :buffer link-buffer) - :to-throw 'user-error '("Views that search non-file-backed buffers can’t be linked to")))))) + :to-throw 'user-error '("Views that search non-file-backed buffers can’t be linked to")))) + (describe "Completion for Files/buffers" + (describe "Contracting org-ql-view-buffers-files" + (it "org-agenda-files from list" + (spy-on 'org-agenda-files :and-return-value temp-filenames) + (expect (org-ql-view--contract-buffers-files temp-filenames) :to-equal "org-agenda-files")) + (it "org-directory" + (spy-on 'org-ql-search-directories-files :and-return-value temp-filenames) + (spy-on 'org-agenda-files :and-return-value '()) + (expect (org-ql-view--contract-buffers-files temp-filenames) :to-equal temp-filenames)) + (it "buffer" + (with-current-buffer (org-ql-test-data-buffer "data.org") + (expect (org-ql-view--contract-buffers-files (current-buffer)) :to-equal "buffer"))) + (it "org-agenda-files from symbol" + (spy-on 'org-agenda-files :and-return-value temp-filenames) + (expect (org-ql-view--contract-buffers-files 'org-agenda-files) :to-equal "org-agenda-files") + (expect (org-ql-view--contract-buffers-files #'org-agenda-files) :to-equal "org-agenda-files")) + (it "Non specific list" + (let ((value1 '("a.org" "b.org")) + (value2 'a)) + (expect (org-ql-view--contract-buffers-files value1) :to-equal value1) + (expect (org-ql-view--contract-buffers-files value2) :to-equal value2)))) + (describe "Expanding org-ql-view-buffers-files" + (it "all" + (let* ((buffer-names (list (make-temp-file "test" nil ".org") (make-temp-file "test" nil ".other"))) + (buffers (mapcar #'get-buffer-create buffer-names))) + (save-excursion + (switch-to-buffer (car buffers)) + (org-mode)) + (spy-on 'buffer-list :and-return-value buffers) + (expect (org-ql-view--expand-buffers-files "all") :to-equal (list (car buffers))))) + (it "org-agenda-files" + (spy-on 'org-agenda-files :and-return-value "value for org-agenda-files") + (expect (org-ql-view--expand-buffers-files "org-agenda-files") :to-equal "value for org-agenda-files")) + (it "org-directory" + (spy-on 'org-ql-search-directories-files :and-return-value "value for org-directory") + (expect (org-ql-view--expand-buffers-files "org-directory") :to-equal "value for org-directory")) + (it "buffer" + (with-temp-buffer + (expect (org-ql-view--expand-buffers-files "buffer") :to-equal (current-buffer)))) + (it "literal values" + (with-temp-buffer + (expect (org-ql-view--expand-buffers-files (current-buffer)) :to-equal (current-buffer))) + (let ((test-buffer (get-buffer-create (make-temp-file "test")))) + (expect (org-ql-view--expand-buffers-files test-buffer) :to-equal test-buffer)) + (let ((test-list '(1 2 3))) + (expect (org-ql-view--expand-buffers-files test-list) :to-equal test-list)) + (let ((string-literal "this is a string")) + (expect (org-ql-view--expand-buffers-files string-literal) :to-equal string-literal)))) + (describe "Testing org-ql-view--complete-buffers-files" + (it "org-agenda-files" + (let ((org-ql-view-buffers-files temp-filenames)) + (spy-on 'org-ql-view--contract-buffers-files :and-call-through) + (spy-on 'org-agenda-files :and-return-value temp-filenames) + (spy-on 'completing-read :and-return-value "org-agenda-files") + (expect (org-ql-view--complete-buffers-files) :to-equal temp-filenames) + (expect 'org-ql-view--contract-buffers-files :to-have-been-called-with temp-filenames) + (expect 'completing-read :to-have-been-called-with "Buffers/Files: " + (list 'buffer 'org-agenda-files 'org-directory 'all) + nil nil "org-agenda-files"))) + (it "nil" + (let ((org-ql-view-buffers-files nil)) + (spy-on 'completing-read :and-return-value nil) + (expect (org-ql-view--complete-buffers-files) :to-equal nil) + (expect 'completing-read :to-have-been-called-with "Buffers/Files: " + (list 'buffer 'org-agenda-files 'org-directory 'all) + nil nil nil))) + (it "list" + (let ((org-ql-view-buffers-files '(1 2 3))) + (spy-on 'completing-read :and-return-value nil) + (expect (org-ql-view--complete-buffers-files) :to-equal org-ql-view-buffers-files) + (expect 'completing-read :not :to-have-been-called))) + (it "buffer" + (let ((org-ql-view-buffers-files (get-buffer-create (make-temp-file "test")))) + (spy-on 'completing-read :and-return-value nil) + (expect (org-ql-view--complete-buffers-files) :to-equal org-ql-view-buffers-files) + (expect 'completing-read :not :to-have-been-called))))))) + ;; MAYBE: Also test `org-ql-views', although I already know it works now. ;; (describe "org-ql-views") From cc6e22dd309be75d462255a7fce0449d92beb188 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Sun, 4 Jul 2021 23:31:31 -0500 Subject: [PATCH 04/14] Allow reading buffer names for buffers-or-names --- org-ql.el | 1 + 1 file changed, 1 insertion(+) diff --git a/org-ql.el b/org-ql.el index 193592a0..602cb195 100644 --- a/org-ql.el +++ b/org-ql.el @@ -367,6 +367,7 @@ returns nil or non-nil." ;; It feels unintuitive that `find-file-noselect' returns ;; a buffer if the filename doesn't exist. (find-file-noselect it)) + (get-buffer it) (user-error "Can't open file: %s" it))))) ;; Ignore special/hidden buffers. (--remove (string-prefix-p " " (buffer-name it))))) From 06bdfc7a9ec8db8d0a4849a16176f3a6c5c370e5 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Sun, 4 Jul 2021 23:32:08 -0500 Subject: [PATCH 05/14] Fn always promts for buffer/files; handle buffer obj and list Fn: org-ql-view--complete-buffers-files With buffers, org-ql-view--contract-buffers-files return the buffer name. When org-ql-view-buffers-files is a list, just dumpt it as a string and check if completion-read returns the same value, if so return the original value of org-ql-view-buffers-files --- org-ql-view.el | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index 14035841..4d0df2b4 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -1048,26 +1048,29 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." "org-agenda-files") ((and (pred bufferp) (guard (buffer-file-name buffers-files))) (buffer-file-name buffers-files)) + ((pred bufferp) + (buffer-name buffers-files)) (_ buffers-files)))) (defun org-ql-view--complete-buffers-files () "Return value for `org-ql-view-buffers-files' using completion. When `org-ql-view-buffers-files' cannot be contracted to a string representation `org-ql-view-buffers-files' is returned." - (if (and org-ql-view-buffers-files - (bufferp org-ql-view-buffers-files)) - ;; Buffers can't be input by name, so if the default value is a buffer, just use it. - ;; TODO: Find a way to fix this. - org-ql-view-buffers-files - (let ((initial-input (when org-ql-view-buffers-files - (org-ql-view--contract-buffers-files - org-ql-view-buffers-files)))) - (if (or (not initial-input) (stringp initial-input)) - (org-ql-view--expand-buffers-files - (completing-read "Buffers/Files: " - (list 'buffer 'org-agenda-files 'org-directory 'all) - nil nil initial-input)) - org-ql-view-buffers-files)))) + (let* ((contracted-org-ql-view-buffers-files + (when org-ql-view-buffers-files + (org-ql-view--contract-buffers-files + org-ql-view-buffers-files))) + (initial-input (if (or (not contracted-org-ql-view-buffers-files) ;; not nil + (stringp contracted-org-ql-view-buffers-files)) + contracted-org-ql-view-buffers-files + (format "%s" contracted-org-ql-view-buffers-files))) + (completion-read-result (completing-read + "Buffers/Files: " + (list 'buffer 'org-agenda-files 'org-directory 'all) + nil nil initial-input))) + (if (equalp completion-read-result initial-input) + org-ql-view-buffers-files + (org-ql-view--expand-buffers-files completion-read-result)))) (defun org-ql-view--expand-buffers-files (buffers-files) "Return BUFFERS-FILES expanded to a list of files or buffers. From b68d83656265cbdbb4133dddc0280f54ee88fa87 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Sun, 18 Jul 2021 23:49:54 -0500 Subject: [PATCH 06/14] Using completing-read-multiple for org-ql-view--complete-buffers-files --- org-ql-view.el | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index 4d0df2b4..97e2c5ef 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -1060,11 +1060,14 @@ representation `org-ql-view-buffers-files' is returned." (when org-ql-view-buffers-files (org-ql-view--contract-buffers-files org-ql-view-buffers-files))) - (initial-input (if (or (not contracted-org-ql-view-buffers-files) ;; not nil - (stringp contracted-org-ql-view-buffers-files)) - contracted-org-ql-view-buffers-files - (format "%s" contracted-org-ql-view-buffers-files))) - (completion-read-result (completing-read + (initial-input (pcase contracted-org-ql-view-buffers-files + ('nil nil) + ('string contracted-org-ql-view-buffers-files) + ((pred listp) + (mapconcat 'identity contracted-org-ql-view-buffers-files + ",")) + (_ (format "%s" contracted-org-ql-view-buffers-files)))) + (completion-read-result (completing-read-multiple "Buffers/Files: " (list 'buffer 'org-agenda-files 'org-directory 'all) nil nil initial-input))) @@ -1075,17 +1078,24 @@ representation `org-ql-view-buffers-files' is returned." (defun org-ql-view--expand-buffers-files (buffers-files) "Return BUFFERS-FILES expanded to a list of files or buffers. The counterpart to `org-ql-view--contract-buffers-files'." - (pcase-exhaustive buffers-files - ("all" (--select (equal (buffer-local-value 'major-mode it) 'org-mode) - (buffer-list))) - ("org-agenda-files" (org-agenda-files)) - ("org-directory" (org-ql-search-directories-files)) - ((or "" "buffer") (current-buffer)) - ((pred bufferp) buffers-files) - ((pred listp) buffers-files) - ;; A single filename. - ((pred stringp) buffers-files))) - + (cl-labels + ((process-buffers-files-elements + (_buffers-files) + (pcase-exhaustive _buffers-files + ("all" (--select (equal (buffer-local-value 'major-mode it) 'org-mode) + (buffer-list))) + ("org-agenda-files" (org-agenda-files)) + ("org-directory" (org-ql-search-directories-files)) + ((or "" "buffer") (current-buffer)) + ((pred bufferp) (list _buffers-files)) + ((pred listp) (list _buffers-files)) + ;; A single filename. + ((pred stringp) (list _buffers-files))))) + (-mapcat #'process-buffers-files-elements + (remove nil (if (listp buffers-files) + buffers-files + (list buffer-files)))))) + (defun org-ql-view--complete-super-groups () "Return value for `org-ql-view-super-groups' using completion." (when (bound-and-true-p org-super-agenda-auto-selector-keywords) From cabf88eea5014149d9aabecc9a80cc853fd3b961 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Mon, 19 Jul 2021 00:19:52 -0500 Subject: [PATCH 07/14] removing duplcates when expanding buffers-files --- org-ql-view.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index 97e2c5ef..1faff2eb 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -1091,10 +1091,11 @@ The counterpart to `org-ql-view--contract-buffers-files'." ((pred listp) (list _buffers-files)) ;; A single filename. ((pred stringp) (list _buffers-files))))) - (-mapcat #'process-buffers-files-elements - (remove nil (if (listp buffers-files) - buffers-files - (list buffer-files)))))) + (remove-duplicates (-mapcat #'process-buffers-files-elements + (remove nil (if (listp buffers-files) + buffers-files + (list buffer-files)))) + :test 'equalp))) (defun org-ql-view--complete-super-groups () "Return value for `org-ql-view-super-groups' using completion." From 788951a4a9068b5ba14240fd25e4293a3712a716 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Mon, 13 Sep 2021 11:01:55 -0500 Subject: [PATCH 08/14] Replacing org-ql-view--expand-buffers-files --- org-ql-view.el | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index 1faff2eb..73a5416e 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -1078,24 +1078,18 @@ representation `org-ql-view-buffers-files' is returned." (defun org-ql-view--expand-buffers-files (buffers-files) "Return BUFFERS-FILES expanded to a list of files or buffers. The counterpart to `org-ql-view--contract-buffers-files'." - (cl-labels - ((process-buffers-files-elements - (_buffers-files) - (pcase-exhaustive _buffers-files - ("all" (--select (equal (buffer-local-value 'major-mode it) 'org-mode) - (buffer-list))) - ("org-agenda-files" (org-agenda-files)) - ("org-directory" (org-ql-search-directories-files)) - ((or "" "buffer") (current-buffer)) - ((pred bufferp) (list _buffers-files)) - ((pred listp) (list _buffers-files)) - ;; A single filename. - ((pred stringp) (list _buffers-files))))) - (remove-duplicates (-mapcat #'process-buffers-files-elements - (remove nil (if (listp buffers-files) - buffers-files - (list buffer-files)))) - :test 'equalp))) + (--> (-list buffers-files) + (pcase-exhaustive it + ("all" (--select (equal (buffer-local-value 'major-mode it) 'org-mode) + (buffer-list))) + ("org-agenda-files" (org-agenda-files)) + ("org-directory" (org-ql-search-directories-files)) + ((or "" "buffer") (current-buffer)) + ((pred bufferp) (list it)) + ((pred listp) (list it)) + ;; A single filename. + ((pred stringp) (list it))) + -non-nil -uniq -flatten)) (defun org-ql-view--complete-super-groups () "Return value for `org-ql-view-super-groups' using completion." From 564d491604f76aa05e6e0a8e9d87b762153a78fd Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Mon, 13 Sep 2021 17:05:14 -0500 Subject: [PATCH 09/14] Fixes for using mulitiple values and related test suite improvements --- org-ql-view.el | 96 ++++++++++++++++++++++------------- tests/test-org-ql.el | 118 +++++++++++++++++++++---------------------- 2 files changed, 120 insertions(+), 94 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index 99277d56..90e41f8b 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -1032,26 +1032,39 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." (string (expand-file-name it)) (otherwise it)) list))) - ;; TODO: Test this more exhaustively. - (pcase buffers-files - ((pred listp) - (pcase (expand-files buffers-files) - ((pred (seq-set-equal-p (mapcar #'expand-file-name (org-agenda-files)))) - "org-agenda-files") - ((and (guard (file-exists-p org-directory)) - (pred (seq-set-equal-p (org-ql-search-directories-files - :directories (list org-directory))))) - "org-directory") - (_ buffers-files))) - ((pred (equal (current-buffer))) - "buffer") - ((or 'org-agenda-files '(function org-agenda-files)) - "org-agenda-files") - ((and (pred bufferp) (guard (buffer-file-name buffers-files))) - (buffer-file-name buffers-files)) - ((pred bufferp) - (buffer-name buffers-files)) - (_ buffers-files)))) + (--> + ;; TODO: Test this more exhaustively. + (pcase buffers-files + ((pred listp) + (pcase (expand-files buffers-files) + ((pred (seq-set-equal-p (mapcar #'expand-file-name (org-agenda-files)))) + "org-agenda-files") + ((and (guard (file-exists-p org-directory)) + (pred (seq-set-equal-p (org-ql-search-directories-files + :directories (list org-directory))))) + "org-directory") + (_ buffers-files))) + ((pred (equal (current-buffer))) + "buffer") + ((or 'org-agenda-files '(function org-agenda-files)) + "org-agenda-files") + ((and (pred bufferp) (guard (buffer-file-name buffers-files))) + (buffer-file-name buffers-files)) + ((pred bufferp) + (buffer-name buffers-files)) + (_ buffers-files)) + ;; All items needs to be strings to pick duplicates when used with the extend conterpart. + ;; So making sure the buffers are convered to file names + (if (stringp it) + it + (-map + (lambda (buffer-file) + (if (bufferp buffer-file) + (--if-let (buffer-file-name buffer-file) + it + (buffer-name buffer-file)) + buffer-file)) + it))))) (defun org-ql-view--complete-buffers-files () "Return value for `org-ql-view-buffers-files' using completion. @@ -1072,26 +1085,39 @@ representation `org-ql-view-buffers-files' is returned." "Buffers/Files: " (list 'buffer 'org-agenda-files 'org-directory 'all) nil nil initial-input))) - (if (equalp completion-read-result initial-input) + (if (equal completion-read-result initial-input) org-ql-view-buffers-files (org-ql-view--expand-buffers-files completion-read-result)))) (defun org-ql-view--expand-buffers-files (buffers-files) "Return BUFFERS-FILES expanded to a list of files or buffers. -The counterpart to `org-ql-view--contract-buffers-files'." - (--> (-list buffers-files) - (pcase-exhaustive it - ("all" (--select (equal (buffer-local-value 'major-mode it) 'org-mode) - (buffer-list))) - ("org-agenda-files" (org-agenda-files)) - ("org-directory" (org-ql-search-directories-files)) - ((or "" "buffer") (current-buffer)) - ((pred bufferp) (list it)) - ((pred listp) (list it)) - ;; A single filename. - ((pred stringp) (list it))) - -non-nil -uniq -flatten)) - +The counterpart to `org-ql-view--contract-buffers-files'. +This always returns a list of string values." + (--> + (-map (lambda (buffer-file) + (pcase-exhaustive buffer-file + ("all" (--select (equal (buffer-local-value 'major-mode it) 'org-mode) + (buffer-list))) + ("org-agenda-files" (org-agenda-files)) + ("org-directory" (org-ql-search-directories-files)) + ((or "" "buffer") + (current-buffer)) + ((pred bufferp) (list buffer-file)) + ;; A single filename. + ((pred stringp) (list buffer-file)) + (_ (error (format "Value %s is not a valid buffer/file" buffer-file))))) + (-list buffers-files)) + -flatten -non-nil + ;; expanding all file-buffers to file names to avoid duplicate entries being formed + (-map (lambda (buffer-file) + (if (bufferp buffer-file) + (--if-let (buffer-file-name buffer-file) + it + (buffer-name buffer-file)) + buffer-file)) + it) + -uniq)) + (defun org-ql-view--complete-super-groups () "Return value for `org-ql-view-super-groups' using completion." (when (bound-and-true-p org-super-agenda-auto-selector-keywords) diff --git a/tests/test-org-ql.el b/tests/test-org-ql.el index aa564f55..dde43c02 100644 --- a/tests/test-org-ql.el +++ b/tests/test-org-ql.el @@ -1686,19 +1686,19 @@ with keyword arg NOW in PLIST." (unquoted-lambda-in-list-link "[[org-ql-search:todo:?buffers-files%3D%28%28lambda%20nil%20%28error%20%22UNSAFE%22%29%29%29]]")) (it "Errors for a quoted lambda" (expect (open-link quoted-lambda-link) - :to-throw 'error '("CAUTION: Link not opened because unsafe buffers-files parameter detected: (lambda nil (error UNSAFE))"))) + :to-throw 'error '("Value lambda is not a valid buffer/file"))) (it "Errors for an unquoted lambda" (expect (open-link unquoted-lambda-link) - :to-throw 'error '("CAUTION: Link not opened because unsafe buffers-files parameter detected: (lambda nil (error UNSAFE))"))) + :to-throw 'error '("Value lambda is not a valid buffer/file"))) (it "Errors for a quoted lambda in a list" (if (version< (org-version) "9.3") (expect (open-link quoted-lambda-in-list-link) - :to-throw 'error '("CAUTION: Link not opened because unsafe buffers-files parameter detected: ((quote (lambda nil (error UNSAFE))))")) + :to-throw 'error '("Value (quote (lambda nil (error UNSAFE))) is not a valid buffer/file")) (expect (open-link quoted-lambda-in-list-link) - :to-throw 'error '("CAUTION: Link not opened because unsafe buffers-files parameter detected: ('(lambda nil (error UNSAFE)))")))) + :to-throw 'error '("Value (quote (lambda nil (error UNSAFE))) is not a valid buffer/file")))) (it "Errors for an unquoted lambda in a list" (expect (open-link unquoted-lambda-in-list-link) - :to-throw 'error '("CAUTION: Link not opened because unsafe buffers-files parameter detected: ((lambda nil (error UNSAFE)))")))) + :to-throw 'error '("Value (lambda nil (error UNSAFE)) is not a valid buffer/file")))) (describe "super-groups parameter" :var ((quoted-lambda-link "[[org-ql-search:todo:?super-groups%3D%28lambda%20nil%20%28error%20%22UNSAFE%22%29%29]]") @@ -2068,7 +2068,7 @@ with keyword arg NOW in PLIST." (it "Can search a file by filename" (expect (var-after-link-save-open 'org-ql-view-buffers-files one-filename query :store-input "M-n M-n RET") - :to-equal one-filename)) + :to-equal (list one-filename))) (it "Can search multiple files by filename" (expect (var-after-link-save-open 'org-ql-view-buffers-files temp-filenames query :store-input "M-n M-n RET") @@ -2112,83 +2112,83 @@ with keyword arg NOW in PLIST." (expect (var-after-link-save-open 'org-ql-view-buffers-files link-buffer query :buffer link-buffer) :to-throw 'user-error '("Views that search non-file-backed buffers can’t be linked to")))) - (describe "Completion for Files/buffers" - (describe "Contracting org-ql-view-buffers-files" - (it "org-agenda-files from list" + (describe "while completion for files/buffers" + (describe "contracting org-ql-view-buffers-files" + (it "list of files to \"org-agenda-files\"" (spy-on 'org-agenda-files :and-return-value temp-filenames) (expect (org-ql-view--contract-buffers-files temp-filenames) :to-equal "org-agenda-files")) - (it "org-directory" - (spy-on 'org-ql-search-directories-files :and-return-value temp-filenames) + (it "list of files to \"org-directory\"" (spy-on 'org-agenda-files :and-return-value '()) - (expect (org-ql-view--contract-buffers-files temp-filenames) :to-equal temp-filenames)) - (it "buffer" + ;; Also indirectly tests org-ql-search-directories-files + (let ((org-directory temp-dir)) ;; the :var binding does not work? https://github.com/jorgenschaefer/emacs-buttercup/issues/127 + (expect (org-ql-view--contract-buffers-files temp-filenames) :to-equal "org-directory"))) + (it "to \"buffer\" when passing current-buffer" (with-current-buffer (org-ql-test-data-buffer "data.org") (expect (org-ql-view--contract-buffers-files (current-buffer)) :to-equal "buffer"))) - (it "org-agenda-files from symbol" + (it "to \"org-agenda-files\" from symbol values ('org-agenda-files or #'org-agenda-files)" (spy-on 'org-agenda-files :and-return-value temp-filenames) (expect (org-ql-view--contract-buffers-files 'org-agenda-files) :to-equal "org-agenda-files") (expect (org-ql-view--contract-buffers-files #'org-agenda-files) :to-equal "org-agenda-files")) - (it "Non specific list" + (it "arbitarary list of buffers/files" (let ((value1 '("a.org" "b.org")) (value2 'a)) (expect (org-ql-view--contract-buffers-files value1) :to-equal value1) - (expect (org-ql-view--contract-buffers-files value2) :to-equal value2)))) - (describe "Expanding org-ql-view-buffers-files" - (it "all" - (let* ((buffer-names (list (make-temp-file "test" nil ".org") (make-temp-file "test" nil ".other"))) - (buffers (mapcar #'get-buffer-create buffer-names))) - (save-excursion - (switch-to-buffer (car buffers)) + ;; If the value does not result to a buffer, file, or string, throws error + (expect (org-ql-view--contract-buffers-files value2) :to-throw)))) + (describe "expanding org-ql-view-buffers-files" + (it "returns all buffers with `org-mode' as the major-mode" + (let ((buffers (list (generate-new-buffer "test.org") (generate-new-buffer "test.other")))) + (with-current-buffer (car buffers) (org-mode)) (spy-on 'buffer-list :and-return-value buffers) - (expect (org-ql-view--expand-buffers-files "all") :to-equal (list (car buffers))))) - (it "org-agenda-files" - (spy-on 'org-agenda-files :and-return-value "value for org-agenda-files") - (expect (org-ql-view--expand-buffers-files "org-agenda-files") :to-equal "value for org-agenda-files")) - (it "org-directory" - (spy-on 'org-ql-search-directories-files :and-return-value "value for org-directory") - (expect (org-ql-view--expand-buffers-files "org-directory") :to-equal "value for org-directory")) - (it "buffer" + (expect (org-ql-view--expand-buffers-files "all") :to-equal (list (buffer-name (car buffers)))))) + (it "returns values of \"org-agenda-files\"" + (let ((org-agenda-files (mapcar + (lambda (it) + (buffer-name it)) + (list (generate-new-buffer "test1") + (generate-new-buffer "test2"))))) + (expect (org-ql-view--expand-buffers-files "org-agenda-files") :to-equal org-agenda-files))) + (it "returns values of \"org-directory\"" + ;; Also indirectly tests `org-ql-view--expand-buffers-files' + (let ((org-directory temp-dir)) + (expect (org-ql-view--expand-buffers-files "org-directory") :to-equal temp-filenames))) + (it "returns the current buffer" (with-temp-buffer - (expect (org-ql-view--expand-buffers-files "buffer") :to-equal (current-buffer)))) - (it "literal values" + (expect (org-ql-view--expand-buffers-files "buffer") :to-equal (list (buffer-name (current-buffer)))))) + (it "returns literal value(s)" (with-temp-buffer - (expect (org-ql-view--expand-buffers-files (current-buffer)) :to-equal (current-buffer))) - (let ((test-buffer (get-buffer-create (make-temp-file "test")))) - (expect (org-ql-view--expand-buffers-files test-buffer) :to-equal test-buffer)) - (let ((test-list '(1 2 3))) - (expect (org-ql-view--expand-buffers-files test-list) :to-equal test-list)) - (let ((string-literal "this is a string")) - (expect (org-ql-view--expand-buffers-files string-literal) :to-equal string-literal)))) - (describe "Testing org-ql-view--complete-buffers-files" - (it "org-agenda-files" + (expect (org-ql-view--expand-buffers-files (current-buffer)) :to-equal (list (buffer-name (current-buffer))))) + (let ((test-buffer (generate-new-buffer "test"))) + (expect (org-ql-view--expand-buffers-files test-buffer) :to-equal (list (buffer-name test-buffer)))) + (let ((list-of-numbers '(1 2 3)) + (literal-string "random string")) + ;; If the value does not result to a buffer, file, or string, throws error + (expect (org-ql-view--expand-buffers-files list-of-numbers) :to-throw) + (expect (org-ql-view--expand-buffers-files literal-string) :to-equal (list literal-string))))) + (describe "testing `org-ql-view--complete-buffers-files'" + (it "returns `org-agenda-files'" (let ((org-ql-view-buffers-files temp-filenames)) (spy-on 'org-ql-view--contract-buffers-files :and-call-through) (spy-on 'org-agenda-files :and-return-value temp-filenames) - (spy-on 'completing-read :and-return-value "org-agenda-files") + (spy-on 'completing-read-multiple :and-return-value "org-agenda-files") (expect (org-ql-view--complete-buffers-files) :to-equal temp-filenames) (expect 'org-ql-view--contract-buffers-files :to-have-been-called-with temp-filenames) - (expect 'completing-read :to-have-been-called-with "Buffers/Files: " - (list 'buffer 'org-agenda-files 'org-directory 'all) - nil nil "org-agenda-files"))) - (it "nil" + ;; Also testing if the initial values are set correctly + (expect 'completing-read-multiple :to-have-been-called-with "Buffers/Files: " + (list 'buffer 'org-agenda-files 'org-directory 'all) + nil nil "org-agenda-files"))) + (it "returns nil" (let ((org-ql-view-buffers-files nil)) - (spy-on 'completing-read :and-return-value nil) + (spy-on 'completing-read-multiple :and-return-value nil) (expect (org-ql-view--complete-buffers-files) :to-equal nil) - (expect 'completing-read :to-have-been-called-with "Buffers/Files: " + (expect 'completing-read-multiple :to-have-been-called-with "Buffers/Files: " (list 'buffer 'org-agenda-files 'org-directory 'all) nil nil nil))) - (it "list" - (let ((org-ql-view-buffers-files '(1 2 3))) - (spy-on 'completing-read :and-return-value nil) - (expect (org-ql-view--complete-buffers-files) :to-equal org-ql-view-buffers-files) - (expect 'completing-read :not :to-have-been-called))) - (it "buffer" - (let ((org-ql-view-buffers-files (get-buffer-create (make-temp-file "test")))) - (spy-on 'completing-read :and-return-value nil) - (expect (org-ql-view--complete-buffers-files) :to-equal org-ql-view-buffers-files) - (expect 'completing-read :not :to-have-been-called))))))) - + (it "returna a list of buffers/files" + (let ((list-of-files temp-filenames)) + (spy-on 'completing-read-multiple :and-return-value list-of-files) + (expect (org-ql-view--complete-buffers-files) :to-equal list-of-files))))))) ;; MAYBE: Also test `org-ql-views', although I already know it works now. ;; (describe "org-ql-views") From aa9c6ccdfa0ca43f020a8828d5874fd32611bdbf Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Wed, 15 Sep 2021 17:56:19 -0500 Subject: [PATCH 10/14] Adding `comma separated` instruction to readme --- README.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.org b/README.org index 1ebfead1..47aad960 100644 --- a/README.org +++ b/README.org @@ -115,7 +115,7 @@ Read ~QUERY~ and search with ~org-ql~. Interactively, prompt for these variable + ~buffer~: search the current buffer + ~all~: search all Org buffers + ~agenda~: search buffers returned by the function ~org-agenda-files~ -+ A space-separated list of file or buffer names ++ A comma-separated list of file, buffer names, or the above keywords ~GROUPS~: An ~org-super-agenda~ group set. See variable ~org-super-agenda-groups~. From cea165192d25e6115adf7e309fa53b45aff2f633 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Thu, 16 Sep 2021 16:49:19 -0500 Subject: [PATCH 11/14] Cleaning up functions and improvements for clarity. --- org-ql-view.el | 121 +++++++++++++++++++++++-------------------- tests/test-org-ql.el | 16 +++--- 2 files changed, 73 insertions(+), 64 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index 90e41f8b..a7076944 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -1032,39 +1032,41 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." (string (expand-file-name it)) (otherwise it)) list))) - (--> - ;; TODO: Test this more exhaustively. - (pcase buffers-files - ((pred listp) - (pcase (expand-files buffers-files) - ((pred (seq-set-equal-p (mapcar #'expand-file-name (org-agenda-files)))) + (let ((contracted-buffers-files + ;; TODO: Test this more exhaustively. + (pcase buffers-files + ((pred listp) + (pcase (expand-files buffers-files) + ((pred (seq-set-equal-p (mapcar #'expand-file-name (org-agenda-files)))) + "org-agenda-files") + ((and (guard (file-exists-p org-directory)) + (pred (seq-set-equal-p (org-ql-search-directories-files + :directories (list org-directory))))) + "org-directory") + (_ buffers-files))) + ((pred (equal (current-buffer))) + "buffer") + ((or 'org-agenda-files '(function org-agenda-files)) "org-agenda-files") - ((and (guard (file-exists-p org-directory)) - (pred (seq-set-equal-p (org-ql-search-directories-files - :directories (list org-directory))))) - "org-directory") - (_ buffers-files))) - ((pred (equal (current-buffer))) - "buffer") - ((or 'org-agenda-files '(function org-agenda-files)) - "org-agenda-files") - ((and (pred bufferp) (guard (buffer-file-name buffers-files))) - (buffer-file-name buffers-files)) - ((pred bufferp) - (buffer-name buffers-files)) - (_ buffers-files)) - ;; All items needs to be strings to pick duplicates when used with the extend conterpart. - ;; So making sure the buffers are convered to file names - (if (stringp it) - it - (-map - (lambda (buffer-file) - (if (bufferp buffer-file) - (--if-let (buffer-file-name buffer-file) - it - (buffer-name buffer-file)) - buffer-file)) - it))))) + ((and (pred bufferp) (guard (buffer-file-name buffers-files))) + (buffer-file-name buffers-files)) + ((pred bufferp) + (buffer-name buffers-files)) + (_ buffers-files)))) + ;; To filter duplicates with the extend counterpart of this function, + ;; this needs to be a string or a list of string. + ;; Hence, making sure the buffers are convered to file names or buffer names. + ;; Using file-names when it's a file-buffer to avoid duplicates resulting from + ;; the file-buffer and file name being entered. + (cl-typecase contracted-buffers-files + (string contracted-buffers-files) + (list (--map + (pcase-exhaustive it + ((pred stringp) it) + ((pred bufferp) (or (buffer-file-name it) + (buffer-name buffer-file)))) + contracted-buffers-files)) + (t (error (format "Value %s is not a string or a list of buffer/strings" contracted-buffers-files))))))) (defun org-ql-view--complete-buffers-files () "Return value for `org-ql-view-buffers-files' using completion. @@ -1093,30 +1095,37 @@ representation `org-ql-view-buffers-files' is returned." "Return BUFFERS-FILES expanded to a list of files or buffers. The counterpart to `org-ql-view--contract-buffers-files'. This always returns a list of string values." - (--> - (-map (lambda (buffer-file) - (pcase-exhaustive buffer-file - ("all" (--select (equal (buffer-local-value 'major-mode it) 'org-mode) - (buffer-list))) - ("org-agenda-files" (org-agenda-files)) - ("org-directory" (org-ql-search-directories-files)) - ((or "" "buffer") - (current-buffer)) - ((pred bufferp) (list buffer-file)) - ;; A single filename. - ((pred stringp) (list buffer-file)) - (_ (error (format "Value %s is not a valid buffer/file" buffer-file))))) - (-list buffers-files)) - -flatten -non-nil - ;; expanding all file-buffers to file names to avoid duplicate entries being formed - (-map (lambda (buffer-file) - (if (bufferp buffer-file) - (--if-let (buffer-file-name buffer-file) - it - (buffer-name buffer-file)) - buffer-file)) - it) - -uniq)) + (let ((expanded-buffers-files + (--> buffers-files + -list -non-nil + (-map (lambda (buffer-file) + (pcase-exhaustive buffer-file + ("all" (--select (equal (buffer-local-value 'major-mode it) 'org-mode) + (buffer-list))) + ("org-agenda-files" (org-agenda-files)) + ("org-directory" (org-ql-search-directories-files)) + ((or "" "buffer") + (current-buffer)) + ((or (pred bufferp) + ;; A single filename. + (pred stringp)) + buffer-file) + (_ (error (format "Value %s is not a valid buffer/file" buffer-file))))) + it)))) + (--> expanded-buffers-files + -flatten + ;; removing `nil' again as some values have been expanded. + -non-nil + ;; Expanding all buffers to file names or buffer names to remove duplicate entries. + (--map + (pcase-exhaustive it + ((pred bufferp) (or (buffer-file-name it) + (buffer-name it))) + ;; Any values at this point should be a buffer or string. + ;; Testing for string anyways. + ((pred stringp) it)) + it) + -uniq))) (defun org-ql-view--complete-super-groups () "Return value for `org-ql-view-super-groups' using completion." diff --git a/tests/test-org-ql.el b/tests/test-org-ql.el index dde43c02..9f76d689 100644 --- a/tests/test-org-ql.el +++ b/tests/test-org-ql.el @@ -2130,11 +2130,11 @@ with keyword arg NOW in PLIST." (expect (org-ql-view--contract-buffers-files 'org-agenda-files) :to-equal "org-agenda-files") (expect (org-ql-view--contract-buffers-files #'org-agenda-files) :to-equal "org-agenda-files")) (it "arbitarary list of buffers/files" - (let ((value1 '("a.org" "b.org")) - (value2 'a)) - (expect (org-ql-view--contract-buffers-files value1) :to-equal value1) - ;; If the value does not result to a buffer, file, or string, throws error - (expect (org-ql-view--contract-buffers-files value2) :to-throw)))) + (let ((list-of-strings '("a.org" "b.org")) + (invalid-type 'a)) + (expect (org-ql-view--contract-buffers-files list-of-strings) :to-equal list-of-strings) + ;; Signal error if value is not a buffer, file, or string. + (expect (org-ql-view--contract-buffers-files invalid-type) :to-throw)))) (describe "expanding org-ql-view-buffers-files" (it "returns all buffers with `org-mode' as the major-mode" (let ((buffers (list (generate-new-buffer "test.org") (generate-new-buffer "test.other")))) @@ -2150,7 +2150,7 @@ with keyword arg NOW in PLIST." (generate-new-buffer "test2"))))) (expect (org-ql-view--expand-buffers-files "org-agenda-files") :to-equal org-agenda-files))) (it "returns values of \"org-directory\"" - ;; Also indirectly tests `org-ql-view--expand-buffers-files' + ;; Also indirectly tests `org-ql-view--expand-buffers-files'. (let ((org-directory temp-dir)) (expect (org-ql-view--expand-buffers-files "org-directory") :to-equal temp-filenames))) (it "returns the current buffer" @@ -2163,7 +2163,7 @@ with keyword arg NOW in PLIST." (expect (org-ql-view--expand-buffers-files test-buffer) :to-equal (list (buffer-name test-buffer)))) (let ((list-of-numbers '(1 2 3)) (literal-string "random string")) - ;; If the value does not result to a buffer, file, or string, throws error + ;; Signal error if any of the values are not a buffer, file, or string. (expect (org-ql-view--expand-buffers-files list-of-numbers) :to-throw) (expect (org-ql-view--expand-buffers-files literal-string) :to-equal (list literal-string))))) (describe "testing `org-ql-view--complete-buffers-files'" @@ -2174,7 +2174,7 @@ with keyword arg NOW in PLIST." (spy-on 'completing-read-multiple :and-return-value "org-agenda-files") (expect (org-ql-view--complete-buffers-files) :to-equal temp-filenames) (expect 'org-ql-view--contract-buffers-files :to-have-been-called-with temp-filenames) - ;; Also testing if the initial values are set correctly + ;; Also testing if the initial values are set correctly. (expect 'completing-read-multiple :to-have-been-called-with "Buffers/Files: " (list 'buffer 'org-agenda-files 'org-directory 'all) nil nil "org-agenda-files"))) From 576f9d3512769a073a470a500a832c70ea1afa04 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Thu, 16 Sep 2021 18:42:32 -0500 Subject: [PATCH 12/14] Handling support for functions \w functions using completing-read-multiple --- org-ql-view.el | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index a7076944..10e78874 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -658,9 +658,12 @@ When opened, the link searches the buffer it's opened from." '("file link is in" "files currently searched") nil t nil nil "file link is in") ("file link is in" nil) - ("files currently searched" buffers-files))) + ("files currently searched" (cl-typecase buffers-files + (function (funcall buffers-files)) + (otherwise buffers-files))))) (strings-or-file-buffers-p (thing) (cl-etypecase thing + (functionp t) (list (cl-every #'strings-or-file-buffers-p thing)) (string thing) (buffer (or (buffer-file-name thing) @@ -1035,6 +1038,7 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." (let ((contracted-buffers-files ;; TODO: Test this more exhaustively. (pcase buffers-files + ((pred functionp) buffers-files) ((pred listp) (pcase (expand-files buffers-files) ((pred (seq-set-equal-p (mapcar #'expand-file-name (org-agenda-files)))) @@ -1059,6 +1063,7 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." ;; Using file-names when it's a file-buffer to avoid duplicates resulting from ;; the file-buffer and file name being entered. (cl-typecase contracted-buffers-files + (function contracted-buffers-files) (string contracted-buffers-files) (list (--map (pcase-exhaustive it @@ -1066,7 +1071,7 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." ((pred bufferp) (or (buffer-file-name it) (buffer-name buffer-file)))) contracted-buffers-files)) - (t (error (format "Value %s is not a string or a list of buffer/strings" contracted-buffers-files))))))) + (t (error (format "Value %s is not a string, a valid function or a list of buffer/strings" contracted-buffers-files))))))) (defun org-ql-view--complete-buffers-files () "Return value for `org-ql-view-buffers-files' using completion. @@ -1079,14 +1084,17 @@ representation `org-ql-view-buffers-files' is returned." (initial-input (pcase contracted-org-ql-view-buffers-files ('nil nil) ('string contracted-org-ql-view-buffers-files) + ((pred functionp) contracted-org-ql-view-buffers-files) ((pred listp) (mapconcat 'identity contracted-org-ql-view-buffers-files ",")) (_ (format "%s" contracted-org-ql-view-buffers-files)))) - (completion-read-result (completing-read-multiple - "Buffers/Files: " - (list 'buffer 'org-agenda-files 'org-directory 'all) - nil nil initial-input))) + (completion-read-result (if (functionp contracted-org-ql-view-buffers-files) + contracted-org-ql-view-buffers-files + (completing-read-multiple + "Buffers/Files: " + (list 'buffer 'org-agenda-files 'org-directory 'all) + nil nil initial-input)))) (if (equal completion-read-result initial-input) org-ql-view-buffers-files (org-ql-view--expand-buffers-files completion-read-result)))) From 2d4974542591a74186bd8ba35e545cd5612be541 Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Fri, 17 Sep 2021 17:53:21 -0500 Subject: [PATCH 13/14] Fix: \w duplicates & func's with buffers/names with expand/contract --- org-ql-view.el | 19 ++++++++++++------- tests/test-org-ql.el | 43 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index 10e78874..787da844 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -1038,7 +1038,9 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." (let ((contracted-buffers-files ;; TODO: Test this more exhaustively. (pcase buffers-files - ((pred functionp) buffers-files) + ((pred functionp) (pcase buffers-files + ('org-agenda-files "org-agenda-files") + (_ buffers-files))) ((pred listp) (pcase (expand-files buffers-files) ((pred (seq-set-equal-p (mapcar #'expand-file-name (org-agenda-files)))) @@ -1065,12 +1067,15 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." (cl-typecase contracted-buffers-files (function contracted-buffers-files) (string contracted-buffers-files) - (list (--map - (pcase-exhaustive it - ((pred stringp) it) - ((pred bufferp) (or (buffer-file-name it) - (buffer-name buffer-file)))) - contracted-buffers-files)) + (list (--> contracted-buffers-files + -flatten -non-nil + (--map + (pcase-exhaustive it + ((pred stringp) it) + ((pred bufferp) (or (buffer-file-name it) + (buffer-name buffer-file)))) + it) + -uniq)) (t (error (format "Value %s is not a string, a valid function or a list of buffer/strings" contracted-buffers-files))))))) (defun org-ql-view--complete-buffers-files () diff --git a/tests/test-org-ql.el b/tests/test-org-ql.el index 9f76d689..c099822b 100644 --- a/tests/test-org-ql.el +++ b/tests/test-org-ql.el @@ -2129,14 +2129,25 @@ with keyword arg NOW in PLIST." (spy-on 'org-agenda-files :and-return-value temp-filenames) (expect (org-ql-view--contract-buffers-files 'org-agenda-files) :to-equal "org-agenda-files") (expect (org-ql-view--contract-buffers-files #'org-agenda-files) :to-equal "org-agenda-files")) - (it "arbitarary list of buffers/files" - (let ((list-of-strings '("a.org" "b.org")) - (invalid-type 'a)) - (expect (org-ql-view--contract-buffers-files list-of-strings) :to-equal list-of-strings) - ;; Signal error if value is not a buffer, file, or string. - (expect (org-ql-view--contract-buffers-files invalid-type) :to-throw)))) + (it "returns function" + (let ((quoted-function (lambda nil temp-filenames)) + (unquoted-function '(lambda nil temp-filenames))) + (expect (org-ql-view--contract-buffers-files quoted-function) :to-equal quoted-function) + (expect (org-ql-view--contract-buffers-files unquoted-function) :to-equal unquoted-function))) + (it "with a list of strings" + (let ((list-of-strings '("a.org" "b.org"))) + (expect (org-ql-view--contract-buffers-files list-of-strings) :to-equal list-of-strings))) + (describe "invalid values" + :var ((list-of-strings-and-functions '("a.org" "b.org" 'org-agenda-files)) + (invalid-type 'a) + (invalid-type-list '(a))) + (it "signals error if called with value not a buffer, or string" + (expect (org-ql-view--contract-buffers-files list-of-strings-and-functions) :to-throw) + (expect (org-ql-view--contract-buffers-files invalid-type) :to-throw) + (expect (org-ql-view--contract-buffers-files invalid-type-list) :to-throw)))) + (describe "handles duplicate values") (describe "expanding org-ql-view-buffers-files" - (it "returns all buffers with `org-mode' as the major-mode" + (it "with \"all\" returns all buffers with `org-mode' as the major-mode" (let ((buffers (list (generate-new-buffer "test.org") (generate-new-buffer "test.other")))) (with-current-buffer (car buffers) (org-mode)) @@ -2156,6 +2167,8 @@ with keyword arg NOW in PLIST." (it "returns the current buffer" (with-temp-buffer (expect (org-ql-view--expand-buffers-files "buffer") :to-equal (list (buffer-name (current-buffer)))))) + (it "signals error when called with a function" + (expect (org-ql-view--expand-buffers-files '((lambda nil temp-filenames))) :to-throw 'error '("Value (lambda nil temp-filenames) is not a valid buffer/file"))) (it "returns literal value(s)" (with-temp-buffer (expect (org-ql-view--expand-buffers-files (current-buffer)) :to-equal (list (buffer-name (current-buffer))))) @@ -2163,9 +2176,21 @@ with keyword arg NOW in PLIST." (expect (org-ql-view--expand-buffers-files test-buffer) :to-equal (list (buffer-name test-buffer)))) (let ((list-of-numbers '(1 2 3)) (literal-string "random string")) - ;; Signal error if any of the values are not a buffer, file, or string. + ;; Signal error if any of the values are not a buffer, function, or string. (expect (org-ql-view--expand-buffers-files list-of-numbers) :to-throw) - (expect (org-ql-view--expand-buffers-files literal-string) :to-equal (list literal-string))))) + (expect (org-ql-view--expand-buffers-files literal-string) :to-equal (list literal-string)))) + (it "contracts to a list without duplicates" + (let* ((list-of-strings '("a.org" "b.org")) + (duplicate-buffer-and-file (list (find-file-noselect (car temp-filenames)) + (car temp-filenames))) + (org-agenda-files (list (car temp-filenames))) + (random-buffer (generate-new-buffer "new-buffer")) + (random-buffer-name (buffer-name random-buffer)) + (duplicate-buffer-and-name (list random-buffer-name random-buffer)) + (buffer-collection (append org-agenda-files (list random-buffer-name)))) + (expect (org-ql-view--expand-buffers-files duplicate-buffer-and-file) :to-equal (list (car temp-filenames))) + (expect (org-ql-view--expand-buffers-files duplicate-buffer-and-name) :to-equal (list random-buffer-name)) + (expect (org-ql-view--expand-buffers-files (list "org-agenda-files" random-buffer)) :to-equal buffer-collection)))) (describe "testing `org-ql-view--complete-buffers-files'" (it "returns `org-agenda-files'" (let ((org-ql-view-buffers-files temp-filenames)) From d8f29225a3735e7e548c1fd7ae0d1d28d43e941e Mon Sep 17 00:00:00 2001 From: Ahmed Shariff Date: Fri, 24 Sep 2021 15:47:38 -0500 Subject: [PATCH 14/14] Abstract buffers-files being flattened to a list of strings --- org-ql-view.el | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/org-ql-view.el b/org-ql-view.el index 787da844..d378c1b2 100644 --- a/org-ql-view.el +++ b/org-ql-view.el @@ -1021,6 +1021,27 @@ property." ;; source code of `check-declare' shows that it searches for "cl-defun" declarations. (declare-function org-ql-search-directories-files "org-ql-search" t) +(defun org-ql-view--buffers-files-to-uniq-strings (buffers-files) + "Flatten, remove duplicates and convert elements in BUFFERS-FILES to strings. +This used by `org-ql-view--contract-buffers-files' and +`org-ql-view--expand-buffers-files'. Would signal error +if an element is not a buffer or string." + (cl-labels ((convert-to-strings + ;; Expanding all buffers to file names or buffer names to remove duplicate entries. + (list) (--map + (pcase-exhaustive it + ((pred bufferp) (or (buffer-file-name it) + (buffer-name it))) + ;; Any values at this point should be a buffer or string. + ;; Testing for string anyways. + ((pred stringp) it)) + list))) + (--> buffers-files + -flatten + -non-nil + convert-to-strings + -uniq))) + (defun org-ql-view--contract-buffers-files (buffers-files) "Return BUFFERS-FILES in its \"contracted\" form. The contracted form is \"org-agenda-files\" if BUFFERS-FILES @@ -1067,15 +1088,7 @@ current buffer. Otherwise BUFFERS-FILES is returned unchanged." (cl-typecase contracted-buffers-files (function contracted-buffers-files) (string contracted-buffers-files) - (list (--> contracted-buffers-files - -flatten -non-nil - (--map - (pcase-exhaustive it - ((pred stringp) it) - ((pred bufferp) (or (buffer-file-name it) - (buffer-name buffer-file)))) - it) - -uniq)) + (list (org-ql-view--buffers-files-to-uniq-strings contracted-buffers-files)) (t (error (format "Value %s is not a string, a valid function or a list of buffer/strings" contracted-buffers-files))))))) (defun org-ql-view--complete-buffers-files () @@ -1125,20 +1138,7 @@ This always returns a list of string values." buffer-file) (_ (error (format "Value %s is not a valid buffer/file" buffer-file))))) it)))) - (--> expanded-buffers-files - -flatten - ;; removing `nil' again as some values have been expanded. - -non-nil - ;; Expanding all buffers to file names or buffer names to remove duplicate entries. - (--map - (pcase-exhaustive it - ((pred bufferp) (or (buffer-file-name it) - (buffer-name it))) - ;; Any values at this point should be a buffer or string. - ;; Testing for string anyways. - ((pred stringp) it)) - it) - -uniq))) + (org-ql-view--buffers-files-to-uniq-strings expanded-buffers-files))) (defun org-ql-view--complete-super-groups () "Return value for `org-ql-view-super-groups' using completion."