From 0b8fd6291719c4913f64a9e322abcb025fbf5e40 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 7 Nov 2014 17:37:29 -0500 Subject: Rename sx-network to sx-site According to the documentation, it's the appropriate terminology. --- sx-network.el | 36 ------------------------------------ sx-site.el | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 36 deletions(-) delete mode 100644 sx-network.el create mode 100644 sx-site.el diff --git a/sx-network.el b/sx-network.el deleted file mode 100644 index dcd2349..0000000 --- a/sx-network.el +++ /dev/null @@ -1,36 +0,0 @@ -;;; sx-network.el --- browsing networks -*- lexical-binding: t; -*- - -;; Copyright (C) 2014 Sean Allred - -;; Author: Sean Allred - -;; 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-request) - -(defun sx-network-get-networks () - (sx-request-make "sites")) - -(provide 'sx-network) -;;; stack-network.el ends here - -;; Local Variables: -;; indent-tabs-mode: nil -;; End: diff --git a/sx-site.el b/sx-site.el new file mode 100644 index 0000000..8d78e8a --- /dev/null +++ b/sx-site.el @@ -0,0 +1,36 @@ +;;; sx-site.el --- browsing sites -*- lexical-binding: t; -*- + +;; Copyright (C) 2014 Sean Allred + +;; Author: Sean Allred + +;; 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-request) + +(defun sx-site-get-sites () + (sx-request-make "sites")) + +(provide 'sx-site) +;;; stack-site.el ends here + +;; Local Variables: +;; indent-tabs-mode: nil +;; End: -- cgit v1.2.3 From 60a07cd274e80d9dfbb37f4d87a51e7b1d8af536 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Mon, 10 Nov 2014 23:10:23 -0500 Subject: Use narrow filter for site list --- sx-site.el | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/sx-site.el b/sx-site.el index 8d78e8a..3c326e4 100644 --- a/sx-site.el +++ b/sx-site.el @@ -23,10 +23,26 @@ ;;; Code: -(require 'sx-request) +(require 'sx-method) + +(defvar sx-site-browse-filter + '((.backoff + .error_id + .error_message + .error_name + .has_more + .items + .quota_max + .quota_remaining + site.site_type + site.name + site.site_url + site.api_site_parameter) + nil + none)) (defun sx-site-get-sites () - (sx-request-make "sites")) + (sx-method-call "sites" nil sx-site-browse-filter)) (provide 'sx-site) ;;; stack-site.el ends here -- cgit v1.2.3 From f6699988fd521703c1d44489e3d89c6f71d418df Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Tue, 11 Nov 2014 12:12:30 -0500 Subject: Move question sorting function to sx.el It is not specific to questions -- it is generally applicable to any alist. --- sx-question-list.el | 2 +- sx-question.el | 6 ------ sx.el | 7 +++++++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/sx-question-list.el b/sx-question-list.el index caf24b1..a164706 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -120,7 +120,7 @@ Letters do not insert themselves; instead, they are commands. (defun sx-question-list--date-more-recent-p (x y) "Non-nil if tabulated-entry X is newer than Y." - (sx-question--< + (sx--< sx-question-list-date-sort-method (car x) (car y) #'>)) diff --git a/sx-question.el b/sx-question.el index 20a71cc..601875f 100644 --- a/sx-question.el +++ b/sx-question.el @@ -56,12 +56,6 @@ "Mark QUESTION as being read, until it is updated again." nil) -(defun sx-question--< (property x y &optional pred) - "Non-nil if PROPERTY attribute of question X is less than that of Y. -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 diff --git a/sx.el b/sx.el index 6165714..0a1b046 100644 --- a/sx.el +++ b/sx.el @@ -160,6 +160,13 @@ Run after `sx-init--internal-hook'.") This is used internally to set initial values for variables such as filters.") +(defun sx--< (property x y &optional pred) + "Non-nil if PROPERTY attribute of question X is less than that of Y. +With optional argument predicate, use it instead of `<'." + (funcall (or pred #'<) + (cdr (assoc property x)) + (cdr (assoc property y)))) + (defmacro sx-init-variable (variable value &optional setter) "Set VARIABLE to VALUE using SETTER. SETTER should be a function of two arguments. If SETTER is nil, -- cgit v1.2.3 From 90492e76951164e3e1bc30e41e6c112fb9b00564 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Tue, 11 Nov 2014 12:43:41 -0500 Subject: Don't need to hexify filter --- sx-filter.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-filter.el b/sx-filter.el index aa815a2..c053070 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))))) + filter)))) ;;; Storage and Retrieval -- cgit v1.2.3 From 768dc0e2a4d924bdb3d4908233e27de236861005 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Tue, 11 Nov 2014 12:44:29 -0500 Subject: Include more fields in the sites filter --- sx-site.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sx-site.el b/sx-site.el index 3c326e4..fcdc383 100644 --- a/sx-site.el +++ b/sx-site.el @@ -37,7 +37,10 @@ site.site_type site.name site.site_url - site.api_site_parameter) + site.api_site_parameter + site.related_sites + related_site.api_site_parameter + related_site.relation) nil none)) -- cgit v1.2.3 From 9f679fdadce15381efba781e8561d4cdc172319c Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Tue, 11 Nov 2014 12:44:42 -0500 Subject: Add favorite sites customization --- sx-site.el | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sx-site.el b/sx-site.el index fcdc383..840bbca 100644 --- a/sx-site.el +++ b/sx-site.el @@ -47,6 +47,11 @@ (defun sx-site-get-sites () (sx-method-call "sites" nil sx-site-browse-filter)) +(defcustom sx-site-favorites + nil + "Favorite sites." + :group 'sx-site) + (provide 'sx-site) ;;; stack-site.el ends here -- cgit v1.2.3 From 0cb05d17c806fb268343dcf932e355dba13cfb3c Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Tue, 11 Nov 2014 12:45:06 -0500 Subject: Convenience argument for `cache-get' Allows for setting the cache if it does not exist. --- sx-cache.el | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sx-cache.el b/sx-cache.el index 098c292..e3b356b 100644 --- a/sx-cache.el +++ b/sx-cache.el @@ -38,18 +38,21 @@ (concat (symbol-name filename) ".el") sx-cache-directory)) -(defun sx-cache-get (cache) +(defun sx-cache-get (cache &optional form) "Return the data within CACHE. +If CACHE does not exist, evaluate FORM and set it to its return. + As with `sx-cache-set', CACHE is a file name within the context of `sx-cache-directory'." (unless (file-exists-p sx-cache-directory) (mkdir sx-cache-directory)) (let ((file (sx-cache-get-file-name cache))) - (when (file-exists-p file) - (with-temp-buffer - (insert-file-contents (sx-cache-get-file-name cache)) - (read (buffer-string)))))) + (if (file-exists-p file) + (with-temp-buffer + (insert-file-contents (sx-cache-get-file-name cache)) + (read (buffer-string))) + (sx-cache-set cache (eval form))))) (defun sx-cache-set (cache data) "Set the content of CACHE to DATA. -- cgit v1.2.3 From 403c021d92bb036be5d95735bc1403056db3780b Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Tue, 11 Nov 2014 12:46:13 -0500 Subject: Use smart caching --- sx-site.el | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/sx-site.el b/sx-site.el index 840bbca..c8de938 100644 --- a/sx-site.el +++ b/sx-site.el @@ -24,6 +24,7 @@ ;;; Code: (require 'sx-method) +(require 'sx-cache) (defvar sx-site-browse-filter '((.backoff @@ -44,8 +45,14 @@ nil none)) -(defun sx-site-get-sites () - (sx-method-call "sites" nil sx-site-browse-filter)) +(defun sx-site-get-sites ()) + +(defun sx-site--get-site-list () + (sx-cache-get + 'site-list + '(sx-method-call + "sites" '((pagesize . 999)) + sx-site-browse-filter))) (defcustom sx-site-favorites nil -- cgit v1.2.3 From 8fb8b122811e9125b8eadcbae19e1fbefc403667 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 14:15:53 +0000 Subject: Disable font-lock-mode --- sx-question-mode.el | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 4aa2688..94f3d81 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -201,12 +201,12 @@ DATA can represent a question or an answer." ;; Questions have title (propertize .title - 'font-lock-face 'sx-question-mode-title + '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 + 'face 'sx-question-mode-title 'sx-question-mode--section 2))) ;; Sections can be hidden with overlays (sx-question-mode--wrap-in-overlay @@ -247,7 +247,7 @@ DATA can represent a question or an answer." "\n" (propertize sx-question-mode-comments-title - 'font-lock-face 'sx-question-mode-title-comments + 'face 'sx-question-mode-title-comments 'sx-question-mode--section 3)) (sx-question-mode--wrap-in-overlay '(sx-question-mode--section-content t) @@ -272,7 +272,7 @@ DATA can represent a question or an answer." "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))) + 'face 'sx-question-mode-author))) (defcustom sx-question-mode-comments-format " %s: %s\n" "Format used to display comments. @@ -318,8 +318,8 @@ 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))))) + (propertize (pop args) 'face 'sx-question-mode-header) + (propertize (pop args) 'face (pop args))))) ;;; Movement commands @@ -401,6 +401,7 @@ If DIRECTION is negative, move backwards instead." Letters do not insert themselves; instead, they are commands. \\ \\{sx-question-mode}" + (font-lock-mode -1) (remove-hook 'after-change-functions 'markdown-check-change-for-wiki-link t) (remove-hook 'window-configuration-change-hook 'markdown-fontify-buffer-wiki-links t) -- cgit v1.2.3 From b88fd09f21360c0ceef092efd21251a1aee388ee Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 14:16:08 +0000 Subject: Fontify when filling --- sx-question-mode.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 94f3d81..859af98 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -237,7 +237,7 @@ DATA can represent a question or an answer." (sx-question-mode--wrap-in-overlay '(face sx-question-mode-content-face) (insert "\n" - (sx-question-mode--fill-string + (sx-question-mode--fill-and-fontify .body_markdown) (propertize sx-question-mode-separator 'face 'sx-question-mode-header)))) @@ -256,12 +256,13 @@ DATA can represent a question or an answer." '(face sx-question-mode-content-face) (mapc #'sx-question-mode--print-comment .comments)))))) -(defun sx-question-mode--fill-string (text) +(defun sx-question-mode--fill-and-fontify (text) "Fill TEXT according to `markdown-mode' and return it." (with-temp-buffer (insert text) (markdown-mode) (goto-char (point-min)) + (font-lock-fontify-region (point-min) (point-max)) ;; ;; Do something here ;; (while (null (eobp)) ;; (skip-chars-forward "\r\n[:blank:]") @@ -291,7 +292,7 @@ Second \"%s\" is replaced with the comment." (substring ;; We fill with three spaces at the start, so the comment is ;; slightly indented. - (sx-question-mode--fill-string + (sx-question-mode--fill-and-fontify (concat " " .body_markdown)) ;; Then we remove the spaces from the first line, since we'll ;; add the username there anyway. -- cgit v1.2.3 From 2d3b595a2f1a3f5c63d04c9353fe7ed4cc129df7 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 14:27:05 +0000 Subject: Fill content! --- sx-question-mode.el | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 859af98..d170d41 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -263,12 +263,21 @@ DATA can represent a question or an answer." (markdown-mode) (goto-char (point-min)) (font-lock-fontify-region (point-min) (point-max)) - ;; ;; Do something here - ;; (while (null (eobp)) - ;; (skip-chars-forward "\r\n[:blank:]") - ;; (markdown-pre-region)) + ;; Do something here + (while (null (eobp)) + ;; Don't fill pre blocks. + (unless (sx-question-mode--move-over-pre) + (fill-paragraph) + (forward-paragraph))) (buffer-string))) +(defun sx-question-mode--move-over-pre () + "Non-nil if paragraph at point can be filled." + (markdown-match-pre-blocks + (save-excursion + (skip-chars-forward "\r\n[:blank:]") + (point)))) + (defun sx-question-mode--propertized-display-name (author) "Return display_name of AUTHOR with `sx-question-mode-author' face." (sx-assoc-let author -- cgit v1.2.3 From f1218544993322fa7c54d095a03761f86586c88d Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 15:06:58 +0000 Subject: Comment --- sx-question-mode.el | 1 + 1 file changed, 1 insertion(+) diff --git a/sx-question-mode.el b/sx-question-mode.el index b25d00c..e80f3d9 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -410,6 +410,7 @@ If DIRECTION is negative, move backwards instead." Letters do not insert themselves; instead, they are commands. \\ \\{sx-question-mode}" + ;; We call font-lock-region manually. See `sx-question-mode--fill-and-fontify' (font-lock-mode -1) (remove-hook 'after-change-functions 'markdown-check-change-for-wiki-link t) (remove-hook 'window-configuration-change-hook -- cgit v1.2.3 From 0a655dfaab1651eeeffed53bf4bbec443a5ce91b Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 15:07:01 +0000 Subject: Font lock @usernames --- sx-question-mode.el | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sx-question-mode.el b/sx-question-mode.el index e80f3d9..4f5116c 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -267,6 +267,11 @@ DATA can represent a question or an answer." (with-temp-buffer (insert text) (markdown-mode) + ;; Highlight usernames. + (font-lock-add-keywords + nil + '(("\\(?: \\|^\\)\\(@\\(?:\\sw\\|\\s_\\)+\\)\\_>" + 1 font-lock-builtin-face))) (goto-char (point-min)) (font-lock-fontify-region (point-min) (point-max)) ;; Do something here -- cgit v1.2.3 From 66a3fc78b49bc6ea87fb19ef718210447e3df695 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 15:50:47 +0000 Subject: Start link implementation --- sx-question-mode.el | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 4f5116c..09872a4 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -262,19 +262,48 @@ DATA can represent a question or an answer." '(face sx-question-mode-content-face) (mapc #'sx-question-mode--print-comment .comments)))))) +(defvar sx-question-mode-bullet-appearance + (propertize (if (char-displayable-p ?•) " •" " *") + 'face 'markdown-list-face) + "String to be displayed as the bullet of markdown list items.") + +;; (rx "[" (group-n 1 (1+ (not (any "]")))) "]" +;; (or (group-n 2 (and "(" (1+ (not (any ")"))) ")")) +;; (group-n 3 (and "[" (1+ (not (any "]"))) "]")))) + +(defvar sx-question-mode--removed-keywords + '("<\\(\\(\\sw\\|\\s_\\|\\s.\\)+@\\(\\sw\\|\\s_\\|\\s.\\)+\\)>" + "\\(?:acap\\|cid\\|da\\(?:ta\\|v\\)\\|f\\(?:ax\\|ile\\|tp\\)\\|gopher\\|https?\\|imap\\|ldap\\|m\\(?:ailto\\|id\\|odem\\)\\|n\\(?:ews\\|fs\\|ntp\\)\\|p\\(?:op\\|rospero\\)\\|rtsp\\|s\\(?:ervice\\|ip\\)\\|t\\(?:el\\(?:net\\)?\\|ip\\)\\|urn\\|vemmi\\|wais\\):[^] \n <>,;() ]+" + "\\(<\\)\\(\\(?:acap\\|cid\\|da\\(?:ta\\|v\\)\\|f\\(?:ax\\|ile\\|tp\\)\\|gopher\\|https?\\|imap\\|ldap\\|m\\(?:ailto\\|id\\|odem\\)\\|n\\(?:ews\\|fs\\|ntp\\)\\|p\\(?:op\\|rospero\\)\\|rtsp\\|s\\(?:ervice\\|ip\\)\\|t\\(?:el\\(?:net\\)?\\|ip\\)\\|urn\\|vemmi\\|wais\\):[^] \n <>,;()]+\\)\\(>\\)" + "\\(!\\)?\\(\\[\\([^]^][^]]*\\|\\)\\]\\)\\((\\([^)]*?\\)\\(?:\\s-+\\(\"[^\"]*\"\\)\\)?)\\)" + "\\(!\\)?\\(\\[\\([^]^][^]]*\\|\\)\\]\\)[ ]?\\(\\[\\([^]]*?\\)\\]\\)" + "^ \\{0,3\\}\\(\\[[^\n]+?\\]\\):\\s *\\(.*?\\)\\s *\\( \"[^\"]*\"$\\|$\\)") + "Elements whose font-lock-keywords are blocked from the question buffer.") + (defun sx-question-mode--fill-and-fontify (text) "Fill TEXT according to `markdown-mode' and return it." (with-temp-buffer + (erase-buffer) (insert text) (markdown-mode) - ;; Highlight usernames. + (font-lock-mode -1) + (setq markdown-mode-font-lock-keywords + (cl-remove-if (lambda (x) (member (car-safe x) sx-question-mode--removed-keywords)) + (copy-sequence markdown-mode-font-lock-keywords))) (font-lock-add-keywords nil - '(("\\(?: \\|^\\)\\(@\\(?:\\sw\\|\\s_\\)+\\)\\_>" + `(("^ *\\(\\*\\|\\+\\|-\\|\\) " + 1 '(face nil display ,sx-question-mode-bullet-appearance) prepend) + ("\\[\\(?1:[^]]+\\)]\\(?:\\(?2:([^)]+)\\)\\|\\(?3:\\[[^]]+]\\)\\)" + 1 '(face link) + 2 '(face nil display "") + 3 '(face nil display "")) + ;; Highlight usernames. + ("\\(?: \\|^\\)\\(@\\(?:\\sw\\|\\s_\\)+\\)\\_>" 1 font-lock-builtin-face))) - (goto-char (point-min)) - (font-lock-fontify-region (point-min) (point-max)) ;; Do something here + (font-lock-fontify-region (point-min) (point-max)) + (goto-char (point-min)) (while (null (eobp)) ;; Don't fill pre blocks. (unless (sx-question-mode--move-over-pre) -- cgit v1.2.3 From 7881572c09281785c1d0303d13305b583b3803cd Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 14 Nov 2014 17:44:55 -0500 Subject: Fix comment --- sx-site.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-site.el b/sx-site.el index 2243fa8..6bef91f 100644 --- a/sx-site.el +++ b/sx-site.el @@ -64,7 +64,7 @@ (sx-site--get-site-list))) (provide 'sx-site) -;;; stack-site.el ends here +;;; sx-site.el ends here ;; Local Variables: ;; indent-tabs-mode: nil -- cgit v1.2.3 From a85c9c3331dd1c2bf265e684a39d8841cc6905b3 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 23:11:13 +0000 Subject: Finish link implementation --- sx-question-mode.el | 170 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 111 insertions(+), 59 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 09872a4..28a786f 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -24,6 +24,8 @@ ;;; Code: (require 'markdown-mode) +(eval-when-compile + (require 'rx)) (require 'sx) (require 'sx-question) @@ -179,11 +181,14 @@ Second \"%s\" is replaced with the comment." :type 'string :group 'sx-question-mode) +(defcustom sx-question-mode-pretty-links t + "If non-nil, markdown links are displayed in a compact form." + :type 'boolean + :group 'sx-question-mode) + ;;; 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'." @@ -262,63 +267,7 @@ DATA can represent a question or an answer." '(face sx-question-mode-content-face) (mapc #'sx-question-mode--print-comment .comments)))))) -(defvar sx-question-mode-bullet-appearance - (propertize (if (char-displayable-p ?•) " •" " *") - 'face 'markdown-list-face) - "String to be displayed as the bullet of markdown list items.") - -;; (rx "[" (group-n 1 (1+ (not (any "]")))) "]" -;; (or (group-n 2 (and "(" (1+ (not (any ")"))) ")")) -;; (group-n 3 (and "[" (1+ (not (any "]"))) "]")))) - -(defvar sx-question-mode--removed-keywords - '("<\\(\\(\\sw\\|\\s_\\|\\s.\\)+@\\(\\sw\\|\\s_\\|\\s.\\)+\\)>" - "\\(?:acap\\|cid\\|da\\(?:ta\\|v\\)\\|f\\(?:ax\\|ile\\|tp\\)\\|gopher\\|https?\\|imap\\|ldap\\|m\\(?:ailto\\|id\\|odem\\)\\|n\\(?:ews\\|fs\\|ntp\\)\\|p\\(?:op\\|rospero\\)\\|rtsp\\|s\\(?:ervice\\|ip\\)\\|t\\(?:el\\(?:net\\)?\\|ip\\)\\|urn\\|vemmi\\|wais\\):[^] \n <>,;() ]+" - "\\(<\\)\\(\\(?:acap\\|cid\\|da\\(?:ta\\|v\\)\\|f\\(?:ax\\|ile\\|tp\\)\\|gopher\\|https?\\|imap\\|ldap\\|m\\(?:ailto\\|id\\|odem\\)\\|n\\(?:ews\\|fs\\|ntp\\)\\|p\\(?:op\\|rospero\\)\\|rtsp\\|s\\(?:ervice\\|ip\\)\\|t\\(?:el\\(?:net\\)?\\|ip\\)\\|urn\\|vemmi\\|wais\\):[^] \n <>,;()]+\\)\\(>\\)" - "\\(!\\)?\\(\\[\\([^]^][^]]*\\|\\)\\]\\)\\((\\([^)]*?\\)\\(?:\\s-+\\(\"[^\"]*\"\\)\\)?)\\)" - "\\(!\\)?\\(\\[\\([^]^][^]]*\\|\\)\\]\\)[ ]?\\(\\[\\([^]]*?\\)\\]\\)" - "^ \\{0,3\\}\\(\\[[^\n]+?\\]\\):\\s *\\(.*?\\)\\s *\\( \"[^\"]*\"$\\|$\\)") - "Elements whose font-lock-keywords are blocked from the question buffer.") - -(defun sx-question-mode--fill-and-fontify (text) - "Fill TEXT according to `markdown-mode' and return it." - (with-temp-buffer - (erase-buffer) - (insert text) - (markdown-mode) - (font-lock-mode -1) - (setq markdown-mode-font-lock-keywords - (cl-remove-if (lambda (x) (member (car-safe x) sx-question-mode--removed-keywords)) - (copy-sequence markdown-mode-font-lock-keywords))) - (font-lock-add-keywords - nil - `(("^ *\\(\\*\\|\\+\\|-\\|\\) " - 1 '(face nil display ,sx-question-mode-bullet-appearance) prepend) - ("\\[\\(?1:[^]]+\\)]\\(?:\\(?2:([^)]+)\\)\\|\\(?3:\\[[^]]+]\\)\\)" - 1 '(face link) - 2 '(face nil display "") - 3 '(face nil display "")) - ;; Highlight usernames. - ("\\(?: \\|^\\)\\(@\\(?:\\sw\\|\\s_\\)+\\)\\_>" - 1 font-lock-builtin-face))) - ;; Do something here - (font-lock-fontify-region (point-min) (point-max)) - (goto-char (point-min)) - (while (null (eobp)) - ;; Don't fill pre blocks. - (unless (sx-question-mode--move-over-pre) - (fill-paragraph) - (forward-paragraph))) - (buffer-string))) - -(defun sx-question-mode--move-over-pre () - "Non-nil if paragraph at point can be filled." - (markdown-match-pre-blocks - (save-excursion - (skip-chars-forward "\r\n[:blank:]") - (point)))) - -(defun sx-question-mode--propertized-display-name (author) +(defun sx-question-mode--propertize-display-name (author) "Return display_name of AUTHOR with `sx-question-mode-author' face." (sx-assoc-let author (propertize .display_name @@ -364,6 +313,109 @@ HEADER is given `sx-question-mode-header' face, and value is given FACE. (propertize (pop args) 'face 'sx-question-mode-header) (propertize (pop args) 'face (pop args))))) + +;;;;; Font-locking the content +(defvar sx-question-mode-bullet-appearance + (propertize (if (char-displayable-p ?•) " •" " *") + 'face 'markdown-list-face) + "String to be displayed as the bullet of markdown list items.") + +(defun sx-question-mode--fill-and-fontify (text) + "Fill TEXT according to `markdown-mode' and return it." + (with-temp-buffer + (erase-buffer) + (insert text) + (markdown-mode) + (font-lock-mode -1) + (when sx-question-mode-bullet-appearance + (font-lock-add-keywords ;; Bullet items. + nil + `(("^ *\\(\\*\\|\\+\\|-\\|\\) " + 1 '(face nil display ,sx-question-mode-bullet-appearance) prepend)))) + (font-lock-add-keywords ;; Highlight usernames. + nil + `(("\\(?: \\|^\\)\\(@\\(?:\\sw\\|\\s_\\)+\\)\\_>" + 1 font-lock-builtin-face))) + ;; Everything. + (font-lock-fontify-region (point-min) (point-max)) + ;; Compact links. + (sx-question-mode--process-links-in-buffer) + ;; And now the filling + (goto-char (point-min)) + (while (null (eobp)) + ;; Don't fill pre blocks. + (unless (sx-question-mode--move-over-pre) + (skip-chars-forward "\r\n[:blank:]") + (fill-paragraph) + (forward-paragraph))) + (buffer-string))) + +(defvar sx-question-mode--link-regexp + ;; Done at compile time. + (rx "[" (group-n 1 (1+ (not (any "]")))) "]" + (or (and "(" (group-n 2 (1+ (not (any ")")))) ")") + (and "[" (group-n 3 (1+ (not (any "]")))) "]"))) + "Regexp matching markdown links.") + +(defun sx-question-mode--process-links-in-buffer () + "Turn all markdown links in this buffer into compact format." + (save-excursion + (goto-char (point-min)) + (while (search-forward-regexp sx-question-mode--link-regexp nil t) + (let* ((text (match-string-no-properties 1)) + (url (or (match-string-no-properties 2) + (sx-question-mode-find-reference + (match-string-no-properties 3) + text)))) + (replace-match + (sx-question-mode--propertize-link + (if sx-question-mode-pretty-links + text + (match-string-no-properties 0)) + url) + :fixedcase :literal nil 0))))) + +(defun sx-question-mode--propertize-link (text url) + "Return a link propertized version of string TEXT. +URL is used as help-echo and as " + (propertize + text + 'face 'link + 'help-echo url + 'url url + 'follow-link t + 'mouse-face 'highlight + 'action #'sx-question-mode-follow-link + 'point-entered + (lambda (&rest _) + (message "%s%s" (propertize "URL: " 'face 'minibuffer-prompt) url)))) + +(defun sx-question-mode-follow-link (&optional pos) + "Follow link at POS or point" + (interactive) + (browse-url + (or (get-text-property (or pos (point)) 'url) + (error "No url under point: %s" (or pos (point)))))) + +(defun sx-question-mode-find-reference (id &optional id2) + "Find url identified by reference ID in current buffer. +If ID is nil, use ID2 instead." + (save-excursion + (save-match-data + (goto-char (point-min)) + (when (search-forward-regexp + (format "^\\s-*\\[\\(%s\\)]:\\s-+\\(?2:[^ ]+\\)" + (or id id2)) + nil t) + (match-string-no-properties 2))))) + +(defun sx-question-mode--move-over-pre () + "Non-nil if paragraph at point can be filled." + (markdown-match-pre-blocks + (save-excursion + (skip-chars-forward "\r\n[:blank:]") + (point)))) + ;;; Movement commands ;; Sections are headers placed above a question's content or an -- cgit v1.2.3 From 0fce5e831997d88fe6e159a3b968ea38c02b5d17 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 23:11:53 +0000 Subject: Refactor propertized-display-name --- sx-question-mode.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 28a786f..37ff7e7 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -225,7 +225,7 @@ DATA can represent a question or an answer." (sx-question-mode--insert-header ;; Author sx-question-mode-header-author - (sx-question-mode--propertized-display-name .owner) + (sx-question-mode--propertize-display-name .owner) 'sx-question-mode-author ;; Date sx-question-mode-header-date @@ -234,7 +234,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) - (sx-question-mode--propertized-display-name .last_editor)))) + (sx-question-mode--propertize-display-name .last_editor)))) 'sx-question-mode-date) (when .title ;; Tags @@ -279,7 +279,7 @@ DATA can represent a question or an answer." (insert (format sx-question-mode-comments-format - (sx-question-mode--propertized-display-name .owner) + (sx-question-mode--propertize-display-name .owner) (substring ;; We fill with three spaces at the start, so the comment is ;; slightly indented. -- cgit v1.2.3 From 56631280e7d254cceddbddce7a87bd76eb727755 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 23:16:50 +0000 Subject: Remove j and k keys They were redundant with n and p, and I have plans for them. --- sx-question-mode.el | 2 -- 1 file changed, 2 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 37ff7e7..9997bf5 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -508,8 +508,6 @@ Letters do not insert themselves; instead, they are commands. (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) ("q" quit-window) (" " scroll-up-command) -- cgit v1.2.3 From e0f0a3af45d5a264ab06bb709bd4c8a6f2da82ad Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 23:43:16 +0000 Subject: Move through buttons with TAB. --- sx-question-mode.el | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 9997bf5..4942889 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -377,18 +377,23 @@ HEADER is given `sx-question-mode-header' face, and value is given FACE. (defun sx-question-mode--propertize-link (text url) "Return a link propertized version of string TEXT. -URL is used as help-echo and as " +URL is used as 'help-echo and 'url properties." (propertize text - 'face 'link - 'help-echo url + ;; Mouse-over + 'help-echo (format + (propertize "URL: %s, %s to visit" 'face 'minibuffer-prompt) + (propertize url 'face 'default) + (propertize "RET" 'face 'font-lock-function-name-face)) + ;; In case we need it. 'url url - 'follow-link t + ;; Decoration + 'face 'link 'mouse-face 'highlight + ;; What RET calls 'action #'sx-question-mode-follow-link - 'point-entered - (lambda (&rest _) - (message "%s%s" (propertize "URL: " 'face 'minibuffer-prompt) url)))) + ;; This is for `sx-question-mode--goto-propety-change'. + 'sx-question-mode--action #'sx-question-mode-follow-link)) (defun sx-question-mode-follow-link (&optional pos) "Follow link at POS or point" @@ -456,6 +461,25 @@ Prefix argument N moves N sections up or down." (interactive "p") (sx-question-mode-next-section (- (or n 1)))) +(defun sx-question-mode-next-button (&optional n) + "Move to next interactible object in this buffer. +These can be links, tags, or copiable code. +With prefix argument N, move N times." + (interactive "p") + (or n (setq n 1)) + (dotimes (_ (abs n)) + (unless (sx-question-mode--goto-propety-change 'action n) + (sx-question-mode--goto-propety-change 'action n))) + (let ((echo (get-text-property (point) 'help-echo))) + (when echo (message "%s" echo)))) + +(defun sx-question-mode-previous-button (&optional n) + "Move to previous interactible object in this buffer. +These can be links, tags, or copiable code. +With prefix argument N, move N times." + (interactive "p") + (sx-question-mode-next-button (- (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. Return the new value of PROP at point. @@ -471,7 +495,6 @@ 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) @@ -513,7 +536,7 @@ Letters do not insert themselves; instead, they are commands. (" " scroll-up-command) (,(kbd "S-SPC") scroll-down-command) ([backspace] scroll-down-command) - ([tab] sx-question-mode-hide-show-section))) + ([tab] sx-question-mode-next-button))) (defun sx-question-mode-refresh () "Refresh currently displayed question. -- cgit v1.2.3 From 7d7c996aa723a9c99d8ceeecc7891a7049e69c98 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Fri, 14 Nov 2014 23:58:05 +0000 Subject: Quick-fix assoc-let --- sx.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx.el b/sx.el index 7ed56d3..796a5d6 100644 --- a/sx.el +++ b/sx.el @@ -106,7 +106,7 @@ is equivalent to (debug t)) (let ((symbol-alist (sx--deep-dot-search body))) `(let ,(mapcar (lambda (x) `(,(car x) (cdr (assoc ',(cdr x) ,alist)))) - symbol-alist) + (delete-dups symbol-alist)) ,@body))) (defcustom sx-init-hook nil -- cgit v1.2.3 From 3565241b2afcf81741c631bfba45304815ce847f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 00:18:35 +0000 Subject: Several navigation improvements. --- sx-question-mode.el | 76 +++++++++++++++++++++++++++++++---------------------- sx.el | 5 ++++ 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 4942889..d08fc0d 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -159,7 +159,7 @@ editor's name." :group 'sx-question-mode) (defcustom sx-question-mode-separator - (concat "\n" (make-string 80 ?_) "\n") + (concat (make-string 80 ?_) "\n") "Separator used between header and body." :type 'string :group 'sx-question-mode) @@ -203,22 +203,33 @@ QUESTION must be a data structure returned by `json-read'." (with-selected-window sx-question-mode--window (sx-question-mode-next-section))) +(defvar sx-question-mode--section-help-echo + (format + (propertize "%s to hide/display content" 'face 'minibuffer-prompt) + (propertize "RET" 'face 'font-lock-function-name-face)) + "") + +(defvar sx-question-mode--title-properties + '(face sx-question-mode-title + action sx-question-mode-hide-show-section + help-echo sx-question-mode--section-help-echo) + "") + (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 - (if .title - ;; Questions have title - (propertize - .title - 'face 'sx-question-mode-title - 'sx-question-mode--section 1) - ;; Answers don't - (propertize - sx-question-mode-answer-title - 'face 'sx-question-mode-title - 'sx-question-mode--section 2))) + (apply + #'propertize + ;; Questions have title + (or .title + ;; Answers don't + sx-question-mode-answer-title) + ;; Section level + 'sx-question-mode--section (if .title 1 2) + ;; face, action and help-echo + sx-question-mode--title-properties)) ;; Sections can be hidden with overlays (sx-question-mode--wrap-in-overlay '(sx-question-mode--section-content t) @@ -243,8 +254,10 @@ DATA can represent a question or an answer." (mapconcat #'sx-question--tag-format .tags " ") 'sx-question-mode-tags)) ;; Body - (insert (propertize sx-question-mode-separator - 'face 'sx-question-mode-header)) + (insert "\n" + (propertize sx-question-mode-separator + 'face 'sx-question-mode-header + 'sx-question-mode--section 4)) (sx-question-mode--wrap-in-overlay '(face sx-question-mode-content-face) (insert "\n" @@ -254,12 +267,12 @@ DATA can represent a question or an answer." 'face 'sx-question-mode-header)))) ;; Comments (when .comments - (insert - "\n" - (propertize - sx-question-mode-comments-title - 'face 'sx-question-mode-title-comments - 'sx-question-mode--section 3)) + (insert "\n" + (apply #'propertize + sx-question-mode-comments-title + 'face 'sx-question-mode-title-comments + 'sx-question-mode--section 3 + sx-question-mode--title-properties)) (sx-question-mode--wrap-in-overlay '(sx-question-mode--section-content t) (insert "\n") @@ -381,7 +394,7 @@ URL is used as 'help-echo and 'url properties." (propertize text ;; Mouse-over - 'help-echo (format + 'help-echo (format (propertize "URL: %s, %s to visit" 'face 'minibuffer-prompt) (propertize url 'face 'default) (propertize "RET" 'face 'font-lock-function-name-face)) @@ -391,9 +404,7 @@ URL is used as 'help-echo and 'url properties." 'face 'link 'mouse-face 'highlight ;; What RET calls - 'action #'sx-question-mode-follow-link - ;; This is for `sx-question-mode--goto-propety-change'. - 'sx-question-mode--action #'sx-question-mode-follow-link)) + 'action #'sx-question-mode-follow-link)) (defun sx-question-mode-follow-link (&optional pos) "Follow link at POS or point" @@ -442,18 +453,21 @@ If nil, no recentering is performed." "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)) + (or n (setq n 1)) (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) + (unless (sx-question-mode--goto-propety-change + 'sx-question-mode--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))) + (sx-question-mode--goto-propety-change + 'sx-question-mode--section n))) (when 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))))) + (recenter sx-question-mode-recenter-line)))) + (sx-message-help-echo)) (defun sx-question-mode-previous-section (&optional n) "Move down to previous section (question or answer) of this buffer. @@ -470,8 +484,7 @@ With prefix argument N, move N times." (dotimes (_ (abs n)) (unless (sx-question-mode--goto-propety-change 'action n) (sx-question-mode--goto-propety-change 'action n))) - (let ((echo (get-text-property (point) 'help-echo))) - (when echo (message "%s" echo)))) + (sx-message-help-echo)) (defun sx-question-mode-previous-button (&optional n) "Move to previous interactible object in this buffer. @@ -484,8 +497,7 @@ With prefix argument N, move N times." "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) + (let ((func (if (and (numberp direction) (< direction 0)) #'previous-single-property-change #'next-single-property-change)) diff --git a/sx.el b/sx.el index 7ed56d3..058230c 100644 --- a/sx.el +++ b/sx.el @@ -31,6 +31,11 @@ "Display a message" (message "[stack] %s" (apply #'format format-string args))) +(defun sx-message-help-echo () + "If there's a 'help-echo property under point, message it." + (let ((echo (get-text-property (point) 'help-echo))) + (when echo (message "%s" echo)))) + (defun sx--thing-as-string (thing &optional sequence-sep) "Return a string representation of THING. If THING is already a string, just return it." -- cgit v1.2.3 From 64f6115be1a1ca9ae9401af7e68d7f7563fbfb16 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 00:46:22 +0000 Subject: Refactor buttons to be actual buttons. --- sx-question-mode.el | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index d08fc0d..8e5d380 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -210,9 +210,11 @@ QUESTION must be a data structure returned by `json-read'." "") (defvar sx-question-mode--title-properties - '(face sx-question-mode-title + `(face sx-question-mode-title action sx-question-mode-hide-show-section - help-echo sx-question-mode--section-help-echo) + help-echo ,sx-question-mode--section-help-echo + button t + follow-link t) "") (defun sx-question-mode--print-section (data) @@ -403,6 +405,10 @@ URL is used as 'help-echo and 'url properties." ;; Decoration 'face 'link 'mouse-face 'highlight + ;; So RET works + 'button t + ;; So mouse works + 'follow-link t ;; What RET calls 'action #'sx-question-mode-follow-link)) @@ -475,29 +481,12 @@ Prefix argument N moves N sections up or down." (interactive "p") (sx-question-mode-next-section (- (or n 1)))) -(defun sx-question-mode-next-button (&optional n) - "Move to next interactible object in this buffer. -These can be links, tags, or copiable code. -With prefix argument N, move N times." - (interactive "p") - (or n (setq n 1)) - (dotimes (_ (abs n)) - (unless (sx-question-mode--goto-propety-change 'action n) - (sx-question-mode--goto-propety-change 'action n))) - (sx-message-help-echo)) - -(defun sx-question-mode-previous-button (&optional n) - "Move to previous interactible object in this buffer. -These can be links, tags, or copiable code. -With prefix argument N, move N times." - (interactive "p") - (sx-question-mode-next-button (- (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. + "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 ((func (if (and (numberp direction) + (let ((prop (intern (format "sx-question-mode--%s" prop))) + (func (if (and (numberp direction) (< direction 0)) #'previous-single-property-change #'next-single-property-change)) @@ -507,7 +496,8 @@ 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 () +;;; Optional argument is for `push-button'. +(defun sx-question-mode-hide-show-section (&optional _) "Hide or show section under point." (interactive) (let ((ov (car (or (sx-question-mode--section-overlays-at (point)) @@ -548,7 +538,11 @@ Letters do not insert themselves; instead, they are commands. (" " scroll-up-command) (,(kbd "S-SPC") scroll-down-command) ([backspace] scroll-down-command) - ([tab] sx-question-mode-next-button))) + ([tab] forward-button) + (,(kbd "") backward-button) + (,(kbd "") backward-button) + (,(kbd "") backward-button) + ([return] push-button))) (defun sx-question-mode-refresh () "Refresh currently displayed question. -- cgit v1.2.3 From 615c3404c0aea4db77c6e417a9a9febdf784f245 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 00:48:12 +0000 Subject: n and p skip invisible sections --- sx-question-mode.el | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 8e5d380..5afb2e8 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -459,16 +459,18 @@ If nil, no recentering is performed." "Move down to next section (question or answer) of this buffer. Prefix argument N moves N sections down or up." (interactive "p") - (or n (setq n 1)) - (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 - 'sx-question-mode--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 - 'sx-question-mode--section n))) + (let ((count (if n (abs n) 1))) + (while (> count 0) + ;; 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)) + (let ((ov (car-safe (sx-question-mode--section-overlays-at (point))))) + (unless (and (overlayp ov) + (overlay-get ov 'invisible)) + (cl-decf count))))) (when 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))) -- cgit v1.2.3 From 598450c62e7ecc971b4cc5227343da10360ba978 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 01:31:08 +0000 Subject: Close question window with q --- sx-question-list.el | 10 +++++++++- sx-question-mode.el | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/sx-question-list.el b/sx-question-list.el index a1dec7a..c6eb58e 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -285,7 +285,7 @@ focus the relevant window." (null (equal sx-question-mode--window (selected-window)))) (setq sx-question-mode--window (condition-case er - (split-window-below sx-question-list-height) + (split-window (selected-window) sx-question-list-height 'below) (error ;; If the window is too small to split, use current one. (if (string-match @@ -293,7 +293,15 @@ focus the relevant window." (car (cdr-safe er))) nil (error (cdr er))))))) + ;; Display the question. (sx-question-mode--display data sx-question-mode--window) + ;; Configure the window to be closed on `q'. + (set-window-prev-buffers sx-question-mode--window nil) + (set-window-parameter + sx-question-mode--window + 'quit-restore + ;; See https://www.gnu.org/software/emacs/manual/html_node/elisp/Window-Parameters.html#Window-Parameters + `(window window ,(selected-window) ,sx-question-mode--buffer)) (when focus (if sx-question-mode--window (select-window sx-question-mode--window) diff --git a/sx-question-mode.el b/sx-question-mode.el index 5afb2e8..2ef22cb 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -523,6 +523,11 @@ If DIRECTION is negative, move backwards instead." Letters do not insert themselves; instead, they are commands. \\ \\{sx-question-mode}" + ;; Determine how to close this window. + (unless (window-parameter nil 'quit-restore) + (set-window-parameter + nil 'quit-restore + `(other window nil ,(current-buffer)))) ;; We call font-lock-region manually. See `sx-question-mode--fill-and-fontify' (font-lock-mode -1) (remove-hook 'after-change-functions 'markdown-check-change-for-wiki-link t) -- cgit v1.2.3 -- cgit v1.2.3 From 6a2252c6e6aec21cb9c9336b706947343f9e4fa6 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 01:52:27 +0000 Subject: Highlighting accepted answers actually works now. --- sx-question-list.el | 10 +++------- sx-question.el | 8 +++++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/sx-question-list.el b/sx-question-list.el index c6eb58e..82f0017 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -51,11 +51,7 @@ :group 'sx-question-list-faces) (defface sx-question-list-answers-accepted - '((((background light)) :background "YellowGreen" - :inherit sx-question-list-answers) - (((background dark)) :background "DarkOliveGreen" - :inherit sx-question-list-answers) - (t :inherit sx-question-list-answers)) + '((t :underline t :overline t :inherit sx-question-list-answers)) "" :group 'sx-question-list-faces) @@ -228,13 +224,13 @@ Used in the questions list to indicate a question was updated \"4d ago\"." '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) + '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) + '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) diff --git a/sx-question.el b/sx-question.el index d15cc80..19251ab 100644 --- a/sx-question.el +++ b/sx-question.el @@ -34,6 +34,7 @@ question.comments question.answers question.last_editor + question.accepted_answer_id user.display_name comment.owner comment.body_markdown @@ -69,10 +70,11 @@ ;; @TODO: (cl-evenp (random))) -(defun sx-question--accepted-answer (question) +(defun sx-question--accepted-answer-id (question) "Return accepted answer in QUESTION, or nil if none." - ;; @TODO: - (cl-evenp (random))) + (sx-assoc-let question + (and (integerp .accepted_answer_id) + .accepted_answer_id))) (defun sx-question--mark-read (question) "Mark QUESTION as being read, until it is updated again." -- cgit v1.2.3 From c0c46f3bed3479f14065d1d28cad63217f9473bc Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 02:05:56 +0000 Subject: Add site date to questions --- sx-question.el | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sx-question.el b/sx-question.el index 19251ab..607b3f6 100644 --- a/sx-question.el +++ b/sx-question.el @@ -47,11 +47,13 @@ (defun sx-question-get-questions (site &optional page) "Get the page PAGE of questions from SITE." - (sx-method-call - "questions" - `((site . ,site) - (page . ,page)) - sx-question-browse-filter)) + (mapcar + (lambda (question) (cons (cons 'site site) question)) + (sx-method-call + "questions" + `((site . ,site) + (page . ,page)) + sx-question-browse-filter))) (defun sx-question-get-question (site id) "Get the question ID from SITE." -- cgit v1.2.3 From ef241cbf39b57a24f8464d35a8616df7833101cc Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 02:28:12 +0000 Subject: sx-question-read-p and mark-read actually work --- sx-question.el | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/sx-question.el b/sx-question.el index 607b3f6..76f9a67 100644 --- a/sx-question.el +++ b/sx-question.el @@ -67,10 +67,18 @@ ;;; Question Properties +(defvar sx-question--user-read-list nil + "Alist of questions read by the user. +Each element has the form (SITE . QUESTION-LIST). +And each element in QUESTION-LIST has the form (QUESTION_ID . LAST-VIEWED-DATE).") + (defun sx-question--read-p (question) "Non-nil if QUESTION has been read since last updated." - ;; @TODO: - (cl-evenp (random))) + (sx-assoc-let question + (let ((ql (cdr (assoc .site sx-question--user-read-list)))) + (and ql + (>= (or (cdr (assoc .question_id ql)) 0) + .last_activity_date))))) (defun sx-question--accepted-answer-id (question) "Return accepted answer in QUESTION, or nil if none." @@ -80,7 +88,22 @@ (defun sx-question--mark-read (question) "Mark QUESTION as being read, until it is updated again." - nil) + (sx-assoc-let question + (let ((site-cell (assoc .site sx-question--user-read-list)) + (q-cell (cons .question_id .last_activity_date)) + cell) + (cond + ;; First question from this site. + ((null site-cell) + (push (list .site q-cell) sx-question--user-read-list)) + ;; Question already has an older time. + ((setq cell (assoc .question_id site-cell)) + (setcdr cell .last_activity_date)) + ;; Question wasn't present. + (t + (setcdr site-cell (cons q-cell (cdr site-cell))))))) + ;; Save the results. + (sx-cache-set 'read-questions sx-question--user-read-list)) (defun sx-question--< (property x y &optional pred) "Non-nil if PROPERTY attribute of question X is less than that of Y. -- cgit v1.2.3 From 46166a8a16f6e0c92d2cfc676ab7f302355fa2a1 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 02:28:34 +0000 Subject: Question list now makes use of read-p --- sx-question-list.el | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sx-question-list.el b/sx-question-list.el index 82f0017..e0bf300 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -183,10 +183,10 @@ Letters do not insert themselves; instead, they are commands. (defun sx-question-list-refresh (&optional redisplay no-update) "Update the list of questions. -If REDISPLAY is non-nil, also call `tabulated-list-print'. +If REDISPLAY is non-nil (or if interactive), also call `tabulated-list-print'. If the prefix argument NO-UPDATE is nil, query StackExchange for a new list before redisplaying." - (interactive "pP") + (interactive "p\nP") ;; Reset the mode-line unread count (we rebuild it here). (setq sx-question-list--unread-count 0) (let ((question-list @@ -206,7 +206,9 @@ a new list before redisplaying." (unless data (setq data (tabulated-list-get-id))) (unless data (error "No question here!")) (sx-assoc-let data - (browse-url .link))) + (browse-url .link)) + (sx-question--mark-read data) + (sx-question-list-refresh 'redisplay 'no-update)) (defcustom sx-question-list-ago-string " ago" "String appended to descriptions of the time since something happened. @@ -274,9 +276,10 @@ focus the relevant window." (interactive '(nil t)) (unless data (setq data (tabulated-list-get-id))) (unless data (error "No question here!")) - (when (sx-question--read-p data) + (unless (sx-question--read-p data) (cl-decf sx-question-list--unread-count) - (sx-question--mark-read data)) + (sx-question--mark-read data) + (sx-question-list-refresh 'redisplay 'no-update)) (unless (and (window-live-p sx-question-mode--window) (null (equal sx-question-mode--window (selected-window)))) (setq sx-question-mode--window -- cgit v1.2.3 From 884363ac75fe0c5f8e082c31cfeb8632ad85f19e Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 02:38:22 +0000 Subject: Ensure we don't overwrite the cache --- sx-question.el | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/sx-question.el b/sx-question.el index 76f9a67..7debf4f 100644 --- a/sx-question.el +++ b/sx-question.el @@ -72,22 +72,24 @@ Each element has the form (SITE . QUESTION-LIST). And each element in QUESTION-LIST has the form (QUESTION_ID . LAST-VIEWED-DATE).") +(defun sx-question--ensure-read-list () + "Ensure the `sx-question--user-read-list' has been read from cache." + (unless sx-question--user-read-list + (setq sx-question--user-read-list + (sx-cache-get 'read-questions)))) + (defun sx-question--read-p (question) "Non-nil if QUESTION has been read since last updated." + (sx-question--ensure-read-list) (sx-assoc-let question (let ((ql (cdr (assoc .site sx-question--user-read-list)))) (and ql (>= (or (cdr (assoc .question_id ql)) 0) .last_activity_date))))) -(defun sx-question--accepted-answer-id (question) - "Return accepted answer in QUESTION, or nil if none." - (sx-assoc-let question - (and (integerp .accepted_answer_id) - .accepted_answer_id))) - (defun sx-question--mark-read (question) "Mark QUESTION as being read, until it is updated again." + (sx-question--ensure-read-list) (sx-assoc-let question (let ((site-cell (assoc .site sx-question--user-read-list)) (q-cell (cons .question_id .last_activity_date)) @@ -105,6 +107,12 @@ And each element in QUESTION-LIST has the form (QUESTION_ID . LAST-VIEWED-DATE). ;; Save the results. (sx-cache-set 'read-questions sx-question--user-read-list)) +(defun sx-question--accepted-answer-id (question) + "Return accepted answer in QUESTION, or nil if none." + (sx-assoc-let question + (and (integerp .accepted_answer_id) + .accepted_answer_id))) + (defun sx-question--< (property x y &optional pred) "Non-nil if PROPERTY attribute of question X is less than that of Y. With optional argument predicate, use it instead of `<'." -- cgit v1.2.3 From fe57abe554146382b60289523d5122d76e20781c Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 02:49:53 +0000 Subject: Finish merge --- sx-question-list.el | 2 +- sx-question.el | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/sx-question-list.el b/sx-question-list.el index 773ce0d..b220097 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -226,7 +226,7 @@ Used in the questions list to indicate a question was updated \"4d ago\"." '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) + 'face (if (sx-question--accepted-answer-id data) 'sx-question-list-answers-accepted 'sx-question-list-answers)) (concat diff --git a/sx-question.el b/sx-question.el index 768e5db..fc44bd8 100644 --- a/sx-question.el +++ b/sx-question.el @@ -71,16 +71,17 @@ Each element has the form (SITE . QUESTION-LIST). And each element in QUESTION-LIST has the form (QUESTION_ID . LAST-VIEWED-DATE).") -(defun sx-question--ensure-read-list () - "Ensure the `sx-question--user-read-list' has been read from cache." +(defun sx-question--ensure-read-list (site) + "Ensure the `sx-question--user-read-list' has been read from cache. +If no cache exists for it, initialize one with SITE." (unless sx-question--user-read-list (setq sx-question--user-read-list - (sx-cache-get 'read-questions)))) + (sx-cache-get 'read-questions `(list ,site))))) (defun sx-question--read-p (question) "Non-nil if QUESTION has been read since last updated." - (sx-question--ensure-read-list) (sx-assoc-let question + (sx-question--ensure-read-list .site) (let ((ql (cdr (assoc .site sx-question--user-read-list)))) (and ql (>= (or (cdr (assoc .question_id ql)) 0) @@ -88,8 +89,8 @@ And each element in QUESTION-LIST has the form (QUESTION_ID . LAST-VIEWED-DATE). (defun sx-question--mark-read (question) "Mark QUESTION as being read, until it is updated again." - (sx-question--ensure-read-list) (sx-assoc-let question + (sx-question--ensure-read-list .site) (let ((site-cell (assoc .site sx-question--user-read-list)) (q-cell (cons .question_id .last_activity_date)) cell) @@ -103,6 +104,8 @@ And each element in QUESTION-LIST has the form (QUESTION_ID . LAST-VIEWED-DATE). ;; Question wasn't present. (t (setcdr site-cell (cons q-cell (cdr site-cell))))))) + ;; This causes a small lag on `j' and `k' as the list gets large. + ;; Should we do this on a timer? ;; Save the results. (sx-cache-set 'read-questions sx-question--user-read-list)) -- cgit v1.2.3 From e59d9e721d8ce65b685993be0b570cf128da8208 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 10:53:53 +0000 Subject: Fix typo --- sx-question-mode.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 5afb2e8..0ac2774 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -463,10 +463,10 @@ Prefix argument N moves N sections down or up." (while (> count 0) ;; 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) + (unless (sx-question-mode--goto-property-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)) + (sx-question-mode--goto-property-change 'section n)) (let ((ov (car-safe (sx-question-mode--section-overlays-at (point))))) (unless (and (overlayp ov) (overlay-get ov 'invisible)) @@ -483,7 +483,7 @@ Prefix argument N moves N sections up or down." (interactive "p") (sx-question-mode-next-section (- (or n 1)))) -(defun sx-question-mode--goto-propety-change (prop &optional direction) +(defun sx-question-mode--goto-property-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." -- cgit v1.2.3 From 5c4f1fe790190d7a6760213d70135d998783bae5 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 11:13:10 +0000 Subject: Use more rx. --- sx-question-mode.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 0ac2774..39d352e 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -345,11 +345,13 @@ HEADER is given `sx-question-mode-header' face, and value is given FACE. (when sx-question-mode-bullet-appearance (font-lock-add-keywords ;; Bullet items. nil - `(("^ *\\(\\*\\|\\+\\|-\\|\\) " + `(((rx line-start (0+ blank) (group-n 1 (any "*+-")) blank) 1 '(face nil display ,sx-question-mode-bullet-appearance) prepend)))) (font-lock-add-keywords ;; Highlight usernames. nil - `(("\\(?: \\|^\\)\\(@\\(?:\\sw\\|\\s_\\)+\\)\\_>" + `(((rx (or blank line-start) + (group-n 1 (and "@" (1+ (or (syntax word) (syntax symbol))))) + symbol-end) 1 font-lock-builtin-face))) ;; Everything. (font-lock-fontify-region (point-min) (point-max)) @@ -426,10 +428,11 @@ If ID is nil, use ID2 instead." (save-match-data (goto-char (point-min)) (when (search-forward-regexp - (format "^\\s-*\\[\\(%s\\)]:\\s-+\\(?2:[^ ]+\\)" + (format (rx line-start (0+ blank) "[%s]:" (1+ blank) + (group-n 1 (1+ (not blank)))) (or id id2)) nil t) - (match-string-no-properties 2))))) + (match-string-no-properties 1))))) (defun sx-question-mode--move-over-pre () "Non-nil if paragraph at point can be filled." -- cgit v1.2.3 From 1a843499ae1ebc4ec56cfb27f79779293696cb2c Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 15 Nov 2014 16:34:55 +0000 Subject: Delete sx-lto.el --- sx-lto.el | 103 -------------------------------------------------------------- 1 file changed, 103 deletions(-) delete mode 100644 sx-lto.el diff --git a/sx-lto.el b/sx-lto.el deleted file mode 100644 index ad58570..0000000 --- a/sx-lto.el +++ /dev/null @@ -1,103 +0,0 @@ -;;; sx-lto.el --- lisp-to-org conversion functions -*- 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: - - -;;; Requirements -(require 'sx) -(require 'org) - -(defun sx-lto--question (data) - "Return question DATA in a format acceptable by `org-element-interpret-data'. -DATA is a list of cons cells representing a question, as received -by the API and read by `json-read'." - `(headline (:title ,(cdr (assoc 'title data)) - :level 1 - :tags ,(mapcar #'identity (cdr (assoc 'tags data)))) - ,(sx-lto--question-answer data) - ,@(mapcar #'sx-lto--answer (cdr (assoc 'answers data))))) - -(defun sx-lto--answer (data) - "Return answer DATA in a format acceptable by `org-element-interpret-data'. -DATA is a list of cons cells representing a question, as received -by the API and read by `json-read'." - ;; Right now this doesn't do anything special. But it should check - ;; whether the answer is accepted. How do we display that? - `(headline (:title "Answer" :level 2) - ,(sx-lto--question-answer data))) - -(defun sx-lto--question-answer (data) - "Process and return the elements of DATA which questions and answers have in common." - (let ((comments - (mapcar #'sx-lto--comment (cdr (assoc 'comments data))))) - `(;; Body as a src block (really NOT nice). - (src-block (:value ,(sx-lto--body data) - . ,sx-lto--body-src-block)) - ;; Comments as descriptive lists. If there are no comments, an - ;; empty list would throw an error. - ,@(when comments `((plain-list (:type descriptive) ,comments)))))) - - -;;; Body rendering -(defvar sx-lto--body-src-block - '(:language "markdown" :switches nil :parameters nil :hiddenp nil) - "Properties used on the markdown src-block which represents the body.") - -(defface sx-lto-body - '((((background light)) :background "Grey90") - (((background dark)) :background "Grey10")) - "Face used on the body content of questions and answers." - :group 'sx-faces) - -;;; This is not used ATM since we got rid of HTML. But it can be used -;;; once we start extending markdown mode. -(defcustom sx-lto-bullet (if (char-displayable-p ?•) " •" " -") - "Bullet used on the display of lists." - :type 'string - :group 'sx) - -(defun sx-lto--body (data) - "Get and cleanup `body_markdown' from DATA." - (concat - (replace-regexp-in-string - "\r\n" "\n" (cdr (assoc 'body_markdown data))) - "\n")) - -;; We need to add padding in case the body contains a * at column 1 -;; (which would break org-mode). -(defvar sx-lto--padding - (propertize "  " 'display " ") - "Left-padding added to each line of a body.") - -(defvar sx-lto-comment-item - '(:bullet "- " :checkbox nil :counter nil :hiddenp nil) - "Properties used on the items which represent comments.") - -(defun sx-lto--comment (data) - "" - (let* ((owner (cdr (assoc 'owner data))) - (owner-name (cdr (assoc 'display_name owner)))) - `(item (:tag ,owner-name . ,sx-lto-comment-item) - (paragraph () ,(cdr (assoc 'body_markdown data)))))) - -(provide 'sx-lto) -;;; sx.el ends here -- cgit v1.2.3 From 97a0d11c657fcb3a7369e62ab4c7b5ee676444ea Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sat, 15 Nov 2014 17:29:40 -0500 Subject: Fix use of `rx' macro It needs to be evaluated. --- sx-question-mode.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 32cd112..03647bc 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -345,11 +345,11 @@ HEADER is given `sx-question-mode-header' face, and value is given FACE. (when sx-question-mode-bullet-appearance (font-lock-add-keywords ;; Bullet items. nil - `(((rx line-start (0+ blank) (group-n 1 (any "*+-")) blank) + `((,(rx line-start (0+ blank) (group-n 1 (any "*+-")) blank) 1 '(face nil display ,sx-question-mode-bullet-appearance) prepend)))) (font-lock-add-keywords ;; Highlight usernames. nil - `(((rx (or blank line-start) + `((,(rx (or blank line-start) (group-n 1 (and "@" (1+ (or (syntax word) (syntax symbol))))) symbol-end) 1 font-lock-builtin-face))) -- cgit v1.2.3 From 829821cc988be06c1f8a0699d11fecb7a6db972a Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sun, 16 Nov 2014 10:45:21 +0000 Subject: Implement sx-initialize --- sx.el | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/sx.el b/sx.el index 53aae84..64c555c 100644 --- a/sx.el +++ b/sx.el @@ -143,10 +143,19 @@ SETTER should be a function of two arguments. If SETTER is nil, (,(or setter #'setq) ,variable ,value)))) nil) -(defun stack-initialize () - (run-hooks - 'sx-init--internal-hook - 'sx-init-hook)) +(defvar sx-initialized nil + "Nil if sx hasn't been initialized yet. +If it has, holds the time at which initialization happened.") + +(defun sx-initialize (&optional force) + "Run initialization hooks if they haven't been run yet. +These are `sx-init--internal-hook' and `sx-init-hook'. +If FORCE is non-nil, run them even if they've already been run." + (when (or force (not sx-initialized)) + (prog1 + (run-hooks 'sx-init--internal-hook + 'sx-init-hook) + (setq sx-initialized (current-time))))) (provide 'sx) ;;; sx.el ends here -- cgit v1.2.3 From 14409012011d389c70ff79baf6dc3e228b08584c Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sun, 16 Nov 2014 10:45:46 +0000 Subject: Call sx-initialize on list-questions Addresses #63 --- sx-question-list.el | 1 + 1 file changed, 1 insertion(+) diff --git a/sx-question-list.el b/sx-question-list.el index b220097..72eabd3 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -323,6 +323,7 @@ focus the relevant window." (defun list-questions (no-update) "Display a list of StackExchange questions." (interactive "P") + (sx-initialize) (unless (buffer-live-p sx-question-list--buffer) (setq sx-question-list--buffer (generate-new-buffer "*question-list*"))) -- cgit v1.2.3