From b4c5f99a50a61e7642dbe6601ba8da3207d0436e Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 8 Nov 2014 20:01:27 +0000 Subject: Implement no-update argument to sx-question-list-refresh --- sx-question-list.el | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'sx-question-list.el') diff --git a/sx-question-list.el b/sx-question-list.el index 86e9194..0494ef1 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -176,6 +176,9 @@ Letters do not insert themselves; instead, they are commands. (defvar sx-question-list--current-site "emacs" "Site being displayed in the *question-list* buffer.") +(defvar sx-question-list--current-dataset nil + "") + (defun sx-question-list-refresh (&optional redisplay no-update) "Update the list of questions. If REDISPLAY is non-nil, also call `tabulated-list-print'. @@ -184,8 +187,12 @@ a new list before redisplaying." (interactive "pP") ;; Reset the mode-line unread count (we rebuild it here). (setq sx-question-list--unread-count 0) - (let ((question-list (sx-question-get-questions - sx-question-list--current-site))) + (let ((question-list + (if (and no-update sx-question-list--current-dataset) + sx-question-list--current-dataset + (sx-question-get-questions + sx-question-list--current-site)))) + (setq sx-question-list--current-dataset question-list) ;; Print the result. (setq tabulated-list-entries (mapcar #'sx-question-list--print-info question-list))) -- 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-list.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 08eea895f1a445156c2e1c382bf167ba6d9d4515 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 12 Nov 2014 21:19:31 +0000 Subject: Update code to new assoc-let --- sx-filter.el | 2 +- sx-question-list.el | 16 ++++++++-------- sx-request.el | 9 +++++---- sx.el | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'sx-question-list.el') diff --git a/sx-filter.el b/sx-filter.el index aa815a2..acd8fc1 100644 --- a/sx-filter.el +++ b/sx-filter.el @@ -54,7 +54,7 @@ or string." "filter/create" keyword-arguments))) (sx-assoc-let (elt response 0) - (url-hexify-string filter))))) + (url-hexify-string .filter))))) ;;; Storage and Retrieval diff --git a/sx-question-list.el b/sx-question-list.el index caf24b1..ebd4e97 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -205,26 +205,26 @@ Used in the questions list to indicate a question was updated \"4d ago\"." (list data (vector - (list (int-to-string score) - 'face (if upvoted 'sx-question-list-score-upvoted + (list (int-to-string .score) + 'face (if .upvoted 'sx-question-list-score-upvoted 'sx-question-list-score)) - (list (int-to-string answer_count) - 'face (if (sx-question--accepted-answer data) + (list (int-to-string .answer_count) + 'face (if (sx-question--accepted-answer .data) 'sx-question-list-answers-accepted 'sx-question-list-answers)) (concat (propertize - title - 'face (if (sx-question--read-p data) + .title + 'face (if (sx-question--read-p .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 (concat (sx-time-since last_activity_date) + (propertize (concat (sx-time-since .last_activity_date) sx-question-list-ago-string) 'face 'sx-question-list-date) - (propertize (concat " [" (mapconcat #'identity tags "] [") "]") + (propertize (concat " [" (mapconcat #'identity .tags "] [") "]") 'face 'sx-question-list-tags) (propertize " " 'display "\n")))))) diff --git a/sx-request.el b/sx-request.el index 56362fc..f8feb22 100644 --- a/sx-request.el +++ b/sx-request.el @@ -107,15 +107,16 @@ number of requests left every time it finishes a call.") (error "Response could not be read by `json-read-from-string'")) ;; If we get here, the response is a valid data structure (sx-assoc-let response - (when error_id + (when .error_id (error "Request failed: (%s) [%i %s] %S" - method error_id error_name error_message)) + .method .error_id .error_name .error_message)) (when (< (setq sx-request-remaining-api-requests - quota_remaining) + .quota_remaining) sx-request-remaining-api-requests-message-threshold) (sx-message "%d API requests reamining" sx-request-remaining-api-requests)) - items))))))) + :hi + .items))))))) ;;; Support Functions diff --git a/sx.el b/sx.el index 34be85e..7ed56d3 100644 --- a/sx.el +++ b/sx.el @@ -81,7 +81,7 @@ symbol, the cdr is the symbol without the `.'." (when (string-match "\\`\\." name) ;; Return the cons cell inside a list, so it can be appended ;; with other results in the clause below. - (list (cons data (intern (replace-match "" name))))))) + (list (cons data (intern (replace-match "" nil nil name))))))) ((not (listp data)) nil) (t (apply #'append -- cgit v1.2.3 From 034e68548e8f41eac8150de33ceb3417b47ca52d Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Wed, 12 Nov 2014 20:11:12 -0500 Subject: Add `sx-question-list-visit' Visits question under point; default binding `v' --- sx-question-list.el | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sx-question-list.el') diff --git a/sx-question-list.el b/sx-question-list.el index ebd4e97..bdbc2f1 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -132,6 +132,7 @@ Letters do not insert themselves; instead, they are commands. ("j" sx-question-list-view-next) ("k" sx-question-list-view-previous) ("g" sx-question-list-refresh) + ("v" sx-question-list-visit) ([?\r] sx-question-list-display-question))) (defvar sx-question-list--current-page "Latest" @@ -193,6 +194,14 @@ a new list before redisplaying." (mapcar #'sx-question-list--print-info question-list))) (when redisplay (tabulated-list-print 'remember))) +(defun sx-question-list-visit (&optional data) + "Visits question under point (or from DATA) using `browse-url'." + (interactive) + (unless data (setq data (tabulated-list-get-id))) + (unless data (error "No question here!")) + (sx-assoc-let data + (browse-url .link))) + (defcustom sx-question-list-ago-string " ago" "String appended to descriptions of the time since something happened. Used in the questions list to indicate a question was updated \"4d ago\"." -- cgit v1.2.3 From c6dfd63b0ec04911f5a768d754b30663c7516db7 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 02:51:30 +0000 Subject: Some more fixups from the merge --- sx-question-list.el | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sx-question-list.el') diff --git a/sx-question-list.el b/sx-question-list.el index 726c58d..79b07be 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -277,8 +277,8 @@ focus the relevant window." (when (sx-question--read-p data) (cl-decf sx-question-list--unread-count) (sx-question--mark-read data)) - (unless (window-live-p sx-question--window) - (setq sx-question--window + (unless (window-live-p sx-question-mode--window) + (setq sx-question-mode--window (condition-case er (split-window-below sx-question-list-height) (error @@ -288,11 +288,11 @@ focus the relevant window." (car (cdr-safe er))) nil (error (cdr er))))))) - (sx-question-mode--display data sx-question--window) + (sx-question-mode--display data sx-question-mode--window) (when focus - (if sx-question--window - (select-window sx-question--window) - (switch-to-buffer sx-question--buffer)))) + (if sx-question-mode--window + (select-window sx-question-mode--window) + (switch-to-buffer sx-question-mode--buffer)))) (defvar sx-question-list--buffer nil "Buffer where the list of questions is displayed.") -- 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-list.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 c7775348c2a83e727ec53e104aa4fd6530465f3c Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Wed, 12 Nov 2014 22:29:33 -0500 Subject: Add missing require --- sx-question-list.el | 1 + 1 file changed, 1 insertion(+) (limited to 'sx-question-list.el') diff --git a/sx-question-list.el b/sx-question-list.el index 2fdd376..70e069b 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -26,6 +26,7 @@ (require 'sx) (require 'sx-time) (require 'sx-question) +(require 'sx-question-mode) ;;; Customization -- cgit v1.2.3 From 14964bc6a4b232b61d06a12fab14e94359b7a14f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 14:05:47 +0000 Subject: Add a couple of sx-encoding-clean-content --- sx-question-list.el | 2 +- sx-question-mode.el | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'sx-question-list.el') diff --git a/sx-question-list.el b/sx-question-list.el index 70e069b..ef5d3f5 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -231,7 +231,7 @@ Used in the questions list to indicate a question was updated \"4d ago\"." 'sx-question-list-answers)) (concat (propertize - .title + (sx-encoding-clean-content .title) 'face (if (sx-question--read-p .data) 'sx-question-list-read-question ;; Increment `sx-question-list--unread-count' for the mode-line. diff --git a/sx-question-mode.el b/sx-question-mode.el index a7a8477..4e35fd9 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -167,7 +167,7 @@ QUESTION must be a data structure returned by `json-read'." ;; Print everything (sx-assoc-let question (insert sx-question-mode-header-title - (propertize .title + (propertize (sx-encoding-clean-content .title) 'font-lock-face 'sx-question-mode-title 'sx-question-mode--section 1)) ;; Sections are hidden with overlays @@ -197,8 +197,8 @@ QUESTION must be a data structure returned by `json-read'." (sx-question-mode--wrap-in-overlay '(face sx-question-mode-content-face) (insert - (sx-encoding-clean-content - .body_markdown) "\n"))))) + (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. -- cgit v1.2.3 From 6a5cd2255b409b956a2582e062b39131639a9564 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 16:07:55 +0000 Subject: Fix window managing --- sx-question-list.el | 3 ++- sx-question-mode.el | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'sx-question-list.el') diff --git a/sx-question-list.el b/sx-question-list.el index ef5d3f5..8c558e6 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -279,7 +279,8 @@ focus the relevant window." (when (sx-question--read-p data) (cl-decf sx-question-list--unread-count) (sx-question--mark-read data)) - (unless (window-live-p sx-question-mode--window) + (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-below sx-question-list-height) diff --git a/sx-question-mode.el b/sx-question-mode.el index 199e082..686d357 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -184,7 +184,8 @@ QUESTION must be a data structure returned by `json-read'." (sx-assoc-let question (mapc #'sx-question-mode--print-section .answers)) (goto-char (point-min)) - (sx-question-mode-next-section)) + (with-selected-window sx-question-mode--window + (sx-question-mode-next-section))) (defun sx-question-mode--print-section (data) "Print a section corresponding to DATA. -- cgit v1.2.3 From 5058ab5ba14220615c6a01911898b2c8ded987e4 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 14 Nov 2014 00:38:33 -0500 Subject: Clean content at the request level --- sx-encoding.el | 32 ++++++++++++++++++++++++++++++++ sx-question-list.el | 2 +- sx-question-mode.el | 6 +++--- sx-request.el | 2 +- 4 files changed, 37 insertions(+), 5 deletions(-) (limited to 'sx-question-list.el') diff --git a/sx-encoding.el b/sx-encoding.el index 58bbd80..7f4765a 100644 --- a/sx-encoding.el +++ b/sx-encoding.el @@ -23,6 +23,8 @@ ;;; Code: +(require 'cl-lib) + (defcustom sx-encoding-html-entities-plist '(Aacute "Á" aacute "á" Acirc "Â" acirc "â" acute "´" AElig "Æ" aelig "æ" Agrave "À" agrave "à" alefsym "ℵ" Alpha "Α" alpha "α" amp "&" and "∧" @@ -86,6 +88,36 @@ Applies `sx-encoding-normalize-line-endings' and (sx-encoding-normalize-line-endings string))) +;;; @TODO: This is a pretty ugly implementation. It can likely be +;;; simplified and it should be improved. +(defun sx-encoding-clean-content-deep (data) + "Clean DATA recursively where necessary. + +See `sx-encoding-clean-content'." + (cond + ;; If we're looking at an atom, clean it if it's a string. + ;; Otherwise, just return it. + ((atom data) + (if (stringp data) (sx-encoding-clean-content data) data)) + ;; Looking at a vector? Recurse by mapping. + ((vectorp data) + (cl-map #'vector #'sx-encoding-clean-content-deep data)) + ;; Is our cdr a vector? Map again, but reconstruct the cons cell. + ((vectorp (cdr data)) + (cons (car data) (cl-map #'vector + #'sx-encoding-clean-content-deep + (cdr data)))) + ;; Is our cdr just a string? Clean and return it, reconstructing. + ((stringp (cdr data)) + (cons (car data) (sx-encoding-clean-content (cdr data)))) + ;; Is this a cons cell where the other part is an atom? Return the + ;; atom. If we got here, it wasn't a string. + ((and (consp data) (atom (cdr data))) data) + ;; If it's a list, map. + ((listp data) (mapcar #'sx-encoding-clean-content-deep data)) + ;; Default to identity. + (t data))) + (defun sx-encoding-gzipped-p (data) "Checks for magic bytes in DATA. diff --git a/sx-question-list.el b/sx-question-list.el index 8c558e6..ff1bdaa 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -231,7 +231,7 @@ Used in the questions list to indicate a question was updated \"4d ago\"." 'sx-question-list-answers)) (concat (propertize - (sx-encoding-clean-content .title) + .title 'face (if (sx-question--read-p .data) 'sx-question-list-read-question ;; Increment `sx-question-list--unread-count' for the mode-line. diff --git a/sx-question-mode.el b/sx-question-mode.el index c17d55a..4cbdf3a 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -200,7 +200,7 @@ DATA can represent a question or an answer." (if .title ;; Questions have title (propertize - (sx-encoding-clean-content .title) + .title 'font-lock-face 'sx-question-mode-title 'sx-question-mode--section 1) ;; Answers don't @@ -237,7 +237,7 @@ DATA can represent a question or an answer." '(face sx-question-mode-content-face) (insert "\n" (sx-question-mode--fill-string - (sx-encoding-clean-content .body_markdown)) + .body_markdown) sx-question-mode-separator))) ;; Comments (when .comments @@ -277,7 +277,7 @@ DATA can represent a question or an answer." (sx-assoc-let data (insert " " - (sx-encoding-clean-content .body_markdown) + .body_markdown " – " (sx-question-mode--propertized-display-name .owner) "\n"))) diff --git a/sx-request.el b/sx-request.el index dd98ead..b16fd9a 100644 --- a/sx-request.el +++ b/sx-request.el @@ -115,7 +115,7 @@ number of requests left every time it finishes a call.") sx-request-remaining-api-requests-message-threshold) (sx-message "%d API requests reamining" sx-request-remaining-api-requests)) - .items))))))) + (sx-encoding-clean-content-deep .items)))))))) ;;; Support Functions -- cgit v1.2.3 From ea5583db069afa160d2cea1c07d9f46d5f97e30b Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 14 Nov 2014 11:11:00 -0500 Subject: Implement basic site switching --- sx-question-list.el | 13 +++++++++++++ sx-site.el | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 sx-site.el (limited to 'sx-question-list.el') diff --git a/sx-question-list.el b/sx-question-list.el index ff1bdaa..2f877cc 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -25,6 +25,7 @@ (require 'sx) (require 'sx-time) +(require 'sx-site) (require 'sx-question) (require 'sx-question-mode) @@ -133,6 +134,7 @@ Letters do not insert themselves; instead, they are commands. ("j" sx-question-list-view-next) ("k" sx-question-list-view-previous) ("g" sx-question-list-refresh) + (":" sx-question-list-switch-site) ("v" sx-question-list-visit) ([?\r] sx-question-list-display-question))) @@ -297,6 +299,17 @@ focus the relevant 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" + (interactive + (list (completing-read + "Switch to site: " (sx-site-get-api-tokens) + (lambda (site) + (not (equal site sx-question-list--current-site))) + t))) + (setq sx-question-list--current-site site) + (call-interactively #'sx-question-list-refresh)) + (defvar sx-question-list--buffer nil "Buffer where the list of questions is displayed.") diff --git a/sx-site.el b/sx-site.el new file mode 100644 index 0000000..20f184f --- /dev/null +++ b/sx-site.el @@ -0,0 +1,35 @@ +;;; sx-site.el --- site functions + +;; Copyright (C) 2014 Sean Allred + +;; Author: Sean Allred +;; Keywords: + +;; 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: + +;;; @TODO use new caching system implemented in branch `network-list' +(defun sx-site-get-api-tokens () + "Return a list of all known " + (mapcar + (lambda (site) (cdr (assoc 'api_site_parameter site))) + (sx-method-call "sites" '((pagesize . 9999))))) + +(provide 'sx-site) +;;; sx-site.el ends here -- cgit v1.2.3 From fd3f074b21738e3305c620325323f7f952318e53 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 14 Nov 2014 14:15:17 -0500 Subject: Use Ido when it is enabled --- sx-question-list.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sx-question-list.el') diff --git a/sx-question-list.el b/sx-question-list.el index 2f877cc..9c03405 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -302,7 +302,7 @@ focus the relevant window." (defun sx-question-list-switch-site (site) "Switch the current site to SITE and display its questions" (interactive - (list (completing-read + (list (funcall (if ido-mode #'ido-completing-read #'completing-read) "Switch to site: " (sx-site-get-api-tokens) (lambda (site) (not (equal site sx-question-list--current-site))) -- cgit v1.2.3 From 9f50150b87d8b1bdd62fec30983f45b0d03de22f Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 14 Nov 2014 14:16:07 -0500 Subject: Use function call instead of `call-interactively' --- sx-question-list.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sx-question-list.el') diff --git a/sx-question-list.el b/sx-question-list.el index 9c03405..a1dec7a 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -308,7 +308,7 @@ focus the relevant window." (not (equal site sx-question-list--current-site))) t))) (setq sx-question-list--current-site site) - (call-interactively #'sx-question-list-refresh)) + (sx-question-list-refresh 'redisplay)) (defvar sx-question-list--buffer nil "Buffer where the list of questions is displayed.") -- cgit v1.2.3