From 5dbe39b002418f5a663984f341dd7a240bca90c2 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 01:39:36 +0000 Subject: .site is now a special symbol in sx-assoc-let sx-question.el no longer manually inserts the `site` property on fetched questions. This was insufficient because we also need this property on other objects. --- sx-question.el | 43 +++++++++++++++++++------------------------ sx.el | 27 ++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/sx-question.el b/sx-question.el index 06e8648..cca789e 100644 --- a/sx-question.el +++ b/sx-question.el @@ -56,13 +56,11 @@ properties returned by the API with an added (site SITE) property. `sx-method-call' is used with `sx-question-browse-filter'." - (mapcar - (lambda (question) (cons (cons 'site site) question)) - (sx-method-call 'questions - :keywords `((page . ,page)) - :site site - :auth t - :filter sx-question-browse-filter))) + (sx-method-call 'questions + :keywords `((page . ,page)) + :site site + :auth t + :filter sx-question-browse-filter)) (defun sx-question-get-question (site question-id) "Query SITE for a QUESTION-ID and return it. @@ -159,23 +157,20 @@ If no cache exists for it, initialize one with SITE." (defun sx-question--mark-hidden (question) "Mark QUESTION as being hidden." - (sx-assoc-let question - (sx-question--ensure-hidden-list .site) - (let ((site-cell (assoc .site sx-question--user-hidden-list)) - cell) - ;; If question already hidden, do nothing. - (unless (memq .question_id site-cell) - ;; First question from this site. - (if (null site-cell) - (push (list .site .question_id) sx-question--user-hidden-list) - ;; Question wasn't present. - ;; Add it in, but make sure it's sorted (just in case we need - ;; it later). - (sx-sorted-insert-skip-first .question_id site-cell >)) - ;; This causes a small lag on `j' and `k' as the list gets large. - ;; Should we do this on a timer? - ;; Save the results. - (sx-cache-set 'hidden-questions sx-question--user-hidden-list))))) + (let ((site-cell (assoc .site sx-question--user-hidden-list)) + cell) + ;; If question already hidden, do nothing. + (unless (memq .question_id site-cell) + ;; First question from this site. + (push (list .site .question_id) sx-question--user-hidden-list) + ;; Question wasn't present. + ;; Add it in, but make sure it's sorted (just in case we need + ;; it later). + (sx-sorted-insert-skip-first .question_id site-cell >) + ;; This causes a small lag on `j' and `k' as the list gets large. + ;; Should we do this on a timer? + ;; Save the results. + (sx-cache-set 'hidden-questions sx-question--user-hidden-list)))) ;;;; Other data diff --git a/sx.el b/sx.el index 0bab861..837d43b 100644 --- a/sx.el +++ b/sx.el @@ -186,6 +186,19 @@ If DATA is a question, also mark it as read." ;;; Assoc-let +(defun sx--site (data) + "Get the site in which DATA belongs. +DATA can be a question, answer, comment, or user (or any object +with a `link' property). +DATA can also be the link itself." + (let ((link (if (stringp data) data + (cdr (assoc 'link data))))) + (unless (stringp link) + (error "Data has no link property")) + (replace-regexp-in-string + "^https?://\\(?:\\(?1:[^/]+\\)\\.stackexchange\\|\\(?2:[^/]+\\)\\)\\.[^.]+/.*$" + "\\1\\2" link))) + (defun sx--deep-dot-search (data) "Find symbols somewhere inside DATA which start with a `.'. Returns a list where each element is a cons cell. The car is the @@ -206,6 +219,8 @@ symbol, the cdr is the symbol without the `.'." "Use dotted symbols let-bound to their values in ALIST and execute BODY. Dotted symbol is any symbol starting with a `.'. Only those present in BODY are letbound, which leads to optimal performance. +The .site symbol is special, it is derived from the .link symbol +using `sx--site'. For instance, the following code @@ -217,11 +232,13 @@ is equivalent to (let ((.title (cdr (assoc 'title alist))) (.body (cdr (assoc 'body alist)))) (list .title .body))" - (declare (indent 1) - (debug t)) - (let ((symbol-alist (sx--deep-dot-search body))) - `(let ,(mapcar (lambda (x) `(,(car x) (cdr (assoc ',(cdr x) ,alist)))) - (delete-dups symbol-alist)) + (declare (indent 1) (debug t)) + (let* ((symbol-alist (sx--deep-dot-search body)) + (has-site (assoc '.site symbol-alist))) + `(let ,(append + (when has-site `((.site (sx--site (cdr (assoc 'link ,alist)))))) + (mapcar (lambda (x) `(,(car x) (cdr (assoc ',(cdr x) ,alist)))) + (remove '(.site . site) (delete-dups symbol-alist)))) ,@body))) (defcustom sx-init-hook nil -- cgit v1.2.3 From 084c5cab6f62a9886bbf07893f85eff901a9f361 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 01:42:09 +0000 Subject: Implement voting --- sx.el | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/sx.el b/sx.el index 837d43b..7935ee0 100644 --- a/sx.el +++ b/sx.el @@ -184,6 +184,39 @@ If DATA is a question, also mark it as read." (when ((derived-mode-p 'sx-question-list-mode)) (sx-question-list-refresh 'redisplay 'no-update))))) +(defun sx-toggle-upvote (data) + "Apply or remove upvote from DATA. +DATA can be a question, answer, or comment. Interactively, it is +guessed from context at point." + (interactive (list (sx--data-here))) + (sx-assoc-let data + (sx-set-vote data "upvote" (null .upvoted)))) + +(defun sx-toggle-downvote (data) + "Apply or remove downvote from DATA. +DATA can be a question or an answer. Interactively, it is guessed +from context at point." + (interactive (list (sx--data-here))) + (sx-assoc-let data + (sx-set-vote data "downvote" (null .downvoted)))) + +(defun sx-set-vote (data type status) + "Set the DATA's vote TYPE to STATUS. +DATA can be a question, answer, or comment. +TYPE can be \"upvote\" or \"downvote\". +Status is a boolean." + (sx-assoc-let data + (sx-method-call + (cond + (.comment_id "comments") + (.answer_id "answers") + (.question_id "questions")) + :id (or .comment_id .answer_id .question_id) + :submethod type + :auth 'warn + :url-method "POST" + :site .site))) + ;;; Assoc-let (defun sx--site (data) -- cgit v1.2.3 From 61bef1ec5efb8f78c2b8f73638693acf912466a9 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 02:42:21 +0000 Subject: Move browse-filter to sx --- sx-question.el | 29 +++-------------------------- sx.el | 28 +++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/sx-question.el b/sx-question.el index cca789e..de07c94 100644 --- a/sx-question.el +++ b/sx-question.el @@ -26,41 +26,18 @@ (require 'sx-filter) (require 'sx-method) -(defvar sx-question-browse-filter - '((question.body_markdown - question.comments - question.answers - question.last_editor - question.accepted_answer_id - question.link - question.upvoted - question.downvoted - user.display_name - comment.owner - comment.body_markdown - comment.body - comment.link - answer.last_editor - answer.link - answer.owner - answer.body_markdown - answer.comments) - (user.profile_image shallow_user.profile_image)) - "The filter applied when retrieving question data. -See `sx-question-get-questions' and `sx-question-get-question'.") - (defun sx-question-get-questions (site &optional page) "Get SITE questions. Return page PAGE (the first if nil). Return a list of question. Each question is an alist of properties returned by the API with an added (site SITE) property. -`sx-method-call' is used with `sx-question-browse-filter'." +`sx-method-call' is used with `sx-browse-filter'." (sx-method-call 'questions :keywords `((page . ,page)) :site site :auth t - :filter sx-question-browse-filter)) + :filter sx-browse-filter)) (defun sx-question-get-question (site question-id) "Query SITE for a QUESTION-ID and return it. @@ -69,7 +46,7 @@ If QUESTION-ID doesn't exist on SITE, raise an error." :id id :site site :auth t - :filter sx-question-browse-filter))) + :filter sx-browse-filter))) (if (vectorp res) (elt res 0) (error "Couldn't find question %S in %S" diff --git a/sx.el b/sx.el index 7935ee0..12b08a1 100644 --- a/sx.el +++ b/sx.el @@ -45,6 +45,31 @@ (interactive) (browse-url "https://github.com/vermiculus/stack-mode/issues/new")) + +;;; Browsing filter +(defvar sx-browse-filter + '((question.body_markdown + question.comments + question.answers + question.last_editor + question.accepted_answer_id + question.link + question.upvoted + question.downvoted + user.display_name + comment.owner + comment.body_markdown + comment.body + comment.link + answer.last_editor + answer.link + answer.owner + answer.body_markdown + answer.comments) + (user.profile_image shallow_user.profile_image)) + "The filter applied when retrieving question data. +See `sx-question-get-questions' and `sx-question-get-question'.") + ;;; Utility Functions @@ -212,9 +237,10 @@ Status is a boolean." (.answer_id "answers") (.question_id "questions")) :id (or .comment_id .answer_id .question_id) - :submethod type + :submethod (concat type (unless status "/undo")) :auth 'warn :url-method "POST" + :filter sx-browse-filter :site .site))) -- cgit v1.2.3 From b5bcd91bffb67ee1a902a15f2857784626435852 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 02:43:28 +0000 Subject: Finish voting implementation on the question list. --- sx.el | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/sx.el b/sx.el index 12b08a1..09b5181 100644 --- a/sx.el +++ b/sx.el @@ -195,6 +195,18 @@ Return the result of BODY." (and (derived-mode-p 'sx-question-list-mode) (tabulated-list-get-id)))) +(defun sx--maybe-update-display () + "Refresh the question list if we're inside it." + (cond + ((derived-mode-p 'sx-question-list-mode) + (sx-question-list-refresh 'redisplay 'no-update)))) + +(defun sx--copy-data (from to) + "Copy all fields of alist FORM onto TO. +Only fields contained in TO are copied." + (setcar to (car from)) + (setcdr to (cdr from))) + (defun sx-visit (data) "Visit DATA in a web browser. DATA can be a question, answer, or comment. Interactively, it is @@ -206,24 +218,31 @@ If DATA is a question, also mark it as read." (browse-url .link)) (when (and .title (fboundp 'sx-question--mark-read)) (sx-question--mark-read data) - (when ((derived-mode-p 'sx-question-list-mode)) - (sx-question-list-refresh 'redisplay 'no-update))))) + (sx--maybe-update-display)))) (defun sx-toggle-upvote (data) "Apply or remove upvote from DATA. DATA can be a question, answer, or comment. Interactively, it is guessed from context at point." (interactive (list (sx--data-here))) - (sx-assoc-let data - (sx-set-vote data "upvote" (null .upvoted)))) + (let ((result + (sx-assoc-let data + (sx-set-vote data "upvote" (eq .upvoted :json-false))))) + (when (> (length result) 0) + (sx--copy-data (elt result 0) data))) + (sx--maybe-update-display)) (defun sx-toggle-downvote (data) "Apply or remove downvote from DATA. DATA can be a question or an answer. Interactively, it is guessed from context at point." (interactive (list (sx--data-here))) - (sx-assoc-let data - (sx-set-vote data "downvote" (null .downvoted)))) + (let ((result + (sx-assoc-let data + (sx-set-vote data "downvote" (eq .downvoted :json-false))))) + (when (> (length result) 0) + (sx--copy-data (elt result 0) data))) + (sx--maybe-update-display)) (defun sx-set-vote (data type status) "Set the DATA's vote TYPE to STATUS. -- cgit v1.2.3 From 73b380e1407bfc688204812bbf58f9872e57f8c5 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 02:45:48 +0000 Subject: sx can also updates question-mode buffer. --- sx-question-mode.el | 19 ++++++++++++------- sx.el | 2 ++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 59313d1..416a4eb 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -552,16 +552,21 @@ Letters do not insert themselves; instead, they are commands. (,(kbd "") backward-button) ([return] push-button))) -(defun sx-question-mode-refresh () +(defun sx-question-mode-refresh (&optional no-update) "Refresh currently displayed question. Queries the API for any changes to the question or its answers or -comments, and redisplays it." - (interactive) +comments, and redisplays it. + +With non-nil prefix argument NO-UPDATE, just redisplay, don't +query the api." + (interactive "P") (sx-question-mode--ensure-mode) - (sx-assoc-let sx-question-mode--data - (sx-question-mode--display - (sx-question-get-question .site .question_id) - (selected-window)))) + (sx-question-mode--display + (if no-update + sx-question-mode--data + (sx-assoc-let sx-question-mode--data + (sx-question-get-question .site .question_id))) + (selected-window))) (defun sx-question-mode--ensure-mode () "Ensures we are in question mode, erroring otherwise." diff --git a/sx.el b/sx.el index 09b5181..5fbfbb8 100644 --- a/sx.el +++ b/sx.el @@ -199,6 +199,8 @@ Return the result of BODY." "Refresh the question list if we're inside it." (cond ((derived-mode-p 'sx-question-list-mode) + (sx-question-list-refresh 'redisplay 'no-update)) + ((derived-mode-p 'sx-question-mode) (sx-question-list-refresh 'redisplay 'no-update)))) (defun sx--copy-data (from to) -- cgit v1.2.3 From 3afb8561dba9569e15fcea69f59d29fa7a19fec2 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 02:49:28 +0000 Subject: Add some comment filters --- sx.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sx.el b/sx.el index 5fbfbb8..f3af6d0 100644 --- a/sx.el +++ b/sx.el @@ -61,6 +61,10 @@ comment.body_markdown comment.body comment.link + comment.edited + comment.creation_date + comment.upvoted + comment.score answer.last_editor answer.link answer.owner -- cgit v1.2.3 From 6ed2414bddb842b43c1158ca1f1a9c6cefed4175 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 02:56:08 +0000 Subject: Fix voting for comments. --- sx.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sx.el b/sx.el index f3af6d0..917ad5b 100644 --- a/sx.el +++ b/sx.el @@ -233,7 +233,7 @@ guessed from context at point." (interactive (list (sx--data-here))) (let ((result (sx-assoc-let data - (sx-set-vote data "upvote" (eq .upvoted :json-false))))) + (sx-set-vote data "upvote" (null (eq .upvoted t)))))) (when (> (length result) 0) (sx--copy-data (elt result 0) data))) (sx--maybe-update-display)) @@ -245,7 +245,7 @@ from context at point." (interactive (list (sx--data-here))) (let ((result (sx-assoc-let data - (sx-set-vote data "downvote" (eq .downvoted :json-false))))) + (sx-set-vote data "downvote" (null (eq .downvoted t)))))) (when (> (length result) 0) (sx--copy-data (elt result 0) data))) (sx--maybe-update-display)) -- cgit v1.2.3 From ddd4c4ed0387acd66d80004154835586f540dcd1 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 02:56:29 +0000 Subject: Binds for voting. --- sx-question-list.el | 2 ++ sx-question-mode.el | 2 ++ 2 files changed, 4 insertions(+) diff --git a/sx-question-list.el b/sx-question-list.el index ddc04c7..36a3c51 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -288,6 +288,8 @@ into consideration. ("g" sx-question-list-refresh) (":" sx-question-list-switch-site) ("v" sx-visit) + ("u" sx-toggle-upvote) + ("d" sx-toggle-downvote) ("h" sx-question-list-hide) ("m" sx-question-list-mark-read) ([?\r] sx-question-list-display-question))) diff --git a/sx-question-mode.el b/sx-question-mode.el index 416a4eb..8ccc576 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -542,6 +542,8 @@ Letters do not insert themselves; instead, they are commands. ("p" sx-question-mode-previous-section) ("g" sx-question-mode-refresh) ("v" sx-visit) + ("u" sx-toggle-upvote) + ("d" sx-toggle-downvote) ("q" quit-window) (" " scroll-up-command) (,(kbd "S-SPC") scroll-down-command) -- cgit v1.2.3 From 02ee06e2ac494cc599f69784ad9ad6166235a712 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 15:51:32 +0000 Subject: Add elements to auth-filter-auth and browse-filter --- sx-auth.el | 5 ++++- sx.el | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sx-auth.el b/sx-auth.el index 217da7d..2217b8b 100644 --- a/sx-auth.el +++ b/sx-auth.el @@ -72,7 +72,10 @@ If all SUBMETHODS require auth or there are no submethods, form will be (METHOD . t)") (defvar sx-auth-filter-auth '(question.upvoted - question.downvoted) + question.downvoted + answer.upvoted + answer.downvoted + comment.upvoted) "List of filter types that require auth. Keywords are of form (OBJECT TYPES) where TYPES is (FILTER FILTER FILTER).") diff --git a/sx.el b/sx.el index 917ad5b..82462ae 100644 --- a/sx.el +++ b/sx.el @@ -69,6 +69,8 @@ answer.link answer.owner answer.body_markdown + answer.upvoted + answer.downvoted answer.comments) (user.profile_image shallow_user.profile_image)) "The filter applied when retrieving question data. -- cgit v1.2.3 From fdacdb9c01d15c53e2642a455c56b91694e5fb1f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 20:26:26 +0000 Subject: Fix wrong function --- sx.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx.el b/sx.el index 82462ae..0f399fc 100644 --- a/sx.el +++ b/sx.el @@ -207,7 +207,7 @@ Return the result of BODY." ((derived-mode-p 'sx-question-list-mode) (sx-question-list-refresh 'redisplay 'no-update)) ((derived-mode-p 'sx-question-mode) - (sx-question-list-refresh 'redisplay 'no-update)))) + (sx-question-mode-refresh 'no-update)))) (defun sx--copy-data (from to) "Copy all fields of alist FORM onto TO. -- cgit v1.2.3 From 1457f2211ca001604dc22be6af463dcb33459a74 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 20:35:35 +0000 Subject: Moved interaction functions to their own file --- sx-interaction.el | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ sx.el | 78 -------------------------------------- 2 files changed, 110 insertions(+), 78 deletions(-) create mode 100644 sx-interaction.el diff --git a/sx-interaction.el b/sx-interaction.el new file mode 100644 index 0000000..a9203bd --- /dev/null +++ b/sx-interaction.el @@ -0,0 +1,110 @@ +;;; sx-interaction.el --- Voting, commenting, and otherwise interacting with questions. -*- lexical-binding: t; -*- + +;; Copyright (C) 2014 Artur Malabarba + +;; Author: Artur Malabarba + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + + +;;; Code: + +(require 'sx) +(require 'sx-question) +(require 'sx-question-mode) +(require 'sx-question-list) + + +;;; Using data in buffer +(defun sx--data-here () + "Get the text property `sx--data-here'." + (or (get-text-property (point) 'sx--data-here) + (and (derived-mode-p 'sx-question-list-mode) + (tabulated-list-get-id)) + (or (derived-mode-p 'sx-question-mode) + sx-question-mode--data))) + +(defun sx--maybe-update-display () + "Refresh the question list if we're inside it." + (cond + ((derived-mode-p 'sx-question-list-mode) + (sx-question-list-refresh 'redisplay 'no-update)) + ((derived-mode-p 'sx-question-mode) + (sx-question-mode-refresh 'no-update)))) + +(defun sx--copy-data (from to) + "Copy all fields of alist FORM onto TO. +Only fields contained in TO are copied." + (setcar to (car from)) + (setcdr to (cdr from))) + +(defun sx-visit (data) + "Visit DATA in a web browser. +DATA can be a question, answer, or comment. Interactively, it is +derived from point position. +If DATA is a question, also mark it as read." + (interactive (list (sx--data-here))) + (sx-assoc-let data + (when (stringp .link) + (browse-url .link)) + (when .title + (sx-question--mark-read data) + (sx--maybe-update-display)))) + +(defun sx-toggle-upvote (data) + "Apply or remove upvote from DATA. +DATA can be a question, answer, or comment. Interactively, it is +guessed from context at point." + (interactive (list (sx--data-here))) + (sx-assoc-let data + (sx-set-vote data "upvote" (null (eq .upvoted t))))) + +(defun sx-toggle-downvote (data) + "Apply or remove downvote from DATA. +DATA can be a question or an answer. Interactively, it is guessed +from context at point." + (interactive (list (sx--data-here))) + (sx-assoc-let data + (sx-set-vote data "downvote" (null (eq .downvoted t))))) + +(defun sx-set-vote (data type status) + "Set the DATA's vote TYPE to STATUS. +DATA can be a question, answer, or comment. TYPE can be +\"upvote\" or \"downvote\". STATUS is a boolean. + +Besides posting to the api, DATA is also altered to reflect the +changes." + (let ((result + (sx-assoc-let data + (sx-method-call + (cond + (.comment_id "comments") + (.answer_id "answers") + (.question_id "questions")) + :id (or .comment_id .answer_id .question_id) + :submethod (concat type (unless status "/undo")) + :auth 'warn + :url-method "POST" + :filter sx-browse-filter + :site .site)))) + ;; The api returns the new DATA. + (when (> (length result) 0) + (sx--copy-data (elt result 0) data) + ;; Display the changes in `data'. + (sx--maybe-update-display)))) + +(provide 'sx-interaction) +;;; sx-interaction.el ends here diff --git a/sx.el b/sx.el index 0f399fc..e5f81a4 100644 --- a/sx.el +++ b/sx.el @@ -193,84 +193,6 @@ Return the result of BODY." (add-text-properties p (point) ,properties) result)) - -;;; Using data in buffer -(defun sx--data-here () - "Get the text property `sx--data-here'." - (or (get-text-property (point) 'sx--data-here) - (and (derived-mode-p 'sx-question-list-mode) - (tabulated-list-get-id)))) - -(defun sx--maybe-update-display () - "Refresh the question list if we're inside it." - (cond - ((derived-mode-p 'sx-question-list-mode) - (sx-question-list-refresh 'redisplay 'no-update)) - ((derived-mode-p 'sx-question-mode) - (sx-question-mode-refresh 'no-update)))) - -(defun sx--copy-data (from to) - "Copy all fields of alist FORM onto TO. -Only fields contained in TO are copied." - (setcar to (car from)) - (setcdr to (cdr from))) - -(defun sx-visit (data) - "Visit DATA in a web browser. -DATA can be a question, answer, or comment. Interactively, it is -derived from point position. -If DATA is a question, also mark it as read." - (interactive (list (sx--data-here))) - (sx-assoc-let data - (when (stringp .link) - (browse-url .link)) - (when (and .title (fboundp 'sx-question--mark-read)) - (sx-question--mark-read data) - (sx--maybe-update-display)))) - -(defun sx-toggle-upvote (data) - "Apply or remove upvote from DATA. -DATA can be a question, answer, or comment. Interactively, it is -guessed from context at point." - (interactive (list (sx--data-here))) - (let ((result - (sx-assoc-let data - (sx-set-vote data "upvote" (null (eq .upvoted t)))))) - (when (> (length result) 0) - (sx--copy-data (elt result 0) data))) - (sx--maybe-update-display)) - -(defun sx-toggle-downvote (data) - "Apply or remove downvote from DATA. -DATA can be a question or an answer. Interactively, it is guessed -from context at point." - (interactive (list (sx--data-here))) - (let ((result - (sx-assoc-let data - (sx-set-vote data "downvote" (null (eq .downvoted t)))))) - (when (> (length result) 0) - (sx--copy-data (elt result 0) data))) - (sx--maybe-update-display)) - -(defun sx-set-vote (data type status) - "Set the DATA's vote TYPE to STATUS. -DATA can be a question, answer, or comment. -TYPE can be \"upvote\" or \"downvote\". -Status is a boolean." - (sx-assoc-let data - (sx-method-call - (cond - (.comment_id "comments") - (.answer_id "answers") - (.question_id "questions")) - :id (or .comment_id .answer_id .question_id) - :submethod (concat type (unless status "/undo")) - :auth 'warn - :url-method "POST" - :filter sx-browse-filter - :site .site))) - - ;;; Assoc-let (defun sx--site (data) "Get the site in which DATA belongs. -- cgit v1.2.3 From 4368b7693a0fa8e9118655014685f7aed299a75f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 22:51:04 +0000 Subject: Cleanup old obsolete line. --- sx-question-mode.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 8ccc576..e762bab 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -332,7 +332,6 @@ where `value' is given `face' as its face. (defun sx-question-mode--fill-and-fontify (text) "Return TEXT filled according to `markdown-mode'." (with-temp-buffer - (erase-buffer) (insert text) (markdown-mode) (font-lock-mode -1) @@ -344,8 +343,8 @@ where `value' is given `face' as its face. (font-lock-add-keywords ;; Highlight usernames. nil `((,(rx (or blank line-start) - (group-n 1 (and "@" (1+ (or (syntax word) (syntax symbol))))) - symbol-end) + (group-n 1 (and "@" (1+ (or (syntax word) (syntax symbol))))) + symbol-end) 1 font-lock-builtin-face))) ;; Everything. (font-lock-fontify-region (point-min) (point-max)) -- cgit v1.2.3 From 3a13169bb9b02a4f5935d992e603f53115ddf3dd Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 25 Nov 2014 23:25:45 +0000 Subject: Question/answer score is displayed in the question buffer --- sx-question-mode.el | 64 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index e762bab..f1a8cc0 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -54,13 +54,17 @@ If WINDOW is nil, use selected one. Returns the question buffer." + (with-current-buffer + (sx-question-mode--display-buffer window) + (sx-question-mode--erase-and-print-question data))) + +(defun sx-question-mode--erase-and-print-question (data) + "Erase contents of buffer and print question given by DATA." (let ((inhibit-read-only t)) - (with-current-buffer - (sx-question-mode--display-buffer window) - (erase-buffer) - (sx-question-mode) - (sx-question-mode--print-question data) - (current-buffer)))) + (erase-buffer) + (sx-question-mode) + (sx-question-mode--print-question data) + (current-buffer))) (defun sx-question-mode--display-buffer (window) "Display and return the buffer used for displaying a question. @@ -134,11 +138,32 @@ If WINDOW is given, use that to display the buffer." "Face used for author names in the question buffer." :group 'sx-question-mode-faces) +(defface sx-question-mode-score + '((t)) + "Face used for the score in the question buffer." + :group 'sx-question-mode-faces) + +(defface sx-question-mode-score-downvoted + '((t :inherit (font-lock-warning-face sx-question-mode-score))) + "Face used for downvoted score in the question buffer." + :group 'sx-question-mode-faces) + +(defface sx-question-mode-score-upvoted + '((t :weight bold + :inherit (font-lock-function-name-face sx-question-mode-score))) + "Face used for downvoted score in the question buffer." + :group 'sx-question-mode-faces) + (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: " + "String used before the question score at the header." + :type 'string + :group 'sx-question-mode) + (defface sx-question-mode-content-face '((((background dark)) :background "#090909") (((background light)) :background "#f4f4f4")) @@ -195,10 +220,9 @@ QUESTION must be a data structure returned by `json-read'." ;; Print everything (sx-question-mode--print-section question) (sx-assoc-let question - (mapc #'sx-question-mode--print-section .answers)) + (mapc #'sx-question-mode--print-section .answers)) (goto-char (point-min)) - (with-selected-window sx-question-mode--window - (sx-question-mode-next-section))) + (sx-question-mode-next-section)) (defvar sx-question-mode--section-help-echo (format @@ -249,6 +273,13 @@ DATA can represent a question or an answer." (sx-time-since .last_edit_date) (sx-question-mode--propertize-display-name .last_editor)))) 'sx-question-mode-date) + (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))) (when .title ;; Tags (sx-question-mode--insert-header @@ -562,12 +593,15 @@ With non-nil prefix argument NO-UPDATE, just redisplay, don't query the api." (interactive "P") (sx-question-mode--ensure-mode) - (sx-question-mode--display - (if no-update - sx-question-mode--data - (sx-assoc-let sx-question-mode--data - (sx-question-get-question .site .question_id))) - (selected-window))) + (let ((point (point))) + (sx-question-mode--erase-and-print-question + (if no-update + sx-question-mode--data + (sx-assoc-let sx-question-mode--data + (sx-question-get-question .site .question_id)))) + (goto-char point) + (when (get-buffer-window (current-buffer)) + (recenter)))) (defun sx-question-mode--ensure-mode () "Ensures we are in question mode, erroring otherwise." -- cgit v1.2.3