From 9bd67acab3c4c81cf4bad14b2da8be835ae086e0 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 23 Jan 2015 23:29:54 -0200 Subject: Refactor part of request-get-data into request-get-url --- sx-request.el | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/sx-request.el b/sx-request.el index 2650c55..77ae1d7 100644 --- a/sx-request.el +++ b/sx-request.el @@ -221,18 +221,14 @@ Currently returns nil." "https://raw.githubusercontent.com/vermiculus/sx.el/data/data/%s.el" "Url of the \"data\" directory inside the SX `data' branch.") -(defun sx-request-get-data (file) - "Fetch and return data stored online by SX. -FILE is a string or symbol, the name of the file which holds the -desired data, relative to `sx-request--data-url-format'. For -instance, `tags/emacs' returns the list of tags on Emacs.SE." +(defun sx-request-get-url (url) + "Fetch and return data stored online at URL." (let* ((url-automatic-caching t) (url-inhibit-uncompression t) - (request-url (format sx-request--data-url-format file)) (url-request-method "GET") (url-request-extra-headers '(("Content-Type" . "application/x-www-form-urlencoded"))) - (response-buffer (url-retrieve-synchronously request-url))) + (response-buffer (url-retrieve-synchronously url))) (if (not response-buffer) (error "Something went wrong in `url-retrieve-synchronously'") (with-current-buffer response-buffer @@ -241,9 +237,17 @@ instance, `tags/emacs' returns the list of tags on Emacs.SE." (if (not (search-forward "\n\n" nil t)) (error "Headers missing; response corrupt") (when (looking-at-p "Not Found") (error "Page not found.")) - (prog1 (read (current-buffer)) + (prog1 (buffer-substring (point) (point-max)) (kill-buffer (current-buffer))))))))) +(defun sx-request-get-data (file) + "Fetch and return data stored online by SX. +FILE is a string or symbol, the name of the file which holds the +desired data, relative to `sx-request--data-url-format'. For +instance, `tags/emacs' returns the list of tags on Emacs.SE." + (read (sx-request-get-url + (format sx-request--data-url-format file)))) + ;;; Support Functions (defun sx-request--build-keyword-arguments (alist &optional kv-sep) -- cgit v1.2.3 From 4bc72b0f622b2565e7b0d0263bb3053ca5252f63 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 23 Jan 2015 23:38:06 -0200 Subject: sx-request-get-url error unless code 200 --- sx-request.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sx-request.el b/sx-request.el index 77ae1d7..7f18a2b 100644 --- a/sx-request.el +++ b/sx-request.el @@ -234,9 +234,10 @@ Currently returns nil." (with-current-buffer response-buffer (progn (goto-char (point-min)) + (unless (string-match "200" (thing-at-point 'line)) + (error "Page not found.")) (if (not (search-forward "\n\n" nil t)) (error "Headers missing; response corrupt") - (when (looking-at-p "Not Found") (error "Page not found.")) (prog1 (buffer-substring (point) (point-max)) (kill-buffer (current-buffer))))))))) -- cgit v1.2.3 From 76bfce36a4808e71aec27fdc2f7f8cef8462500f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 23 Jan 2015 23:41:13 -0200 Subject: Add tests for sx-request-get-... --- test/test-api.el | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/test-api.el b/test/test-api.el index b7d5dbb..30590d7 100644 --- a/test/test-api.el +++ b/test/test-api.el @@ -14,3 +14,11 @@ (ert-deftest test-method-get-all () "Tests sx-method interface to `sx-request-all-items'" (should (< 250 (length (sx-method-call 'sites :get-all t))))) + +(ert-deftest request-get () + (should (sx-request-get-url "http://google.com")) + (should-error (sx-request-get-url "http://github.com/Bruce-Connor/does-not-exist")) + (should-error (sx-request-get-data "tags/emacs-does-not-exist")) + (let ((emacs-tags (length (sx-request-get-data 'tags/emacs)))) + (should (> emacs-tags 450)) + (should (not (cl-remove-if #'stringp emacs-tags))))) -- cgit v1.2.3 From 684d093b24c0c1bb4627176109611980afad6617 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 24 Jan 2015 00:49:15 -0200 Subject: sx-question-mode--insert-link takes images too --- sx-question-print.el | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index b53b86a..7d1d69e 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -388,20 +388,24 @@ E.g.: (or (if sx-question-mode-pretty-links text full-text) url) url)))))))) -(defun sx-question-mode--insert-link (text url) - "Return a link propertized version of string TEXT. +(defun sx-question-mode--insert-link (text-or-image url) + "Return a link propertized version of TEXT-OR-IMAGE. URL is used as 'help-echo and 'url properties." - (insert-text-button - text - ;; Mouse-over - 'help-echo - (format sx-button--link-help-echo - (propertize (sx--shorten-url url) - 'face 'font-lock-function-name-face)) - ;; For visiting and stuff. - 'sx-button-url url - 'sx-button-copy url - :type 'sx-button-link)) + (let ((imagep (not (stringp text-or-image)))) + (apply #'insert-text-button + (if imagep " " text-or-image) + ;; Mouse-over + 'help-echo + (format sx-button--link-help-echo + (propertize (sx--shorten-url url) + 'face 'font-lock-function-name-face)) + ;; For visiting and stuff. + 'sx-button-url url + 'sx-button-copy url + :type 'sx-button-link + ;; The last argument of `apply' is a list. + (when imagep + `(face default display ,text-or-image))))) (defun sx-question-mode-find-reference (id &optional fallback-id) "Find url identified by reference ID in current buffer. -- cgit v1.2.3 From f54ab739c432af99356fd57d5abb6bd63a39e667 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 24 Jan 2015 00:49:38 -0200 Subject: Detect if link is an image, download it, and pass it to sx-question-mode--insert-link --- sx-question-print.el | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index 7d1d69e..63bfaa4 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -157,6 +157,16 @@ replaced with the comment." (const :tag "More active first" sx-answer-more-active-p)) :group 'sx-question-mode) +(defcustom sx-question-mode-use-images t + "Non-nil if SX should download and display images." + :type 'boolean + :group 'sx-question-mode) + +(defcustom sx-question-mode-image-max-width 500 + "Maximum width, in pixels, of images in the question buffer." + :type 'integer + :group 'sx-question-mode) + ;;; Functions ;;;; Printing the general structure @@ -321,7 +331,7 @@ E.g.: (defconst sx-question-mode--link-regexp ;; Done at compile time. (rx (or (and "[tag:" (group-n 5 (+ (not (any " ]")))) "]") - (and "[" (group-n 1 (1+ (not (any "]")))) "]" + (and (opt "!") "[" (group-n 1 (1+ (not (any "]")))) "]" (or (and "(" (group-n 2 (1+ (not (any ")")))) ")") (and "[" (group-n 3 (1+ (not (any "]")))) "]"))) (group-n 4 (and (and "http" (opt "s") "://") "" @@ -365,7 +375,9 @@ E.g.: ;;; Handling links (defun sx-question-mode--process-links-in-buffer () - "Turn all markdown links in this buffer into compact format." + "Turn all markdown links in this buffer into compact format. +Image links are downloaded and displayed, if +`sx-question-mode-use-images' is non-nil." (save-excursion (goto-char (point-min)) (while (search-forward-regexp sx-question-mode--link-regexp nil t) @@ -385,7 +397,13 @@ E.g.: (when (stringp url) (replace-match "") (sx-question-mode--insert-link - (or (if sx-question-mode-pretty-links text full-text) url) + (if (and sx-question-mode-use-images (eq ?! (elt full-text 0))) + ;; Is it an image? + (create-image (sx-request-get-url url) 'imagemagick t + :width (min sx-question-mode-image-max-width + (window-body-width nil 'pixel))) + ;; Or a regular link + (or (if sx-question-mode-pretty-links text full-text) url)) url)))))))) (defun sx-question-mode--insert-link (text-or-image url) -- cgit v1.2.3 From a7a83411ccf68114847112bf27dced3d833cd524 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 24 Jan 2015 00:52:03 -0200 Subject: Test downloading image. --- test/test-api.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/test-api.el b/test/test-api.el index 30590d7..f7f54c1 100644 --- a/test/test-api.el +++ b/test/test-api.el @@ -21,4 +21,10 @@ (should-error (sx-request-get-data "tags/emacs-does-not-exist")) (let ((emacs-tags (length (sx-request-get-data 'tags/emacs)))) (should (> emacs-tags 450)) - (should (not (cl-remove-if #'stringp emacs-tags))))) + (should (not (cl-remove-if #'stringp emacs-tags)))) + (should + ;; If image is not recognized, this returns nil. + (create-image (sx-request-get-url "https://raw.githubusercontent.com/vermiculus/sx.el/master/list-and-question.png") + 'imagemagick t + :width (min sx-question-mode-image-max-width + (window-body-width nil 'pixel))))) -- cgit v1.2.3 From d0a09664737d82985c0db040afeb89f69c5b61b3 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 24 Jan 2015 01:07:10 -0200 Subject: sx-question-mode--reference-regexp no longer matches newlines --- sx-question-print.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-question-print.el b/sx-question-print.el index 63bfaa4..963bfb4 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -323,7 +323,7 @@ where `value' is given `face' as its face. (defconst sx-question-mode--reference-regexp (rx line-start (0+ blank) "[%s]:" (0+ blank) - (group-n 1 (1+ (not blank)))) + (group-n 1 (1+ (not (any blank "\n\r"))))) "Regexp used to find the url of labeled links. E.g.: [1]: https://...") -- cgit v1.2.3 From 5a4c4dba75ad605110a4415478072cdb4a907b20 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 24 Jan 2015 01:18:42 -0200 Subject: With nested links, just do the inner one. --- sx-question-print.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-question-print.el b/sx-question-print.el index 963bfb4..e76e006 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -331,7 +331,7 @@ E.g.: (defconst sx-question-mode--link-regexp ;; Done at compile time. (rx (or (and "[tag:" (group-n 5 (+ (not (any " ]")))) "]") - (and (opt "!") "[" (group-n 1 (1+ (not (any "]")))) "]" + (and (opt "!") "[" (group-n 1 (1+ (not (any "[]")))) "]" (or (and "(" (group-n 2 (1+ (not (any ")")))) ")") (and "[" (group-n 3 (1+ (not (any "]")))) "]"))) (group-n 4 (and (and "http" (opt "s") "://") "" -- cgit v1.2.3 From e8becf0aa8fd823a73a16292fffc261be7c4b9f5 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 24 Jan 2015 01:25:29 -0200 Subject: Sorround images with newlines --- sx-question-print.el | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sx-question-print.el b/sx-question-print.el index e76e006..924608b 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -410,6 +410,9 @@ Image links are downloaded and displayed, if "Return a link propertized version of TEXT-OR-IMAGE. URL is used as 'help-echo and 'url properties." (let ((imagep (not (stringp text-or-image)))) + ;; Images need to be at the start of a line. + (when (and imagep (not (looking-at-p "^"))) + (insert "\n")) (apply #'insert-text-button (if imagep " " text-or-image) ;; Mouse-over @@ -423,7 +426,9 @@ URL is used as 'help-echo and 'url properties." :type 'sx-button-link ;; The last argument of `apply' is a list. (when imagep - `(face default display ,text-or-image))))) + `(face default display ,text-or-image))) + ;; Images need to be at the end of a line too. + (insert "\n"))) (defun sx-question-mode-find-reference (id &optional fallback-id) "Find url identified by reference ID in current buffer. -- cgit v1.2.3 From ba80f6594f39b09a6212f1288297ad1a6124dcbe Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 24 Jan 2015 01:25:41 -0200 Subject: Clean up nested links --- sx-question-print.el | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sx-question-print.el b/sx-question-print.el index 924608b..0528944 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -409,6 +409,14 @@ Image links are downloaded and displayed, if (defun sx-question-mode--insert-link (text-or-image url) "Return a link propertized version of TEXT-OR-IMAGE. URL is used as 'help-echo and 'url properties." + ;; For now, the only way to handle nested links is to remove them. + (when (eq (char-before) ?\[) + (insert "a") + (forward-char -2) + (if (looking-at sx-question-mode--link-regexp) + (replace-match "") + (forward-char 1) + (delete-char 1))) (let ((imagep (not (stringp text-or-image)))) ;; Images need to be at the start of a line. (when (and imagep (not (looking-at-p "^"))) -- cgit v1.2.3 From 458231a11d0d54f8d7011addd9246661898cb2e0 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 24 Jan 2015 01:58:32 -0200 Subject: Fix and reorganize tests --- test/test-api.el | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/test-api.el b/test/test-api.el index f7f54c1..0715a2e 100644 --- a/test/test-api.el +++ b/test/test-api.el @@ -15,16 +15,18 @@ "Tests sx-method interface to `sx-request-all-items'" (should (< 250 (length (sx-method-call 'sites :get-all t))))) -(ert-deftest request-get () +(ert-deftest request-get-url () (should (sx-request-get-url "http://google.com")) (should-error (sx-request-get-url "http://github.com/Bruce-Connor/does-not-exist")) - (should-error (sx-request-get-data "tags/emacs-does-not-exist")) - (let ((emacs-tags (length (sx-request-get-data 'tags/emacs)))) - (should (> emacs-tags 450)) - (should (not (cl-remove-if #'stringp emacs-tags)))) (should ;; If image is not recognized, this returns nil. (create-image (sx-request-get-url "https://raw.githubusercontent.com/vermiculus/sx.el/master/list-and-question.png") 'imagemagick t :width (min sx-question-mode-image-max-width (window-body-width nil 'pixel))))) + +(ert-deftest request-get-data () + (should-error (sx-request-get-data "tags/emacs-does-not-exist")) + (let ((emacs-tags (sx-request-get-data 'tags/emacs))) + (should (> (length emacs-tags) 450)) + (should (not (cl-remove-if #'stringp emacs-tags))))) -- cgit v1.2.3 From 62592e73af739b898fa401fd3c3503f43e8bb267 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 24 Jan 2015 02:51:50 -0200 Subject: Larger max width --- sx-question-print.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-question-print.el b/sx-question-print.el index 0528944..fe34d9c 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -162,7 +162,7 @@ replaced with the comment." :type 'boolean :group 'sx-question-mode) -(defcustom sx-question-mode-image-max-width 500 +(defcustom sx-question-mode-image-max-width 550 "Maximum width, in pixels, of images in the question buffer." :type 'integer :group 'sx-question-mode) -- cgit v1.2.3 From d53dc5e22d6ebd7905c194b9dcd006e763a1aa3d Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 24 Jan 2015 02:52:09 -0200 Subject: Only shrink images, don't enlarge them. --- sx-question-print.el | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index fe34d9c..3a2eedf 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -399,13 +399,23 @@ Image links are downloaded and displayed, if (sx-question-mode--insert-link (if (and sx-question-mode-use-images (eq ?! (elt full-text 0))) ;; Is it an image? - (create-image (sx-request-get-url url) 'imagemagick t - :width (min sx-question-mode-image-max-width - (window-body-width nil 'pixel))) + (sx-question-mode--create-image url) ;; Or a regular link (or (if sx-question-mode-pretty-links text full-text) url)) url)))))))) +(defun sx-question-mode--create-image (url) + "Get and create an image from URL. +Its size is bound by `sx-question-mode-image-max-width' and +`window-body-width'." + (let* ((image + (create-image (sx-request-get-url url) 'imagemagick t)) + (image-width (car (image-size image 'pixels)))) + (append image + (list :width (min sx-question-mode-image-max-width + (window-body-width nil 'pixel) + image-width))))) + (defun sx-question-mode--insert-link (text-or-image url) "Return a link propertized version of TEXT-OR-IMAGE. URL is used as 'help-echo and 'url properties." -- cgit v1.2.3 From 131f48d0407f9721a467c84ea4aaecc4c3db62ba Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 26 Jan 2015 15:18:38 -0200 Subject: Only use images if imagemagick is available. --- sx-question-print.el | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index 3a2eedf..e21c998 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -157,8 +157,13 @@ replaced with the comment." (const :tag "More active first" sx-answer-more-active-p)) :group 'sx-question-mode) -(defcustom sx-question-mode-use-images t - "Non-nil if SX should download and display images." +(defcustom sx-question-mode-use-images + (eval-when-compile + (image-type-available-p 'imagemagick)) + "Non-nil if SX should download and display images. +By default, this is `t' if the `imagemagick' image type is +available (checked with `image-type-available-p'). If this image +type is not available, images won't work." :type 'boolean :group 'sx-question-mode) -- cgit v1.2.3 From 75c4a7d4e02e81c42a4914e2b798f60415a3b505 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 26 Jan 2015 15:28:19 -0200 Subject: Check image-type-available-p in the tests --- test/test-api.el | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/test/test-api.el b/test/test-api.el index 0715a2e..faf2e0a 100644 --- a/test/test-api.el +++ b/test/test-api.el @@ -18,12 +18,19 @@ (ert-deftest request-get-url () (should (sx-request-get-url "http://google.com")) (should-error (sx-request-get-url "http://github.com/Bruce-Connor/does-not-exist")) - (should - ;; If image is not recognized, this returns nil. - (create-image (sx-request-get-url "https://raw.githubusercontent.com/vermiculus/sx.el/master/list-and-question.png") - 'imagemagick t - :width (min sx-question-mode-image-max-width - (window-body-width nil 'pixel))))) + (when sx-question-mode-use-images + (should + ;; If image is not recognized, this returns nil. + (create-image (sx-request-get-url "https://raw.githubusercontent.com/vermiculus/sx.el/master/list-and-question.png") + 'imagemagick t + :width sx-question-mode-image-max-width))) + ;; In case imagemacgick is not available, let's try png so we at + ;; least test the function. + (when (image-type-available-p 'png) + (should + (create-image (sx-request-get-url "https://raw.githubusercontent.com/vermiculus/sx.el/master/list-and-question.png") + 'png t + :width sx-question-mode-image-max-width)))) (ert-deftest request-get-data () (should-error (sx-request-get-data "tags/emacs-does-not-exist")) -- cgit v1.2.3 From 21d1b2594efaff4089d2bab736e0a18a1ee1a9db Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 12 Feb 2015 14:16:50 -0200 Subject: Get rid of sx-question-list--update-mode-line --- sx-inbox.el | 6 +----- sx-question-list.el | 24 ++++-------------------- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/sx-inbox.el b/sx-inbox.el index 21589fb..3048509 100644 --- a/sx-inbox.el +++ b/sx-inbox.el @@ -127,11 +127,7 @@ These are identified by their links.") (setq tabulated-list-format [("Type" 30 t nil t) ("Date" 10 t :right-align t) ("Title" 0)]) (setq mode-line-format sx-inbox--mode-line) - (setq header-line-format sx-inbox--header-line) - ;; @TODO: This will no longer be necessary once we properly - ;; refactor sx-question-list-mode. - (remove-hook 'tabulated-list-revert-hook - #'sx-question-list--update-mode-line t)) + (setq header-line-format sx-inbox--header-line)) ;;; Keybinds diff --git a/sx-question-list.el b/sx-question-list.el index 7757503..de15704 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -308,7 +308,8 @@ into consideration. The same holds for `sx-question-list--order'. \\{sx-question-list-mode-map}" (hl-line-mode 1) - (sx-question-list--update-mode-line) + (setq mode-line-format + sx-question-list--mode-line-format) (setq sx-question-list--pages-so-far 0) (setq tabulated-list-format [(" V" 3 t :right-align t) @@ -320,8 +321,6 @@ into consideration. The same holds for `sx-question-list--order'. (setq tabulated-list-sort-key nil) (add-hook 'tabulated-list-revert-hook #'sx-question-list-refresh nil t) - (add-hook 'tabulated-list-revert-hook - #'sx-question-list--update-mode-line nil t) (setq header-line-format sx-question-list--header-line)) (defcustom sx-question-list-date-sort-method 'last_activity_date @@ -400,14 +399,8 @@ Non-interactively, DATA is a question alist." ;; "Unanswered", etc. "Variable describing current tab being viewed.") -(defvar sx-question-list--total-count 0 - "Holds the total number of questions in the current buffer.") -(make-variable-buffer-local 'sx-question-list--total-count) - (defconst sx-question-list--mode-line-format - '(" " - mode-name - " " + '(" " mode-name ": " (:propertize sx-question-list--current-tab face mode-line-buffer-id) " [" @@ -418,7 +411,7 @@ Non-interactively, DATA is a question alist." ", " "Total: " (:propertize - (:eval (int-to-string sx-question-list--total-count)) + (:eval (int-to-string (length tabulated-list-entries))) face mode-line-buffer-id) "] ") "Mode-line construct to use in question-list buffers.") @@ -429,15 +422,6 @@ Non-interactively, DATA is a question alist." (cl-count-if-not #'sx-question--read-p sx-question-list--dataset))) -(defun sx-question-list--update-mode-line () - "Fill the mode-line with useful information." - ;; All the data we need is right in the buffer. - (when (derived-mode-p 'sx-question-list-mode) - (setq mode-line-format - sx-question-list--mode-line-format) - (setq sx-question-list--total-count - (length tabulated-list-entries)))) - (defvar sx-question-list--site nil "Site being displayed in the *question-list* buffer.") (make-variable-buffer-local 'sx-question-list--site) -- cgit v1.2.3 From ade5fe434b5fd031db8cd4e601f26f2933354ab2 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 12 Feb 2015 14:18:55 -0200 Subject: Compiler warnings --- sx-button.el | 10 +++++----- sx.el | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sx-button.el b/sx-button.el index 5a2f052..46855e7 100644 --- a/sx-button.el +++ b/sx-button.el @@ -77,23 +77,23 @@ This is usually a link's URL, or the content of a code block." (point) 'sx-button-copy-type) content))))) -(defun sx-button-edit-this (text-or-marker &optional major-mode) - "Open a temp buffer populated with the string TEXT-OR-MARKER using MAJOR-MODE. +(defun sx-button-edit-this (text-or-marker &optional majormode) + "Open a temp buffer populated with the string TEXT-OR-MARKER using MAJORMODE. When given a marker (or interactively), use the 'sx-button-copy and the 'sx-mode text-properties under the marker. These are usually part of a code-block." (interactive (list (point-marker))) ;; Buttons receive markers. (when (markerp text-or-marker) - (setq major-mode (get-text-property text-or-marker 'sx-mode)) + (setq majormode (get-text-property text-or-marker 'sx-mode)) (unless (setq text-or-marker (get-text-property text-or-marker 'sx-button-copy)) (sx-message "Nothing of interest here."))) (with-current-buffer (pop-to-buffer (generate-new-buffer "*sx temp buffer*")) (insert text-or-marker) - (when major-mode - (funcall major-mode)))) + (when majormode + (funcall majormode)))) (defun sx-button-follow-link (&optional pos) "Follow link at POS. If POS is nil, use `point'." diff --git a/sx.el b/sx.el index 33b36b6..73d874f 100644 --- a/sx.el +++ b/sx.el @@ -204,7 +204,7 @@ is intentionally skipped." (while (and ;; We're not at the end. (cdr-safe tail) ;; We're not at the right place. - (,(or predicate #'<) x (cadr tail))) + (funcall (or ,predicate #'<) x (cadr tail))) (setq tail (cdr tail))) (setcdr tail (cons x (cdr tail))))) -- cgit v1.2.3 From 85109b7dd2ffb896151ccef2c014c7d9ea33e682 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 12 Feb 2015 14:59:46 -0200 Subject: Define possible values for answer sorting --- sx-question-print.el | 19 +++++++++++++++---- sx.el | 6 ++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index 778b580..4f50560 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -153,13 +153,24 @@ replaced with the comment." :type 'boolean :group 'sx-question-mode) +(defconst sx-question-mode--sort-methods + (let ((methods + '(("Higher-scoring" . sx-answer-higher-score-p) + ("Newer" . sx-answer-newer-p) + ("More active" . sx-answer-more-active-p)))) + (append (mapcar (lambda (x) (cons (concat (car x) " first") (cdr x))) + methods) + (mapcar (lambda (x) (cons (concat (car x) " last") + (sx--invert-predicate (cdr x)))) + methods)))) + (defcustom sx-question-mode-answer-sort-function #'sx-answer-higher-score-p "Function used to sort answers in the question buffer." - :type '(choice - (const :tag "Higher-scoring first" sx-answer-higher-score-p) - (const :tag "Newer first" sx-answer-newer-p) - (const :tag "More active first" sx-answer-more-active-p)) + :type + (cons 'choice + (mapcar (lambda (x) `(const :tag ,(car x) ,(cdr x))) + sx-question-mode--sort-methods)) :group 'sx-question-mode) diff --git a/sx.el b/sx.el index 33b36b6..381f78e 100644 --- a/sx.el +++ b/sx.el @@ -335,6 +335,12 @@ GET-FUNC and performs the actual comparison." "Return STRING with consecutive whitespace squashed together." (replace-regexp-in-string "[ \r\n]+" " " string)) +(defun sx--invert-predicate (predicate) + "Return PREDICATE function with arguments inverted. +For instance (sx--invert-predicate #'<) is the same as #'>. +Note this is not the same as negating PREDICATE." + (lambda (&rest args) (apply predicate (reverse args)))) + ;;; Printing request data (defvar sx--overlays nil -- cgit v1.2.3 From fe7e31a55cdd9c25f3d0c1fba088d5499a887af9 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 12 Feb 2015 15:00:05 -0200 Subject: Sort answers with O --- sx-question-mode.el | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sx-question-mode.el b/sx-question-mode.el index 6125416..846ad7f 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -214,6 +214,7 @@ Letters do not insert themselves; instead, they are commands. ("v" sx-visit-externally) ("u" sx-upvote) ("d" sx-downvote) + ("O" sx-question-mode-order-by) ("q" quit-window) (" " scroll-up-command) ("a" sx-answer) @@ -256,6 +257,18 @@ query the api." (unless (derived-mode-p 'sx-question-mode) (error "Not in `sx-question-mode'"))) +(defun sx-question-mode-order-by (sort) + "Order answers in the current buffer by the method SORT. +Sets `sx-question-list--order' and then calls +`sx-question-list-refresh' with `redisplay'." + (interactive + (list (let ((order (sx-completing-read "Order answers by: " + (mapcar #'car sx-question-mode--sort-methods)))) + (cdr-safe (assoc-string order sx-question-mode--sort-methods))))) + (when (and sort (functionp sort)) + (setq sx-question-mode-answer-sort-function sort) + (sx-question-mode-refresh 'no-update))) + (provide 'sx-question-mode) ;;; sx-question-mode.el ends here -- cgit v1.2.3 From 2f398913b77d190f2e0c96ba15296c231ba21e18 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 14 Feb 2015 16:44:01 -0200 Subject: Show the site name on the mode-lien --- sx-question-list.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sx-question-list.el b/sx-question-list.el index de15704..06af161 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -400,7 +400,13 @@ Non-interactively, DATA is a question alist." "Variable describing current tab being viewed.") (defconst sx-question-list--mode-line-format - '(" " mode-name ": " + '(" " + (:propertize + (:eval (mapconcat #'capitalize + (split-string sx-question-list--site "\\.") + " ")) + face mode-line-buffer-id) + " " mode-name ": " (:propertize sx-question-list--current-tab face mode-line-buffer-id) " [" -- cgit v1.2.3 From d4531c7a605e4d442632c7b54accbefc465a4601 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 20 Feb 2015 20:01:27 -0200 Subject: Fix "Asked on" for answers --- sx-question-print.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index abf3236..056c265 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -63,7 +63,7 @@ Some faces of this mode might be defined in the `sx-user' group." :type 'string :group 'sx-question-mode) -(defcustom sx-question-mode-header-author-format "\nAuthor: %d %r" +(defcustom sx-question-mode-header-author-format "\nAuthor: %d %r" "String used to display the question author at the header. % constructs have special meaning here. See `sx-user--format'." :type 'string @@ -74,7 +74,7 @@ Some faces of this mode might be defined in the `sx-user' group." "Face used on the question date in the question buffer." :group 'sx-question-mode-faces) -(defcustom sx-question-mode-header-date "\nAsked on: " +(defcustom sx-question-mode-header-date "\nPosted on: " "String used before the question date at the header." :type 'string :group 'sx-question-mode) @@ -95,12 +95,12 @@ Some faces of this mode might be defined in the `sx-user' group." "Face used for downvoted score in the question buffer." :group 'sx-question-mode-faces) -(defcustom sx-question-mode-header-tags "\nTags: " +(defcustom sx-question-mode-header-tags "\nTags: " "String used before the question tags at the header." :type 'string :group 'sx-question-mode) -(defcustom sx-question-mode-header-score "\nScore: " +(defcustom sx-question-mode-header-score "\nScore: " "String used before the question score at the header." :type 'string :group 'sx-question-mode) -- cgit v1.2.3 From 3ff90f54e74d034224d70fe90bc2fc67aae6d8f4 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 20 Feb 2015 20:02:22 -0200 Subject: Improve legibility of sx-question-mode--print-section --- sx-question-print.el | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sx-question-print.el b/sx-question-print.el index 056c265..8ba0157 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -193,17 +193,20 @@ DATA can represent a question or an answer." 'sx-question-mode--section (if .title 1 2) 'sx-button-copy .share_link :type 'sx-question-mode-title) + ;; Sections can be hidden with overlays (sx--wrap-in-overlay '(sx-question-mode--section-content t) + ;; Author (insert (sx-user--format (propertize sx-question-mode-header-author-format 'face 'sx-question-mode-header) .owner)) + + ;; Date (sx-question-mode--insert-header - ;; Date sx-question-mode-header-date (concat (sx-time-seconds-to-date .creation_date) @@ -212,6 +215,8 @@ DATA can represent a question or an answer." (sx-time-since .last_edit_date) (sx-user--format "%d" .last_editor)))) 'sx-question-mode-date) + + ;; Score and upvoted/downvoted status. (sx-question-mode--insert-header sx-question-mode-header-score (format "%s" .score) @@ -219,6 +224,8 @@ DATA can represent a question or an answer." ((eq .upvoted t) 'sx-question-mode-score-upvoted) ((eq .downvoted t) 'sx-question-mode-score-downvoted) (t 'sx-question-mode-score))) + + ;; Tags (when .title ;; Tags (sx-question-mode--insert-header -- cgit v1.2.3 From 60df6a23b27bc1ff1dfa8f3bdd37c1c4543d980f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 20 Feb 2015 20:08:53 -0200 Subject: Up and Down arrows when you vote --- sx-question-print.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index 8ba0157..bd764da 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -219,11 +219,11 @@ DATA can represent a question or an answer." ;; Score and upvoted/downvoted status. (sx-question-mode--insert-header sx-question-mode-header-score - (format "%s" .score) - (cond - ((eq .upvoted t) 'sx-question-mode-score-upvoted) - ((eq .downvoted t) 'sx-question-mode-score-downvoted) - (t 'sx-question-mode-score))) + (format "%s%s" .score + (cond ((eq .upvoted t) "↑") ((eq .downvoted t) "↓") (t ""))) + (cond ((eq .upvoted t) 'sx-question-mode-score-upvoted) + ((eq .downvoted t) 'sx-question-mode-score-downvoted) + (t 'sx-question-mode-score))) ;; Tags (when .title -- cgit v1.2.3 From bbc6383dea772a62c7ddc8bbcfec72e2ddd14969 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 20 Feb 2015 20:09:18 -0200 Subject: Indicate which answer is accepted --- sx-question-print.el | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sx-question-print.el b/sx-question-print.el index bd764da..62253a7 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -131,6 +131,16 @@ the editor's name." :type 'string :group 'sx-question-mode) +(defface sx-question-mode-accepted + '((t :foreground "ForestGreen" :inherit sx-question-mode-title)) + "Face used for accepted answers in the question buffer." + :group 'sx-question-mode-faces) + +(defcustom sx-question-mode-answer-accepted-title "Accepted Answer" + "Title used at the start of accepted \"Answer\" section." + :type 'string + :group 'sx-question-mode) + (defcustom sx-question-mode-comments-title " Comments" "Title used at the start of \"Comments\" sections." :type 'string @@ -188,10 +198,14 @@ DATA can represent a question or an answer." (insert sx-question-mode-header-title) (insert-text-button ;; Questions have title, Answers don't - (or .title sx-question-mode-answer-title) + (cond (.title) + ((eq .is_accepted t) sx-question-mode-answer-accepted-title) + (t sx-question-mode-answer-title)) ;; Section level 'sx-question-mode--section (if .title 1 2) 'sx-button-copy .share_link + 'face (if (eq .is_accepted t) 'sx-question-mode-accepted + 'sx-question-mode-title) :type 'sx-question-mode-title) ;; Sections can be hidden with overlays -- cgit v1.2.3 From 24090d48422233b31f9eef041814e99c47c2534d Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 26 Feb 2015 11:28:48 -0300 Subject: Manually string-trim for older emacsen. Fix #267 --- sx-compose.el | 6 ++---- sx.el | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sx-compose.el b/sx-compose.el index eb5e2eb..ae13fb6 100644 --- a/sx-compose.el +++ b/sx-compose.el @@ -193,8 +193,7 @@ tags. Return a list of already inserted tags." 'noerror) (error "No Tags header found")) (save-match-data - (split-string (match-string 1) (rx (any space ",;")) - 'omit-nulls (rx space)))) + (sx--split-string (match-string 1) (rx (any space ",;"))))) (defun sx-compose--check-tags () "Check if tags in current compose buffer are valid." @@ -313,8 +312,7 @@ other keywords are read from the header " (unless (search-forward-regexp "^Tags : *\\([^[:space:]].*\\) *$" header-end 'noerror) (error "No Tags header found")) - (push (cons 'tags (split-string (match-string 1) - "[[:space:],;]" 'omit-nulls)) + (push (cons 'tags (sx--split-string (match-string 1) "[[:space:],;]")) keywords) ;; And erase the header so it doesn't get sent. (delete-region diff --git a/sx.el b/sx.el index 33b36b6..ee7f0a6 100644 --- a/sx.el +++ b/sx.el @@ -189,6 +189,11 @@ If ALIST doesn't have a `site' property, one is created using the ;;; Utility Functions +(defun sx--split-string (string &optional separators) + "Split STRING into substrings bounded by matches for SEPARATORS." + (mapcar (lambda (s) (replace-regexp-in-string "\\` +\\| +\\'" "" s)) + (split-string string separators 'omit-nulls))) + (defun sx-completing-read (&rest args) "Like `completing-read', but possibly use ido. All ARGS are passed to `completing-read' or `ido-completing-read'." -- cgit v1.2.3