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(-) (limited to 'sx-interaction.el') 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(+) (limited to 'sx-interaction.el') 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 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(-) (limited to 'sx-interaction.el') 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 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(-) (limited to 'sx-interaction.el') 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 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(-) (limited to 'sx-interaction.el') 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 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(-) (limited to 'sx-interaction.el') 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(-) (limited to 'sx-interaction.el') 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