aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sx-babel.el32
-rw-r--r--sx-button.el13
-rw-r--r--sx-compose.el23
-rw-r--r--sx-interaction.el30
-rw-r--r--sx-question-mode.el6
-rw-r--r--sx-question-print.el31
-rw-r--r--sx.el3
-rw-r--r--test/tests.el16
8 files changed, 92 insertions, 62 deletions
diff --git a/sx-babel.el b/sx-babel.el
index 5544642..24e56c2 100644
--- a/sx-babel.el
+++ b/sx-babel.el
@@ -51,38 +51,44 @@ 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))
- (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)
+ (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)))
-
-(defun sx-babel--determine-and-activate-major-mode ()
- "Activate the major-mode most suitable for the current buffer."
+ (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."
(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.
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'."
diff --git a/sx-compose.el b/sx-compose.el
index 96f47f3..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 _)
@@ -146,19 +149,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 +262,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..c6f2639 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.
@@ -215,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"))
@@ -240,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'.
@@ -286,11 +299,12 @@ 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
- .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)
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))))
diff --git a/sx-question-print.el b/sx-question-print.el
index c6176fc..2f07132 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"
@@ -290,18 +289,22 @@ 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
+ (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)
- (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.
@@ -340,7 +343,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.
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.
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'"