aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Allred <code@seanallred.com>2014-11-29 09:36:59 -0500
committerSean Allred <code@seanallred.com>2014-11-29 09:36:59 -0500
commitf5ca9c6d7629817569c3bd58e5569fee88cd5f2b (patch)
treebcbbdcb2e2fdc91f9431d3b3ad605aa0a7fa0bde
parenta8e882d99a037075595260dc74fad0fb67c69d81 (diff)
parent00a187f5bb7dc08117965eae05df51d0eedac90e (diff)
Merge branch 'master' into issue-100
Conflicts: sx-question-mode.el
-rw-r--r--README.org50
-rw-r--r--sx-auth.el18
-rw-r--r--sx-cache.el8
-rw-r--r--sx-encoding.el4
-rw-r--r--sx-interaction.el3
-rw-r--r--sx-method.el3
-rw-r--r--sx-question-list.el14
-rw-r--r--sx-question-mode.el435
-rw-r--r--sx-question-print.el440
-rw-r--r--sx-question.el32
-rw-r--r--sx-request.el20
-rw-r--r--sx-site.el2
-rw-r--r--sx-tab.el11
-rw-r--r--sx-time.el6
-rw-r--r--sx.el23
-rw-r--r--sx.org2
16 files changed, 560 insertions, 511 deletions
diff --git a/README.org b/README.org
index ea115dd..df8d907 100644
--- a/README.org
+++ b/README.org
@@ -1,21 +1,25 @@
-#+Title: Stack-Mode
+#+Title: SX -- Stack Exchange for Emacs
-[[https://travis-ci.org/vermiculus/stack-mode][https://travis-ci.org/vermiculus/stack-mode.svg?branch=master]]
-[[https://gitter.im/vermiculus/stack-mode?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/stack-mode][https://badge.waffle.io/vermiculus/stack-mode.svg]]
+[[https://travis-ci.org/vermiculus/sx.el][https://travis-ci.org/vermiculus/sx.el.svg?branch=master]]
+[[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]]
-StackMode will be a full featured Stack Exchange mode for GNU Emacs 24
-and up. Using the official API, we aim to create a more versatile
-experience for the Stack Exchange network within Emacs itself.
+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.
* Features
-- ~list-questions~ ::
+- ~sx-tab-frontpage~ ::
List questions on a StackExchange site.
- Viewing Posts ::
- Use =jknp= to open questions from within ~list-questions~; use
=RET= to move focus.
- - Use =v= to open the site in your browser.
+ - Use =v= to open the object at point in your browser.
- Use =TAB= to fold questions and answers.
+ - Use =RET= to open a link at point.
+ - Use =:= to switch sites.
+ - Vote up and down with =u= and =d=.
+
** Planned
- Archiving questions for offline access
- Browsing and favoriting networks
@@ -30,37 +34,37 @@ Have a feature in mind that isn't on the list? Submit a pull request
to add it to the list! If you want to discuss it first, pop in our
Gitter chatroom (badge above) -- someone will be around shortly to
talk about it.
+
* Installation
To install the development version, follow the usual steps:
- Clone this repository
- Add this directory to your ~load-path~
-- Issue ~(require 'sx-question-list)~
+- Issue ~(require 'sx)~
This should give you access to the only entry point function at the
-moment, ~list-questions~.
+moment, ~sx-tab-frontpage~.
+
+Eventually, this package will be available on MELPA.
-Eventually, this package will at least be available on MELPA.
-Depending on community involvement, it may even be submitted to the
-official GNU ELPA.
* Contributing
Please help contribute! Doing any of the following will help us immensely:
- - [[https://github.com/vermiculus/stack-mode/issues/new][Open an issue]]
- - [[https://github.com/vermiculus/stack-mode/pulls][Submit a pull request]]
- - [[https://gitter.im/vermiculus/stack-mode][Suggest a package or library in our Chat on Gitter]]
+ - [[https://github.com/vermiculus/sx.el/issues/new][Open an issue]]
+ - [[https://github.com/vermiculus/sx.el/pulls][Submit a pull request]]
+ - [[https://gitter.im/vermiculus/sx.el][Suggest a package or library in our Chat on Gitter]] (or just hang out =:)=)
- Spread the word!
For a better view of all of the open issues, take a look at our lovely
-[[http://www.waffle.io/vermiculus/stack-mode][Waffle board]]. Feel free to take the torch on anything in =backlog= or
+[[http://www.waffle.io/vermiculus/sx.el][Waffle board]]. Feel free to take the torch on anything in =backlog= or
=ready=. If you have thoughts on any other issues, don't hesitate to
chime in!
+
* Resources
- [[http://www.gnu.org/software/emacs/][GNU Emacs]]
- [[https://api.stackexchange.com/docs][Stack Exchange API v2.2]]
-- [[http://stackapps.com/apps/oauth/register][StackApps Registration Page]]
-- [[http://www.emacswiki.org/emacs/ModeTutorial][Creating Major Modes for Emacs]]
+
** Icons
-Stack Exchange Mode for Emacs has no explicit use for an icon,
-although standard SVG files have been gathered in =resources/= if
-anyone would fancy a crack at it.
+SX has no explicit /need/ for an icon, although standard SVG files
+have been gathered in =resources/= if anyone would fancy a crack at
+it.
- [[file:resources/emacs.svg][Emacs icon]]
- [[file:resources/stackexchange.svg][Stack Exchange icon]]
diff --git a/sx-auth.el b/sx-auth.el
index 2217b8b..b6c0411 100644
--- a/sx-auth.el
+++ b/sx-auth.el
@@ -28,7 +28,7 @@
(defconst sx-auth-root
"https://stackexchange.com/oauth/dialog")
(defconst sx-auth-redirect-uri
- "http://vermiculus.github.io/stack-mode/auth/auth.htm")
+ "http://vermiculus.github.io/sx.el/auth/auth.htm")
(defconst sx-auth-client-id
"3291")
(defvar sx-auth-access-token
@@ -154,14 +154,14 @@ If it has `auth-required' properties, return a filter that has
removed those properties."
(let* ((incl-filter (if (listp filter) (car filter)))
(rest-filter (if incl-filter (cdr filter)))
- (auth-filters (cl-remove-if #'nil
- ;; Only retrieve the elements that
- ;; are issues.
- (mapcar (lambda (prop)
- (car
- (member prop
- sx-auth-filter-auth)))
- (or incl-filter filter))))
+ (auth-filters (remove nil
+ ;; Only retrieve the elements that
+ ;; are issues.
+ (mapcar (lambda (prop)
+ (car
+ (member prop
+ sx-auth-filter-auth)))
+ (or incl-filter filter))))
clean-filter out-filter)
(lwarn "sx-auth filter" :debug "Filter: %S" filter)
;; Auth-filters is the filters that are issues
diff --git a/sx-cache.el b/sx-cache.el
index 07352d0..51c2267 100644
--- a/sx-cache.el
+++ b/sx-cache.el
@@ -1,4 +1,4 @@
-;;; sx-cache.el --- caching for stack-mode
+;;; sx-cache.el --- caching
;; Copyright (C) 2014 Sean Allred
@@ -29,10 +29,10 @@
;;; Code:
(defcustom sx-cache-directory
- (expand-file-name ".stackmode" user-emacs-directory)
+ (expand-file-name ".sx" user-emacs-directory)
"Directory containing cached data."
:type 'directory
- :group 'sx-cache)
+ :group 'sx)
(defun sx-cache--ensure-sx-cache-directory-exists ()
"Ensure `sx-cache-directory' exists."
@@ -99,7 +99,7 @@ as delete the list of hidden questions."
(when save-auth
(setq caches (cl-remove-if (lambda (x)
(string= x "auth.el")) caches)))
- (lwarn 'stack-mode :debug "Invalidating: %S" caches)
+ (lwarn 'sx :debug "Invalidating: %S" caches)
(mapc #'delete-file caches)
(sx-initialize 'force)))
diff --git a/sx-encoding.el b/sx-encoding.el
index f683615..0e66677 100644
--- a/sx-encoding.el
+++ b/sx-encoding.el
@@ -1,4 +1,4 @@
-;;; sx-encoding.el --- encoding for stack-mode
+;;; sx-encoding.el --- encoding
;; Copyright (C) 2014 Sean Allred
@@ -145,7 +145,7 @@ See URL `http://www.gzip.org/zlib/rfc-gzip.html'."
"Check if BUFFER is gzip-compressed.
See `sx-encoding-gzipped-p'."
(with-current-buffer buffer
- (sx-encoding-gzip-check-magic
+ (sx-encoding-gzipped-p
(buffer-string))))
(defun sx-encoding-gzipped-file-p (file)
diff --git a/sx-interaction.el b/sx-interaction.el
index e4234b0..5f3ece6 100644
--- a/sx-interaction.el
+++ b/sx-interaction.el
@@ -180,8 +180,7 @@ ID is an integer."
(defun sx--add-comment-to-object (comment object)
"Add COMMENT to OBJECT's `comments' property.
OBJECT can be a question or an answer."
- (let ((com-cell (assoc 'comments object))
- (count-cell (assoc 'comment_count object)))
+ (let ((com-cell (assoc 'comments object)))
(if com-cell
(progn
(setcdr
diff --git a/sx-method.el b/sx-method.el
index 1b20cbf..83455b8 100644
--- a/sx-method.el
+++ b/sx-method.el
@@ -82,7 +82,8 @@ Return the entire response as a complex alist."
(prog1
(format "?site=%s" site)
(setq site nil)))))
- (call #'sx-request-make))
+ (call #'sx-request-make)
+ parameters)
(lwarn "sx-call-method" :debug "A: %S T: %S. M: %S,%s. F: %S" (equal 'warn auth)
access-token method-auth full-method filter-auth)
(unless access-token
diff --git a/sx-question-list.el b/sx-question-list.el
index fbed4ea..9709b99 100644
--- a/sx-question-list.el
+++ b/sx-question-list.el
@@ -30,10 +30,22 @@
(require 'sx-question-mode)
(require 'sx-favorites)
+(defgroup sx-question-list nil
+ "Customization group for sx-question-list."
+ :prefix "sx-question-list-"
+ :tag "SX Question List"
+ :group 'sx)
+
+(defgroup sx-question-list-faces nil
+ "Customization group for the faces of `sx-question-list'."
+ :prefix "sx-question-list-"
+ :tag "SX Question List Faces"
+ :group 'sx-question-list)
+
;;; Customization
(defcustom sx-question-list-height 12
- "Height, in lines, of stack-mode's *question-list* buffer."
+ "Height, in lines, of SX's *question-list* buffer."
:type 'integer
:group 'sx-question-list)
diff --git a/sx-question-mode.el b/sx-question-mode.el
index db3bb95..01a980a 100644
--- a/sx-question-mode.el
+++ b/sx-question-mode.el
@@ -1,4 +1,4 @@
-;;; sx-question-mode.el --- Creating the buffer that displays questions -*- lexical-binding: t; -*-
+;;; sx-question-mode.el --- Creating the buffer that displays questions
;; Copyright (C) 2014 Artur Malabarba
@@ -21,22 +21,12 @@
;;; Code:
-(require 'markdown-mode)
(eval-when-compile
(require 'rx))
(require 'sx)
(require 'sx-question)
-
-(defgroup sx-question-mode nil
- "Customization group for sx-question-mode."
- :prefix "sx-question-mode-"
- :group 'sx)
-
-(defgroup sx-question-mode-faces nil
- "Customization group for the faces of `sx-question-mode'."
- :prefix "sx-question-mode-"
- :group 'sx-question-mode)
+(require 'sx-question-print)
;;; Displaying a question
@@ -73,7 +63,7 @@ If WINDOW is given, use that to display the buffer."
;; Create the buffer if necessary.
(unless (buffer-live-p sx-question-mode--buffer)
(setq sx-question-mode--buffer
- (generate-new-buffer "*stack-question*")))
+ (generate-new-buffer "*sx-question*")))
(cond
;; Window was given, use it.
((window-live-p window)
@@ -85,415 +75,6 @@ If WINDOW is given, use that to display the buffer."
sx-question-mode--buffer)
-;;; Printing a question's content
-;;;; Faces and Variables
-
-(defface sx-question-mode-header
- '((t :inherit font-lock-variable-name-face))
- "Face used on the question headers in the question buffer."
- :group 'sx-question-mode-faces)
-
-(defface sx-question-mode-title
- '((t :height 1.3 :weight bold :inherit default))
- "Face used on the question title in the question buffer."
- :group 'sx-question-mode-faces)
-
-(defface sx-question-mode-title-comments
- '((t :height 1.1 :inherit sx-question-mode-title))
- "Face used on the question title in the question buffer."
- :group 'sx-question-mode-faces)
-
-(defcustom sx-question-mode-deleted-user
- '((display_name . "(deleted user)"))
- "The structure used to represent a deleted account."
- :type '(alist :options ((display_name string)))
- :group 'sx-question-mode)
-
-(defcustom sx-question-mode-header-title "\n"
- "String used before the question title at the header."
- :type 'string
- :group 'sx-question-mode)
-
-(defface sx-question-mode-author
- '((t :inherit font-lock-string-face))
- "Face used on the question author in the question buffer."
- :group 'sx-question-mode-faces)
-
-(defcustom sx-question-mode-header-author "\nAuthor: "
- "String used before the question author at the header."
- :type 'string
- :group 'sx-question-mode)
-
-(defface sx-question-mode-date
- '((t :inherit font-lock-string-face))
- "Face used on the question date in the question buffer."
- :group 'sx-question-mode-faces)
-
-(defcustom sx-question-mode-header-date "\nAsked on: "
- "String used before the question date at the header."
- :type 'string
- :group 'sx-question-mode)
-
-(defface sx-question-mode-tags
- '((t :underline nil :inherit font-lock-function-name-face))
- "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."
- :group 'sx-question-mode-faces)
-
-(defface sx-question-mode-score-downvoted
- '((t :inherit (font-lock-warning-face sx-question-mode-score)))
- "Face used for downvoted score in the question buffer."
- :group 'sx-question-mode-faces)
-
-(defface sx-question-mode-score-upvoted
- '((t :weight bold
- :inherit (font-lock-function-name-face sx-question-mode-score)))
- "Face used for downvoted score in the question buffer."
- :group 'sx-question-mode-faces)
-
-(defcustom sx-question-mode-header-tags "\nTags: "
- "String used before the question tags at the header."
- :type 'string
- :group 'sx-question-mode)
-
-(defcustom sx-question-mode-header-score "\nScore: "
- "String used before the question score at the header."
- :type 'string
- :group 'sx-question-mode)
-
-(defface sx-question-mode-content-face
- '((((background dark)) :background "#090909")
- (((background light)) :background "#f4f4f4"))
- "Face used on the question body in the question buffer.
-This shouldn't have a foreground, or this will interfere with
-font-locking."
- :group 'sx-question-mode-faces)
-
-(defcustom sx-question-mode-last-edit-format " (edited %s ago by %s)"
- "Format used to describe last edit date in the header.
-First \"%s\" is replaced with the date and the second \"%s\" with
-the editor's name."
- :type 'string
- :group 'sx-question-mode)
-
-(defcustom sx-question-mode-separator
- (concat (make-string 80 ?_) "\n")
- "Separator used between header and body."
- :type 'string
- :group 'sx-question-mode)
-
-(defcustom sx-question-mode-answer-title "Answer"
- "Title used at the start of \"Answer\" sections."
- :type 'string
- :group 'sx-question-mode)
-
-(defcustom sx-question-mode-comments-title " Comments"
- "Title used at the start of \"Comments\" sections."
- :type 'string
- :group 'sx-question-mode)
-
-(defcustom sx-question-mode-comments-format "%s: %s\n"
- "Format used to display comments.
-First \"%s\" is replaced with user name. Second \"%s\" is
-replaced with the comment."
- :type 'string
- :group 'sx-question-mode)
-
-(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
-(defun sx-question-mode--print-question (question)
- "Print a buffer describing QUESTION.
-QUESTION must be a data structure returned by `json-read'."
- (setq sx-question-mode--data question)
- ;; Clear the overlays
- (mapc #'delete-overlay sx--overlays)
- (setq sx--overlays nil)
- ;; Print everything
- (sx-question-mode--print-section question)
- (sx-assoc-let question
- (mapc #'sx-question-mode--print-section .answers))
- (goto-char (point-min))
- (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))
- "Help echoed in the minibuffer when point is on a section.")
-
-(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
- button t
- follow-link t)
- "Title properties.")
-
-(defun sx-question-mode--print-section (data)
- "Print a section corresponding to DATA.
-DATA can represent a question or an answer."
- ;; This makes `data' accessible through `sx--data-here'.
- (sx-assoc-let data
- (sx--wrap-in-text-property
- (list 'sx--data-here data)
- (insert sx-question-mode-header-title
- (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--wrap-in-overlay
- '(sx-question-mode--section-content t)
- (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
- (sx-time-seconds-to-date .creation_date)
- (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-question-mode-date)
- (sx-question-mode--insert-header
- sx-question-mode-header-score
- (format "%s" .score)
- (cond
- ((eq .upvoted t) 'sx-question-mode-score-upvoted)
- ((eq .downvoted t) 'sx-question-mode-score-downvoted)
- (t 'sx-question-mode-score)))
- (when .title
- ;; Tags
- (sx-question-mode--insert-header
- sx-question-mode-header-tags
- (mapconcat #'sx-question--tag-format .tags " ")
- 'sx-question-mode-tags))
- ;; Body
- (insert "\n"
- (propertize sx-question-mode-separator
- 'face 'sx-question-mode-header
- 'sx-question-mode--section 4))
- (sx--wrap-in-overlay
- '(face sx-question-mode-content-face)
- (insert "\n"
- (sx-question-mode--fill-and-fontify
- .body_markdown)
- "\n"
- (propertize sx-question-mode-separator
- 'face 'sx-question-mode-header)))))
- ;; Comments have their own `sx--data-here' property (so they can
- ;; be upvoted too).
- (when .comments
- (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--wrap-in-overlay
- '(sx-question-mode--section-content t)
- (insert "\n")
- (sx--wrap-in-overlay
- '(face sx-question-mode-content-face)
- (mapc #'sx-question-mode--print-comment .comments))))))
-
-(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
- '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
-`sx-question-mode-comments-format'."
- (sx--wrap-in-text-property
- (list 'sx--data-here comment-data)
- (sx-assoc-let comment-data
- (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))))))
-
-(defun sx-question-mode--insert-header (&rest args)
- "Insert propertized ARGS.
-ARGS is a list of repeating values -- `header', `value', and
-`face'. `header' is given `sx-question-mode-header' as a face,
-where `value' is given `face' as its face.
-
-\(fn HEADER VALUE FACE [HEADER VALUE FACE] [HEADER VALUE FACE] ...)"
- (while args
- (insert
- (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)
- "Return TEXT filled according to `markdown-mode'."
- (with-temp-buffer
- (insert text)
- (markdown-mode)
- (font-lock-mode -1)
- (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
- `((,(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))
- ;; 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--dont-fill-here)
- (skip-chars-forward "\r\n[:blank:]")
- (fill-paragraph)
- (forward-paragraph)))
- (buffer-string)))
-
-(defvar sx-question-mode--reference-regexp
- (rx line-start (0+ blank) "[%s]:" (0+ blank)
- (group-n 1 (1+ (not blank))))
- "Regexp used to find the url of labeled links.
-E.g.:
- [1]: https://...")
-
-(defun sx-question-mode--dont-fill-here ()
- "If text shouldn't be filled here, return t and skip over it."
- (or (sx-question-mode--move-over-pre)
- ;; 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))))
-
-(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 'url properties."
- (propertize
- text
- ;; 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
- ;; 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))
-
-(defun sx-question-mode-follow-link (&optional pos)
- "Follow link at POS. If POS is nil, use `point'."
- (interactive)
- (browse-url
- (or (get-text-property (or pos (point)) 'url)
- (user-error "No url under point: %s" (or pos (point))))))
-
-(defun sx-question-mode-find-reference (id &optional fallback-id)
- "Find url identified by reference ID in current buffer.
-If ID is nil, use FALLBACK-ID instead."
- (save-excursion
- (save-match-data
- (goto-char (point-min))
- (when (search-forward-regexp
- (format sx-question-mode--reference-regexp
- (or id fallback-id))
- nil t)
- (match-string-no-properties 1)))))
-
-(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
;; answer's content, or above the list of comments. They are
@@ -591,8 +172,7 @@ Letters do not insert themselves; instead, they are commands.
(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)
- (read-only-mode))
+ 'markdown-fontify-buffer-wiki-links t))
(mapc
(lambda (x) (define-key sx-question-mode-map
@@ -611,8 +191,7 @@ Letters do not insert themselves; instead, they are commands.
([tab] forward-button)
(,(kbd "<S-iso-lefttab>") backward-button)
(,(kbd "<S-tab>") backward-button)
- (,(kbd "<backtab>") backward-button)
- ([return] push-button)))
+ (,(kbd "<backtab>") backward-button)))
(defun sx-question-mode-refresh (&optional no-update)
"Refresh currently displayed question.
@@ -644,3 +223,7 @@ query the api."
(provide 'sx-question-mode)
;;; sx-question-mode.el ends here
+
+;; Local Variables:
+;; lexical-binding: t
+;; End:
diff --git a/sx-question-print.el b/sx-question-print.el
new file mode 100644
index 0000000..0959f36
--- /dev/null
+++ b/sx-question-print.el
@@ -0,0 +1,440 @@
+;;; sx-question-print.el --- Populating the question-mode buffer with content.
+
+;; 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 'markdown-mode)
+(require 'button)
+(eval-when-compile
+ (require 'rx))
+
+(require 'sx)
+(require 'sx-question)
+
+(defgroup sx-question-mode nil
+ "Customization group for sx-question-mode."
+ :prefix "sx-question-mode-"
+ :tag "SX Question Mode"
+ :group 'sx)
+
+(defgroup sx-question-mode-faces nil
+ "Customization group for the faces of `sx-question-mode'."
+ :prefix "sx-question-mode-"
+ :tag "SX Question Mode Faces"
+ :group 'sx-question-mode)
+
+
+;;; Faces and Variables
+
+(defface sx-question-mode-header
+ '((t :inherit font-lock-variable-name-face))
+ "Face used on the question headers in the question buffer."
+ :group 'sx-question-mode-faces)
+
+(defface sx-question-mode-title
+ '((t :weight bold :inherit default))
+ "Face used on the question title in the question buffer."
+ :group 'sx-question-mode-faces)
+
+(defface sx-question-mode-title-comments
+ '((t :inherit sx-question-mode-title))
+ "Face used on the question title in the question buffer."
+ :group 'sx-question-mode-faces)
+
+(defcustom sx-question-mode-header-title "\n"
+ "String used before the question title at the header."
+ :type 'string
+ :group 'sx-question-mode)
+
+(defface sx-question-mode-author
+ '((t :inherit font-lock-string-face))
+ "Face used on the question author in the question buffer."
+ :group 'sx-question-mode-faces)
+
+(defcustom sx-question-mode-header-author "\nAuthor: "
+ "String used before the question author at the header."
+ :type 'string
+ :group 'sx-question-mode)
+
+(defface sx-question-mode-date
+ '((t :inherit font-lock-string-face))
+ "Face used on the question date in the question buffer."
+ :group 'sx-question-mode-faces)
+
+(defcustom sx-question-mode-header-date "\nAsked on: "
+ "String used before the question date at the header."
+ :type 'string
+ :group 'sx-question-mode)
+
+(defface sx-question-mode-tags
+ '((t :underline nil :inherit font-lock-function-name-face))
+ "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."
+ :group 'sx-question-mode-faces)
+
+(defface sx-question-mode-score-downvoted
+ '((t :inherit (font-lock-warning-face sx-question-mode-score)))
+ "Face used for downvoted score in the question buffer."
+ :group 'sx-question-mode-faces)
+
+(defface sx-question-mode-score-upvoted
+ '((t :weight bold
+ :inherit (font-lock-function-name-face sx-question-mode-score)))
+ "Face used for downvoted score in the question buffer."
+ :group 'sx-question-mode-faces)
+
+(defcustom sx-question-mode-header-tags "\nTags: "
+ "String used before the question tags at the header."
+ :type 'string
+ :group 'sx-question-mode)
+
+(defcustom sx-question-mode-header-score "\nScore: "
+ "String used before the question score at the header."
+ :type 'string
+ :group 'sx-question-mode)
+
+(defface sx-question-mode-content-face
+ '((((background dark)) :background "#090909")
+ (((background light)) :background "#f4f4f4"))
+ "Face used on the question body in the question buffer.
+This shouldn't have a foreground, or this will interfere with
+font-locking."
+ :group 'sx-question-mode-faces)
+
+(defcustom sx-question-mode-last-edit-format " (edited %s ago by %s)"
+ "Format used to describe last edit date in the header.
+First \"%s\" is replaced with the date and the second \"%s\" with
+the editor's name."
+ :type 'string
+ :group 'sx-question-mode)
+
+(defcustom sx-question-mode-separator
+ (concat (make-string 80 ?_) "\n")
+ "Separator used between header and body."
+ :type 'string
+ :group 'sx-question-mode)
+
+(defcustom sx-question-mode-answer-title "Answer"
+ "Title used at the start of \"Answer\" sections."
+ :type 'string
+ :group 'sx-question-mode)
+
+(defcustom sx-question-mode-comments-title " Comments"
+ "Title used at the start of \"Comments\" sections."
+ :type 'string
+ :group 'sx-question-mode)
+
+(defcustom sx-question-mode-comments-format "%s: %s\n"
+ "Format used to display comments.
+First \"%s\" is replaced with user name. Second \"%s\" is
+replaced with the comment."
+ :type 'string
+ :group 'sx-question-mode)
+
+(defcustom sx-question-mode-pretty-links t
+ "If non-nil, markdown links are displayed in a compact form."
+ :type 'boolean
+ :group 'sx-question-mode)
+
+
+;;; Buttons
+(define-button-type 'sx-question-mode-title
+ 'face 'sx-question-mode-title
+ 'action #'sx-question-mode-hide-show-section
+ 'help-echo 'sx-question-mode--section-help-echo
+ 'follow-link t)
+
+(define-button-type 'sx-question-mode-link
+ 'follow-link t
+ 'action #'sx-question-mode-follow-link)
+
+
+;;; Functions
+;;;; Printing the general structure
+(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))
+ "Help echoed in the minibuffer when point is on a section.")
+
+(defun sx-question-mode--print-question (question)
+ "Print a buffer describing QUESTION.
+QUESTION must be a data structure returned by `json-read'."
+ (setq sx-question-mode--data question)
+ ;; Clear the overlays
+ (mapc #'delete-overlay sx--overlays)
+ (setq sx--overlays nil)
+ ;; Print everything
+ (sx-question-mode--print-section question)
+ (sx-assoc-let question
+ (mapc #'sx-question-mode--print-section .answers))
+ (goto-char (point-min))
+ (sx-question-mode-next-section))
+
+(defun sx-question-mode--print-section (data)
+ "Print a section corresponding to DATA.
+DATA can represent a question or an answer."
+ ;; This makes `data' accessible through `sx--data-here'.
+ (sx-assoc-let data
+ (sx--wrap-in-text-property
+ (list 'sx--data-here data)
+ (insert sx-question-mode-header-title)
+ (insert-text-button
+ ;; Questions have title, Answers don't
+ (or .title sx-question-mode-answer-title)
+ ;; Section level
+ 'sx-question-mode--section (if .title 1 2)
+ :type 'sx-question-mode-title)
+ ;; Sections can be hidden with overlays
+ (sx--wrap-in-overlay
+ '(sx-question-mode--section-content t)
+ (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
+ (sx-time-seconds-to-date .creation_date)
+ (when .last_edit_date
+ (format sx-question-mode-last-edit-format
+ (sx-time-since .last_edit_date)
+ (sx-question-mode--propertize-display-name .last_editor))))
+ 'sx-question-mode-date)
+ (sx-question-mode--insert-header
+ sx-question-mode-header-score
+ (format "%s" .score)
+ (cond
+ ((eq .upvoted t) 'sx-question-mode-score-upvoted)
+ ((eq .downvoted t) 'sx-question-mode-score-downvoted)
+ (t 'sx-question-mode-score)))
+ (when .title
+ ;; Tags
+ (sx-question-mode--insert-header
+ sx-question-mode-header-tags
+ (mapconcat #'sx-question--tag-format .tags " ")
+ 'sx-question-mode-tags))
+ ;; Body
+ (insert "\n"
+ (propertize sx-question-mode-separator
+ 'face 'sx-question-mode-header
+ 'sx-question-mode--section 4))
+ (sx--wrap-in-overlay
+ '(face sx-question-mode-content-face)
+ (insert "\n"
+ (sx-question-mode--fill-and-fontify
+ .body_markdown)
+ "\n"
+ (propertize sx-question-mode-separator
+ 'face 'sx-question-mode-header)))))
+ ;; Comments have their own `sx--data-here' property (so they can
+ ;; be upvoted too).
+ (when .comments
+ (insert "\n")
+ (insert-text-button
+ sx-question-mode-comments-title
+ 'face 'sx-question-mode-title-comments
+ 'sx-question-mode--section 3
+ :type 'sx-question-mode-title)
+ (sx--wrap-in-overlay
+ '(sx-question-mode--section-content t)
+ (insert "\n")
+ (sx--wrap-in-overlay
+ '(face sx-question-mode-content-face)
+ (mapc #'sx-question-mode--print-comment .comments))))))
+
+(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
+ '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
+`sx-question-mode-comments-format'."
+ (sx--wrap-in-text-property
+ (list 'sx--data-here comment-data)
+ (sx-assoc-let comment-data
+ (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))))))
+
+(defun sx-question-mode--insert-header (&rest args)
+ "Insert propertized ARGS.
+ARGS is a list of repeating values -- `header', `value', and
+`face'. `header' is given `sx-question-mode-header' as a face,
+where `value' is given `face' as its face.
+
+\(fn HEADER VALUE FACE [HEADER VALUE FACE] [HEADER VALUE FACE] ...)"
+ (while args
+ (insert
+ (propertize (pop args) 'face 'sx-question-mode-header)
+ (propertize (pop args) 'face (pop args)))))
+
+
+;;;; Printing and Font-locking the content (body)
+(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.")
+
+(defvar sx-question-mode--reference-regexp
+ (rx line-start (0+ blank) "[%s]:" (0+ blank)
+ (group-n 1 (1+ (not blank))))
+ "Regexp used to find the url of labeled links.
+E.g.:
+ [1]: https://...")
+
+(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--fill-and-fontify (text)
+ "Return TEXT filled according to `markdown-mode'."
+ (with-temp-buffer
+ (insert text)
+ (markdown-mode)
+ (font-lock-mode -1)
+ (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
+ `((,(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))
+ ;; 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--dont-fill-here)
+ (skip-chars-forward "\r\n[:blank:]")
+ (fill-paragraph)
+ (forward-paragraph)))
+ (buffer-string)))
+
+(defun sx-question-mode--dont-fill-here ()
+ "If text shouldn't be filled here, return t and skip over it."
+ (or (sx-question-mode--move-over-pre)
+ ;; 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))))
+
+(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)))
+ (full-text (match-string-no-properties 0)))
+ (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.
+URL is used as 'help-echo and 'url properties."
+ (insert-text-button
+ text
+ ;; 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))
+ ;; For visiting and stuff.
+ 'url url
+ :type 'sx-question-mode-link))
+
+(defun sx-question-mode-follow-link (&optional pos)
+ "Follow link at POS. If POS is nil, use `point'."
+ (interactive)
+ (browse-url
+ (or (get-text-property (or pos (point)) 'url)
+ (user-error "No url under point: %s" (or pos (point))))))
+
+(defun sx-question-mode-find-reference (id &optional fallback-id)
+ "Find url identified by reference ID in current buffer.
+If ID is nil, use FALLBACK-ID instead."
+ (save-excursion
+ (save-match-data
+ (goto-char (point-min))
+ (when (search-forward-regexp
+ (format sx-question-mode--reference-regexp
+ (or id fallback-id))
+ nil t)
+ (match-string-no-properties 1)))))
+
+(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))))
+
+(provide 'sx-question-print)
+;;; sx-question-print.el ends here
+
+;; Local Variables:
+;; lexical-binding: t
+;; End:
diff --git a/sx-question.el b/sx-question.el
index a7aadb2..f80a9bd 100644
--- a/sx-question.el
+++ b/sx-question.el
@@ -1,4 +1,4 @@
-;;; sx-question.el --- question logic for stack-mode -*- lexical-binding: t; -*-
+;;; sx-question.el --- question logic
;; Copyright (C) 2014 Sean Allred
@@ -134,20 +134,21 @@ If no cache exists for it, initialize one with SITE."
(defun sx-question--mark-hidden (question)
"Mark QUESTION as being hidden."
- (let ((site-cell (assoc .site sx-question--user-hidden-list))
- cell)
- ;; If question already hidden, do nothing.
- (unless (memq .question_id site-cell)
- ;; First question from this site.
- (push (list .site .question_id) sx-question--user-hidden-list)
- ;; Question wasn't present.
- ;; Add it in, but make sure it's sorted (just in case we need
- ;; it later).
- (sx-sorted-insert-skip-first .question_id 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 'hidden-questions sx-question--user-hidden-list))))
+ (sx-assoc-let question
+ (let ((site-cell (assoc .site sx-question--user-hidden-list))
+ cell)
+ ;; If question already hidden, do nothing.
+ (unless (memq .question_id site-cell)
+ ;; First question from this site.
+ (push (list .site .question_id) sx-question--user-hidden-list)
+ ;; Question wasn't present.
+ ;; Add it in, but make sure it's sorted (just in case we need
+ ;; it later).
+ (sx-sorted-insert-skip-first .question_id 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 'hidden-questions sx-question--user-hidden-list)))))
;;;; Other data
@@ -167,4 +168,5 @@ If no cache exists for it, initialize one with SITE."
;; Local Variables:
;; indent-tabs-mode: nil
+;; lexical-binding: t
;; End:
diff --git a/sx-request.el b/sx-request.el
index 6b09988..6be363d 100644
--- a/sx-request.el
+++ b/sx-request.el
@@ -71,7 +71,7 @@
"gunzip"
"Program used to unzip the response if it is compressed.
This program must accept compressed data on standard input."
- :group 'sx-request
+ :group 'sx
:type 'string)
(defvar sx-request-remaining-api-requests
@@ -85,15 +85,14 @@ Set by `sx-request-make'.")
After `sx-request-remaining-api-requests' drops below this
number, `sx-request-make' will begin printing out the
number of requests left every time it finishes a call."
- :group 'sx-request
+ :group 'sx
:type 'integer)
;;; Making Requests
-(defun sx-request-make
- (method &optional args request-method)
- "Make a request to the API, executing METHOD with ARGS.
+(defun sx-request-make (method &optional args request-method)
+ "Make a request to the API, executing METHOD with ARGS.
You should almost certainly be using `sx-method-call' instead of
this function. REQUEST-METHOD is one of `GET' (default) or `POST'.
@@ -116,8 +115,7 @@ then read with `json-read-from-string'.
the main content of the response is returned."
(let* ((url-automatic-caching t)
(url-inhibit-uncompression t)
- (url-request-data (sx-request--build-keyword-arguments args
- nil))
+ (url-request-data (sx-request--build-keyword-arguments args nil))
(request-url (concat sx-request-api-root method))
(url-request-method request-method)
(url-request-extra-headers
@@ -168,15 +166,11 @@ Currently returns nil."
;;; Support Functions
-
-(defun sx-request--build-keyword-arguments (alist &optional
- kv-sep need-auth)
+(defun sx-request--build-keyword-arguments (alist &optional kv-sep)
"Format ALIST as a key-value list joined with KV-SEP.
If authentication is needed, include it also or error if it is
not available.
-If NEED-AUTH is non-nil, authentication is required.
-
Build a \"key=value&key=value&...\"-style string with the elements
of ALIST. If any value in the alist is nil, that pair will not
be included in the return. If you wish to pass a notion of
@@ -185,7 +179,7 @@ false, use the symbol `false'. Each element is processed with
;; Add API key to list of arguments, this allows for increased quota
;; automatically.
(let ((api-key (cons "key" sx-request-api-key))
- (auth (car (sx-cache-get 'auth))))
+ (auth (car (sx-cache-get 'auth))))
(push api-key alist)
(when auth
(push auth alist))
diff --git a/sx-site.el b/sx-site.el
index 04b5240..8bd4fc0 100644
--- a/sx-site.el
+++ b/sx-site.el
@@ -57,7 +57,7 @@
"List of favorite sites.
Each entry is a string corresponding to a single site's
api_site_parameter."
- :group 'sx-site)
+ :group 'sx)
(defun sx-site-get-api-tokens ()
"Return a list of all known site tokens."
diff --git a/sx-tab.el b/sx-tab.el
index 8a51236..7ccbf18 100644
--- a/sx-tab.el
+++ b/sx-tab.el
@@ -30,11 +30,11 @@
(defcustom sx-tab-default-site "emacs"
"Name of the site to use by default when listing questions."
:type 'string
- :group 'stack-exchange)
+ :group 'sx)
(defmacro sx-tab--define (tab pager &optional printer refresher
&rest body)
- "Define a stack-exchange tab called TAB.
+ "Define a StackExchange tab called TAB.
TAB is a capitalized string.
This defines a command `sx-tab-TAB' for displaying the tab,
@@ -97,6 +97,13 @@ If SITE is nil, use `sx-tab-default-site'."
(lambda (page)
(sx-question-get-questions
sx-question-list--site page)))
+;;;###autoload
+(autoload 'sx-tab-frontpage
+ (expand-file-name
+ "sx-tab"
+ (when load-file-name
+ (file-name-directory load-file-name)))
+ nil t)
(provide 'sx-tab)
;;; sx-tab.el ends here
diff --git a/sx-time.el b/sx-time.el
index 057a397..e65bb50 100644
--- a/sx-time.el
+++ b/sx-time.el
@@ -1,4 +1,4 @@
-;;; sx-time.el --- time for stack-mode
+;;; sx-time.el --- time
;; Copyright (C) 2014 Sean Allred
@@ -57,13 +57,13 @@
"Format used for dates on a past year.
See also `sx-time-date-format'."
:type 'string
- :group 'sx-time)
+ :group 'sx)
(defcustom sx-time-date-format "%H:%M - %d %b"
"Format used for dates on this year.
See also `sx-time-date-format-year'."
:type 'string
- :group 'sx-time)
+ :group 'sx)
(defun sx-time-seconds-to-date (seconds)
"Return the integer SECONDS as a date string."
diff --git a/sx.el b/sx.el
index e47e6e3..0fe98c7 100644
--- a/sx.el
+++ b/sx.el
@@ -1,9 +1,9 @@
-;;; sx.el --- Core functions of the sx package. -*- lexical-binding: t; -*-
+;;; sx.el --- core functions of the sx package.
;; Copyright (C) 2014 Sean Allred
;; Author: Sean Allred <code@seanallred.com>
-;; URL: https://github.com/vermiculus/stack-mode/
+;; URL: https://github.com/vermiculus/sx.el/
;; Version: 0.1
;; Keywords: help, hypermedia, tools
;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0"))
@@ -23,14 +23,20 @@
;;; Commentary:
-;; This file defines basic commands used by all other parts of
-;; StackMode.
+;; This file defines basic commands used by all other parts of SX.
;;; Code:
(require 'tabulated-list)
(defconst sx-version "0.1" "Version of the `sx' package.")
+(defgroup sx nil
+ "Customization group for sx-question-mode."
+ :prefix "sx-"
+ :tag "SX"
+ :group 'applications)
+
+
;;; User commands
(defun sx-version ()
@@ -43,7 +49,7 @@
(defun sx-bug-report ()
"File a bug report about the `sx' package."
(interactive)
- (browse-url "https://github.com/vermiculus/stack-mode/issues/new"))
+ (browse-url "https://github.com/vermiculus/sx.el/issues/new"))
;;; Browsing filter
@@ -100,7 +106,7 @@ is intentionally skipped."
(defun sx-message (format-string &rest args)
"Display FORMAT-STRING as a message with ARGS.
See `format'."
- (message "[stack] %s" (apply #'format format-string args)))
+ (message "[sx] %s" (apply #'format format-string args)))
(defun sx-message-help-echo ()
"If there's a 'help-echo property under point, message it."
@@ -265,13 +271,13 @@ is equivalent to
,@body)))
(defcustom sx-init-hook nil
- "Hook run when stack-mode initializes.
+ "Hook run when SX initializes.
Run after `sx-init--internal-hook'."
:group 'sx
:type 'hook)
(defvar sx-init--internal-hook nil
- "Hook run when stack-mode initializes.
+ "Hook run when SX initializes.
This is used internally to set initial values for variables such
as filters.")
@@ -313,4 +319,5 @@ If FORCE is non-nil, run them even if they've already been run."
;; Local Variables:
;; indent-tabs-mode: nil
+;; lexical-binding: t
;; End:
diff --git a/sx.org b/sx.org
index cb1c109..f866aa5 100644
--- a/sx.org
+++ b/sx.org
@@ -4,7 +4,7 @@
#+TITLE: SX: A StackExchange Client (v{{{version}}})
#+DATE: 16 November 2014
-#+AUTHOR: @@texinfo:@url{@@www.github.com/vermiculus/stack-mode@@texinfo:}@@
+#+AUTHOR: @@texinfo:@url{@@www.github.com/vermiculus/sx.el@@texinfo:}@@
#+LANGUAGE: en
#+OPTIONS: ':t toc:t