aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sx-babel.el2
-rw-r--r--sx-button.el31
-rw-r--r--sx-filter.el4
-rw-r--r--sx-interaction.el21
-rw-r--r--sx-question-list.el21
-rw-r--r--sx-question-print.el51
-rw-r--r--sx-question.el2
-rw-r--r--sx-search.el2
-rw-r--r--sx-switchto.el2
-rw-r--r--sx-user.el203
-rw-r--r--sx.el71
-rw-r--r--sx.org79
-rw-r--r--test/test-printing.el67
13 files changed, 410 insertions, 146 deletions
diff --git a/sx-babel.el b/sx-babel.el
index e115817..7f84fe0 100644
--- a/sx-babel.el
+++ b/sx-babel.el
@@ -22,7 +22,7 @@
;; This file contains functions and a variable for font-locking the
;; content of markdown pre blocks according to their language. The
;; main configuration point, for both the user and the developer is
-;; the varuable `sx-babel-major-mode-alist', which see.
+;; the variable `sx-babel-major-mode-alist', which see.
;;; Code:
diff --git a/sx-button.el b/sx-button.el
index 4c0666b..5a2f052 100644
--- a/sx-button.el
+++ b/sx-button.el
@@ -23,7 +23,7 @@
;; buttons, see:
;; http://www.gnu.org/software/emacs/manual/html_node/elisp/Buttons.html
;;
-;; Most interactible parts of the SX buffers are buttons. Wherever you
+;; Most interactive parts of the SX buffers are buttons. Wherever you
;; are, you can always cycle through all buttons by hitting `TAB',
;; that should help identify what's a button in each buffer.
;;
@@ -34,7 +34,7 @@
;;
;; Buttons can then be inserted in their respective files using
;; `insert-text-button'. Give it the string, the `:type' you defined,
-;; and any aditional properties that can only be determined at
+;; and any additional properties that can only be determined at
;; creation. Existing text can be transformed into a button with
;; `make-text-button' instead.
@@ -104,23 +104,29 @@ usually part of a code-block."
;;; Help-echo definitions
-(defvar sx-button--help-echo
+(defconst sx-button--help-echo
(concat "mouse-1, RET"
(propertize ": %s -- " 'face 'minibuffer-prompt)
"w"
(propertize ": copy %s" 'face 'minibuffer-prompt))
"Base help-echo on which others can be written.")
-(defvar sx-button--question-title-help-echo
+(defconst sx-button--user-help-echo
(format sx-button--help-echo
- (propertize "hide content" 'face 'minibuffer-prompt)
- (propertize "link" 'face 'minibuffer-prompt))
+ "visit user page"
+ "link")
+ "Help echoed in the minibuffer when point is on a user.")
+
+(defconst sx-button--question-title-help-echo
+ (format sx-button--help-echo
+ "hide content"
+ "link")
"Help echoed in the minibuffer when point is on a section.")
-(defvar sx-button--link-help-echo
+(defconst sx-button--link-help-echo
(format sx-button--help-echo
- (propertize "visit %s" 'face 'minibuffer-prompt)
- (propertize "URL" 'face 'minibuffer-prompt))
+ "visit %s"
+ "URL")
"Help echoed in the minibuffer when point is on a section.")
@@ -145,6 +151,13 @@ usually part of a code-block."
'action #'sx-button-follow-link
:supertype 'sx-button)
+(define-button-type 'sx-button-user
+ 'action #'sx-button-follow-link
+ 'help-echo sx-button--user-help-echo
+ ;; We use different faces on different parts of the user button.
+ 'face 'sx-user-name
+ :supertype 'sx-button)
+
(define-button-type 'sx-button-comment
'help-echo (concat "mouse-1, RET"
(propertize ": write a comment"
diff --git a/sx-filter.el b/sx-filter.el
index a3f6861..af3717f 100644
--- a/sx-filter.el
+++ b/sx-filter.el
@@ -128,8 +128,12 @@ return the compiled filter."
question_id
share_link)
(user display_name
+ link
+ accept_rate
reputation)
(shallow_user display_name
+ link
+ accept_rate
reputation)
(comment owner
body_markdown
diff --git a/sx-interaction.el b/sx-interaction.el
index 4d71c17..97c68b6 100644
--- a/sx-interaction.el
+++ b/sx-interaction.el
@@ -272,7 +272,7 @@ TEXT is a string. Interactively, it is read from the minibufer."
(setq text (read-string
"Comment text: "
(when .comment_id
- (concat (sx--user-@name .owner) " "))))
+ (substring-no-properties (sx-user--format "%@ " .owner)))))
(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.
@@ -291,10 +291,8 @@ TEXT is a string. Interactively, it is read from the minibufer."
;; The api returns the new DATA.
(when (> (length result) 0)
(sx--add-comment-to-object
- (elt result 0)
- (if .post_id
- (sx--get-post .post_type .site_par .post_id)
- data))
+ (sx--ensure-owner-in-object (list (cons 'display_name "(You)")) (elt result 0))
+ (if .post_id (sx--get-post .post_type .site_par .post_id) data))
;; Display the changes in `data'.
(sx--maybe-update-display)))))
@@ -344,7 +342,15 @@ OBJECT can be a question or an answer."
(list comment)))))
;; No previous comments, add it manually.
(setcdr object (cons (car object) (cdr object)))
- (setcar object `(comments . [,comment])))))
+ (setcar object `(comments . [,comment]))))
+ object)
+
+(defun sx--ensure-owner-in-object (owner object)
+ "Add `owner' property with value OWNER to OBJECT."
+ (unless (cdr-safe (assq 'owner object))
+ (setcdr object (cons (car object) (cdr object)))
+ (setcar object `(owner . ,owner)))
+ object)
;;; Editing
@@ -439,7 +445,8 @@ context at point. "
(append (cdr cell) (list answer))))
;; No previous comments, add it manually.
(setcdr question (cons (car question) (cdr question)))
- (setcar question `(answers . [,answer])))))
+ (setcar question `(answers . [,answer])))
+ question))
(provide 'sx-interaction)
;;; sx-interaction.el ends here
diff --git a/sx-question-list.el b/sx-question-list.el
index 1b7fe5a..884f994 100644
--- a/sx-question-list.el
+++ b/sx-question-list.el
@@ -111,16 +111,6 @@
""
:group 'sx-question-list-faces)
-(defface sx-question-list-reputation
- '((t :inherit sx-question-list-date))
- ""
- :group 'sx-question-list-faces)
-
-(defface sx-question-list-user
- '((t :inherit font-lock-builtin-face))
- ""
- :group 'sx-question-list-faces)
-
;;; Backend variables
(defvar sx-question-list--print-function #'sx-question-list--print-info
@@ -138,8 +128,9 @@ change `tabulated-list-format' accordingly.")
This is the default printer used by `sx-question-list'. It
assumes QUESTION-DATA is an alist containing (at least) the
elements:
- `site', `score', `upvoted', `answer_count', `title',
- `last_activity_date', `tags', `uestion_id'.
+ `question_id', `site_par', `score', `upvoted', `answer_count',
+ `title', `bounty_amount', `bounty_amount', `bounty_amount',
+ `last_activity_date', `tags', `owner'.
Also see `sx-question-list-refresh'."
(sx-assoc-let question-data
@@ -182,11 +173,7 @@ Also see `sx-question-list-refresh'."
(propertize (format "%-40s" (mapconcat #'sx-question--tag-format .tags " "))
'face 'sx-question-list-tags)
" "
- (let-alist .owner
- (format "%15s %5s"
- (propertize (or .display_name "") 'face 'sx-question-list-user)
- (propertize (number-to-string (or .reputation 0))
- 'face 'sx-question-list-reputation)))
+ (sx-user--format "%15d %4r" .owner)
(propertize " " 'display "\n")))))))
(defvar sx-question-list--pages-so-far 0
diff --git a/sx-question-print.el b/sx-question-print.el
index 737844a..9a51efb 100644
--- a/sx-question-print.el
+++ b/sx-question-print.el
@@ -26,6 +26,7 @@
(require 'sx)
(require 'sx-question)
(require 'sx-babel)
+(require 'sx-user)
(defgroup sx-question-mode nil
"Customization group for sx-question-mode."
@@ -33,20 +34,15 @@
:tag "SX Question Mode"
:group 'sx)
-(defgroup sx-question-mode-faces nil
- "Customization group for the faces of `sx-question-mode'."
+(defgroup sx-question-mode-faces '((sx-user custom-group))
+ "Customization group for the faces of `sx-question-mode'.
+Some faces of this mode might be defined in the `sx-user' group."
:prefix "sx-question-mode-"
:tag "SX Question Mode Faces"
:group 'sx-question-mode)
;;; Faces and Variables
-(defcustom sx-question-mode-deleted-user
- '((display_name . "(deleted user)"))
- "The structure used to represent a deleted account."
- :type '(alist :options ((display_name string)))
- :group 'sx-question-mode)
-
(defface sx-question-mode-header
'((t :inherit font-lock-variable-name-face))
"Face used on the question headers in the question buffer."
@@ -67,13 +63,9 @@
:type 'string
:group 'sx-question-mode)
-(defface sx-question-mode-author
- '((t :inherit font-lock-string-face))
- "Face used on the question author in the question buffer."
- :group 'sx-question-mode-faces)
-
-(defcustom sx-question-mode-header-author "\nAuthor: "
- "String used before the question author at the header."
+(defcustom sx-question-mode-header-author-format "\nAuthor: %d %r"
+ "String used to display the question author at the header.
+% constructs have special meaning here. See `sx-user--format'."
:type 'string
:group 'sx-question-mode)
@@ -92,11 +84,6 @@
"Face used on the question tags in the question buffer."
:group 'sx-question-mode-faces)
-(defface sx-question-mode-author
- '((t :inherit font-lock-variable-name-face))
- "Face used for author names in the question buffer."
- :group 'sx-question-mode-faces)
-
(defface sx-question-mode-score
'((t))
"Face used for the score in the question buffer."
@@ -209,11 +196,13 @@ DATA can represent a question or an answer."
;; Sections can be hidden with overlays
(sx--wrap-in-overlay
'(sx-question-mode--section-content t)
+ ;; Author
+ (insert
+ (sx-user--format
+ (propertize sx-question-mode-header-author-format
+ 'face 'sx-question-mode-header)
+ .owner))
(sx-question-mode--insert-header
- ;; Author
- sx-question-mode-header-author
- (sx-question-mode--propertize-display-name .owner)
- 'sx-question-mode-author
;; Date
sx-question-mode-header-date
(concat
@@ -221,8 +210,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--propertize-display-name
- (or .last_editor sx-question-mode-deleted-user)))))
+ (sx-user--format "%d" .last_editor))))
'sx-question-mode-date)
(sx-question-mode--insert-header
sx-question-mode-header-score
@@ -278,12 +266,6 @@ DATA can represent a question or an answer."
:type 'sx-button-comment)
(insert "\n")))))
-(defun sx-question-mode--propertize-display-name (author)
- "Return display_name of AUTHOR with `sx-question-mode-author' face."
- (sx-assoc-let author
- (propertize (or .display_name "??")
- 'face 'sx-question-mode-author)))
-
(defun sx-question-mode--print-comment (comment-data)
"Print the comment described by alist COMMENT-DATA.
The comment is indented, filled, and then printed according to
@@ -296,9 +278,8 @@ The comment is indented, filled, and then printed according to
(if (eq .upvoted t) "^" "")
" "))
(insert
- (format
- sx-question-mode-comments-format
- (sx-question-mode--propertize-display-name .owner)
+ (format sx-question-mode-comments-format
+ (sx-user--format "%d" .owner)
(substring
;; We fill with three spaces at the start, so the comment is
;; slightly indented.
diff --git a/sx-question.el b/sx-question.el
index b9fc78a..1adbc24 100644
--- a/sx-question.el
+++ b/sx-question.el
@@ -19,7 +19,7 @@
;;; Commentary:
-;; Thie file provides an API for retrieving questions and defines
+;; This file provides an API for retrieving questions and defines
;; additional logic for marking questions as read or hidden.
diff --git a/sx-search.el b/sx-search.el
index fa08e56..aefd12e 100644
--- a/sx-search.el
+++ b/sx-search.el
@@ -19,7 +19,7 @@
;;; Commentary:
-;; Implements sarch functionality. The basic function is
+;; Implements search functionality. The basic function is
;; `sx-search-get-questions', which returns an array of questions
;; according to a search term.
;;
diff --git a/sx-switchto.el b/sx-switchto.el
index ed83360..6a195e0 100644
--- a/sx-switchto.el
+++ b/sx-switchto.el
@@ -33,7 +33,7 @@
(mapc (lambda (x) (define-key sx-switchto-map (car x) (cadr x)))
'(
- ;; These immitate the site's G hotkey.
+ ;; These imitate the site's G hotkey.
("a" sx-ask)
("h" sx-tab-frontpage)
("m" sx-tab-meta-or-main)
diff --git a/sx-user.el b/sx-user.el
new file mode 100644
index 0000000..c0f3a78
--- /dev/null
+++ b/sx-user.el
@@ -0,0 +1,203 @@
+;;; sx-user.el --- handling and printing user information -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014 Artur Malabarba
+
+;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
+
+;; 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 <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+
+;;; Code:
+(require 'sx)
+(require 'sx-button)
+
+(defgroup sx-user nil
+ "How users are displayed by SX."
+ :prefix "sx-user-"
+ :tag "SX User"
+ :group 'sx)
+
+(defcustom sx-question-mode-fallback-user
+ '(
+ (about_me . "")
+ (accept_rate . -1)
+ (account_id . -1)
+ (age . -1)
+ (answer_count . -1)
+ (badge_counts . ((bronze . -1) (silver . -1) (gold . -1)))
+ (creation_date . -1)
+ (display_name . "(unknown user)")
+ (down_vote_count . -1)
+ (is_employee . :json-false)
+ (last_access_date . -1)
+ (last_modified_date . -1)
+ (link . "")
+ (location . "")
+ (profile_image . ":(")
+ (question_count . -1)
+ (reputation . -1)
+ (reputation_change_day . -1)
+ (reputation_change_month . -1)
+ (reputation_change_quarter . -1)
+ (reputation_change_week . -1)
+ (reputation_change_year . -1)
+ (timed_penalty_date . -1)
+ (up_vote_count . -1)
+ (user_id . -1)
+ (user_type . does_not_exist)
+ (view_count . -1)
+ (website_url . "")
+ )
+ "The structure used to represent missing user information.
+NOOTE: SX relies on this variable containing all necessary user
+information. You may edit any of its fields, but you'll run into
+errors if you remove them."
+ :type '(alist :options ((about_me string)
+ (accept_rate integer)
+ (account_id integer)
+ (age integer)
+ (answer_count integer)
+ (badge_counts alist)
+ (creation_date integer)
+ (display_name string)
+ (down_vote_count integer)
+ (is_employee boolean)
+ (last_access_date integer)
+ (last_modified_date integer)
+ (link string)
+ (location string)
+ (profile_image string)
+ (question_count integer)
+ (reputation integer)
+ (reputation_change_day integer)
+ (reputation_change_month integer)
+ (reputation_change_quarter integer)
+ (reputation_change_week integer)
+ (reputation_change_year integer)
+ (timed_penalty_date integer)
+ (up_vote_count integer)
+ (user_id integer)
+ (user_type symbol)
+ (view_count integer)
+ (website_url string)))
+ :group 'sx-user)
+
+
+;;; Text properties
+(defface sx-user-name
+ '((t :inherit font-lock-builtin-face))
+ "Face used for user names."
+ :group 'sx-user)
+
+(defface sx-user-reputation
+ '((t :inherit font-lock-comment-face))
+ "Face used for user reputations."
+ :group 'sx-user)
+
+(defface sx-user-accept-rate
+ '((t))
+ "Face used for user accept-rates."
+ :group 'sx-user)
+
+(defvar sx-user--format-property-alist
+ `((?d button ,(list t) category ,(button-category-symbol 'sx-button-user))
+ (?n button ,(list t) category ,(button-category-symbol 'sx-button-user))
+ (?@ button ,(list t) category ,(button-category-symbol 'sx-button-user))
+ (?r face sx-user-reputation)
+ (?a face sx-user-accept-rate))
+ "Alist relating % constructs with text properties.
+See `sx-user--format'.")
+
+
+;;; Formatting function
+(defun sx-user--format (format-string user)
+ "Use FORMAT-STRING to format the user object USER.
+The value is a copy of FORMAT-STRING, but with certain constructs
+replaced by text that describes the specified USER:
+
+%d is the display name.
+%@ is the display name in a format suitable for @mentions.
+%l is the link to the profile.
+%r is the reputation.
+%a is the accept rate.
+
+The string replaced in each of these construct is also given the
+text-properties specified in `sx-user--format-property-alist'.
+Specially, %d and %@ are turned into buttons with the
+`sx-button-user' category."
+ (sx-assoc-let (append user sx-question-mode-fallback-user)
+ (let* ((text (sx-format-replacements
+ format-string
+ `((?d . ,\.display_name)
+ (?n . ,\.display_name)
+ (?l . ,\.link)
+ (?r . ,\.reputation)
+ (?a . ,\.accept_rate)
+ (?@ . ,(when (string-match "%@" format-string)
+ (sx-user--@name .display_name)))
+ )
+ sx-user--format-property-alist)))
+ (if (< 0 (string-width .link))
+ (propertize text
+ ;; For visiting and stuff.
+ 'sx-button-url .link
+ 'sx-button-copy .link)
+ text))))
+
+
+;;; @name conversion
+(defconst sx-user--ascii-replacement-list
+ '(("[:space:]" . "")
+ ("àåáâäãåą" . "a")
+ ("èéêëę" . "e")
+ ("ìíîïı" . "i")
+ ("òóôõöøőð" . "o")
+ ("ùúûüŭů" . "u")
+ ("çćčĉ" . "c")
+ ("żźž" . "z")
+ ("śşšŝ" . "s")
+ ("ñń" . "n")
+ ("ýÿ" . "y")
+ ("ğĝ" . "g")
+ ("ř" . "r")
+ ("ł" . "l")
+ ("đ" . "d")
+ ("ß" . "ss")
+ ("Þ" . "th")
+ ("ĥ" . "h")
+ ("ĵ" . "j")
+ ("^[:ascii:]" . ""))
+ "List of replacements to use for non-ascii characters.
+Used to convert user names into @mentions.")
+
+(defun sx-user--@name (display-name)
+ "Convert DISPLAY-NAME into an @mention.
+In order to correctly @mention the user, all whitespace is
+removed from DISPLAY-NAME and a series of unicode conversions are
+performed before it is returned.
+See `sx-user--ascii-replacement-list'.
+
+If all you need is the @name, this is very slightly faster than
+using `sx-user--format', but it doesn't do any sanity checking."
+ (concat "@" (sx--recursive-replace
+ sx-user--ascii-replacement-list display-name)))
+
+(provide 'sx-user)
+;;; sx-user.el ends here
+
+;; Local Variables:
+;; indent-tabs-mode: nil
+;; End:
diff --git a/sx.el b/sx.el
index e080271..36ecfca 100644
--- a/sx.el
+++ b/sx.el
@@ -294,39 +294,6 @@ Return the result of BODY."
(push ov sx--overlays))
result))
-(defconst sx--ascii-replacement-list
- '(("[:space:]" . "")
- ("àåáâäãåą" . "a")
- ("èéêëę" . "e")
- ("ìíîïı" . "i")
- ("òóôõöøőð" . "o")
- ("ùúûüŭů" . "u")
- ("çćčĉ" . "c")
- ("żźž" . "z")
- ("śşšŝ" . "s")
- ("ñń" . "n")
- ("ýÿ" . "y")
- ("ğĝ" . "g")
- ("ř" . "r")
- ("ł" . "l")
- ("đ" . "d")
- ("ß" . "ss")
- ("Þ" . "th")
- ("ĥ" . "h")
- ("ĵ" . "j")
- ("^[:ascii:]" . ""))
- "List of replacements to use for non-ascii characters.
-Used to convert user names into @mentions.")
-
-(defun sx--user-@name (user)
- "Get the `display_name' of USER prepended with @.
-In order to correctly @mention the user, all whitespace is
-removed from the display name before it is returned."
- (sx-assoc-let user
- (when (stringp .display_name)
- (concat "@" (sx--recursive-replace
- sx--ascii-replacement-list .display_name)))))
-
(defun sx--recursive-replace (alist string)
"Replace each car of ALIST with its cdr in STRING."
(if alist
@@ -337,6 +304,44 @@ removed from the display name before it is returned."
(format "[%s]" (car kar)) (cdr kar) string)))
string))
+(defun sx-format-replacements (format alist &optional property-alist)
+ "Use FORMAT-STRING to format the values in ALIST.
+ALIST is a list with elements of the form (CHAR . STRING).
+The value is a copy of FORMAT-STRING, but with certain constructs
+replaced by text as given by ALIST.
+
+The construct is a `%' character followed by any other character.
+The replacement is the STRING corresponding to CHAR in ALIST. In
+addition, if CHAR is also the car of an element in
+PROPERTY-ALIST, the cdr of that element should be a list of text
+properties which will be applied on the replacement.
+
+The %% construct is special, it is replaced with a single %, even
+if ALIST contains a different string at the ?% entry."
+ (let ((alist (cons '(?% . "%") alist)))
+ (with-temp-buffer
+ (insert format)
+ (goto-char (point-min))
+ (while (search-forward-regexp
+ (rx "%" (group-n 1 (* (any "-+ #0-9.")))) nil 'noerror)
+ (let* ((char (char-after))
+ ;; Understand flags
+ (flag (match-string 1))
+ (val (cdr-safe (assq char alist))))
+ (unless val
+ (error "Invalid format character: `%%%c'" char))
+ ;; Insert first, to preserve text properties.
+ (insert-and-inherit (format (concat "%" flag "s") val))
+ (when property-alist
+ (add-text-properties (match-end 0) (point)
+ (cdr-safe (assq char property-alist))))
+ ;; Delete the specifier body.
+ (delete-region (match-beginning 0)
+ (match-end 0))
+ ;; Delete `char-after'.
+ (delete-char 1)))
+ (buffer-string))))
+
(defcustom sx-init-hook nil
"Hook run when SX initializes.
diff --git a/sx.org b/sx.org
index 7ccb51b..e206cc2 100644
--- a/sx.org
+++ b/sx.org
@@ -97,7 +97,7 @@ Scrolling past the bottom of the list fetches more questions.
- ~sx-init-hook~ :: Run when ~sx-initialize~ is called.
- ~sx-compose-before-send-hook~ :: Run before POSTing to the API from
a buffer in ~sx-compose-mode~. If any of the functions in this
- hook, return nil, the transaction is cancelled.
+ hook, return nil, the transaction is canceled.
- ~sx-compose-after-send-functions~ :: Run after POSTing to the API
from a buffer in ~sx-compose-mode~, if the transaction was
successful.
@@ -156,44 +156,45 @@ has a descriptive header explaining its purpose. Still, to help you
find your way around, we describe below the current project
structure. This list is very loosely ordered form low to high-level.
-- ~sx.el~ - Utility functions used throughout the package. Essentially
- every file indirectly requires this one. If you're adding a function
- that's used by different parts of the package, add it to this file.
-- ~sx-time.el~ - Similar to ~sx.el~, but only contains a few
- time-related functions.
-- ~sx-filter.el~ - Handles retrieval of filters.
-- ~sx-cache.el~ - Saves and restores persistent data between sessions.
-- ~sx-button.el~ - Defines all button types used throughout the
- package. Currently used only by ~sx-question-print.el~.
-
-- ~sx-request.el~ - Requests and url manipulation. Backend used by
- ~sx-method.el~. It shouldn't be necessary to use the functions in
- this file outside ~sx-method.el~.
-- ~sx-method.el~ - Main interface for API method calls.
-
-- ~sx-favorites.el~ - Starred questions.
-- ~sx-networks.el~ - User network information.
-- ~sx-site.el~ - Browsing sites.
-- ~sx-auth.el~ - Handles user authentication.
-
-- ~sx-question.el~ - Base question logic. Holds several functions for
- retrieving questions and for processing retrieved questions. Doesn't
- do any sort of user interface, that is left for
- ~sx-question-list.el~ and ~sx-question-mode.el~.
-- ~sx-question-list.el~ - Major-mode for navigating questions list.
-- ~sx-question-mode.el~ - User interface for displaying a
- question. Creates the buffer and defines the major-mode.
-- ~sx-question-print.el~ - Populating the question buffer with
- content. Used by ~sx-question-mode.el~ to actually print the content
- of a question.
-- ~sx-babel.el~ - Font-locking code blocks printed by
- ~sx-question-print.el~ according to the language.
-
-- ~sx-compose.el~ - Major-mode for composing questions and answers.
-- ~sx-interaction.el~ - Voting, commenting, and otherwise interacting with questions.
-- ~sx-tab.el~ - Functions for viewing different tabs.
-
-- ~sx-load.el~ - Load all files of the sx package. Designed as an easy way in for users who install the package manually (since they don't have autoloads).
+- ~sx.el~ :: Utility functions used throughout the
+ package. Essentially every file indirectly requires this
+ one. If you're adding a function that's used by different
+ parts of the package, add it to this file.
+- ~sx-time.el~ :: Similar to ~sx.el~, but only contains a few
+ time-related functions.
+- ~sx-filter.el~ :: Handles retrieval of filters.
+- ~sx-cache.el~ :: Saves and restores persistent data between
+ sessions.
+- ~sx-button.el~ :: Defines all button types used throughout the
+ package. Currently used only by
+ ~sx-question-print.el~.
+- ~sx-request.el~ :: Requests and url manipulation. Back-end used by
+ ~sx-method.el~. It shouldn't be necessary to use the functions in
+ this file outside ~sx-method.el~.
+- ~sx-method.el~ :: Main interface for API method calls.
+- ~sx-favorites.el~ :: Starred questions.
+- ~sx-networks.el~ :: User network information.
+- ~sx-site.el~ :: Browsing sites.
+- ~sx-auth.el~ :: Handles user authentication.
+- ~sx-question.el~ :: Base question logic. Holds several functions for
+ retrieving questions and for processing retrieved
+ questions. Doesn't do any sort of user interface, that is left
+ for ~sx-question-list.el~ and ~sx-question-mode.el~.
+- ~sx-question-list.el~ :: Major-mode for navigating questions list.
+- ~sx-question-mode.el~ :: User interface for displaying a
+ question. Creates the buffer and defines the major-mode.
+- ~sx-question-print.el~ :: Populating the question buffer with
+ content. Used by ~sx-question-mode.el~ to actually print the
+ content of a question.
+- ~sx-babel.el~ :: Font-locking code blocks printed by
+ ~sx-question-print.el~ according to the language.
+- ~sx-compose.el~ :: Major-mode for composing questions and answers.
+- ~sx-interaction.el~ :: Voting, commenting, and otherwise interacting
+ with questions.
+- ~sx-tab.el~ :: Functions for viewing different tabs.
+- ~sx-load.el~ :: Load all files of the SX package. Designed as an
+ easy way in for users who install the package
+ manually (since they don't have autoloads).
* COMMENT Local Variables
# LocalWords: StackExchange SX inbox sx API url json inline Org
diff --git a/test/test-printing.el b/test/test-printing.el
index c477b28..7384829 100644
--- a/test/test-printing.el
+++ b/test/test-printing.el
@@ -64,13 +64,76 @@ after being run through `sx-question--tag-format'."
"Test `sx--user-@name' character substitution"
(should
(string=
- (sx--user-@name '((display_name . "ĥÞßđłřğĝýÿñńśşšŝżźžçćčĉùúûüŭůòóôõöøőðìíîïıèéêëęàåáâäãåąĵ★")))
+ (sx-user--@name "ĥÞßđłřğĝýÿñńśşšŝżźžçćčĉùúûüŭůòóôõöøőðìíîïıèéêëęàåáâäãåąĵ★")
"@hTHssdlrggyynnsssszzzccccuuuuuuooooooooiiiiieeeeeaaaaaaaaj"))
(should
(string=
- (sx--user-@name '((display_name . "ĤÞßĐŁŘĞĜÝŸÑŃŚŞŠŜŻŹŽÇĆČĈÙÚÛÜŬŮÒÓÔÕÖØŐÐÌÍÎÏıÈÉÊËĘÀÅÁÂÄÃÅĄĴ")))
+ (sx-user--@name "ĤÞßĐŁŘĞĜÝŸÑŃŚŞŠŜŻŹŽÇĆČĈÙÚÛÜŬŮÒÓÔÕÖØŐÐÌÍÎÏıÈÉÊËĘÀÅÁÂÄÃÅĄĴ")
+ "@HTHssDLRGGYYNNSSSSZZZCCCCUUUUUUOOOOOOOOIIIIiEEEEEAAAAAAAAJ"))
+ (should-error
+ (sx-user--@name 2)))
+
+(ert-deftest sx-user--format ()
+ "Test various `sx-user--format' features."
+ (let ((user
+ '((display_name . "ĥÞßđłřğĝýÿñńśşšŝżźžçćčĉùúûüŭůòóôõöøőðìíîïıèéêëęàåáâäãåąĵ★")
+ (accept_rate . 90)
+ (reputation . 10)
+ (link . "link"))))
+ (should
+ (equal (sx-user--format "%l" user) "link"))
+ (should
+ (equal
+ (sx-user--format "%@" user)
+ "@hTHssdlrggyynnsssszzzccccuuuuuuooooooooiiiiieeeeeaaaaaaaaj"))
+ (should
+ (equal
+ (sx-user--format "%@%%d%%%-30d %9r%l" user)
+ "@hTHssdlrggyynnsssszzzccccuuuuuuooooooooiiiiieeeeeaaaaaaaaj%d%ĥÞßđłřğĝýÿñńśşšŝżźžçćčĉùúûüŭůòóôõöøőðìíîïıèéêëęàåáâäãåąĵ★ 10link")))
+ (should
+ (string=
+ (sx-user--format "%@" '((display_name . "ĤÞßĐŁŘĞĜÝŸÑŃŚŞŠŜŻŹŽÇĆČĈÙÚÛÜŬŮÒÓÔÕÖØŐÐÌÍÎÏıÈÉÊËĘÀÅÁÂÄÃÅĄĴ")))
"@HTHssDLRGGYYNNSSSSZZZCCCCUUUUUUOOOOOOOOIIIIiEEEEEAAAAAAAAJ")))
+(ert-deftest sx-object-modification ()
+ "Test adding things to objects"
+ (let ((object (list (cons 'owner "me"))))
+ (should
+ (equal (sx--ensure-owner-in-object 1 object)
+ '((owner . "me"))))
+ (should
+ (equal object '((owner . "me")))))
+ (let ((object (list (cons 'not-owner "me"))))
+ (should
+ (equal (sx--ensure-owner-in-object 1 object)
+ '((owner . 1) (not-owner . "me"))))
+ (should
+ (equal object '((owner . 1) (not-owner . "me")))))
+ (let ((object (list (cons 'comments [something]))))
+ (should
+ (equal (sx--add-comment-to-object "comment" object)
+ '((comments . [something "comment"]))))
+ (should
+ (equal object '((comments . [something "comment"])))))
+ (let ((object (list (cons 'not-comments [something]))))
+ (should
+ (equal (sx--add-comment-to-object "comment" object)
+ '((comments . ["comment"]) (not-comments . [something]))))
+ (should
+ (equal object '((comments . ["comment"]) (not-comments . [something])))))
+ (let ((object (list (cons 'not-answers [something]))))
+ (should
+ (equal (sx--add-answer-to-question-object "answer" object)
+ '((answers . ["answer"]) (not-answers . [something]))))
+ (should
+ (equal object '((answers . ["answer"]) (not-answers . [something])))))
+ (let ((object (list (cons 'answers [something]))))
+ (should
+ (equal (sx--add-answer-to-question-object "answer" object)
+ '((answers . [something "answer"]))))
+ (should
+ (equal object '((answers . [something "answer"]))))))
+
(ert-deftest sx-question-mode--fill-and-fontify ()
"Check complicated questions are filled correctly."
(should