From b13351287605751a03437b5659575c026affe901 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 8 Nov 2014 20:12:18 +0000 Subject: Function for getting a single question. --- sx-question.el | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sx-question.el') diff --git a/sx-question.el b/sx-question.el index e9634f7..c681a82 100644 --- a/sx-question.el +++ b/sx-question.el @@ -46,6 +46,16 @@ (page . ,page)) sx-question-browse-filter)) +(defun sx-question-get-question (site id) + "Get the question ID from SITE." + (let ((res (sx-request-make + (format "questions/%s" id) + `((site . ,site)) + sx-question-browse-filter))) + (if (vectorp res) + (elt res 0) + (error "Couldn't find question %s in %s" id site)))) + ;;; Question Properties (defun sx-question--read-p (question) -- cgit v1.2.3 From 4cf7825918bfb60e2d1d1ce1dd342665f1161fa2 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 8 Nov 2014 20:12:54 +0000 Subject: Implement sx-question-mode Also moves question-display code to its own file. --- sx-question-list.el | 4 +- sx-question-mode.el | 299 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sx-question.el | 48 --------- 3 files changed, 301 insertions(+), 50 deletions(-) create mode 100644 sx-question-mode.el (limited to 'sx-question.el') diff --git a/sx-question-list.el b/sx-question-list.el index 0494ef1..f305306 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -67,7 +67,7 @@ :group 'sx-question-list-faces) (defface sx-question-list-tags - '((t :inherit font-lock-function-name-face)) + '((t :inherit sx-question-mode-tags)) "" :group 'sx-question-list-faces) @@ -277,7 +277,7 @@ focus the relevant window." (car (cdr-safe er))) nil (error (cdr er))))))) - (sx-question--display data sx-question--window) + (sx-question-mode--display data sx-question--window) (when focus (if sx-question--window (select-window sx-question--window) diff --git a/sx-question-mode.el b/sx-question-mode.el new file mode 100644 index 0000000..2c72bba --- /dev/null +++ b/sx-question-mode.el @@ -0,0 +1,299 @@ +;;; sx-question-mode.el --- Creating the buffer that displays 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-filter) +(require 'sx-lto) +(require 'markdown-mode) + + +;;; Displaying a question +(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--display (data &optional window) + "Display question given by DATA on WINDOW. +If WINDOW is nil, use selected one. +Returns the question buffer." + (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)))) + +(defun sx-question-mode--display-buffer (window) + "Display and return the buffer used for displaying a question. +Create the buffer if necessary. +If WINDOW is given, use that to display the buffer." + ;; Create the buffer if necessary. + (unless (buffer-live-p sx-question-mode--buffer) + (setq sx-question-mode--buffer + (generate-new-buffer "*stack-question*"))) + (cond + ;; Window was given, use it. + ((window-live-p window) + (set-window-buffer window sx-question-mode--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))) + sx-question-mode--buffer) + + +;;; Printing a question's content +(defvar sx-question-mode--overlays nil + "") +(make-variable-buffer-local 'sx-question-mode--overlays) + +(defface sx-question-mode-header + '((t :inherit font-lock-variable-name-face)) + "Face used on the question headers in the question buffer." + :group 'sx-question-mode-faces) + +(defface sx-question-mode-title + '((t :height 1.3 :weight bold :inherit default)) + "Face used on the question title in the question buffer." + :group 'sx-question-mode-faces) + +(defcustom sx-question-mode-header-title "\n" + "String used before the question title at the header." + :type 'string + :group 'sx-question-mode) + +(defface sx-question-mode-author + '((t :inherit font-lock-string-face)) + "Face used on the question author in the question buffer." + :group 'sx-question-mode-faces) + +(defcustom sx-question-mode-header-author "\nAuthor: " + "String used before the question author at the header." + :type 'string + :group 'sx-question-mode) + +(defface sx-question-mode-date + '((t :inherit font-lock-string-face)) + "Face used on the question date in the question buffer." + :group 'sx-question-mode-faces) + +(defcustom sx-question-mode-header-date "\nAsked on: " + "String used before the question date at the header." + :type 'string + :group 'sx-question-mode) + +(defface sx-question-mode-tags + '((t :inherit font-lock-function-name-face)) + "Face used on the question tags 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-last-edit-format " (edited %s ago by %s)" + "Format used to describe last edit date in the header. +First %s is replaced with the date, and the second %s with the +editor's name." + :type 'string + :group 'sx-question-mode) + +(defcustom sx-question-mode-separator + (concat "\n" (make-string 80 ?_) "\n") + "Separator used between header and body." + :type 'string + :group 'sx-question-mode) + +;;; This is where most of the work is still left to be done! Need to +;;; insert more data from QUESTION. +(defun sx-question-mode--print-question (question) + "Print a buffer describing QUESTION. +QUESTION must be a data structure returned by `json-read'." + (sx-assoc-let question + (insert sx-question-mode-header-title + (propertize title + 'font-lock-face 'sx-question-mode-title + 'sx-question-mode--section 1)) + (sx-question-mode--wrap-in-overlay + (sx-question-mode--insert-header + sx-question-mode-header-author + (cdr (assoc 'display_name owner)) + 'sx-question-mode-author + ;; Date + sx-question-mode-header-date + (concat + (sx-time-seconds-to-date creation_date) + (when last_edit_date + (format sx-question-mode-last-edit-format + (sx-time-since last_edit_date) + (cdr (assoc 'display_name last_editor))))) + 'sx-question-mode-date + ;; Tags + sx-question-mode-header-tags + (concat "(" (mapconcat 'identity tags ") (") ")") + 'sx-question-mode-tags) + (insert sx-question-mode-separator + ;; @TODO: This is temporary, of course. It prevents + ;; errors while the filters aren't setup yet. + (or body "BODY"))))) + +(defmacro sx-question-mode--wrap-in-overlay (&rest body) + "Execute BODY and wrap any inserted text in an overlay. +Overlay is stored in `sx-question-mode--overlays' and given the +property `sx-question-mode--section-content'." + `(let ((p (point-marker))) + ,@body + (let ((ov (make-overlay p (point)))) + (overlay-put ov 'sx-question-mode--section-content t) + (overlay-put ov 'face 'sx-question-mode-content-face) + (push ov sx-question-mode--overlays)))) + +(defun sx-question-mode--insert-header (&rest args) + "Insert HEADER and VALUE. +HEADER is given `sx-question-mode-header' face, and value is given FACE. +\(fn header value face [header value face] [header value face] ...)" + (while args + (insert + (propertize (pop args) 'font-lock-face 'sx-question-mode-header) + (propertize (pop args) 'font-lock-face (pop args))))) + + +;;; Movement commands +;; Sections are headers placed above a question's content or an +;; answer's content, or above the list of comments. They are +;; identified with the `sx-question-mode--section' text property. +;; To move between sections, just search for the property. The value +;; of the text-property is the depth of the section (1 for contents, 2 +;; for comments). +(defcustom sx-question-mode-recenter-line 1 + "Screen line to which we recenter after moving between sections. +This is used as an argument to `recenter'. +If nil, no recentering is performed." + :type '(choice (const :tag "Don't recenter" nil) + integer) + :group 'sx-question-mode) + +(defun sx-question-mode-next-section (n) + "Move down to next section (question or answer) of this buffer. +Prefix argument N moves N sections down or up." + (interactive "p") + (dotimes (_ (abs n)) + ;; This will either move us to the next section, or move out of + ;; the current one. + (unless (sx-question-mode--goto-propety-change 'section n) + ;; If all we did was move out the current one, then move again + ;; and we're guaranteed to reach the next section. + (sx-question-mode--goto-propety-change 'section n))) + (when sx-question-mode-recenter-line + (recenter sx-question-mode-recenter-line))) + +(defun sx-question-mode-previous-section (n) + "Move down to previous section (question or answer) of this buffer. +Prefix argument N moves N sections up or down." + (interactive "p") + (sx-question-mode-next-section (- n))) + +(defun sx-question-mode--goto-propety-change (prop &optional direction) + "Move forward until the value of text-property `sx-question-mode--PROP' changes. +Return the new value of PROP at point. +If DIRECTION is negative, move backwards instead." + (let ((prop (intern (format "sx-question-mode--%s" prop))) + (func (if (and (numberp direction) + (< direction 0)) + #'previous-single-property-change + #'next-single-property-change)) + (limit (if (and (numberp direction) + (< direction 0)) + (point-min) (point-max)))) + (goto-char (funcall func (point) prop nil limit)) + (get-text-property (point) prop))) + + +;;; Major-mode +(define-derived-mode sx-question-mode markdown-mode "Question" + "Major mode for a question and its answers. +Letters do not insert themselves; instead, they are commands. +\\ +\\{sx-question-mode}" + (sx-question-mode--update-mode-line) + (remove-hook 'after-change-functions 'markdown-check-change-for-wiki-link t) + (remove-hook 'window-configuration-change-hook + 'markdown-fontify-buffer-wiki-links t) + (read-only-mode)) + +(defun sx-question-mode--update-mode-line () + "" + ) + +(mapc + (lambda (x) (define-key sx-question-mode-map + (car x) (cadr x))) + '(("n" sx-question-mode-next-section) + ("p" sx-question-mode-previous-section) + ("j" sx-question-mode-next-section) + ("k" sx-question-mode-previous-section) + ("g" sx-question-mode-refresh))) + +(defun sx-question-mode-refresh () + "Refresh currently displayed question. +Queries the API for any changes to the question or its answers or +comments, and redisplays it." + (interactive) + (unless (derived-mode-p 'sx-question-mode) + (error "Not in `sx-question-mode'")) + (sx-assoc-let sx-question-mode--data + (sx-question-mode--display + (sx-question-get-question sx-question-list--current-site question_id) + (selected-window)))) + +(defconst stack-question-list--mode-line-format + '(" " + mode-name + " " + (:propertize stack-question-list--current-page + face mode-line-buffer-id) + " [" + "Unread: " + (:propertize + (:eval (int-to-string stack-question-list--unread-count)) + face mode-line-buffer-id) + ", " + "Total: " + (:propertize + (:eval (int-to-string stack-question-list--total-count)) + face mode-line-buffer-id) + "] ") + "Mode-line construct to use in question-list buffers.") + +(provide 'sx-question-mode) +;;; sx-question-mode.el ends here diff --git a/sx-question.el b/sx-question.el index c681a82..2d65af3 100644 --- a/sx-question.el +++ b/sx-question.el @@ -78,54 +78,6 @@ With optional argument predicate, use it instead of `<'." (funcall (or pred #'<) (cdr (assoc property x)) (cdr (assoc property y)))) - -;;; Displaying a question -(defvar sx-question--window nil - "Window where the content of questions is displayed.") - -(defvar sx-question--buffer nil - "Buffer being used to display questions.") - -(defcustom sx-question-use-html t - "If nil, markdown is used for the body." - :type 'boolean - :group 'sx-question) - -(defun sx-question--display (data &optional window) - "Display question given by DATA on WINDOW. -If WINDOW is nil, use selected one." - (let ((sx-lto--body-src-block - (if sx-question-use-html nil - sx-lto--body-src-block)) - (inhibit-read-only t)) - (with-current-buffer - (sx-question--display-buffer window) - (erase-buffer) - (insert - (org-element-interpret-data - (sx-lto--question data))) - (org-mode) - (show-all) - (view-mode) - (current-buffer)))) - -(defun sx-question--display-buffer (window) - "Display and return the buffer used for displaying a question. -Create the buffer if necessary. -If WINDOW is given, use that to display the buffer." - ;; Create the buffer if necessary. - (unless (buffer-live-p sx-question--buffer) - (setq sx-question--buffer - (generate-new-buffer "*sx-question*"))) - (cond - ;; Window was given, use it. - ((window-live-p window) - (set-window-buffer window sx-question--buffer)) - ;; No window, but the buffer is already being displayed somewhere. - ((get-buffer-window sx-question--buffer 'visible)) - ;; Neither, so we create the window. - (t (switch-to-buffer sx-question--buffer))) - sx-question--buffer) (provide 'sx-question) ;;; sx-question.el ends here -- cgit v1.2.3 From f1c3473e5b6d20c80b2f2f2ae569748fc247fccd Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Wed, 12 Nov 2014 22:15:16 -0500 Subject: Use consistent tag formatting Puts `mapconcat' to work by defining a helper function `sx-question--tag-format' and `concat'ing them with ` '. Simpler, more maintainable construction. --- sx-question-list.el | 3 ++- sx-question-mode.el | 2 +- sx-question.el | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'sx-question.el') diff --git a/sx-question-list.el b/sx-question-list.el index 79b07be..2fdd376 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -240,7 +240,8 @@ Used in the questions list to indicate a question was updated \"4d ago\"." (propertize (concat (sx-time-since .last_activity_date) sx-question-list-ago-string) 'face 'sx-question-list-date) - (propertize (concat " [" (mapconcat #'identity .tags "] [") "]") + " " + (propertize (mapconcat #'sx-question--tag-format .tags " ") 'face 'sx-question-list-tags) (propertize " " 'display "\n")))))) diff --git a/sx-question-mode.el b/sx-question-mode.el index 0153feb..48c82a4 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -188,7 +188,7 @@ QUESTION must be a data structure returned by `json-read'." 'sx-question-mode-date ;; Tags sx-question-mode-header-tags - (concat "(" (mapconcat 'identity .tags ") (") ")") + (mapconcat 'sx-question--tag-format .tags " ") 'sx-question-mode-tags) (insert sx-question-mode-separator) (sx-question-mode--wrap-in-overlay diff --git a/sx-question.el b/sx-question.el index d3fd79f..0142929 100644 --- a/sx-question.el +++ b/sx-question.el @@ -73,6 +73,10 @@ With optional argument predicate, use it instead of `<'." (cdr (assoc property x)) (cdr (assoc property y)))) +(defun sx-question--tag-format (tag) + "Formats TAG for display" + (concat "[" tag "]")) + (provide 'sx-question) ;;; sx-question.el ends here -- cgit v1.2.3 From 85b9b8d2b392210f350af5f7cc96456d0c2002cb Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Wed, 12 Nov 2014 22:29:47 -0500 Subject: Pull in question body Added this field to the filter as well. --- sx-question-mode.el | 7 +++---- sx-question.el | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'sx-question.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 48c82a4..b93cc3c 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -193,10 +193,9 @@ QUESTION must be a data structure returned by `json-read'." (insert sx-question-mode-separator) (sx-question-mode--wrap-in-overlay '(face sx-question-mode-content-face) - (insert - ;; @TODO: This is temporary, of course. It prevents - ;; errors while the filters aren't setup yet. - (or .body_markdown "BODY") "\n"))))) + (insert + (sx-encoding-clean-content + .body_markdown) "\n"))))) (defmacro sx-question-mode--wrap-in-overlay (properties &rest body) "Execute BODY and wrap any inserted text in an overlay. diff --git a/sx-question.el b/sx-question.el index 0142929..5aab158 100644 --- a/sx-question.el +++ b/sx-question.el @@ -30,7 +30,7 @@ (require 'sx-method) (defvar sx-question-browse-filter - '(nil (user.profile_image shallow_user.profile_image))) + '(question.body_markdown (user.profile_image shallow_user.profile_image))) (defun sx-question-get-questions (site &optional page) "Get the page PAGE of questions from SITE." -- cgit v1.2.3 From 5f6640e6af708097379ba419d8735713dbecff6e Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 14:36:53 +0000 Subject: Improve question filter --- sx-question.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sx-question.el') diff --git a/sx-question.el b/sx-question.el index 5aab158..e04598b 100644 --- a/sx-question.el +++ b/sx-question.el @@ -30,7 +30,7 @@ (require 'sx-method) (defvar sx-question-browse-filter - '(question.body_markdown (user.profile_image shallow_user.profile_image))) + '((question.body_markdown question.comments question.answers answer.body_markdown answer.comments) (user.profile_image shallow_user.profile_image))) (defun sx-question-get-questions (site &optional page) "Get the page PAGE of questions from SITE." -- cgit v1.2.3 From 70469eb3e31223606fa141dd4deb7684d8a09977 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 14:37:05 +0000 Subject: Also print answers --- sx-question-mode.el | 49 ++++++++++++++++++++++++++++++++++++++----------- sx-question.el | 7 ++++++- 2 files changed, 44 insertions(+), 12 deletions(-) (limited to 'sx-question.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 812340f..1c8d19a 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -153,6 +153,11 @@ editor's name." :type 'string :group 'sx-question-mode) +(defcustom sx-question-mode-answer-title " Answer" + "Title used at the start of \"Answer\" sections." + :type 'string + :group 'sx-question-mode) + ;;; Printing a question's content ;;;; Functions @@ -162,15 +167,30 @@ editor's name." "Print a buffer describing QUESTION. QUESTION must be a data structure returned by `json-read'." ;; Clear the overlays - (mapc #'delete-overlay sx-question-mode--overlays) + (mapc #'delete-overlayj sx-question-mode--overlays) (setq sx-question-mode--overlays nil) ;; Print everything + (sx-question-mode--print-section question) (sx-assoc-let question + (mapc #'sx-question-mode--print-section .answers))) + +(defun sx-question-mode--print-section (data) + "Print a section corresponding to DATA. +DATA can represent a question or an answer." + (sx-assoc-let data (insert sx-question-mode-header-title - (propertize (sx-encoding-clean-content .title) - 'font-lock-face 'sx-question-mode-title - 'sx-question-mode--section 1)) - ;; Sections are hidden with overlays + (if .title + ;; Questions have title + (propertize + (sx-encoding-clean-content .title) + 'font-lock-face 'sx-question-mode-title + 'sx-question-mode--section 1) + ;; Answers don't + (propertize + sx-question-mode-answer-title + 'font-lock-face 'sx-question-mode-title + 'sx-question-mode--section 2))) + ;; Sections can be hidden with overlays (sx-question-mode--wrap-in-overlay '(sx-question-mode--section-content t) (sx-question-mode--insert-header @@ -187,18 +207,25 @@ QUESTION must be a data structure returned by `json-read'." (sx-time-since .last_edit_date) (cdr (assoc 'display_name .last_editor))))) 'sx-question-mode-date) - ;; Tags - (insert sx-question-mode-header-tags) - (sx-question-mode--wrap-in-overlay - '(face sx-question-mode-tags) - (insert (mapconcat #'sx-question--tag-format .tags " "))) + (when .title + ;; Tags + (insert sx-question-mode-header-tags) + (sx-question-mode--wrap-in-overlay + '(face sx-question-mode-tags) + (insert (mapconcat #'sx-question--tag-format .tags " ")))) ;; Body (insert sx-question-mode-separator) (sx-question-mode--wrap-in-overlay '(face sx-question-mode-content-face) (insert (sx-encoding-clean-content .body_markdown) - "\n"))))) + "\n"))) + ;; Answers + (mapc #'sx-question-mode--print-comment .comments))) + +(defun sx-question-mode--print-comment (data) + "Print the comment described by alist DATA." + ) (defmacro sx-question-mode--wrap-in-overlay (properties &rest body) "Execute BODY and wrap any inserted text in an overlay. diff --git a/sx-question.el b/sx-question.el index e04598b..59d5ded 100644 --- a/sx-question.el +++ b/sx-question.el @@ -30,7 +30,12 @@ (require 'sx-method) (defvar sx-question-browse-filter - '((question.body_markdown question.comments question.answers answer.body_markdown answer.comments) (user.profile_image shallow_user.profile_image))) + '((question.body_markdown + question.comments + question.answers + answer.body_markdown + answer.comments) + (user.profile_image shallow_user.profile_image))) (defun sx-question-get-questions (site &optional page) "Get the page PAGE of questions from SITE." -- cgit v1.2.3 From 6aaa87a1f8c169bd7fa744e96b8729cd196ed07f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 15:45:02 +0000 Subject: Comments --- sx-question-mode.el | 64 ++++++++++++++++++++++++++++++++++++++++++----------- sx-question.el | 9 +++++++- 2 files changed, 59 insertions(+), 14 deletions(-) (limited to 'sx-question.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 1c8d19a..720abae 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -83,7 +83,7 @@ If WINDOW is given, use that to display the buffer." ;;; Printing a question's content ;;;; Faces and Variables -(defvar sx-question-mode--overlays nil +(defvar sx-question-mode--overlays nil "") (make-variable-buffer-local 'sx-question-mode--overlays) @@ -127,6 +127,11 @@ If WINDOW is given, use that to display the buffer." "Face used on the question tags in the question buffer." :group 'sx-question-mode-faces) +(defface sx-question-mode-author + '((t :inherit font-lock-variable-name-face)) + "Face used for author names 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 @@ -158,6 +163,11 @@ editor's name." :type 'string :group 'sx-question-mode) +(defcustom sx-question-mode-comments-title " Comments" + "Title used at the start of \"Comments\" sections." + :type 'string + :group 'sx-question-mode) + ;;; Printing a question's content ;;;; Functions @@ -167,12 +177,14 @@ editor's name." "Print a buffer describing QUESTION. QUESTION must be a data structure returned by `json-read'." ;; Clear the overlays - (mapc #'delete-overlayj sx-question-mode--overlays) + (mapc #'delete-overlay sx-question-mode--overlays) (setq sx-question-mode--overlays nil) ;; 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)) + (sx-question-mode-next-section)) (defun sx-question-mode--print-section (data) "Print a section corresponding to DATA. @@ -196,7 +208,7 @@ DATA can represent a question or an answer." (sx-question-mode--insert-header ;; Author sx-question-mode-header-author - (cdr (assoc 'display_name .owner)) + (sx-question-mode--propertized-display-name .owner) 'sx-question-mode-author ;; Date sx-question-mode-header-date @@ -205,7 +217,7 @@ 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) - (cdr (assoc 'display_name .last_editor))))) + (sx-question-mode--propertized-display-name .last_editor)))) 'sx-question-mode-date) (when .title ;; Tags @@ -221,11 +233,32 @@ DATA can represent a question or an answer." (sx-encoding-clean-content .body_markdown) "\n"))) ;; Answers - (mapc #'sx-question-mode--print-comment .comments))) + (when .comments + (insert + (propertize + sx-question-mode-comments-title + 'font-lock-face 'sx-question-mode-title + 'sx-question-mode--section 3)) + (sx-question-mode--wrap-in-overlay + '(sx-question-mode--section-content t) + (insert "\n") + (mapc #'sx-question-mode--print-comment .comments))))) + +(defun sx-question-mode--propertized-display-name (author) + "Return display_name of AUTHOR with `sx-question-mode-author' face." + (sx-assoc-let author + (propertize .display_name + 'font-lock-face 'sx-question-mode-author))) (defun sx-question-mode--print-comment (data) "Print the comment described by alist DATA." - ) + (sx-assoc-let data + (insert + " " + (sx-encoding-clean-content .body_markdown) + " – " + (sx-question-mode--propertized-display-name .owner) + "\n"))) (defmacro sx-question-mode--wrap-in-overlay (properties &rest body) "Execute BODY and wrap any inserted text in an overlay. @@ -247,7 +280,7 @@ Return the result of BODY." HEADER is given `sx-question-mode-header' face, and value is given FACE. \(fn header value face [header value face] [header value face] ...)" (while args - (insert + (insert (propertize (pop args) 'font-lock-face 'sx-question-mode-header) (propertize (pop args) 'font-lock-face (pop args))))) @@ -261,16 +294,18 @@ HEADER is given `sx-question-mode-header' face, and value is given FACE. ;; for comments). (defcustom sx-question-mode-recenter-line 1 "Screen line to which we recenter after moving between sections. -This is used as an argument to `recenter'. +This is used as an argument to `recenter', only used if the end +of section is outside the window. If nil, no recentering is performed." :type '(choice (const :tag "Don't recenter" nil) integer) :group 'sx-question-mode) -(defun sx-question-mode-next-section (n) +(defun sx-question-mode-next-section (&optional n) "Move down to next section (question or answer) of this buffer. Prefix argument N moves N sections down or up." (interactive "p") + (unless n (setq n 1)) (dotimes (_ (abs n)) ;; This will either move us to the next section, or move out of ;; the current one. @@ -279,13 +314,15 @@ Prefix argument N moves N sections down or up." ;; and we're guaranteed to reach the next section. (sx-question-mode--goto-propety-change 'section n))) (when sx-question-mode-recenter-line - (recenter sx-question-mode-recenter-line))) + (let ((ov (car-safe (sx-question-mode--section-overlays-at (line-end-position))))) + (when (and (overlayp ov) (> (overlay-end ov) (window-end))) + (recenter sx-question-mode-recenter-line))))) -(defun sx-question-mode-previous-section (n) +(defun sx-question-mode-previous-section (&optional n) "Move down to previous section (question or answer) of this buffer. Prefix argument N moves N sections up or down." (interactive "p") - (sx-question-mode-next-section (- n))) + (sx-question-mode-next-section (- (or n 1)))) (defun sx-question-mode--goto-propety-change (prop &optional direction) "Move forward until the value of text-property `sx-question-mode--PROP' changes. @@ -340,6 +377,7 @@ Letters do not insert themselves; instead, they are commands. ("j" sx-question-mode-next-section) ("k" sx-question-mode-previous-section) ("g" sx-question-mode-refresh) + ("q" quit-window) (" " scroll-up-command) (,(kbd "S-SPC") scroll-down-command) ([backspace] scroll-down-command) diff --git a/sx-question.el b/sx-question.el index 59d5ded..69a4d7f 100644 --- a/sx-question.el +++ b/sx-question.el @@ -32,7 +32,14 @@ (defvar sx-question-browse-filter '((question.body_markdown question.comments - question.answers + question.answers + question.last_editor + user.display_name + comment.owner + comment.body_markdown + comment.body + answer.last_editor + answer.owner answer.body_markdown answer.comments) (user.profile_image shallow_user.profile_image))) -- cgit v1.2.3 From 76dde309a49b8959d482257b5622ef6dcf72072c Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 14 Nov 2014 09:24:53 -0500 Subject: Use correct method `sx-request-make' wouldn't behave correctly on this --- sx-question.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sx-question.el') diff --git a/sx-question.el b/sx-question.el index 69a4d7f..40adbcb 100644 --- a/sx-question.el +++ b/sx-question.el @@ -54,7 +54,7 @@ (defun sx-question-get-question (site id) "Get the question ID from SITE." - (let ((res (sx-request-make + (let ((res (sx-method-call (format "questions/%s" id) `((site . ,site)) sx-question-browse-filter))) -- cgit v1.2.3 From 378fb090ab1826a7ee09e20c1ed439be283c72d4 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 14 Nov 2014 09:25:15 -0500 Subject: Ensure error string can print "%s" must take a string, but what we get might not be string-y. --- sx-question.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sx-question.el') diff --git a/sx-question.el b/sx-question.el index 40adbcb..d15cc80 100644 --- a/sx-question.el +++ b/sx-question.el @@ -60,7 +60,7 @@ sx-question-browse-filter))) (if (vectorp res) (elt res 0) - (error "Couldn't find question %s in %s" id site)))) + (error "Couldn't find question %S in %S" id site)))) ;;; Question Properties -- cgit v1.2.3