From a39fa2255a253194b2a080a7aa545a0fb52db7ec Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 16 Dec 2014 20:44:56 -0200 Subject: delay-mode-hooks on markdown-mode. Fix #159 --- sx-question-print.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-question-print.el b/sx-question-print.el index dc853ba..2bfa60d 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -340,7 +340,7 @@ E.g.: "Return TEXT filled according to `markdown-mode'." (with-temp-buffer (insert text) - (markdown-mode) + (delay-mode-hooks (markdown-mode)) (font-lock-mode -1) (when sx-question-mode-bullet-appearance (font-lock-add-keywords ;; Bullet items. -- cgit v1.2.3 From 2171ad472e1c5180bb9ac021197db04d94f3c078 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 16 Dec 2014 20:52:23 -0200 Subject: delay-mode-hooks on code blocks --- sx-babel.el | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/sx-babel.el b/sx-babel.el index 5544642..b4ff306 100644 --- a/sx-babel.el +++ b/sx-babel.el @@ -56,11 +56,16 @@ on a match.") (insert text) (setq indent (sx-babel--unindent-buffer)) (goto-char (point-min)) - (make-text-button - (point-min) (point-max) - 'sx-button-copy (buffer-string) - :type 'sx-question-mode-code-block) - (sx-babel--determine-and-activate-major-mode) + (let ((mode (sx-babel--determine-major-mode))) + (make-text-button + (point-min) (point-max) + 'sx-button-copy (buffer-string) + ;; We store the mode here so it can be used if the user wants + ;; to edit the code block. + 'sx-mode mode + :type 'sx-question-mode-code-block) + (when mode + (delay-mode-hooks (funcall mode)))) (font-lock-fontify-region (point-min) (point-max)) (goto-char (point-min)) (let ((space (make-string indent ?\s))) @@ -72,17 +77,18 @@ on a match.") (delete-region beg end) (insert text))) -(defun sx-babel--determine-and-activate-major-mode () - "Activate the major-mode most suitable for the current buffer." +(defun sx-babel--determine-major-mode () + "Return the major-mode most suitable for the current buffer." (let ((alist sx-babel-major-mode-alist) - cell) + cell out) (while (setq cell (pop alist)) (goto-char (point-min)) (skip-chars-forward "\r\n[:blank:]") (let ((kar (car cell))) (when (if (stringp kar) (looking-at kar) (funcall kar)) (setq alist nil) - (funcall (cadr cell))))))) + (setq out (cadr cell))))) + out)) (defun sx-babel--unindent-buffer () "Remove absolute indentation in current buffer. -- cgit v1.2.3 From 28bb9c2d582f301bb328c2ca84ad97dcbed87abb Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 16 Dec 2014 21:08:58 -0200 Subject: Activate major-mode when editing code-blocks --- sx-button.el | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sx-button.el b/sx-button.el index 283fe0d..f166164 100644 --- a/sx-button.el +++ b/sx-button.el @@ -77,20 +77,23 @@ This is usually a link's URL, or the content of a code block." (point) 'sx-button-copy-type) content))))) -(defun sx-button-edit-this (text-or-marker) - "Open a temp buffer populated with the string TEXT-OR-MARKER. +(defun sx-button-edit-this (text-or-marker &optional major-mode) + "Open a temp buffer populated with the string TEXT-OR-MARKER using MAJOR-MODE. When given a marker (or interactively), use the 'sx-button-copy -text-property under the marker. This is usually the content of a -code-block." +and the 'sx-mode text-properties under the marker. These are +usually part of a code-block." (interactive (list (point-marker))) ;; Buttons receive markers. (when (markerp text-or-marker) + (setq major-mode (get-text-property text-or-marker 'sx-mode)) (unless (setq text-or-marker (get-text-property text-or-marker 'sx-button-copy)) (sx-message "Nothing of interest here."))) (with-current-buffer (pop-to-buffer (generate-new-buffer "*sx temp buffer*")) - (insert text-or-marker))) + (insert text-or-marker) + (when major-mode + (funcall major-mode)))) (defun sx-button-follow-link (&optional pos) "Follow link at POS. If POS is nil, use `point'." -- cgit v1.2.3 From c3d6d1f689598940f29f19c328e10d7c5fff0ade Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 16 Dec 2014 21:20:31 -0200 Subject: Make entire code-block a single button This fixes a bug we had. Hitting TAB on a codeblock would move us to the next line on the code block, instead of going to another button. Now the entire code block is a single button, so that is fixed. --- sx-babel.el | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/sx-babel.el b/sx-babel.el index b4ff306..24e56c2 100644 --- a/sx-babel.el +++ b/sx-babel.el @@ -51,31 +51,31 @@ on a match.") (defun sx-babel--make-pre-button (beg end) "Turn the region between BEG and END into a button." (let ((text (buffer-substring-no-properties beg end)) - indent) + indent mode copy) (with-temp-buffer (insert text) (setq indent (sx-babel--unindent-buffer)) (goto-char (point-min)) - (let ((mode (sx-babel--determine-major-mode))) - (make-text-button - (point-min) (point-max) - 'sx-button-copy (buffer-string) - ;; We store the mode here so it can be used if the user wants - ;; to edit the code block. - 'sx-mode mode - :type 'sx-question-mode-code-block) - (when mode - (delay-mode-hooks (funcall mode)))) + (setq mode (sx-babel--determine-major-mode)) + (setq copy (string-trim-right (buffer-string))) + (when mode + (delay-mode-hooks (funcall mode))) (font-lock-fontify-region (point-min) (point-max)) (goto-char (point-min)) (let ((space (make-string indent ?\s))) (while (not (eobp)) - (insert space) + (insert-and-inherit space) (forward-line 1))) (setq text (buffer-string))) (goto-char beg) (delete-region beg end) - (insert text))) + (insert-text-button + text + 'sx-button-copy copy + ;; We store the mode here so it can be used if the user wants + ;; to edit the code block. + 'sx-mode mode + :type 'sx-question-mode-code-block))) (defun sx-babel--determine-major-mode () "Return the major-mode most suitable for the current buffer." -- cgit v1.2.3 From 4983653696a89a6f7030f761307771035b92d168 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Tue, 16 Dec 2014 21:55:17 -0200 Subject: Display comment score --- sx-question-print.el | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index dc853ba..6131684 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -291,17 +291,20 @@ The comment is indented, filled, and then printed according to (list 'sx--data-here comment-data) (sx-assoc-let comment-data (insert + (if (> .score 0) (number-to-string .score) "") + (if (eq .upvoted t) "^" "") + (if (or (> .score 0) .upvoted) " " "") (format - sx-question-mode-comments-format - (sx-question-mode--propertize-display-name .owner) - (substring - ;; We fill with three spaces at the start, so the comment is - ;; slightly indented. - (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. - 3)))))) + sx-question-mode-comments-format + (sx-question-mode--propertize-display-name .owner) + (substring + ;; We fill with three spaces at the start, so the comment is + ;; slightly indented. + (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. + 3)))))) (defun sx-question-mode--insert-header (&rest args) "Insert propertized ARGS. -- cgit v1.2.3 From 6b370f8dfc9ffbef757230732f5d158707d7f6e0 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 17 Dec 2014 12:36:11 -0200 Subject: Support editing comments. --- sx-compose.el | 18 +++++++++++++----- sx-interaction.el | 1 - 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/sx-compose.el b/sx-compose.el index 96f47f3..af9d861 100644 --- a/sx-compose.el +++ b/sx-compose.el @@ -146,19 +146,22 @@ respectively added locally to `sx-compose-before-send-hook' and (error "Invalid PARENT")) (let ((is-question (and (listp parent) - (null (cdr (assoc 'answer_id parent)))))) + (cdr (assoc 'title parent))))) (with-current-buffer (sx-compose--get-buffer-create site parent) (sx-compose-mode) (setq sx-compose--send-function (if (consp parent) (sx-assoc-let parent - (lambda () (sx-method-call (if .title 'questions 'answers) + (lambda () (sx-method-call (cond + (.title 'questions) + (.comment_id 'comments) + (t 'answers)) :auth 'warn :url-method "POST" :filter sx-browse-filter :site site :keywords (sx-compose--generate-keywords is-question) - :id (or .answer_id .question_id) + :id (or .comment_id .answer_id .question_id) :submethod 'edit))) (lambda () (sx-method-call 'questions :auth 'warn @@ -256,8 +259,13 @@ the id property." site data))) (t (get-buffer-create - (format "*sx draft edit %s %s*" - site (sx-assoc-let data (or .answer_id .question_id))))))) + (sx-assoc-let data + (format "*sx draft edit %s %s %s*" + site + (cond (.title "question") + (.comment_id "comment") + (t "answer")) + (or .comment_id .answer_id .question_id))))))) (provide 'sx-compose) ;;; sx-compose.el ends here diff --git a/sx-interaction.el b/sx-interaction.el index e7a4d94..1e14062 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -286,7 +286,6 @@ from context at point." ;; If we ever make an "Edit" button, first arg is a marker. (when (markerp data) (setq data (sx--data-here))) (sx-assoc-let data - (when .comment_id (sx-user-error "Editing comments is not supported yet")) (let ((buffer (current-buffer))) (pop-to-buffer (sx-compose-create -- cgit v1.2.3 From d202e1ebeddf98d3109c8f7352fceb8dd5c07eb7 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 17 Dec 2014 12:36:37 -0200 Subject: Fix buffer not updating after posting answers/edits. --- sx-interaction.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sx-interaction.el b/sx-interaction.el index 1e14062..92f0a63 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -93,10 +93,11 @@ If it's not a question, or if it is read, return DATA." If BUFFER is not live, nothing is done." (setq buffer (or buffer (current-buffer))) (when (buffer-live-p buffer) - (cond ((derived-mode-p 'sx-question-list-mode) - (sx-question-list-refresh 'redisplay 'no-update)) - ((derived-mode-p 'sx-question-mode) - (sx-question-mode-refresh 'no-update))))) + (with-current-buffer buffer + (cond ((derived-mode-p 'sx-question-list-mode) + (sx-question-list-refresh 'redisplay 'no-update)) + ((derived-mode-p 'sx-question-mode) + (sx-question-mode-refresh 'no-update)))))) (defun sx--copy-data (from to) "Copy all fields of alist FORM onto TO. -- cgit v1.2.3 From 6e38818cf6d3c79eaf0d105e8628f066bb536579 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 17 Dec 2014 12:36:58 -0200 Subject: Close compose window when buffer is killed. --- sx-compose.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sx-compose.el b/sx-compose.el index af9d861..5201435 100644 --- a/sx-compose.el +++ b/sx-compose.el @@ -117,9 +117,12 @@ contents to the API, then calls `sx-compose-after-send-functions'." (current-buffer) result))))) (defun sx-compose-quit (buffer _) - "Kill BUFFER." + "Close BUFFER's window and kill it." (interactive (list (current-buffer) nil)) (when (buffer-live-p buffer) + (let ((w (get-buffer-window buffer))) + (when (window-live-p w) + (delete-window w))) (kill-buffer buffer))) (defun sx-compose--copy-as-kill (buffer _) -- cgit v1.2.3 From cd28aeb50a49a625047f9a3d7eb20694955f87aa Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 17 Dec 2014 12:53:25 -0200 Subject: Refactor comment validity checking --- sx-interaction.el | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sx-interaction.el b/sx-interaction.el index 92f0a63..8aa82a2 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -216,8 +216,8 @@ TEXT is a string. Interactively, it is read from the minibufer." "Comment text: " (when .comment_id (concat (sx--user-@name .owner) " ")))) - (while (< (string-width text) 15) - (setq text (read-string "Comment text (at least 15 characters): " text)))) + (while (not (sx--comment-valid-p text 'silent)) + (setq text (read-string "Comment text (between 16 and 600 characters): " text)))) ;; If non-interactive, `text' could be anything. (unless (stringp text) (error "Comment body must be a string")) @@ -241,6 +241,18 @@ TEXT is a string. Interactively, it is read from the minibufer." ;; Display the changes in `data'. (sx--maybe-update-display))))) +(defun sx--comment-valid-p (&optional text silent) + "Non-nil if TEXT fits stack exchange comment length limits. +If TEXT is nil, use `buffer-string'. Must have more than 15 and +less than 601 characters. +If SILENT is nil, message the user about this limit." + (let ((w (string-width (or text (buffer-string))))) + (if (and (< 15 w) (< w 601)) + t + (unless silent + (message "Comments must be within 16 and 600 characters.")) + nil))) + (defun sx--get-post (type site id) "Find in the database a post identified by TYPE, SITE and ID. TYPE is `question' or `answer'. -- cgit v1.2.3 From 34753b9cb10beaa529f9f56cbc5bea3d11960ea3 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Wed, 17 Dec 2014 12:53:35 -0200 Subject: Do comment validity checking on edits too --- sx-interaction.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sx-interaction.el b/sx-interaction.el index 8aa82a2..c6f2639 100644 --- a/sx-interaction.el +++ b/sx-interaction.el @@ -302,7 +302,9 @@ from context at point." (let ((buffer (current-buffer))) (pop-to-buffer (sx-compose-create - .site data nil + .site data + ;; Before send hook + (when .comment_id (list #'sx--comment-valid-p)) ;; After send functions (list (lambda (_ res) (sx--copy-data (elt res 0) data) -- cgit v1.2.3 From 641beffbc3ce72b4e1b9b6bbb79880ba795fdf72 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 16:08:51 -0200 Subject: Hotfix Don't use string-trim. Fix #164 --- sx-question-print.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-question-print.el b/sx-question-print.el index 223049a..3d698cc 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -366,7 +366,7 @@ E.g.: (skip-chars-forward "\r\n[:blank:]") (forward-paragraph) (fill-region beg (point))))) - (string-trim-right (buffer-string)))) + (replace-regexp-in-string "[[:blank:]]+\\'" "" (buffer-string)))) (defun sx-question-mode--dont-fill-here () "If text shouldn't be filled here, return t and skip over it." -- cgit v1.2.3 From 9ba5f7751c088a3bbad7d98a5cf3cf23a08f1bf8 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 16:25:59 -0200 Subject: Don't fill comments. Affects #141 --- sx-question-print.el | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sx-question-print.el b/sx-question-print.el index 3d698cc..a1b7589 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -371,6 +371,7 @@ E.g.: (defun sx-question-mode--dont-fill-here () "If text shouldn't be filled here, return t and skip over it." (or (sx-question-mode--skip-and-fontify-pre) + (sx-question-mode--skip-comments) ;; Skip headers and references (let ((pos (point))) (skip-chars-forward "\r\n[:blank:]") @@ -446,5 +447,16 @@ font-locking." (goto-char before) nil))) +(defun sx-question-mode--skip-comments () + "If there's an html comment ahead, skip it and return t." + ;; @TODO: Handle the comment. + ;; "Handling means to store any relevant metadata it might be holding." + (let ((before (point))) + (skip-chars-forward "\r\n[:blank:]") + (if (markdown-match-comments (line-end-position)) + t + (goto-char before) + nil))) + (provide 'sx-question-print) ;;; sx-question-print.el ends here -- cgit v1.2.3 From df276818b6275f795f4504b5df36505a7f79c8ee Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 17:45:42 -0200 Subject: Refactor and simplify sx-question-mode--skip-FOO functions. They no longer need to worry about restoring point. Just move point to its destination and return non-nil if it worked. --- sx-question-print.el | 53 +++++++++++++++++++--------------------------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index a1b7589..46e18ca 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -370,19 +370,19 @@ E.g.: (defun sx-question-mode--dont-fill-here () "If text shouldn't be filled here, return t and skip over it." - (or (sx-question-mode--skip-and-fontify-pre) - (sx-question-mode--skip-comments) - ;; Skip headers and references - (let ((pos (point))) - (skip-chars-forward "\r\n[:blank:]") - (goto-char (line-beginning-position)) - (if (or (looking-at-p (format sx-question-mode--reference-regexp ".+")) - (looking-at-p "^#")) - ;; Returns non-nil - (forward-paragraph) - ;; Go back and return nil - (goto-char pos) - nil)))) + (catch 'sx-question-mode-done + (let ((before (point))) + (skip-chars-forward "\r\n[:blank:]") + (let ((first-non-blank (point))) + (dolist (it '(sx-question-mode--skip-and-fontify-pre + sx-question-mode--skip-comments)) + ;; If something worked, keep point where it is and return t. + (if (funcall it) (throw 'sx-question-mode-done t) + ;; Before calling each new function. Go back to the first + ;; non-blank char. + (goto-char first-non-blank))) + ;; If nothing matched, go back to the very beginning. + (goto-char before))))) (defun sx-question-mode--process-links-in-buffer () "Turn all markdown links in this buffer into compact format." @@ -431,32 +431,17 @@ If ID is nil, use FALLBACK-ID instead." "If there's a pre block ahead, handle it, skip it and return t. Handling means to turn it into a button and remove erroneous font-locking." - (let ((before (point)) - beg end) - (if (markdown-match-pre-blocks - (save-excursion - (skip-chars-forward "\r\n[:blank:]") - (setq beg (point)))) - (progn - (setq end (point)) - (sx-babel--make-pre-button - (save-excursion - (goto-char beg) - (line-beginning-position)) - end)) - (goto-char before) - nil))) + (let ((beg (line-beginning-position))) + ;; To identify code-blocks we need to be at start of line. + (goto-char beg) + (when (markdown-match-pre-blocks (line-end-position)) + (sx-babel--make-pre-button beg (point))))) (defun sx-question-mode--skip-comments () "If there's an html comment ahead, skip it and return t." ;; @TODO: Handle the comment. ;; "Handling means to store any relevant metadata it might be holding." - (let ((before (point))) - (skip-chars-forward "\r\n[:blank:]") - (if (markdown-match-comments (line-end-position)) - t - (goto-char before) - nil))) + (markdown-match-comments (line-end-position))) (provide 'sx-question-print) ;;; sx-question-print.el ends here -- cgit v1.2.3 From b3008bf4e11bb4a5bc67a82f7bebd3f38520d0af Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 17:49:18 -0200 Subject: Don't fill headlines --- sx-question-print.el | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sx-question-print.el b/sx-question-print.el index 46e18ca..6c98c36 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -375,6 +375,7 @@ E.g.: (skip-chars-forward "\r\n[:blank:]") (let ((first-non-blank (point))) (dolist (it '(sx-question-mode--skip-and-fontify-pre + sx-question-mode--skip-headline sx-question-mode--skip-comments)) ;; If something worked, keep point where it is and return t. (if (funcall it) (throw 'sx-question-mode-done t) @@ -443,5 +444,12 @@ font-locking." ;; "Handling means to store any relevant metadata it might be holding." (markdown-match-comments (line-end-position))) +(defun sx-question-mode--skip-headline () + "If there's a headline ahead, skip it and return non-nil." + (when (or (looking-at-p "^#+ ") + (progn (forward-line 1) (looking-at-p "===\\|---"))) + ;; Returns non-nil. + (forward-line 1))) + (provide 'sx-question-print) ;;; sx-question-print.el ends here -- cgit v1.2.3 From 2a77336ccbe19acdfd176b7198736a78be887e04 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 17:49:29 -0200 Subject: Reimplement reference not-filling --- sx-question-print.el | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sx-question-print.el b/sx-question-print.el index 6c98c36..97124e9 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -376,6 +376,7 @@ E.g.: (let ((first-non-blank (point))) (dolist (it '(sx-question-mode--skip-and-fontify-pre sx-question-mode--skip-headline + sx-question-mode--skip-references sx-question-mode--skip-comments)) ;; If something worked, keep point where it is and return t. (if (funcall it) (throw 'sx-question-mode-done t) @@ -451,5 +452,11 @@ font-locking." ;; Returns non-nil. (forward-line 1))) +(defun sx-question-mode--skip-references () + "If there's a reference ahead, skip it and return non-nil." + (while (looking-at-p (format sx-question-mode--reference-regexp ".+")) + ;; Returns non-nil + (forward-line 1))) + (provide 'sx-question-print) ;;; sx-question-print.el ends here -- cgit v1.2.3 From 806910361bb26b76a585c23c1bd695e38da2dc9a Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 17:53:18 -0200 Subject: Reorganize functions. --- sx-question-print.el | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index 97124e9..996b057 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -368,24 +368,8 @@ E.g.: (fill-region beg (point))))) (replace-regexp-in-string "[[:blank:]]+\\'" "" (buffer-string)))) -(defun sx-question-mode--dont-fill-here () - "If text shouldn't be filled here, return t and skip over it." - (catch 'sx-question-mode-done - (let ((before (point))) - (skip-chars-forward "\r\n[:blank:]") - (let ((first-non-blank (point))) - (dolist (it '(sx-question-mode--skip-and-fontify-pre - sx-question-mode--skip-headline - sx-question-mode--skip-references - sx-question-mode--skip-comments)) - ;; If something worked, keep point where it is and return t. - (if (funcall it) (throw 'sx-question-mode-done t) - ;; Before calling each new function. Go back to the first - ;; non-blank char. - (goto-char first-non-blank))) - ;; If nothing matched, go back to the very beginning. - (goto-char before))))) - + +;;; Handling links (defun sx-question-mode--process-links-in-buffer () "Turn all markdown links in this buffer into compact format." (save-excursion @@ -429,6 +413,26 @@ If ID is nil, use FALLBACK-ID instead." nil t) (match-string-no-properties 1))))) + +;;; Things we don't fill +(defun sx-question-mode--dont-fill-here () + "If text shouldn't be filled here, return t and skip over it." + (catch 'sx-question-mode-done + (let ((before (point))) + (skip-chars-forward "\r\n[:blank:]") + (let ((first-non-blank (point))) + (dolist (it '(sx-question-mode--skip-and-fontify-pre + sx-question-mode--skip-headline + sx-question-mode--skip-references + sx-question-mode--skip-comments)) + ;; If something worked, keep point where it is and return t. + (if (funcall it) (throw 'sx-question-mode-done t) + ;; Before calling each new function. Go back to the first + ;; non-blank char. + (goto-char first-non-blank))) + ;; If nothing matched, go back to the very beginning. + (goto-char before))))) + (defun sx-question-mode--skip-and-fontify-pre () "If there's a pre block ahead, handle it, skip it and return t. Handling means to turn it into a button and remove erroneous -- cgit v1.2.3 From 5eb81aebc2b5131fff7c4a4d33c163cec2cf3844 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 18:07:04 -0200 Subject: Fix return values --- sx-question-print.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index 996b057..c6176fc 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -431,7 +431,9 @@ If ID is nil, use FALLBACK-ID instead." ;; non-blank char. (goto-char first-non-blank))) ;; If nothing matched, go back to the very beginning. - (goto-char before))))) + (goto-char before) + ;; And return nil + nil)))) (defun sx-question-mode--skip-and-fontify-pre () "If there's a pre block ahead, handle it, skip it and return t. @@ -441,7 +443,8 @@ font-locking." ;; To identify code-blocks we need to be at start of line. (goto-char beg) (when (markdown-match-pre-blocks (line-end-position)) - (sx-babel--make-pre-button beg (point))))) + (sx-babel--make-pre-button beg (point)) + t))) (defun sx-question-mode--skip-comments () "If there's an html comment ahead, skip it and return t." -- cgit v1.2.3 From 3be41a8af1f98abfc8b2a43ce09c08586bee1075 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 18:17:53 -0200 Subject: Fix score checking --- sx-question-print.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index 6131684..d0802d6 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -290,10 +290,11 @@ The comment is indented, filled, and then printed according to (sx--wrap-in-overlay (list 'sx--data-here comment-data) (sx-assoc-let comment-data - (insert - (if (> .score 0) (number-to-string .score) "") - (if (eq .upvoted t) "^" "") - (if (or (> .score 0) .upvoted) " " "") + (when (> .score 0) + (insert (number-to-string .score) + (if (eq .upvoted t) "^" "") + " ")) + (insert (format sx-question-mode-comments-format (sx-question-mode--propertize-display-name .owner) -- cgit v1.2.3 From af62c86d0c2032949644941eaf524ead4ffa0adf Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 18:49:38 -0200 Subject: Test sx-assoc-let not let-alist Since let-alist is no longer defined here. It makes no sense to test its contents. --- test/tests.el | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/test/tests.el b/test/tests.el index 8969c37..66d8d88 100644 --- a/test/tests.el +++ b/test/tests.el @@ -123,20 +123,14 @@ (should (equal '(progn (require 'let-alist) (sx--ensure-site data) - (let ((.test (cdr (assq 'test data)))) - .test)) - (macroexpand-all - '(sx-assoc-let data - .test)))) + (let-alist data .test)) + (macroexpand '(sx-assoc-let data .test)))) (should (equal '(progn (require 'let-alist) (sx--ensure-site data) - (let ((.test-one (cdr (assq 'test-one data))) - (.test-two (cdr (assq 'test-two data)))) - (cons .test-one .test-two))) - (macroexpand-all - '(sx-assoc-let data - (cons .test-one .test-two)))))) + (let-alist data (cons .test-one .test-two))) + (macroexpand + '(sx-assoc-let data (cons .test-one .test-two)))))) (ert-deftest sx--user-@name () "Tests macro expansion for `sx-assoc-let'" -- cgit v1.2.3 From 767570b1d5d6511726609265ee61c26aa9fe0332 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 22:31:43 -0200 Subject: The separator above body is no longer a section I got tired of having to hit `n` twice to move past the body. --- sx-question-print.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index 9f37b10..f11449b 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -238,8 +238,7 @@ DATA can represent a question or an answer." ;; Body (insert "\n" (propertize sx-question-mode-separator - 'face 'sx-question-mode-header - 'sx-question-mode--section 4)) + 'face 'sx-question-mode-header)) (sx--wrap-in-overlay '(face sx-question-mode-content-face) (insert "\n" -- cgit v1.2.3 From 486b950836a99b8eff847cecadd861426467284b Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Thu, 18 Dec 2014 22:37:42 -0200 Subject: Properly skip invisible sections. --- sx-question-mode.el | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index b376616..8fe6dfb 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -120,10 +120,8 @@ Prefix argument N moves N sections down or up." ;; 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-property-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))))) + (unless (get-char-property (point) 'invisible) + (cl-decf count)))) (when (equal (selected-window) (get-buffer-window)) (when sx-question-mode-recenter-line (let ((ov (sx-question-mode--section-overlays-at (line-end-position)))) -- cgit v1.2.3 From f80f38e87c86070433a9a8c2a6fbf75b490b6875 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sat, 20 Dec 2014 02:00:24 -0500 Subject: Hotfix undefined variable --- sx.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sx.el b/sx.el index 4ad0fd5..6f4e7c7 100644 --- a/sx.el +++ b/sx.el @@ -107,7 +107,8 @@ is intentionally skipped." (defun sx-user-error (format-string &rest args) "Like `user-error', but prepend FORMAT-STRING with \"[sx]\". See `format'." - (signal 'user-error (list (apply #'format (concat "[sx] " format) args)))) + (signal 'user-error + (list (apply #'format (concat "[sx] " format-string) args)))) (defun sx-message (format-string &rest args) "Display FORMAT-STRING as a message with ARGS. -- cgit v1.2.3 From d7ebf1515548e3bb01612fabba5de5d68a783f23 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sat, 20 Dec 2014 03:13:47 -0500 Subject: Hotfix require subr-x For `string-trim-right' --- sx-babel.el | 1 + 1 file changed, 1 insertion(+) diff --git a/sx-babel.el b/sx-babel.el index 24e56c2..7346f99 100644 --- a/sx-babel.el +++ b/sx-babel.el @@ -26,6 +26,7 @@ ;;; Code: +(require 'subr-x) (require 'sx-button) (defvar sx-babel-major-mode-alist -- cgit v1.2.3 From b15813ac1a681ef075c207f1a90f3cb167653d83 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 20 Dec 2014 15:08:43 -0200 Subject: Hotfix switch-tab --- sx-question-list.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-question-list.el b/sx-question-list.el index 94b5be4..6537d2b 100644 --- a/sx-question-list.el +++ b/sx-question-list.el @@ -315,7 +315,7 @@ into consideration. ("K" sx-question-list-previous-far) ("g" sx-question-list-refresh) (":" sx-question-list-switch-site) - ("t" sx-question-list-switch-tab) + ("t" sx-tab-switch) ("a" sx-ask) ("v" sx-visit) ("u" sx-toggle-upvote) -- cgit v1.2.3 From 30eeaaad19858edb4543d60772b93adb7e7736be Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 20 Dec 2014 15:10:08 -0200 Subject: Hotfix subr-x didn't exist before 24.3 --- sx-babel.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sx-babel.el b/sx-babel.el index 7346f99..b30a044 100644 --- a/sx-babel.el +++ b/sx-babel.el @@ -26,7 +26,6 @@ ;;; Code: -(require 'subr-x) (require 'sx-button) (defvar sx-babel-major-mode-alist @@ -58,7 +57,7 @@ on a match.") (setq indent (sx-babel--unindent-buffer)) (goto-char (point-min)) (setq mode (sx-babel--determine-major-mode)) - (setq copy (string-trim-right (buffer-string))) + (setq copy (replace-regexp-in-string "[[:space:]]+\\'" "" (buffer-string))) (when mode (delay-mode-hooks (funcall mode))) (font-lock-fontify-region (point-min) (point-max)) -- cgit v1.2.3 From 139c4d81545cd8428de7864294c754fbe8f3267e Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 20 Dec 2014 15:29:30 -0200 Subject: Displaying questions uses pop-to-buffer instead of switch by default Fixes #153 See variable `sx-question-mode-display-buffer-function' --- sx-question-mode.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sx-question-mode.el b/sx-question-mode.el index 8fe6dfb..8d06078 100644 --- a/sx-question-mode.el +++ b/sx-question-mode.el @@ -30,11 +30,13 @@ ;;; Displaying a question -(defcustom sx-question-mode-display-buffer-function #'switch-to-buffer +(defcustom sx-question-mode-display-buffer-function #'pop-to-buffer "Function used to display the question buffer. Called, for instance, when hitting \\`\\[sx-question-list-display-question]' on an entry in the question list. -This is not used when navigating the question list with `\\[sx-question-list-view-next]." +This is not used when navigating the question list with `\\[sx-question-list-view-next]. + +Common values for this variable are `pop-to-buffer' and `switch-to-buffer'." :type 'function :group 'sx-question-mode) -- cgit v1.2.3 From 5ee595c3740c2b24274c9405e4bed4a5ae81953f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 20 Dec 2014 15:39:58 -0200 Subject: Indentation was off by one --- sx-request.el | 68 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/sx-request.el b/sx-request.el index 2d894f0..389a471 100644 --- a/sx-request.el +++ b/sx-request.el @@ -121,40 +121,40 @@ the main content of the response is returned." (url-request-extra-headers '(("Content-Type" . "application/x-www-form-urlencoded"))) (response-buffer (url-retrieve-synchronously request-url))) - (if (not response-buffer) - (error "Something went wrong in `url-retrieve-synchronously'") - (with-current-buffer response-buffer - (let* ((data (progn - ;; @TODO use url-http-end-of-headers - (goto-char (point-min)) - (if (not (search-forward "\n\n" nil t)) - (error "Headers missing; response corrupt") - (delete-region (point-min) (point)) - (buffer-string)))) - (response-zipped-p (sx-encoding-gzipped-p data)) - (data (if (not response-zipped-p) data - (shell-command-on-region - (point-min) (point-max) - sx-request-unzip-program - nil t) - (buffer-string))) - ;; @TODO should use `condition-case' here -- set - ;; RESPONSE to 'corrupt or something - (response (with-demoted-errors "`json' error: %S" - (json-read-from-string data)))) - (when (and (not response) (string-equal data "{}")) - (sx-message "Unable to parse response: %S" response) - (error "Response could not be read by `json-read-from-string'")) - ;; If we get here, the response is a valid data structure - (sx-assoc-let response - (when .error_id - (error "Request failed: (%s) [%i %s] %S" - .method .error_id .error_name .error_message)) - (when (< (setq sx-request-remaining-api-requests .quota_remaining) - sx-request-remaining-api-requests-message-threshold) - (sx-message "%d API requests reamining" - sx-request-remaining-api-requests)) - (sx-encoding-clean-content-deep .items))))))) + (if (not response-buffer) + (error "Something went wrong in `url-retrieve-synchronously'") + (with-current-buffer response-buffer + (let* ((data (progn + ;; @TODO use url-http-end-of-headers + (goto-char (point-min)) + (if (not (search-forward "\n\n" nil t)) + (error "Headers missing; response corrupt") + (delete-region (point-min) (point)) + (buffer-string)))) + (response-zipped-p (sx-encoding-gzipped-p data)) + (data (if (not response-zipped-p) data + (shell-command-on-region + (point-min) (point-max) + sx-request-unzip-program + nil t) + (buffer-string))) + ;; @TODO should use `condition-case' here -- set + ;; RESPONSE to 'corrupt or something + (response (with-demoted-errors "`json' error: %S" + (json-read-from-string data)))) + (when (and (not response) (string-equal data "{}")) + (sx-message "Unable to parse response: %S" response) + (error "Response could not be read by `json-read-from-string'")) + ;; If we get here, the response is a valid data structure + (sx-assoc-let response + (when .error_id + (error "Request failed: (%s) [%i %s] %S" + .method .error_id .error_name .error_message)) + (when (< (setq sx-request-remaining-api-requests .quota_remaining) + sx-request-remaining-api-requests-message-threshold) + (sx-message "%d API requests reamining" + sx-request-remaining-api-requests)) + (sx-encoding-clean-content-deep .items))))))) (defun sx-request-fallback (_method &optional _args _request-method) "Fallback method when authentication is not available. -- cgit v1.2.3 From 0c380f42cf56e64d3b4446b6168fa8028760d8c5 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 20 Dec 2014 15:47:56 -0200 Subject: Prefer zlib-decompress-region when available. Fix #157 --- sx-request.el | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sx-request.el b/sx-request.el index 389a471..9f2ecdb 100644 --- a/sx-request.el +++ b/sx-request.el @@ -70,7 +70,11 @@ (defcustom sx-request-unzip-program "gunzip" "Program used to unzip the response if it is compressed. -This program must accept compressed data on standard input." +This program must accept compressed data on standard input. + +This is only used (and necessary) if the function +`zlib-decompress-region' is not defined, which is the case for +Emacs versions < 24.4." :group 'sx :type 'string) @@ -133,10 +137,11 @@ the main content of the response is returned." (buffer-string)))) (response-zipped-p (sx-encoding-gzipped-p data)) (data (if (not response-zipped-p) data - (shell-command-on-region - (point-min) (point-max) - sx-request-unzip-program - nil t) + (if (fboundp 'zlib-decompress-region) + (zlib-decompress-region (point-min) (point-max)) + (shell-command-on-region + (point-min) (point-max) + sx-request-unzip-program nil t)) (buffer-string))) ;; @TODO should use `condition-case' here -- set ;; RESPONSE to 'corrupt or something -- cgit v1.2.3 From a56f77ba8d990e0bf0333b5b93b9cdd1bf55b33f Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 20 Dec 2014 16:10:01 -0200 Subject: Handle utf-8 encoding the right way. --- sx-question-print.el | 3 --- sx-request.el | 19 ++++++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index 2f07132..b58a84a 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -182,9 +182,6 @@ QUESTION must be a data structure returned by `json-read'." (mapc #'sx-question-mode--print-section .answers)) (insert "\n\n ") (insert-text-button "Write an Answer" :type 'sx-button-answer) - ;; Display weird chars correctly - (set-buffer-multibyte nil) - (set-buffer-multibyte t) ;; Go up (goto-char (point-min)) (sx-question-mode-next-section)) diff --git a/sx-request.el b/sx-request.el index 9f2ecdb..1031ea7 100644 --- a/sx-request.el +++ b/sx-request.el @@ -136,13 +136,18 @@ the main content of the response is returned." (delete-region (point-min) (point)) (buffer-string)))) (response-zipped-p (sx-encoding-gzipped-p data)) - (data (if (not response-zipped-p) data - (if (fboundp 'zlib-decompress-region) - (zlib-decompress-region (point-min) (point-max)) - (shell-command-on-region - (point-min) (point-max) - sx-request-unzip-program nil t)) - (buffer-string))) + (data + ;; Turn string of bytes into string of characters. See + ;; http://emacs.stackexchange.com/q/4100/50 + (decode-coding-string + (if (not response-zipped-p) data + (if (fboundp 'zlib-decompress-region) + (zlib-decompress-region (point-min) (point-max)) + (shell-command-on-region + (point-min) (point-max) + sx-request-unzip-program nil t)) + (buffer-string)) + 'utf-8 'nocopy)) ;; @TODO should use `condition-case' here -- set ;; RESPONSE to 'corrupt or something (response (with-demoted-errors "`json' error: %S" -- cgit v1.2.3 From 29946095d687a44224e30b21198b2c872233e8d3 Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 20 Dec 2014 16:19:35 -0200 Subject: Hotfix, don't barf on links we don't understand. These are usually not links. See the end of http://emacs.stackexchange.com/q/3727/50 --- sx-question-print.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index 2f07132..fe1e2b0 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -384,10 +384,11 @@ E.g.: (match-string-no-properties 3) text))) (full-text (match-string-no-properties 0))) - (replace-match "") - (sx-question-mode--insert-link - (if sx-question-mode-pretty-links text full-text) - url))))) + (when (stringp url) + (replace-match "") + (sx-question-mode--insert-link + (if sx-question-mode-pretty-links text full-text) + url)))))) (defun sx-question-mode--insert-link (text url) "Return a link propertized version of string TEXT. -- cgit v1.2.3 From e0f4f8e85e818f718dd8b04deed1b5d4c50541df Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sat, 20 Dec 2014 16:53:20 -0200 Subject: Hot fix #171. Check if .score is a number --- sx-question-print.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sx-question-print.el b/sx-question-print.el index fe1e2b0..98d3308 100644 --- a/sx-question-print.el +++ b/sx-question-print.el @@ -289,11 +289,11 @@ The comment is indented, filled, and then printed according to (sx--wrap-in-overlay (list 'sx--data-here comment-data) (sx-assoc-let comment-data - (when (> .score 0) + (when (and (numberp .score) (> .score 0)) (insert (number-to-string .score) (if (eq .upvoted t) "^" "") " ")) - (insert + (insert (format sx-question-mode-comments-format (sx-question-mode--propertize-display-name .owner) -- cgit v1.2.3 From 845cd697b1326f8c270dd2c76a6c120d3196428b Mon Sep 17 00:00:00 2001 From: Artur Malabarba Date: Sun, 21 Dec 2014 21:42:01 -0200 Subject: Add screenshot --- README.org | 8 +++++--- list-and-question.png | Bin 0 -> 450796 bytes 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 list-and-question.png diff --git a/README.org b/README.org index db47904..b9888a7 100644 --- a/README.org +++ b/README.org @@ -4,9 +4,11 @@ [[https://gitter.im/vermiculus/sx.el?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge][https://badges.gitter.im/Join Chat.svg]] [[https://www.waffle.io/vermiculus/sx.el][https://badge.waffle.io/vermiculus/sx.el.svg]] -SX will be a full featured Stack Exchange mode for GNU Emacs 24+. Using the -official API, we aim to create a more versatile experience for the Stack -Exchange network within Emacs itself. +SX is a full featured Stack Exchange mode for GNU Emacs 24+. Using the official +API, it provides a versatile experience for the Stack Exchange network within +Emacs itself. + +[[file:list-and-question.png]] * Features ** Viewing Questions diff --git a/list-and-question.png b/list-and-question.png new file mode 100644 index 0000000..9e89fec Binary files /dev/null and b/list-and-question.png differ -- cgit v1.2.3