From 14c8351e551c6c2ee30486948fdc857b5813ad52 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Wed, 26 Nov 2014 00:59:15 -0500 Subject: Default to post author if post editor is missing Fixes #100 --- sx-question-mode.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 70b8866..947c35d 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -271,7 +271,8 @@ DATA can represent a question or an answer." (when .last_edit_date (format sx-question-mode-last-edit-format (sx-time-since .last_edit_date) - (sx-question-mode--propertize-display-name .last_editor)))) + (sx-question-mode--propertize-display-name + (if .last_editor .last_editor .owner))))) 'sx-question-mode-date) (sx-question-mode--insert-header sx-question-mode-header-score -- cgit v1.2.3 From 31549b4e5a9e67180ad6a0d0b8b61e17274b54a2 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Wed, 26 Nov 2014 14:58:50 -0500 Subject: Use customizable deleted-user structure --- sx-question-mode.el | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 947c35d..a80704c 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -103,6 +103,13 @@ If WINDOW is given, use that to display the buffer." "Face used on the question title in the question buffer." :group 'sx-question-mode-faces) +(defcustom sx-question-mode-deleted-user + '((display_name . "(deleted user)")) + "The structure used to represent a deleted account." + :type '(alist :options (display_name)) + :risky t + :group 'sx-question-mode) + (defcustom sx-question-mode-header-title "\n" "String used before the question title at the header." :type 'string @@ -272,7 +279,7 @@ DATA can represent a question or an answer." (format sx-question-mode-last-edit-format (sx-time-since .last_edit_date) (sx-question-mode--propertize-display-name - (if .last_editor .last_editor .owner))))) + (or .last_editor sx-question-mode-deleted-user))))) 'sx-question-mode-date) (sx-question-mode--insert-header sx-question-mode-header-score -- cgit v1.2.3 From 8aa2db45afa43da88269c7efa71a6def0b778fdc Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Wed, 26 Nov 2014 18:01:44 -0500 Subject: Enforce string value for display_name --- sx-question-mode.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index a80704c..b2856e6 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -106,7 +106,7 @@ If WINDOW is given, use that to display the buffer." (defcustom sx-question-mode-deleted-user '((display_name . "(deleted user)")) "The structure used to represent a deleted account." - :type '(alist :options (display_name)) + :type '(alist :options ((display_name string)) :risky t :group 'sx-question-mode) -- cgit v1.2.3 From d0aa34258e3492ec5ac884dccb766bdb1776b36f Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Wed, 26 Nov 2014 18:12:06 -0500 Subject: Add missing ')' Staged only part of a change -- stupid mistake. --- sx-question-mode.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index b2856e6..8f96a01 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -106,7 +106,7 @@ If WINDOW is given, use that to display the buffer." (defcustom sx-question-mode-deleted-user '((display_name . "(deleted user)")) "The structure used to represent a deleted account." - :type '(alist :options ((display_name string)) + :type '(alist :options ((display_name string))) :risky t :group 'sx-question-mode) -- cgit v1.2.3 From 9c2df708ac4dc62c34882b7978761cf1f73e063f Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Thu, 27 Nov 2014 11:05:08 -0500 Subject: Deleted-user variable is not risky --- sx-question-mode.el | 1 - 1 file changed, 1 deletion(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 8f96a01..bc7c62c 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -107,7 +107,6 @@ If WINDOW is given, use that to display the buffer." '((display_name . "(deleted user)")) "The structure used to represent a deleted account." :type '(alist :options ((display_name string))) - :risky t :group 'sx-question-mode) (defcustom sx-question-mode-header-title "\n" -- cgit v1.2.3 From f26a55ce664d1840efef36079ab3634394006c5b Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 29 Nov 2014 00:00:28 +0000 Subject: Fix byte-compile warnings Fixes #115 --- sx-method.el | 3 ++- sx-question.el | 29 +++++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/sx-method.el b/sx-method.el index 1b20cbf..83455b8 100644 --- a/sx-method.el +++ b/sx-method.el @@ -82,7 +82,8 @@ Return the entire response as a complex alist." (prog1 (format "?site=%s" site) (setq site nil))))) - (call #'sx-request-make)) + (call #'sx-request-make) + parameters) (lwarn "sx-call-method" :debug "A: %S T: %S. M: %S,%s. F: %S" (equal 'warn auth) access-token method-auth full-method filter-auth) (unless access-token diff --git a/sx-question.el b/sx-question.el index 01ba030..f80a9bd 100644 --- a/sx-question.el +++ b/sx-question.el @@ -134,20 +134,21 @@ If no cache exists for it, initialize one with SITE." (defun sx-question--mark-hidden (question) "Mark QUESTION as being hidden." - (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)))) + (sx-assoc-let question + (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 -- cgit v1.2.3 From e731fd754cb4d5ef7e0d5d7e094a98b4162cba61 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 29 Nov 2014 00:00:48 +0000 Subject: Remove need-auth from sx-request--build-keyword-arguments --- sx-request.el | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/sx-request.el b/sx-request.el index a98af5a..6be363d 100644 --- a/sx-request.el +++ b/sx-request.el @@ -91,9 +91,8 @@ number of requests left every time it finishes a call." ;;; Making Requests -(defun sx-request-make - (method &optional args request-method) - "Make a request to the API, executing METHOD with ARGS. +(defun sx-request-make (method &optional args request-method) + "Make a request to the API, executing METHOD with ARGS. You should almost certainly be using `sx-method-call' instead of this function. REQUEST-METHOD is one of `GET' (default) or `POST'. @@ -116,8 +115,7 @@ then read with `json-read-from-string'. the main content of the response is returned." (let* ((url-automatic-caching t) (url-inhibit-uncompression t) - (url-request-data (sx-request--build-keyword-arguments args - nil)) + (url-request-data (sx-request--build-keyword-arguments args nil)) (request-url (concat sx-request-api-root method)) (url-request-method request-method) (url-request-extra-headers @@ -168,15 +166,11 @@ Currently returns nil." ;;; Support Functions - -(defun sx-request--build-keyword-arguments (alist &optional - kv-sep need-auth) +(defun sx-request--build-keyword-arguments (alist &optional kv-sep) "Format ALIST as a key-value list joined with KV-SEP. If authentication is needed, include it also or error if it is not available. -If NEED-AUTH is non-nil, authentication is required. - Build a \"key=value&key=value&...\"-style string with the elements of ALIST. If any value in the alist is nil, that pair will not be included in the return. If you wish to pass a notion of @@ -185,7 +179,7 @@ false, use the symbol `false'. Each element is processed with ;; Add API key to list of arguments, this allows for increased quota ;; automatically. (let ((api-key (cons "key" sx-request-api-key)) - (auth (car (sx-cache-get 'auth)))) + (auth (car (sx-cache-get 'auth)))) (push api-key alist) (when auth (push auth alist)) -- cgit v1.2.3 -- cgit v1.2.3 From fafda82d046c3735a5e6d5e188622be40fe707d0 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 29 Nov 2014 00:19:43 +0000 Subject: Autoload sx-tab-frontpage --- sx-tab.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sx-tab.el b/sx-tab.el index 154a90f..5b3f3c4 100644 --- a/sx-tab.el +++ b/sx-tab.el @@ -97,6 +97,9 @@ If SITE is nil, use `sx-tab-default-site'." (lambda (page) (sx-question-get-questions sx-question-list--site page))) +;;;###autoload +(autoload 'sx-tab-frontpage (expand-file-name load-file-name) + nil t) (provide 'sx-tab) ;;; sx-tab.el ends here -- cgit v1.2.3 From 73e37ca539fe7b620f066f4dd56ef97f0e4b91c7 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 29 Nov 2014 01:08:30 +0000 Subject: Autoload sx-tab-frontpage correctly --- sx-tab.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sx-tab.el b/sx-tab.el index 5b3f3c4..7ccbf18 100644 --- a/sx-tab.el +++ b/sx-tab.el @@ -98,7 +98,11 @@ If SITE is nil, use `sx-tab-default-site'." (sx-question-get-questions sx-question-list--site page))) ;;;###autoload -(autoload 'sx-tab-frontpage (expand-file-name load-file-name) +(autoload 'sx-tab-frontpage + (expand-file-name + "sx-tab" + (when load-file-name + (file-name-directory load-file-name))) nil t) (provide 'sx-tab) -- cgit v1.2.3 From deebbf0f032d4649f78206d5d8ef570afb065579 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 29 Nov 2014 22:52:59 +0000 Subject: Fix #57. Set the buffer as unibyte then multibyte again. --- sx-question-print.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sx-question-print.el b/sx-question-print.el index 0959f36..9245331 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -195,6 +195,10 @@ QUESTION must be a data structure returned by `json-read'." (sx-question-mode--print-section question) (sx-assoc-let question (mapc #'sx-question-mode--print-section .answers)) + ;; Display weird chars correctly + (set-buffer-multibyte nil) + (set-buffer-multibyte t) + ;; Go up (goto-char (point-min)) (sx-question-mode-next-section)) -- cgit v1.2.3 From bf56eba935f73c1803ea9964671a36bccb4efa21 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 01:00:06 +0000 Subject: Define and use sx-question-mode-display-buffer-function --- sx-question-mode.el | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 01a980a..6423ad8 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -30,6 +30,14 @@ ;;; Displaying a question +(defcustom sx-question-mode-display-buffer-function #'switch-to-buffer + "Function used to display the question buffer. +Called, for instance, when hitting \\`\\[sx-question-list-display-question]' on an entry in the +question list. +This is not used when navigating the question list with `\\[sx-question-list-view-next]." + :type 'function + :group 'sx-question-mode) + (defvar sx-question-mode--window nil "Window where the content of questions is displayed.") @@ -71,7 +79,8 @@ If WINDOW is given, use that to display the buffer." ;; No window, but the buffer is already being displayed somewhere. ((get-buffer-window sx-question-mode--buffer 'visible)) ;; Neither, so we create the window. - (t (switch-to-buffer sx-question-mode--buffer))) + (t (funcall sx-question-mode-display-buffer-function + sx-question-mode--buffer))) sx-question-mode--buffer) -- cgit v1.2.3 From d2ac145a981521e23cd975ff41e84dc90fa161e5 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 01:13:04 +0000 Subject: Make sx--data-here throw error on failure. --- sx-interaction.el | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/sx-interaction.el b/sx-interaction.el index 5f3ece6..87f72a5 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -29,13 +29,20 @@ ;;; Using data in buffer -(defun sx--data-here () - "Get the text property `sx--data-here'." +(defun sx--data-here (&optional noerror) + "Get data for the question or other object under point. +If NOERROR is non-nil, don't throw an error on failure. + +This looks at the text property `sx--data-here'. If it's not set, +it looks at a few other reasonable variables. If those fail too, +it throws an error." (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))) + sx-question-mode--data) + (and (null noerror) + (error "No question data found here")))) (defun sx--maybe-update-display () "Refresh the question list if we're inside it." -- cgit v1.2.3 From 5924ca1de182fc6a865179e7166029cb68c86d1a Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 01:13:52 +0000 Subject: Section Comment --- sx-interaction.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sx-interaction.el b/sx-interaction.el index 87f72a5..8f87a47 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -58,6 +58,8 @@ Only fields contained in TO are copied." (setcar to (car from)) (setcdr to (cdr from))) + +;;; Visiting (defun sx-visit (data &optional copy-as-kill) "Visit DATA in a web browser. DATA can be a question, answer, or comment. Interactively, it is -- cgit v1.2.3 From feb3e5d9c61302d71d333cb8472b5e16a9c60e80 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 01:23:35 +0000 Subject: Refactor sx-question-list--unread-count into a function. Less bookeeping --- sx-question-list.el | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/sx-question-list.el b/sx-question-list.el index 9709b99..01cdc61 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -145,9 +145,6 @@ Also see `sx-question-list-refresh'." .title 'face (if (sx-question--read-p question-data) 'sx-question-list-read-question - ;; Increment `sx-question-list--unread-count' for - ;; the mode-line. - (cl-incf sx-question-list--unread-count) 'sx-question-list-unread-question)) (propertize " " 'display "\n ") (propertize favorite 'face 'sx-question-list-favorite) @@ -334,10 +331,6 @@ Non-interactively, DATA is a question alist." ;; "Unanswered", etc. "Variable describing current tab being viewed.") -(defvar sx-question-list--unread-count 0 - "Holds the number of unread questions in the current buffer.") -(make-variable-buffer-local 'sx-question-list--unread-count) - (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) @@ -351,7 +344,7 @@ Non-interactively, DATA is a question alist." " [" "Unread: " (:propertize - (:eval (int-to-string sx-question-list--unread-count)) + (:eval (sx-question-list--unread-count)) face mode-line-buffer-id) ", " "Total: " @@ -361,6 +354,12 @@ Non-interactively, DATA is a question alist." "] ") "Mode-line construct to use in question-list buffers.") +(defun sx-question-list--unread-count () + "Number of unread questions in current dataset, as a string." + (int-to-string + (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. @@ -380,7 +379,6 @@ If the prefix argument NO-UPDATE is nil, query StackExchange for a new list before redisplaying." (interactive "p\nP") ;; Reset the mode-line unread count (we rebuild it here). - (setq sx-question-list--unread-count 0) (unless no-update (setq sx-question-list--pages-so-far 1)) (let* ((question-list @@ -494,7 +492,6 @@ relevant window." (unless data (setq data (tabulated-list-get-id))) (unless data (error "No question here!")) (unless (sx-question--read-p data) - (cl-decf sx-question-list--unread-count) (sx-question--mark-read data) (sx-question-list-refresh 'redisplay 'no-update)) (unless (and (window-live-p sx-question-mode--window) -- cgit v1.2.3 From f0d788a34a6d3e9ea564299703c35eee26f64374 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 01:29:44 +0000 Subject: sx-question--mark-read returns nil if nothing changed --- sx-question-list.el | 3 +-- sx-question.el | 45 +++++++++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/sx-question-list.el b/sx-question-list.el index 01cdc61..9185531 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -491,8 +491,7 @@ relevant window." (interactive '(nil t)) (unless data (setq data (tabulated-list-get-id))) (unless data (error "No question here!")) - (unless (sx-question--read-p data) - (sx-question--mark-read data) + (when (sx-question--mark-read data) (sx-question-list-refresh 'redisplay 'no-update)) (unless (and (window-live-p sx-question-mode--window) (null (equal sx-question-mode--window (selected-window)))) diff --git a/sx-question.el b/sx-question.el index f80a9bd..24fd97f 100644 --- a/sx-question.el +++ b/sx-question.el @@ -86,27 +86,32 @@ See `sx-question--user-read-list'." (defun sx-question--mark-read (question) "Mark QUESTION as being read until it is updated again. +Returns nil if question (in its current state) was already marked +read, i.e., if it was `sx-question--read-p'. See `sx-question--user-read-list'." - (sx-assoc-let question - (sx-question--ensure-read-list .site) - (let ((site-cell (assoc .site sx-question--user-read-list)) - (q-cell (cons .question_id .last_activity_date)) - cell) - (cond - ;; First question from this site. - ((null site-cell) - (push (list .site q-cell) sx-question--user-read-list)) - ;; Question already has an older time. - ((setq cell (assoc .question_id site-cell)) - (setcdr cell .last_activity_date)) - ;; Question wasn't present. - (t - (sx-sorted-insert-skip-first - q-cell site-cell (lambda (x y) (> (car x) (car y)))))))) - ;; Save the results. - ;; @TODO This causes a small lag on `j' and `k' as the list gets - ;; large. Should we do this on a timer? - (sx-cache-set 'read-questions sx-question--user-read-list)) + (prog1 + (sx-assoc-let question + (sx-question--ensure-read-list .site) + (let ((site-cell (assoc .site sx-question--user-read-list)) + (q-cell (cons .question_id .last_activity_date)) + cell) + (cond + ;; First question from this site. + ((null site-cell) + (push (list .site q-cell) sx-question--user-read-list)) + ;; Question already present. + ((setq cell (assoc .question_id site-cell)) + ;; Current version is newer than cached version. + (when (> .last_activity_date (cdr cell)) + (setcdr cell .last_activity_date))) + ;; Question wasn't present. + (t + (sx-sorted-insert-skip-first + q-cell site-cell (lambda (x y) (> (car x) (car y)))))))) + ;; Save the results. + ;; @TODO This causes a small lag on `j' and `k' as the list gets + ;; large. Should we do this on a timer? + (sx-cache-set 'read-questions sx-question--user-read-list))) ;;;; Hidden -- cgit v1.2.3 From 867224348a5d979af7a965c9bbe476dea3c3638f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 01:46:34 +0000 Subject: Refactor sx-question-mode--window into a function. It made little sense the way it was, and it was complicated. Now that it's a function, it's always up to date. --- sx-question-mode.el | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 6423ad8..bee3e29 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -38,15 +38,20 @@ This is not used when navigating the question list with `\\[sx-question-list-vie :type 'function :group 'sx-question-mode) -(defvar sx-question-mode--window nil - "Window where the content of questions is displayed.") - (defvar sx-question-mode--buffer nil "Buffer being used to display questions.") (defvar sx-question-mode--data nil "The data of the question being displayed.") +(defun sx-question-mode--get-window () + "Return a window displaying a question, or nil." + (car-safe + (cl-member-if + (lambda (x) (with-selected-window x + (derived-mode-p 'sx-question-mode))) + (window-list nil 'never nil)))) + (defun sx-question-mode--display (data &optional window) "Display question given by DATA on WINDOW. If WINDOW is nil, use selected one. -- cgit v1.2.3 From 5bb980d36c6e0346da629ceeb1835a020e75a9b4 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 02:02:14 +0000 Subject: Refactor sx-question-list-display-question into 2 other functions --- sx-interaction.el | 24 ++++++++++++++++++ sx-question-list.el | 70 ++++++++++++++++++++++++----------------------------- 2 files changed, 56 insertions(+), 38 deletions(-) diff --git a/sx-interaction.el b/sx-interaction.el index 8f87a47..f34c49c 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -82,6 +82,30 @@ If DATA is a question, also mark it as read." (sx-question--mark-read data) (sx--maybe-update-display)))) + +;;; Displaying +(defun sx-display-question (&optional data focus window) + "Display question given by DATA, on WINDOW. +When DATA is nil, display question under point. When FOCUS is +non-nil (the default when called interactively), also focus the +relevant window. + +If WINDOW nil, the window is decided by +`sx-question-mode-display-buffer-function'." + (interactive (list (sx--data-here) t)) + (when (sx-question--mark-read data) + (sx--maybe-update-display)) + ;; Display the question. + (setq window + (get-buffer-window + (sx-question-mode--display data window))) + (when focus + (if (window-live-p window) + (select-window window) + (switch-to-buffer sx-question-mode--buffer)))) + + +;;; Voting (defun sx-toggle-upvote (data) "Apply or remove upvote from DATA. DATA can be a question, answer, or comment. Interactively, it is diff --git a/sx-question-list.el b/sx-question-list.el index 9185531..d2e1bb6 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -301,7 +301,7 @@ into consideration. ("d" sx-toggle-downvote) ("h" sx-question-list-hide) ("m" sx-question-list-mark-read) - ([?\r] sx-question-list-display-question))) + ([?\r] sx-display-question))) (defun sx-question-list-hide (data) "Hide question under point. @@ -421,7 +421,37 @@ Displayed in `sx-question-mode--window', replacing any question that may currently be there." (interactive "p") (sx-question-list-next n) - (sx-question-list-display-question)) + (sx-display-question + (tabulated-list-get-id) + nil + (sx-question-list--create-question-window))) + +(defun sx-question-list--create-question-window () + "Create or find a window where a question can be displayed. + +If any current window displays a question, that window is +returned. If none do, a new one is created such that the +question-list window remains `sx-question-list-height' lines +high (if possible)." + (or (sx-question-mode--get-window) + ;; Create a proper window. + (let ((window + (condition-case er + (split-window (selected-window) sx-question-list-height 'below) + (error + ;; If the window is too small to split, use any one. + (if (string-match + "Window # too small for splitting" + (car (cdr-safe er))) + (next-window) + (error (cdr er))))))) + ;; Configure the window to be closed on `q'. + (set-window-prev-buffers window nil) + (set-window-parameter + window 'quit-restore + ;; See (info "(elisp) Window Parameters") + `(window window ,(selected-window) ,sx-question-mode--buffer)) + window))) (defun sx-question-list-next (n) "Move cursor down N questions. @@ -483,42 +513,6 @@ This does not update `sx-question-mode--window'." (interactive "p") (sx-question-list-next-far (- n))) -(defun sx-question-list-display-question (&optional data focus) - "Display question given by DATA. -When DATA is nil, display question under point. When FOCUS is -non-nil (the default when called interactively), also focus the -relevant window." - (interactive '(nil t)) - (unless data (setq data (tabulated-list-get-id))) - (unless data (error "No question here!")) - (when (sx-question--mark-read data) - (sx-question-list-refresh 'redisplay 'no-update)) - (unless (and (window-live-p sx-question-mode--window) - (null (equal sx-question-mode--window (selected-window)))) - (setq sx-question-mode--window - (condition-case er - (split-window (selected-window) sx-question-list-height 'below) - (error - ;; If the window is too small to split, use current one. - (if (string-match - "Window # too small for splitting" - (car (cdr-safe er))) - nil - (error (cdr er))))))) - ;; Display the question. - (sx-question-mode--display data sx-question-mode--window) - ;; Configure the window to be closed on `q'. - (set-window-prev-buffers sx-question-mode--window nil) - (set-window-parameter - sx-question-mode--window - 'quit-restore - ;; See (info "(elisp) Window Parameters") - `(window window ,(selected-window) ,sx-question-mode--buffer)) - (when focus - (if sx-question-mode--window - (select-window sx-question-mode--window) - (switch-to-buffer sx-question-mode--buffer)))) - (defun sx-question-list-switch-site (site) "Switch the current site to SITE and display its questions. Use `ido-completing-read' if variable `ido-mode' is active. -- cgit v1.2.3 From 30217190b487a9b0acb7e367fe4bdf226e0819e5 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 02:30:49 +0000 Subject: Maintain good line position while navigating the question list. --- sx-question-list.el | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sx-question-list.el b/sx-question-list.el index d2e1bb6..18f8ba0 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -463,7 +463,21 @@ This does not update `sx-question-mode--window'." ;; If we were trying to move forward, but we hit the end. (when (eobp) ;; Try to get more questions. - (sx-question-list-next-page)))) + (sx-question-list-next-page)) + (sx-question-list--ensure-line-good-line-position))) + +(defun sx-question-list--ensure-line-good-line-position () + "Scroll window such that current line is a good place. +Check if we're at least 6 lines from the bottom. Scroll up if +we're not. Do the same for 3 lines from the top." + ;; At least one entry below us. + (let ((lines-to-bottom (count-screen-lines (point) (window-end)))) + (unless (>= lines-to-bottom 6) + (recenter (- 6)))) + ;; At least one entry above us. + (let ((lines-to-top (count-screen-lines (point) (window-start)))) + (unless (>= lines-to-top 3) + (recenter 3)))) (defun sx-question-list-next-page () "Fetch and display the next page of questions." -- cgit v1.2.3 From 039ef045855fa4e2c039e228dda998d2173eca01 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 02:50:10 +0000 Subject: sx-question-get-questions takes arbitrary keywords. --- sx-question.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sx-question.el b/sx-question.el index f80a9bd..00b5f7f 100644 --- a/sx-question.el +++ b/sx-question.el @@ -26,15 +26,17 @@ (require 'sx-filter) (require 'sx-method) -(defun sx-question-get-questions (site &optional page) +(defun sx-question-get-questions (site &optional page keywords) "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. +KEYWORDS are added to the method call along with PAGE. + `sx-method-call' is used with `sx-browse-filter'." (sx-method-call 'questions - :keywords `((page . ,page)) + :keywords `((page . ,page) ,@keywords) :site site :auth t :filter sx-browse-filter)) -- cgit v1.2.3 From 3e4386a0ec6383eb92ae5535db165c82ed7092b2 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 03:07:32 +0000 Subject: Define 5 new tabs, as per http://api.stackexchange.com/docs/questions --- sx-tab.el | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/sx-tab.el b/sx-tab.el index 7ccbf18..1152528 100644 --- a/sx-tab.el +++ b/sx-tab.el @@ -96,7 +96,7 @@ If SITE is nil, use `sx-tab-default-site'." (sx-tab--define "FrontPage" (lambda (page) (sx-question-get-questions - sx-question-list--site page))) + sx-question-list--site page '((sort . activity))))) ;;;###autoload (autoload 'sx-tab-frontpage (expand-file-name @@ -105,5 +105,79 @@ If SITE is nil, use `sx-tab-default-site'." (file-name-directory load-file-name))) nil t) + +;;; Newest +(sx-tab--define "Newest" + (lambda (page) + (sx-question-get-questions + sx-question-list--site page '((sort . creation))))) +;;;###autoload +(autoload 'sx-tab-newest + (expand-file-name + "sx-tab" + (when load-file-name + (file-name-directory load-file-name))) + nil t) + + + +;;; TopVoted +(sx-tab--define "TopVoted" + (lambda (page) + (sx-question-get-questions + sx-question-list--site page '((sort . votes))))) +;;;###autoload +(autoload 'sx-tab-topvoted + (expand-file-name + "sx-tab" + (when load-file-name + (file-name-directory load-file-name))) + nil t) + + + +;;; Hot +(sx-tab--define "Hot" + (lambda (page) + (sx-question-get-questions + sx-question-list--site page '((sort . hot))))) +;;;###autoload +(autoload 'sx-tab-hot + (expand-file-name + "sx-tab" + (when load-file-name + (file-name-directory load-file-name))) + nil t) + + + +;;; Week +(sx-tab--define "Week" + (lambda (page) + (sx-question-get-questions + sx-question-list--site page '((sort . week))))) +;;;###autoload +(autoload 'sx-tab-week + (expand-file-name + "sx-tab" + (when load-file-name + (file-name-directory load-file-name))) + nil t) + + + +;;; Month +(sx-tab--define "Month" + (lambda (page) + (sx-question-get-questions + sx-question-list--site page '((sort . month))))) +;;;###autoload +(autoload 'sx-tab-month + (expand-file-name + "sx-tab" + (when load-file-name + (file-name-directory load-file-name))) + nil t) + (provide 'sx-tab) ;;; sx-tab.el ends here -- cgit v1.2.3 From 10597c39adbcf775841f908452082ed344110ce9 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 03:08:52 +0000 Subject: Define command for changing tab --- sx-tab.el | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/sx-tab.el b/sx-tab.el index 1152528..b497ce0 100644 --- a/sx-tab.el +++ b/sx-tab.el @@ -32,6 +32,18 @@ :type 'string :group 'sx) +(defvar sx-tab--list nil + "List of the names of all defined tabs.") + +(defun sx-tab-switch (tab) + "Switch to another question-list tab." + (interactive + (list (funcall (if ido-mode #'ido-completing-read #'completing-read) + "Switch to tab: " sx-tab--list + (lambda (tab) (not (equal tab sx-question-list--current-tab))) + t))) + (funcall (intern (format "sx-tab-%s" (downcase tab))))) + (defmacro sx-tab--define (tab pager &optional printer refresher &rest body) "Define a StackExchange tab called TAB. @@ -55,7 +67,7 @@ variables, but before refreshing the display." `(progn (defvar ,buffer-variable nil ,(format "Buffer where the %s questions are displayed." - tab)) + tab)) (defun ,(intern (concat "sx-tab-" name)) (&optional no-update site) @@ -63,7 +75,7 @@ variables, but before refreshing the display." NO-UPDATE (the prefix arg) is passed to `sx-question-list-refresh'. If SITE is nil, use `sx-tab-default-site'." - tab) + tab) (interactive (list current-prefix-arg (funcall (if ido-mode #'ido-completing-read #'completing-read) @@ -89,7 +101,10 @@ If SITE is nil, use `sx-tab-default-site'." (setq sx-question-list--current-tab ,tab) ,@body (sx-question-list-refresh 'redisplay no-update)) - (switch-to-buffer ,buffer-variable))))) + (switch-to-buffer ,buffer-variable)) + ;; Add this tab to the list of existing tabs. So we can prompt + ;; the user with completion and stuff. + (add-to-list 'sx-tab--list ,tab)))) ;;; FrontPage -- cgit v1.2.3 From 4f4b4d9f9a1000ce5f91e6d835cb71268f9525ec Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 03:08:59 +0000 Subject: Bind sx-tab-switch to t in question list Fixes #17 --- sx-question-list.el | 1 + 1 file changed, 1 insertion(+) diff --git a/sx-question-list.el b/sx-question-list.el index 9709b99..852c11a 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -299,6 +299,7 @@ into consideration. ("K" sx-question-list-previous-far) ("g" sx-question-list-refresh) (":" sx-question-list-switch-site) + ("t" sx-question-list-switch-tab) ("v" sx-visit) ("u" sx-toggle-upvote) ("d" sx-toggle-downvote) -- cgit v1.2.3 From 7d6110a64c3b9e6d95a2976b8423ab26fe312b2c Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Mon, 1 Dec 2014 11:14:00 +0000 Subject: Reimplement deleted-user --- sx-question-print.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sx-question-print.el b/sx-question-print.el index 0959f36..3200ead 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -43,6 +43,11 @@ ;;; Faces and Variables +(defcustom sx-question-mode-deleted-user + '((display_name . "(deleted user)")) + "The structure used to represent a deleted account." + :type '(alist :options ((display_name string))) + :group 'sx-question-mode) (defface sx-question-mode-header '((t :inherit font-lock-variable-name-face)) @@ -227,7 +232,8 @@ DATA can represent a question or an answer." (when .last_edit_date (format sx-question-mode-last-edit-format (sx-time-since .last_edit_date) - (sx-question-mode--propertize-display-name .last_editor)))) + (sx-question-mode--propertize-display-name + (or .last_editor sx-question-mode-deleted-user))))) 'sx-question-mode-date) (sx-question-mode--insert-header sx-question-mode-header-score -- cgit v1.2.3 From 8795a394f90f143239edeabd870bf0767303543d Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 2 Dec 2014 15:24:47 +0000 Subject: Redo sx--data-here take &optional noerror --- sx-interaction.el | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sx-interaction.el b/sx-interaction.el index b04a49e..b67e0df 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -29,13 +29,20 @@ ;;; Using data in buffer -(defun sx--data-here () - "Get the text property `sx--data-here'." - (or (get-char-property (point) 'sx--data-here) +(defun sx--data-here (&optional noerror) + "Get data for the question or other object under point. +If NOERROR is non-nil, don't throw an error on failure. + +This looks at the text property `sx--data-here'. If it's not set, +it looks at a few other reasonable variables. If those fail too, +it throws an error." + (or (get-text-property (point) 'sx--data-here) (and (derived-mode-p 'sx-question-list-mode) (tabulated-list-get-id)) (and (derived-mode-p 'sx-question-mode) - sx-question-mode--data))) + sx-question-mode--data) + (and (null noerror) + (error "No question data found here")))) (defun sx--maybe-update-display () "Refresh the question list if we're inside it." -- cgit v1.2.3 From e556a8127ab976725729b3221d82db990e9a27b9 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Tue, 2 Dec 2014 11:27:52 -0500 Subject: Update README with some basic instructions. Notes: `sx-tab' should not need to be required. The necessary functions should be auto-loaded. --- README.org | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.org b/README.org index df8d907..db75b90 100644 --- a/README.org +++ b/README.org @@ -40,11 +40,28 @@ To install the development version, follow the usual steps: - Clone this repository - Add this directory to your ~load-path~ - Issue ~(require 'sx)~ +- Issue ~(require 'sx-tab)~ This should give you access to the only entry point function at the moment, ~sx-tab-frontpage~. Eventually, this package will be available on MELPA. +** Some Basic Controls +- =RET= :: interact with things under point (collapse headers, visit + links, etc.) +- =TAB= :: go to next element +- =n= :: go to next section +- =p= :: go to previous section +- =j= :: go to next question, viewing it +- =k= :: go to previous question, viewing it +- =v= :: visit a question on the browser +- =w= :: copy things under point (usually a url) +- =e= :: edit +- =a= :: answer +- =c= :: comment +- =u= :: vote up +- =d= :: vote down + * Contributing Please help contribute! Doing any of the following will help us immensely: - [[https://github.com/vermiculus/sx.el/issues/new][Open an issue]] -- cgit v1.2.3 From 9c6385498246bfa7e446ae875424e24681a20c29 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Tue, 2 Dec 2014 19:57:41 -0500 Subject: Refill to 80 characters --- README.org | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.org b/README.org index db75b90..245719d 100644 --- a/README.org +++ b/README.org @@ -4,9 +4,9 @@ [[https://gitter.im/vermiculus/sx.el?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge][https://badges.gitter.im/Join Chat.svg]] [[https://www.waffle.io/vermiculus/sx.el][https://badge.waffle.io/vermiculus/sx.el.svg]] -SX will be a full featured Stack Exchange mode for GNU Emacs 24+. -Using the official API, we aim to create a more versatile experience -for the Stack Exchange network within Emacs itself. +SX will be a full featured Stack Exchange mode for GNU Emacs 24+. Using the +official API, we aim to create a more versatile experience for the Stack +Exchange network within Emacs itself. * Features - ~sx-tab-frontpage~ :: @@ -69,10 +69,9 @@ Please help contribute! Doing any of the following will help us immensely: - [[https://gitter.im/vermiculus/sx.el][Suggest a package or library in our Chat on Gitter]] (or just hang out =:)=) - Spread the word! -For a better view of all of the open issues, take a look at our lovely -[[http://www.waffle.io/vermiculus/sx.el][Waffle board]]. Feel free to take the torch on anything in =backlog= or -=ready=. If you have thoughts on any other issues, don't hesitate to -chime in! +For a better view of all of the open issues, take a look at our lovely [[http://www.waffle.io/vermiculus/sx.el][Waffle +board]]. Feel free to take the torch on anything in =backlog= or =ready=. If you +have thoughts on any other issues, don't hesitate to chime in! * Resources - [[http://www.gnu.org/software/emacs/][GNU Emacs]] @@ -85,3 +84,7 @@ it. - [[file:resources/emacs.svg][Emacs icon]] - [[file:resources/stackexchange.svg][Stack Exchange icon]] +* COMMENT Local Variables +# Local Variables: +# fill-column: 80 +# End: -- cgit v1.2.3 From a0556c38eb39d66fcde2a7e09eaa9f1ed7e3a7e6 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Tue, 2 Dec 2014 19:58:02 -0500 Subject: More complete README --- README.org | 67 ++++++++++++++++++++++++-------------------------------------- 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/README.org b/README.org index 245719d..9194a10 100644 --- a/README.org +++ b/README.org @@ -9,31 +9,28 @@ official API, we aim to create a more versatile experience for the Stack Exchange network within Emacs itself. * Features -- ~sx-tab-frontpage~ :: - List questions on a StackExchange site. -- Viewing Posts :: - - Use =jknp= to open questions from within ~list-questions~; use - =RET= to move focus. - - Use =v= to open the object at point in your browser. - - Use =TAB= to fold questions and answers. - - Use =RET= to open a link at point. - - Use =:= to switch sites. - - Vote up and down with =u= and =d=. +** Viewing Questions +View questions with one of the ~sx-tab-~ commands. These translate to the +different 'tabs' that you can view on the official site. Implemented tabs +include: +- =frontpage= :: The default front page of questions. +- =newest= :: Newest questions first. +- =topvoted= :: Highest-voted questions first. +- =hot= :: Questions with the most views, answers, and votes over the last few + days. +- =week= :: Questions with the most views, answers, and votes this week. +- =month= :: Questions with the most views, answers, and votes this month. +The meaning of these tabs hopefully needs no explanation, but the official +behavior is given as a tooltip on any site in the StackExchange network. -** Planned -- Archiving questions for offline access -- Browsing and favoriting networks -- Advanced searching -- Writing questions, answers, and comments (with source code in its - native major mode) -- Notifications -- Reputation reporting -- ... +Each of these opens up a list of questions. Switch sites with =:=. Navigate +this list of questions with =jk= or =np=. =jk= will also view the question in a +separate buffer. =v= will visit the question in your browser where =w= will +simply copy a link. Upvote and downvote with =u= and =d=. =RET= will take you +to the question buffer, where =RET= on headlines will expand and collapse each +section. Add comments with =c=. -Have a feature in mind that isn't on the list? Submit a pull request -to add it to the list! If you want to discuss it first, pop in our -Gitter chatroom (badge above) -- someone will be around shortly to -talk about it. +As always, =C-h m= is the definitive resource for the functions of this mode. * Installation To install the development version, follow the usual steps: @@ -41,26 +38,14 @@ To install the development version, follow the usual steps: - Add this directory to your ~load-path~ - Issue ~(require 'sx)~ - Issue ~(require 'sx-tab)~ -This should give you access to the only entry point function at the -moment, ~sx-tab-frontpage~. +This should give you access to the ~sx-tab-~ functions (the main entry points at +this time). -Eventually, this package will be available on MELPA. +If you are going to be doing any asking / answering / commenting / upvoting / +downvoting / /etc./, you must use ~sx-authenticate~ to provide SX with an +authentication token to act on your behalf. -** Some Basic Controls -- =RET= :: interact with things under point (collapse headers, visit - links, etc.) -- =TAB= :: go to next element -- =n= :: go to next section -- =p= :: go to previous section -- =j= :: go to next question, viewing it -- =k= :: go to previous question, viewing it -- =v= :: visit a question on the browser -- =w= :: copy things under point (usually a url) -- =e= :: edit -- =a= :: answer -- =c= :: comment -- =u= :: vote up -- =d= :: vote down +Eventually, this package will be available on MELPA. * Contributing Please help contribute! Doing any of the following will help us immensely: -- cgit v1.2.3 From 539debfc6c07b1cd9bf478452869f36583877eca Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Tue, 2 Dec 2014 20:16:36 -0500 Subject: Add CONTRIBUTING.org --- CONTRIBUTING.org | 23 +++++++++++++++++++++++ README.org | 4 +++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 CONTRIBUTING.org diff --git a/CONTRIBUTING.org b/CONTRIBUTING.org new file mode 100644 index 0000000..46fcf13 --- /dev/null +++ b/CONTRIBUTING.org @@ -0,0 +1,23 @@ +If you need help, search the issue tracker to see if anyone has asked +your question before. If it hasn't, a good place to ask first is our +chat room on [[https://gitter.im/vermiculus/sx.el][Gitter]]. Opening an issue is welcome of course, but chat +will likely be faster for you. If a code change needs to be made, an +issue can be written up as necessary. + +Have a great idea for SX? Again, discuss it on [[https://gitter.im/vermiculus/sx.el][Gitter]] first! Don't +limit ideas to mimicking the official website, either -- this is +Emacs; we should take advantage of its abilities. + +To see what we're working on /right now/, check out our [[http://www.waffle.io/vermiculus/sx.el][Waffle board]]. +If you would like to contribute, feel free to take on anything in the +=ready= column (which translates to the =ready= label in GitHub +issues). These are issues which have been discussed enough to provide +a good idea of what should be done. Issues in =backlog= are either +still under discussion or simply are in the backlog. + +Of course, the greatest gift you can give to SX is a good word. Star +the project on GitHub, mention it to others who would use it in your +StackExchange chat rooms (as always, be courteous and respectful), and +use it yourself. + +Enjoy! diff --git a/README.org b/README.org index 9194a10..460ba34 100644 --- a/README.org +++ b/README.org @@ -48,7 +48,7 @@ authentication token to act on your behalf. Eventually, this package will be available on MELPA. * Contributing -Please help contribute! Doing any of the following will help us immensely: +Please help contribute! Doing any of the following will help us immensely: - [[https://github.com/vermiculus/sx.el/issues/new][Open an issue]] - [[https://github.com/vermiculus/sx.el/pulls][Submit a pull request]] - [[https://gitter.im/vermiculus/sx.el][Suggest a package or library in our Chat on Gitter]] (or just hang out =:)=) @@ -58,6 +58,8 @@ For a better view of all of the open issues, take a look at our lovely [[http:// board]]. Feel free to take the torch on anything in =backlog= or =ready=. If you have thoughts on any other issues, don't hesitate to chime in! +See also =CONTRIBUTING.org=. + * Resources - [[http://www.gnu.org/software/emacs/][GNU Emacs]] - [[https://api.stackexchange.com/docs][Stack Exchange API v2.2]] -- cgit v1.2.3 From 6f2f7f6cc68a673c75ae0ec9059c299967b43975 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 3 Dec 2014 14:12:03 +0000 Subject: Header commentary on sx-button Affects #123 --- sx-button.el | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/sx-button.el b/sx-button.el index 8f0b6b9..f1d7e4d 100644 --- a/sx-button.el +++ b/sx-button.el @@ -1,4 +1,4 @@ -;;; sx-button.el --- Defining buttons used throughout SX. +;;; sx-button.el --- Defining buttons used throughout SX. -*- lexical-binding: t; -*- ;; Copyright (C) 2014 Artur Malabarba @@ -18,6 +18,25 @@ ;; along with this program. If not, see . ;;; Commentary: +;; +;; This file defines all buttons used by SX. For information on +;; buttons, see: +;; http://www.gnu.org/software/emacs/manual/html_node/elisp/Buttons.html +;; +;; Most interactible parts of the SX buffers are buttons. Wherever you +;; are, you can always cycle through all buttons by hitting `TAB', +;; that should help identify what's a button in each buffer. +;; +;; To define a new type of button follow the examples below using +;; `define-button-type' with :supertype `sx-button'. Required +;; properties are `action' and `help-echo'. You'll probably want to +;; give it a `face' as well, unless you want it to look like a link. +;; +;; Buttons can then be inserted in their respective files using +;; `insert-text-button'. Give it the string, the `:type' you defined, +;; and any aditional properties that can only be determined at +;; creation. Existing text can be transformed into a button with +;; `make-text-button' instead. ;;; Code: @@ -132,7 +151,3 @@ code-block." (provide 'sx-button) ;;; sx-button.el ends here - -;; Local Variables: -;; lexical-binding: t -;; End: -- cgit v1.2.3 From 46de3f0ddba5a7611f025b816d637136593b20b1 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 3 Dec 2014 14:13:29 +0000 Subject: Not adamant --- sx-compose.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sx-compose.el b/sx-compose.el index f5aef79..d7d3ff3 100644 --- a/sx-compose.el +++ b/sx-compose.el @@ -20,9 +20,9 @@ ;;; Commentary: ;; This file defines `sx-compose-mode' and its auxiliary functions and -;; variables. In order to use `sx-compose-mode', it is adamant that -;; the variable `sx-compose--send-function' be set. Otherwise it's -;; just a regular markdown buffer. +;; variables. In order to use `sx-compose-mode', it is vital that the +;; variable `sx-compose--send-function' be set. Otherwise it's just a +;; regular markdown buffer. ;; ;; In order to help avoid mistakes, there is the function ;; `sx-compose-create'. This is the preferred way of activating the -- cgit v1.2.3 From 7373bde63cbb066d9aa4f05d0ac04d7ec2781cba Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 3 Dec 2014 15:08:29 +0000 Subject: Extend sx--data-here --- sx-interaction.el | 73 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/sx-interaction.el b/sx-interaction.el index 9a5bbcb..1603ca7 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -36,6 +36,8 @@ ;;; Code: +(eval-when-compile + '(require 'cl-lib)) (require 'sx) (require 'sx-question) @@ -46,20 +48,46 @@ ;;; Using data in buffer -(defun sx--data-here (&optional noerror) - "Get data for the question or other object under point. -If NOERROR is non-nil, don't throw an error on failure. - -This looks at the text property `sx--data-here'. If it's not set, -it looks at a few other reasonable variables. If those fail too, -it throws an error." - (or (get-text-property (point) 'sx--data-here) - (and (derived-mode-p 'sx-question-list-mode) - (tabulated-list-get-id)) - (and (derived-mode-p 'sx-question-mode) - sx-question-mode--data) - (and (null noerror) - (error "No question data found here")))) +(cl-defun sx--data-here (&key (error t) + (type nil) + (question-read-p nil)) + "Get the alist regarding object under point. +Looks at the text property `sx--data-here'. If it's not set, it +looks at a few other reasonable variables. If those fail too, it +throws an error. + +Possible keyword arguments are: + + :error If explicit given as nil, no errors are thrown. + :type Symbol restricting the type of object desired. Possible + values are 'question, 'answer, 'comment. If nothing is found of + that type. + :question-read-p If non-nil, and if object found is a question, + throw a `user-error' if it isn't `sx-question--read-p'. If + object found is not a question, this argument is ignored." + (let ((result + (or (let ((data (get-char-property (point) 'sx--data-here))) + (if (null type) data + (sx-assoc-let type + ;; Is data of the right type? + (cl-case type + (question (when .title data)) + (answer (when .answer_id data)) + (comment (when .comment_id data)))))) + ;; The following two only ever return questions. + (when (or (null type) (eq type 'question)) + ;; @TODO: `sx-question-list-mode' may one day display answers. + (or (and (derived-mode-p 'sx-question-list-mode) + (tabulated-list-get-id)) + (and (derived-mode-p 'sx-question-mode) + sx-question-mode--data))) + ;; Nothing was found + (and error (error "No %s found here" (or type "data")))))) + ;; If we found a question, we may need to check if it's read. + (if (and question-read-p (assoc 'title result)) + (if (sx-question--read-p result) result + (user-error "Question still unread. View it before acting on it")) + result))) (defun sx--maybe-update-display (&optional buffer) "Refresh whatever is displayed in BUFFER or the current buffer. @@ -129,7 +157,7 @@ If WINDOW nil, the window is decided by "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))) + (interactive (list (sx--data-here :question-read-p t))) (sx-assoc-let data (sx-set-vote data "upvote" (null (eq .upvoted t))))) @@ -137,7 +165,7 @@ guessed from context at point." "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))) + (interactive (list (sx--data-here :question-read-p t))) (sx-assoc-let data (sx-set-vote data "downvote" (null (eq .downvoted t))))) @@ -176,7 +204,7 @@ it is guessed from context at point. If DATA is a comment, the comment is posted as a reply to it. TEXT is a string. Interactively, it is read from the minibufer." - (interactive (list (sx--data-here) 'query)) + (interactive (list (sx--data-here :question-read-p t) 'query)) ;; When clicking the "Add a Comment" button, first arg is a marker. (when (markerp data) (setq data (sx--data-here)) @@ -255,8 +283,6 @@ OBJECT can be a question or an answer." "Start editing an answer or question given by DATA. DATA is an answer or question alist. Interactively, it is guessed from context at point." - ;; Answering doesn't really make sense from anywhere other than - ;; inside a question. So we don't need `sx--data-here' here. (interactive (list (sx--data-here))) ;; If we ever make an "Edit" button, first arg is a marker. (when (markerp data) (setq data (sx--data-here))) @@ -276,8 +302,6 @@ from context at point." (defun sx-ask (site) "Start composing a question for SITE. SITE is a string, indicating where the question will be posted." - ;; Answering doesn't really make sense from anywhere other than - ;; inside a question. So we don't need `sx--data-here' here. (interactive (list (sx-tab--interactive-site-prompt))) (let ((buffer (current-buffer))) (pop-to-buffer @@ -292,9 +316,10 @@ SITE is a string, indicating where the question will be posted." "Start composing an answer for question given by DATA. DATA is a question alist. Interactively, it is guessed from context at point. " - ;; Answering doesn't really make sense from anywhere other than - ;; inside a question. So we don't need `sx--data-here' here. - (interactive (list sx-question-mode--data)) + ;; If the user tries to answer a question that's not viewed, he + ;; probaby hit the button by accident. + (interactive + (list (sx--data-here :question-read-p t :type 'question))) ;; When clicking the "Write an Answer" button, first arg is a marker. (when (markerp data) (setq data (sx--data-here))) (let ((buffer (current-buffer))) -- cgit v1.2.3 From 7ecd6404ea465c547f3217016c1940f94682fb96 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 3 Dec 2014 15:24:15 +0000 Subject: Add comments to sx--unindent-text --- sx.el | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sx.el b/sx.el index f1d3634..3010bdc 100644 --- a/sx.el +++ b/sx.el @@ -189,24 +189,31 @@ Anything before the (sub)domain is removed." "" url))) (defun sx--unindent-text (text) - "Remove indentation from TEXT." + "Remove indentation from TEXT. +Primarily designed to extract the content of markdown code +blocks." (with-temp-buffer (insert text) (goto-char (point-min)) (let (result) + ;; Get indentation of each non-blank line (while (null (eobp)) (skip-chars-forward "[:blank:]") (unless (looking-at "$") (push (current-column) result)) (forward-line 1)) (when result + ;; Build a regexp with the smallest indentation (let ((rx (format "^ \\{0,%s\\}" (apply #'min result)))) (goto-char (point-min)) + ;; Use this regexp to remove that much indentation + ;; throughout the buffer. (while (and (null (eobp)) (search-forward-regexp rx nil 'noerror)) (replace-match "") (forward-line 1))))) + ;; Return the buffer (buffer-string))) -- cgit v1.2.3 From 034430657c658e712de5ce8b827cf63ff45ca6e3 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 3 Dec 2014 15:28:49 +0000 Subject: sx--site gracefully accepts nil argument --- sx.el | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sx.el b/sx.el index 3010bdc..431643c 100644 --- a/sx.el +++ b/sx.el @@ -269,11 +269,10 @@ 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))) + (when (stringp link) + (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 `.'. -- cgit v1.2.3 From 4b3fa6cf6171950d4ed48de736395f8e74c6709f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 3 Dec 2014 15:39:35 +0000 Subject: Don't rely on cus-edit for the faces --- sx-button.el | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sx-button.el b/sx-button.el index f1d7e4d..dbadc2e 100644 --- a/sx-button.el +++ b/sx-button.el @@ -45,6 +45,14 @@ (require 'sx) (require 'sx-question) + +;;; Face +(defface sx-custom-button + '((((type x w32 ns) (class color)) ; Like default mode line + :box (:line-width 2 :style released-button) + :background "lightgrey" :foreground "black")) + "Face used on buttons such as \"Write an Answer\".") + ;;; Command definitions ;; This extends `button-map', which already defines RET and mouse-1. @@ -137,7 +145,7 @@ code-block." 'help-echo (concat "mouse-1, RET" (propertize ": write a comment" 'face 'minibuffer-prompt)) - 'face 'custom-button + 'face 'sx-custom-button 'action #'sx-comment :supertype 'sx-button) @@ -145,7 +153,7 @@ code-block." 'help-echo (concat "mouse-1, RET" (propertize ": write an answer" 'face 'minibuffer-prompt)) - 'face 'custom-button + 'face 'sx-custom-button 'action #'sx-answer :supertype 'sx-button) -- cgit v1.2.3 From 62615786ec83d959ee4e6e9db7da7198dbebe04e Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 3 Dec 2014 15:39:51 +0000 Subject: Fix unicode display on question list --- sx-question-list.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sx-question-list.el b/sx-question-list.el index 628de30..c5c32d9 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -263,9 +263,9 @@ into consideration. ;; it's not terribly intuitive. (setq tabulated-list-sort-key nil) (add-hook 'tabulated-list-revert-hook - #'sx-question-list-refresh nil t) + #'sx-question-list-refresh nil t) (add-hook 'tabulated-list-revert-hook - #'sx-question-list--update-mode-line nil t) + #'sx-question-list--update-mode-line nil t) (tabulated-list-init-header)) (defcustom sx-question-list-date-sort-method 'last_activity_date @@ -398,7 +398,11 @@ a new list before redisplaying." (setq tabulated-list-entries (mapcar sx-question-list--print-function (cl-remove-if #'sx-question--hidden-p question-list))) - (when redisplay (tabulated-list-print 'remember)) + (when redisplay + (tabulated-list-print 'remember) + ;; Display weird chars correctly + (set-buffer-multibyte nil) + (set-buffer-multibyte t)) (when window (set-window-start window old-start))) (sx-message "Done.")) -- cgit v1.2.3 From dda1b93cc57fbb7f4076950c43689b7fb7a7fb53 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Wed, 3 Dec 2014 14:05:30 -0500 Subject: Link to `ready' issues --- CONTRIBUTING.org | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.org b/CONTRIBUTING.org index 46fcf13..3fcf111 100644 --- a/CONTRIBUTING.org +++ b/CONTRIBUTING.org @@ -10,10 +10,10 @@ Emacs; we should take advantage of its abilities. To see what we're working on /right now/, check out our [[http://www.waffle.io/vermiculus/sx.el][Waffle board]]. If you would like to contribute, feel free to take on anything in the -=ready= column (which translates to the =ready= label in GitHub -issues). These are issues which have been discussed enough to provide -a good idea of what should be done. Issues in =backlog= are either -still under discussion or simply are in the backlog. +=ready= Waffle column (or the [[https://github.com/vermiculus/sx.el/issues?q=is%3Aopen+is%3Aissue+label%3Aready+-label%3A%22in+progress%22][=ready= GitHub label]]). These are issues +which have been discussed enough to provide a good idea of what should +be done. Issues in =backlog= are either still under discussion or +simply are in the backlog. Of course, the greatest gift you can give to SX is a good word. Star the project on GitHub, mention it to others who would use it in your -- cgit v1.2.3 From 665668a3fcf6122837bca8845c98cf77872e18b9 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 3 Dec 2014 19:50:27 +0000 Subject: Refactor :question-read-p in data-here as a separate function --- sx-interaction.el | 80 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/sx-interaction.el b/sx-interaction.el index 1603ca7..64bcc40 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -48,46 +48,46 @@ ;;; Using data in buffer -(cl-defun sx--data-here (&key (error t) - (type nil) - (question-read-p nil)) - "Get the alist regarding object under point. +(defun sx--data-here (&optional type noerror) + "Get the alist regarding object under point of type TYPE. Looks at the text property `sx--data-here'. If it's not set, it looks at a few other reasonable variables. If those fail too, it throws an error. -Possible keyword arguments are: - - :error If explicit given as nil, no errors are thrown. - :type Symbol restricting the type of object desired. Possible - values are 'question, 'answer, 'comment. If nothing is found of - that type. - :question-read-p If non-nil, and if object found is a question, - throw a `user-error' if it isn't `sx-question--read-p'. If - object found is not a question, this argument is ignored." - (let ((result - (or (let ((data (get-char-property (point) 'sx--data-here))) - (if (null type) data - (sx-assoc-let type - ;; Is data of the right type? - (cl-case type - (question (when .title data)) - (answer (when .answer_id data)) - (comment (when .comment_id data)))))) - ;; The following two only ever return questions. - (when (or (null type) (eq type 'question)) - ;; @TODO: `sx-question-list-mode' may one day display answers. - (or (and (derived-mode-p 'sx-question-list-mode) - (tabulated-list-get-id)) - (and (derived-mode-p 'sx-question-mode) - sx-question-mode--data))) - ;; Nothing was found - (and error (error "No %s found here" (or type "data")))))) - ;; If we found a question, we may need to check if it's read. - (if (and question-read-p (assoc 'title result)) - (if (sx-question--read-p result) result - (user-error "Question still unread. View it before acting on it")) - result))) +TYPE is a symbol restricting the type of object desired. Possible +values are 'question, 'answer, 'comment, or nil (for any type). + +If no object of the requested type could be returned, an error is +thrown unless NOERROR is non-nil." + (or (let ((data (get-char-property (point) 'sx--data-here))) + (if (null type) data + (sx-assoc-let type + ;; Is data of the right type? + (cl-case type + (question (when .title data)) + (answer (when .answer_id data)) + (comment (when .comment_id data)))))) + ;; The following two only ever return questions. + (when (or (null type) (eq type 'question)) + ;; @TODO: `sx-question-list-mode' may one day display answers. + ;; Ideally, it would use the `sx--data-here' (so no special + ;; handling would be necessary. + (or (and (derived-mode-p 'sx-question-list-mode) + (tabulated-list-get-id)) + (and (derived-mode-p 'sx-question-mode) + sx-question-mode--data))) + ;; Nothing was found + (and (null noerror) + (error "No %s found here" (or type "data"))))) + +(defun sx--error-if-unread (data) + "Throw a user-error if DATA is an unread question. +If it's not a question, or if it is read, return DATA." + ;; If we found a question, we may need to check if it's read. + (if (assoc 'title data) + (if (sx-question--read-p data) data + (user-error "Question not yet read. View it before acting on it")) + data)) (defun sx--maybe-update-display (&optional buffer) "Refresh whatever is displayed in BUFFER or the current buffer. @@ -157,7 +157,7 @@ If WINDOW nil, the window is decided by "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 :question-read-p t))) + (interactive (list (sx--error-if-unread (sx--data-here)))) (sx-assoc-let data (sx-set-vote data "upvote" (null (eq .upvoted t))))) @@ -165,7 +165,7 @@ guessed from context at point." "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 :question-read-p t))) + (interactive (list (sx--error-if-unread (sx--data-here)))) (sx-assoc-let data (sx-set-vote data "downvote" (null (eq .downvoted t))))) @@ -204,7 +204,7 @@ it is guessed from context at point. If DATA is a comment, the comment is posted as a reply to it. TEXT is a string. Interactively, it is read from the minibufer." - (interactive (list (sx--data-here :question-read-p t) 'query)) + (interactive (list (sx--error-if-unread (sx--data-here)) 'query)) ;; When clicking the "Add a Comment" button, first arg is a marker. (when (markerp data) (setq data (sx--data-here)) @@ -319,7 +319,7 @@ context at point. " ;; If the user tries to answer a question that's not viewed, he ;; probaby hit the button by accident. (interactive - (list (sx--data-here :question-read-p t :type 'question))) + (list (sx--error-if-unread (sx--data-here 'question)))) ;; When clicking the "Write an Answer" button, first arg is a marker. (when (markerp data) (setq data (sx--data-here))) (let ((buffer (current-buffer))) -- cgit v1.2.3 From e549dce5556c9550eedf220d247764e84b4879b7 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 3 Dec 2014 19:52:34 +0000 Subject: Slight simplification --- sx-interaction.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sx-interaction.el b/sx-interaction.el index 64bcc40..89050c3 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -84,9 +84,9 @@ thrown unless NOERROR is non-nil." "Throw a user-error if DATA is an unread question. If it's not a question, or if it is read, return DATA." ;; If we found a question, we may need to check if it's read. - (if (assoc 'title data) - (if (sx-question--read-p data) data - (user-error "Question not yet read. View it before acting on it")) + (if (and (assoc 'title data) + (null (sx-question--read-p data))) + (user-error "Question not yet read. View it before acting on it") data)) (defun sx--maybe-update-display (&optional buffer) -- cgit v1.2.3