aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArtur Malabarba <bruce.connor.am@gmail.com>2014-11-17 22:33:07 +0000
committerArtur Malabarba <bruce.connor.am@gmail.com>2014-11-17 22:33:07 +0000
commit3e17a40c0701dcd3cfa03141b2669abeb55e160a (patch)
tree2d2003fa91889bb54f0beeb4dac5232b99cb788b
parent34d28e79f4a6049dc4ceb44397a2ca0725cecd5f (diff)
parent278fa010e6a63474fce6bf46aaf3327b0d9cf7f7 (diff)
Merge branch 'master' into question-mode-keybindings++
-rw-r--r--sx-auth.el22
-rw-r--r--sx-method.el13
-rw-r--r--sx-question-list.el1
-rw-r--r--sx-question-mode.el4
-rw-r--r--sx-request.el85
-rw-r--r--sx.el17
6 files changed, 92 insertions, 50 deletions
diff --git a/sx-auth.el b/sx-auth.el
index f32e7aa..b470523 100644
--- a/sx-auth.el
+++ b/sx-auth.el
@@ -28,7 +28,7 @@
(require 'sx-cache)
(defconst sx-auth-root
- "https://stackexchange.com/oauth/")
+ "https://stackexchange.com/oauth/dialog")
(defconst sx-auth-redirect-uri
"http://vermiculus.github.io/stack-mode/auth/auth.htm")
(defconst sx-auth-client-id
@@ -50,15 +50,17 @@ questions)."
(interactive)
(setq
sx-auth-access-token
- (let ((url (sx-request-build
- "dialog"
- `((client_id . ,sx-auth-client-id)
- (scope . (read_inbox
- no_expiry
- write_access))
- (redirect_uri . ,(url-hexify-string
- sx-auth-redirect-uri)))
- "," sx-auth-root)))
+ (let ((url (concat
+ sx-auth-root
+ "?"
+ (sx-request--build-keyword-arguments
+ `((client_id . ,sx-auth-client-id)
+ (scope . (read_inbox
+ no_expiry
+ write_access))
+ (redirect_uri . ,(url-hexify-string
+ sx-auth-redirect-uri)))
+ ","))))
(browse-url url)
(read-string "Enter the access token displayed on the webpage: ")))
(if (string-equal "" sx-auth-access-token)
diff --git a/sx-method.el b/sx-method.el
index 6f0a36b..e9c4f60 100644
--- a/sx-method.el
+++ b/sx-method.el
@@ -29,9 +29,16 @@
(require 'sx-filter)
(defun sx-method-call
- (method &optional keyword-arguments filter silent)
+ (method &optional keyword-arguments filter need-auth use-post silent)
"Call METHOD with KEYWORD-ARGUMENTS using FILTER.
+If NEED-AUTH is non-nil, an auth-token is required. If 'WARN,
+warn the user `(user-error ...)' if they do not have an AUTH
+token set.
+
+If USE-POST is non-nil, use `POST' rather than `GET' for passing
+arguments.
+
If SILENT is non-nil, no messages will be printed.
Return the entire response as a complex alist."
@@ -41,7 +48,9 @@ Return the entire response as a complex alist."
(sx-filter-get-var
(cond (filter filter)
((boundp 'stack-filter) stack-filter))))
- keyword-arguments)))
+ keyword-arguments)
+ need-auth
+ use-post))
(provide 'sx-method)
;;; sx-method.el ends here
diff --git a/sx-question-list.el b/sx-question-list.el
index b220097..72eabd3 100644
--- a/sx-question-list.el
+++ b/sx-question-list.el
@@ -323,6 +323,7 @@ focus the relevant window."
(defun list-questions (no-update)
"Display a list of StackExchange questions."
(interactive "P")
+ (sx-initialize)
(unless (buffer-live-p sx-question-list--buffer)
(setq sx-question-list--buffer
(generate-new-buffer "*question-list*")))
diff --git a/sx-question-mode.el b/sx-question-mode.el
index 20d93c4..fa58512 100644
--- a/sx-question-mode.el
+++ b/sx-question-mode.el
@@ -360,11 +360,11 @@ HEADER is given `sx-question-mode-header' face, and value is given FACE.
(when sx-question-mode-bullet-appearance
(font-lock-add-keywords ;; Bullet items.
nil
- `(((rx line-start (0+ blank) (group-n 1 (any "*+-")) blank)
+ `((,(rx line-start (0+ blank) (group-n 1 (any "*+-")) blank)
1 '(face nil display ,sx-question-mode-bullet-appearance) prepend))))
(font-lock-add-keywords ;; Highlight usernames.
nil
- `(((rx (or blank line-start)
+ `((,(rx (or blank line-start)
(group-n 1 (and "@" (1+ (or (syntax word) (syntax symbol)))))
symbol-end)
1 font-lock-builtin-face)))
diff --git a/sx-request.el b/sx-request.el
index 6dc54e7..d982057 100644
--- a/sx-request.el
+++ b/sx-request.el
@@ -71,19 +71,19 @@ number of requests left every time it finishes a call.")
;;; Making Requests
(defun sx-request-make
- (method &optional args silent)
+ (method &optional args need-auth use-post silent)
(let ((url-automatic-caching sx-request-cache-p)
(url-inhibit-uncompression t)
(silent (or silent sx-request-silent-p))
- (call (sx-request-build
- method
- (cons (cons 'key sx-request-api-key)
- args))))
- (unless silent (sx-message "Request: %S" call))
- (let ((response-buffer (cond
- ((equal '(24 . 4) (cons emacs-major-version emacs-minor-version))
- (url-retrieve-synchronously call silent))
- (t (url-retrieve-synchronously call)))))
+ (request-method (if use-post "POST" "GET"))
+ (request-args
+ (sx-request--build-keyword-arguments args nil need-auth))
+ (request-url (concat sx-request-api-root method)))
+ (unless silent (sx-message "Request: %S" request-url))
+ (let ((response-buffer (sx-request--request request-url
+ request-args
+ request-method
+ silent)))
(if (not response-buffer)
(error "Something went wrong in `url-retrieve-synchronously'")
(with-current-buffer response-buffer
@@ -120,33 +120,54 @@ number of requests left every time it finishes a call.")
;;; Support Functions
-(defun sx-request-build (method keyword-arguments &optional kv-value-sep root)
- "Build the request string that will be used to process REQUEST
-with the given KEYWORD-ARGUMENTS."
- (let ((base (concat (or root sx-request-api-root) method))
- (args (sx-request--build-keyword-arguments
- keyword-arguments kv-value-sep)))
- (if (string-equal "" args)
- base
- (concat base "?" args))))
-
-(defun sx-request--build-keyword-arguments (alist &optional kv-value-sep)
+(defun sx-request--request (url args method silent)
+ (let ((url-request-method method)
+ (url-request-extra-headers
+ '(("Content-Type" . "application/x-www-form-urlencoded")))
+ (url-request-data args))
+ (cond
+ ((equal '(24 . 4) (cons emacs-major-version emacs-minor-version))
+ (url-retrieve-synchronously url silent))
+ (t (url-retrieve-synchronously url)))))
+
+(defun sx-request--build-keyword-arguments (alist &optional
+ kv-value-sep need-auth)
"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
false, use the symbol `false'. Each element is processed with
`sx--thing-as-string'."
- (mapconcat
- (lambda (pair)
- (concat
- (sx--thing-as-string (car pair))
- "="
- (sx--thing-as-string (cdr pair) kv-value-sep)))
- (delq nil (mapcar
- (lambda (pair)
- (when (cdr pair) pair))
- alist))
- "&"))
+ ;; Add API key to list of arguments, this allows for increased quota
+ ;; automatically.
+ (let* ((warn (equal need-auth 'warn))
+ (api-key (cons "key" sx-request-api-key))
+ (auth
+ (let ((auth (car (sx-cache-get 'auth))))
+ (cond
+ (auth)
+ ;; Pass user error when asking to warn
+ (warn
+ (user-error
+ "This query requires authentication. Please run `M-x sx-auth-authenticate' and try again."))
+ ((not auth)
+ (lwarn "stack-mode" :debug
+ "This query requires authentication")
+ nil)))))
+ (push api-key alist)
+ (if (and need-auth auth)
+ (push auth alist))
+ (mapconcat
+ (lambda (pair)
+ (concat
+ (sx--thing-as-string (car pair))
+ "="
+ (sx--thing-as-string (cdr pair) kv-value-sep)))
+ (delq nil (mapcar
+ (lambda (pair)
+ (when (cdr pair) pair))
+ alist))
+ "&")))
+
(provide 'sx-request)
;;; sx-request.el ends here
diff --git a/sx.el b/sx.el
index 53aae84..64c555c 100644
--- a/sx.el
+++ b/sx.el
@@ -143,10 +143,19 @@ SETTER should be a function of two arguments. If SETTER is nil,
(,(or setter #'setq) ,variable ,value))))
nil)
-(defun stack-initialize ()
- (run-hooks
- 'sx-init--internal-hook
- 'sx-init-hook))
+(defvar sx-initialized nil
+ "Nil if sx hasn't been initialized yet.
+If it has, holds the time at which initialization happened.")
+
+(defun sx-initialize (&optional force)
+ "Run initialization hooks if they haven't been run yet.
+These are `sx-init--internal-hook' and `sx-init-hook'.
+If FORCE is non-nil, run them even if they've already been run."
+ (when (or force (not sx-initialized))
+ (prog1
+ (run-hooks 'sx-init--internal-hook
+ 'sx-init-hook)
+ (setq sx-initialized (current-time)))))
(provide 'sx)
;;; sx.el ends here