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-mode.el | 299 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) create mode 100644 sx-question-mode.el (limited to 'sx-question-mode.el') 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 -- cgit v1.2.3 From dfd74ac0b3671d44451cb128980cdf3b4638eaf0 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 02:11:50 +0000 Subject: Fix for new assoc-let --- sx-question-mode.el | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 2c72bba..fbafc0b 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -141,31 +141,31 @@ editor's name." QUESTION must be a data structure returned by `json-read'." (sx-assoc-let question (insert sx-question-mode-header-title - (propertize 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)) + (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 + (when .last_edit_date (format sx-question-mode-last-edit-format - (sx-time-since last_edit_date) - (cdr (assoc 'display_name last_editor))))) + (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 ") (") ")") + (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"))))) + (or .body "BODY"))))) (defmacro sx-question-mode--wrap-in-overlay (&rest body) "Execute BODY and wrap any inserted text in an overlay. @@ -273,7 +273,8 @@ comments, and redisplays it." (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) + (sx-question-get-question + sx-question-list--current-site .question_id) (selected-window)))) (defconst stack-question-list--mode-line-format -- cgit v1.2.3 From abc7daf8cf1429df699d27e4d3b240f79d53fcfa Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 02:12:04 +0000 Subject: Remove mode-line placeholder code for now --- sx-question-mode.el | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index fbafc0b..2197338 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -245,16 +245,11 @@ If DIRECTION is negative, move backwards instead." 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))) @@ -277,24 +272,5 @@ comments, and redisplays it." 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 -- cgit v1.2.3 From e8a386babe35f85ecbc8e7d755a2d6bdbb545c6b Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 02:50:41 +0000 Subject: Define groups --- sx-question-mode.el | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 2197338..5be5ab1 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -29,6 +29,16 @@ (require 'sx-lto) (require 'markdown-mode) +(defgroup sx-question-mode nil + "Customization group for sx-question-mode." + :prefix "sx-question-mode-" + :group 'sx) + +(defgroup sx-question-mode-faces nil + "Customization group for the faces of `sx-question-mode'." + :prefix "sx-question-mode-" + :group 'sx-question-mode) + ;;; Displaying a question (defvar sx-question-mode--window nil @@ -72,6 +82,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 "") (make-variable-buffer-local 'sx-question-mode--overlays) @@ -252,12 +263,15 @@ Letters do not insert themselves; instead, they are commands. (mapc (lambda (x) (define-key sx-question-mode-map - (car x) (cadr x))) + (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))) + ("g" sx-question-mode-refresh) + ([spc] scroll-up-command) + ([s-spc] scroll-down-command) + ([backspace] scroll-down-command))) (defun sx-question-mode-refresh () "Refresh currently displayed question. -- cgit v1.2.3 From e7496bae0da2dea73e3587eec45626a56a413b1d Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 02:50:49 +0000 Subject: Background face for question. --- sx-question-mode.el | 82 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 30 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 5be5ab1..ae0d3c4 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -132,6 +132,14 @@ If WINDOW is given, use that to display the buffer." :type 'string :group 'sx-question-mode) +(defface sx-question-mode-content-face + '((((background dark)) :background "#090909") + (((background light)) :background "#f4f4f4")) + "Face used on the question body in the question buffer. +Shouldn't have a foreground, or this will interfere with +font-locking." + :group 'sx-question-mode-faces) + (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 @@ -145,48 +153,62 @@ editor's name." :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. + +;;; Printing a question's content +;;;; Functions +;; 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'." + ;; Clear the overlays + (mapc #'delete-overlay sx-question-mode--overlays) + (setq sx-question-mode--overlays nil) + ;; Print everything (sx-assoc-let question (insert sx-question-mode-header-title (propertize .title 'font-lock-face 'sx-question-mode-title 'sx-question-mode--section 1)) + ;; Sections are hidden with overlays (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) + '(sx-question-mode--section-content t) + (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) + (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"))))) + +(defmacro sx-question-mode--wrap-in-overlay (properties &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'." +Overlay is stored in `sx-question-mode--overlays' and given PROPERTIES." + (declare (indent 1) + (debug t)) `(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) + (let ((ov (make-overlay p (point))) + (props ,properties)) + (while props + (overlay-put ov (pop props) (pop props))) (push ov sx-question-mode--overlays)))) (defun sx-question-mode--insert-header (&rest args) -- cgit v1.2.3 From a6c2bdd9a5bd53485396108a992c7d0f7d31e65e Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 02:51:09 +0000 Subject: Use TAB to hide/show content --- sx-question-mode.el | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index ae0d3c4..0153feb 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -271,6 +271,24 @@ If DIRECTION is negative, move backwards instead." (goto-char (funcall func (point) prop nil limit)) (get-text-property (point) prop))) + +(defun sx-question-mode-hide-show-section () + "Hide or show section under point." + (interactive) + (let ((ov (car (or (sx-question-mode--section-overlays-at (point)) + (sx-question-mode--section-overlays-at + (line-end-position)))))) + (goto-char (overlay-start ov)) + (forward-line 0) + (overlay-put + ov 'invisible + (null (overlay-get ov 'invisible))))) + +(defun sx-question-mode--section-overlays-at (pos) + "Return a list of `sx-question-mode--section-content' overlays at POS." + (cl-remove-if (lambda (x) (null (overlay-get x 'sx-question-mode--section-content))) + (overlays-at pos))) + ;;; Major-mode (define-derived-mode sx-question-mode markdown-mode "Question" @@ -286,14 +304,15 @@ Letters do not insert themselves; instead, they are commands. (mapc (lambda (x) (define-key sx-question-mode-map (car x) (cadr x))) - '(("n" sx-question-mode-next-section) + `(("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) - ([spc] scroll-up-command) - ([s-spc] scroll-down-command) - ([backspace] scroll-down-command))) + (" " scroll-up-command) + (,(kbd "S-SPC") scroll-down-command) + ([backspace] scroll-down-command) + ([tab] sx-question-mode-hide-show-section))) (defun sx-question-mode-refresh () "Refresh currently displayed question. -- 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-mode.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-mode.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 cd956435bae189b2aaf5d85fa3e6783b025ca86e Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 13:58:49 +0000 Subject: Fix tag font-locking --- sx-question-mode.el | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index b93cc3c..a7a8477 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -123,7 +123,7 @@ If WINDOW is given, use that to display the buffer." :group 'sx-question-mode) (defface sx-question-mode-tags - '((t :inherit font-lock-function-name-face)) + '((t :underline nil :inherit font-lock-function-name-face)) "Face used on the question tags in the question buffer." :group 'sx-question-mode-faces) @@ -174,6 +174,7 @@ QUESTION must be a data structure returned by `json-read'." (sx-question-mode--wrap-in-overlay '(sx-question-mode--section-content t) (sx-question-mode--insert-header + ;; Author sx-question-mode-header-author (cdr (assoc 'display_name .owner)) 'sx-question-mode-author @@ -185,17 +186,19 @@ QUESTION must be a data structure returned by `json-read'." (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 - (mapconcat 'sx-question--tag-format .tags " ") - 'sx-question-mode-tags) + '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 " "))) + ;; 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"))))) + .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 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-mode.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 0be479ddb2a01c530bb1606c6b6beb52d43c2257 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 14:08:17 +0000 Subject: Small fix to sx-question-mode--wrap-in-overlay --- sx-question-mode.el | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 4e35fd9..812340f 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -202,16 +202,18 @@ QUESTION must be a data structure returned by `json-read'." (defmacro sx-question-mode--wrap-in-overlay (properties &rest body) "Execute BODY and wrap any inserted text in an overlay. -Overlay is stored in `sx-question-mode--overlays' and given PROPERTIES." +Overlay is pushed on `sx-question-mode--overlays' and given PROPERTIES. +Return the result of BODY." (declare (indent 1) (debug t)) - `(let ((p (point-marker))) - ,@body + `(let ((p (point-marker)) + (result (progn ,@body))) (let ((ov (make-overlay p (point))) (props ,properties)) (while props (overlay-put ov (pop props) (pop props))) - (push ov sx-question-mode--overlays)))) + (push ov sx-question-mode--overlays)) + result)) (defun sx-question-mode--insert-header (&rest args) "Insert HEADER and VALUE. -- 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-mode.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-mode.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 1576a556a9634826f0720adacc73ee72882c02f6 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 15:47:46 +0000 Subject: Add a newline --- sx-question-mode.el | 1 + 1 file changed, 1 insertion(+) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 720abae..8ac2e57 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -235,6 +235,7 @@ DATA can represent a question or an answer." ;; Answers (when .comments (insert + "\n" (propertize sx-question-mode-comments-title 'font-lock-face 'sx-question-mode-title -- cgit v1.2.3 From 71e8f1455f40e19bae24e93c308ab0696535c41e Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 15:54:46 +0000 Subject: Fixup --- sx-question-mode.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 8ac2e57..199e082 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -232,7 +232,7 @@ DATA can represent a question or an answer." (insert (sx-encoding-clean-content .body_markdown) "\n"))) - ;; Answers + ;; Comments (when .comments (insert "\n" -- 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-mode.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 605254d8771e816d9f27c22547eed68fb4ff23d2 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 13 Nov 2014 16:28:03 +0000 Subject: Start implementing sx-question-mode--fill-string Address #43 --- sx-question-mode.el | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 686d357..c17d55a 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -97,6 +97,11 @@ 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) +(defface sx-question-mode-title-comments + '((t :height 1.1 :inherit sx-question-mode-title)) + "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 @@ -230,21 +235,36 @@ DATA can represent a question or an answer." (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"))) + (insert "\n" + (sx-question-mode--fill-string + (sx-encoding-clean-content .body_markdown)) + sx-question-mode-separator))) ;; Comments (when .comments (insert "\n" (propertize sx-question-mode-comments-title - 'font-lock-face 'sx-question-mode-title + 'font-lock-face 'sx-question-mode-title-comments '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))))) + (sx-question-mode--wrap-in-overlay + '(face sx-question-mode-content-face) + (mapc #'sx-question-mode--print-comment .comments)))))) + +(defun sx-question-mode--fill-string (text) + "Fill TEXT according to `markdown-mode' and return it." + (with-temp-buffer + (insert text) + (markdown-mode) + (goto-char (point-min)) + ;; ;; Do something here + ;; (while (null (eobp)) + ;; (skip-chars-forward "\r\n[:blank:]") + ;; (markdown-pre-region)) + (buffer-string))) (defun sx-question-mode--propertized-display-name (author) "Return display_name of AUTHOR with `sx-question-mode-author' face." -- 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-mode.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 9be98a008181aaa43f0ae25e3a2de83fdebe2f5c Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 11:31:56 +0000 Subject: Fix requires --- sx-question-mode.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 4cbdf3a..90a88ce 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -23,11 +23,11 @@ ;;; Code: +(require 'markdown-mode) (require 'sx) (require 'sx-filter) -(require 'sx-lto) -(require 'markdown-mode) +(require 'sx-question) (defgroup sx-question-mode nil "Customization group for sx-question-mode." -- cgit v1.2.3 From 86d5369af55463d49e8fc8232b20ca50419c227d Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 14:09:33 +0000 Subject: Correct comment formatting --- sx-question-mode.el | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 90a88ce..4aa2688 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -163,7 +163,7 @@ editor's name." :type 'string :group 'sx-question-mode) -(defcustom sx-question-mode-answer-title " Answer" +(defcustom sx-question-mode-answer-title "Answer" "Title used at the start of \"Answer\" sections." :type 'string :group 'sx-question-mode) @@ -227,18 +227,20 @@ DATA can represent a question or an answer." 'sx-question-mode-date) (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 " ")))) + (sx-question-mode--insert-header + sx-question-mode-header-tags + (mapconcat #'sx-question--tag-format .tags " ") + 'sx-question-mode-tags)) ;; Body - (insert sx-question-mode-separator) + (insert (propertize sx-question-mode-separator + 'face 'sx-question-mode-header)) (sx-question-mode--wrap-in-overlay '(face sx-question-mode-content-face) (insert "\n" (sx-question-mode--fill-string .body_markdown) - sx-question-mode-separator))) + (propertize sx-question-mode-separator + 'face 'sx-question-mode-header)))) ;; Comments (when .comments (insert @@ -272,15 +274,28 @@ DATA can represent a question or an answer." (propertize .display_name 'font-lock-face 'sx-question-mode-author))) +(defcustom sx-question-mode-comments-format " %s: %s\n" + "Format used to display comments. +First \"%s\" is replaced with user name. +Second \"%s\" is replaced with the comment." + :type 'string + :group 'sx-question-mode) + (defun sx-question-mode--print-comment (data) "Print the comment described by alist DATA." (sx-assoc-let data (insert - " " - .body_markdown - " – " - (sx-question-mode--propertized-display-name .owner) - "\n"))) + (format + sx-question-mode-comments-format + (sx-question-mode--propertized-display-name .owner) + (substring + ;; We fill with three spaces at the start, so the comment is + ;; slightly indented. + (sx-question-mode--fill-string + (concat " " .body_markdown)) + ;; Then we remove the spaces from the first line, since we'll + ;; add the username there anyway. + 3))))) (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 8dbe42a9e66e2dd6650409d2a8025a11e581d2b5 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 14 Nov 2014 09:24:31 -0500 Subject: Fix require We don't seem to use sx-filter anywhere in here --- sx-question-mode.el | 1 - 1 file changed, 1 deletion(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 90a88ce..00ef447 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -26,7 +26,6 @@ (require 'markdown-mode) (require 'sx) -(require 'sx-filter) (require 'sx-question) (defgroup sx-question-mode nil -- cgit v1.2.3 From 96a45d820fb4499acadb2e8103ba786d06054e03 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 14:28:42 +0000 Subject: Improve comment formating --- sx-question-mode.el | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sx-question-mode.el') diff --git a/sx-question-mode.el b/sx-question-mode.el index 4aa2688..72b139d 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -168,11 +168,18 @@ editor's name." :type 'string :group 'sx-question-mode) -(defcustom sx-question-mode-comments-title " Comments" +(defcustom sx-question-mode-comments-title " Comments" "Title used at the start of \"Comments\" sections." :type 'string :group 'sx-question-mode) +(defcustom sx-question-mode-comments-format "%s: %s\n" + "Format used to display comments. +First \"%s\" is replaced with user name. +Second \"%s\" is replaced with the comment." + :type 'string + :group 'sx-question-mode) + ;;; Printing a question's content ;;;; Functions @@ -274,13 +281,6 @@ DATA can represent a question or an answer." (propertize .display_name 'font-lock-face 'sx-question-mode-author))) -(defcustom sx-question-mode-comments-format " %s: %s\n" - "Format used to display comments. -First \"%s\" is replaced with user name. -Second \"%s\" is replaced with the comment." - :type 'string - :group 'sx-question-mode) - (defun sx-question-mode--print-comment (data) "Print the comment described by alist DATA." (sx-assoc-let data -- cgit v1.2.3