#+Title: Stack Exchange Mode for Emacs #+Author: Sean Allred #+Date: [2013-02-28 Thu] # This line crashes Emacs. Remove it for successful export. #+INFOJS_OPT view:content This document is largely incomplete. All information is more than subject to change. As Github's =org-mode= parsing is currently broken, it is advised that you view this document locally. * TODO implement noweb-style references to control the order of entanglement. An example of how to do so has been worked out under [[id:D365DE92-82B6-4754-98EA-26E28F1D8916][Installing]]. * Installing :PROPERTIES: :ID: D365DE92-82B6-4754-98EA-26E28F1D8916 :END: - Note taken on [2013-03-01 Fri 01:04] \\ More work to be done here. Figure out exactly what needs to be done and update the high-level to do it. Installation is automatic if done through a package manager such as MELPA; in this case, it is unbefitting to require extra user involvement. But, since this is a /literate programming document/, I'd like to explain how the process could be done manually. If you are viewing this lovely file in Emacs but are still similarly 'eager' about using =stack-mode=, go ahead and =C-c C-c= [fn::which, on a default installation, runs =org-babel-execute-src-block=] the following line. It will take care of installation for you. You will need to confirm execution twice if you have not configured =org-babel-confirm-evaluate= to allow execuation of Emacs Lisp snippets without confirmation. #+call: $install() (This document does not yet add the necessary files to the =load-path=. Please do so manually. What we're basically going to do is this: #+name: $install #+begin_src emacs-lisp :noweb strip-export :results output (defun $d (m) (message (princ m)) (terpri)) <<$install-tangle-buffer>> <<$install-move-to-load-path>> #+end_src oh shit #+name: $install-tangle-buffer #+begin_src emacs-lisp ($d "Tangling stack-mode files...") (org-babel-tangle) ($d "Tangling stack-mode files...done") #+end_src i'm dumb #+name: $install-move-to-load-path #+begin_src emacs-lisp (message "Moving source files to load-path...") #+end_src really dumb #+name: $install-move-to-load-path #+begin_src emacs-lisp (message "More moving...") #+end_src * Resources - [[https://api.stackexchange.com/docs][SX.API v2.1]] - [[http://stackapps.com/apps/oauth/register][StackApps Registration Page]] - [[http://www.emacswiki.org/emacs/ModeTutorial][Creating Major Modes for Emacs]] * Icon 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. - [[file:resources/emacs.svg][Emacs icon]] - [[file:resources/stackexchange.svg][Stack Exchange icon]] * Stack Exchange API Version 2.1 :PROPERTIES: :ID: DC2032C5-BC11-47E2-8DDB-34467C2BC479 :END: Version 2.1 of the Stack Exchange API allows for read-only access (and write-access for comments!) to the Stack Exchange network. The Stack Exchange API uses JSON as its main method of interaction. Available Features: - Write methods (yeah, this needs to be expanded.) ** COMMENT This is meant more as a reference for when I am (or another user is) not online. This is not official documentation, although I hope it is accurate up to [2013-02-28 Thu 17:00]. * Implementation ** Conventions and Terminology *** Code Conventions - All functions will have `stack-network-` / `stack-comment-` / etc. (as appropriate) as a prefix. - Functions which must alter the mode the user ends up in will be further prefixed with `do-` as to flag its impact. - Functions with no arguments will effect its lack thereof with =()=, not =nil=. (While =()= is /precisely/ the same as =nil= in Lisp (and more specifically, Emacs Lisp), it is convention do this. Failure to follow this convention may needlessly confuse potential contributors. *** Document conventions - When referring to a function using =a teletype font= and the first character is a dash, apply the appropriate prefix, e.g. =-do-enter-site= --> =stack-network-do-enter-site=. It becomes tedious to read (not to mention to write!) these functions and variables involved in the same package/section so repetitively. - When explaining a snippet of code, the related code block must be directly beneath or above the explanation. Blank lines should only be used to make logical distinctions in the document. - except in special circumstances, like keymap code. *** Terminology of Emacs Lisp It is expected that the reader has a basic knowledge of Lisp in general. However, there are more than a few terms in this document which are particularly important to Emacs Lisp programming, and are a bit of research to find adequate definitions for. While there are myriad functions and symbols specific to Emacs Lisp programming, these functions are far better documented in Emacs' own 'Info' system. Place point on a symbol you'd like to learn about and use =C-h f= or =C-c v= (for functions or variables, respectively) to find out more. (Hint: it even works in this very document!) - a-list :: a list of conses, usually pairing a symbol with a description or action ** Stack Mode (Entry Point): =stack-mode= Stack mode is /the/ major mode. What do I mean by this? Stack mode is the entry point of the whole package. There is no other way to obtain the full, original functionality of the package without first running =M-x stack-mode=. Stack Mode is the only mode available interactively. It is a dispatcher that decides, based on user preferences, how the whole system shall behave. It provides the basic framework upon which the rest of the package is built. Thus, there are a few packages that it itself requires: #+begin_src emacs-lisp :tangle elisp/stack-mode.el ;; stack-mode.el starts here (require 'json) #+end_src JSON (JavaScript Object Notation) is the standard by which we communicate with Stack Exchange itself. The details of this communication has [[id:DC2032C5-BC11-47E2-8DDB-34467C2BC479][already been discussed]] so I will not repeat myself here. The JSON package provides many utilities for manipulating JSON snippets within Emacs Lisp, and is required for the operation of this package. =json.el= is included with Emacs 24+ (and can easily be obtained from the ELPA if missing). #+begin_src emacs-lisp :tangle elisp/stack-mode.el (require 'request) #+end_src This package also requires =request.el=, a package designed to simplify making HTTP requests. =request.el= was written by [[http://stackoverflow.com/users/727827][SX@tkf]] and is maintained and documented on [[http://tkf.github.com/emacs-request/manual.html][Github]]. The package is also available for automatic install via MELPA. #+begin_src emacs-lisp :tangle elisp/stack-mode.el (defgroup stack-exchange nil "Stack Exchange mode." :group 'environment) #+end_src Simply put, =defgroup= defines a customization group for the graphical interface within Emacs. Since it pulls all of the customizable settings together and how to customize them, it is also useful as a 'wordbank' of sorts for customizing the package manually. Every customizable variable in the entire package is listed here. Every mode needs a hook, so we here define one. This hook is run /after/ stack-mode is finished loading (when called interactively or from Emacs Lisp). #+begin_src emacs-lisp :tangle elisp/stack-mode.el (defvar stack-mode-hook nil) #+end_src In addition to a hook, most if not all major modes define their own keymap. Stack mode as a whole is no exception, but remembering the nature of =stack-mode= as a dispatcher, a keymap seems out of place here. As such, the official keymap for =stack-mode= defines all keys to be nil except those that are necessary for the smooth use of Emacs as an operating system. Such necessary keystrokes include =C-g=, =M-x=, and others. #+begin_src emacs-lisp :tangle elisp/stack-mode.el (defvar stack-mode-map (let ((map (make-sparse-keymap))) map) "Keymap for Stack Exchange major mode. This keymap is not used.") #+end_src All that is left to do now is to define each customizable variable and load all of the child modes. We will define the variables later, and they will be placed in this file. We do this via =require=. #+begin_src emacs-lisp :tangle elisp/stack-mode.el (require 'stack-network-mode) #+end_src We are done here, and =stack-mode= is provided to the user. #+begin_src emacs-lisp :tangle elisp/stack-mode.el (provide 'stack-mode) ;; stack-mode.el ends here #+end_src For features that require unsupported write access, the message shall be displayed: "Version of the Stack Exchange API does not support this action." The functions shall exist (along with their keymaps), but the functionality will be replaced by the display of such a message. When I say minor mode, I may mean a major mode mostly based off of another mode, as these functions don't make any sense in any other context. At all times, the percentage of API requests left can be displayed in the modeline according to the customizable variable (nil|t) =stack-display-API-requests-in-modeline= and shall turn red (if activated) when use exceeds =stack-display-API-color-threshold= (a float in [0, 1], where 0 never changes the color). ** Network Mode: =stack-network-mode= Network mode is a major mode (derived from =special-mode=) that defines functions and binds keys useful for browsing and organizing sites in the Stack Exchange network. Since it is an integrated part of =stack-mode= and dispatches to several sibling modes, it is imperative that =stack-mode= (and all of its child modes) be available upon entering =stack-network-mode=. (For the purposes of testing, =(require 'stack-mode)= has been commented out to avoid =load-path= issues. (I'm really lazy.) In addition, a debugging function has been added to facilitate messaging that would be useful to track down bugs.) #+begin_src emacs-lisp :tangle elisp/stack-network-mode.el ;; stack-network-mode.el starts here ;(require 'stack-mode) (defvar stack-network-display-debug-messages t) (defun stack-network-debug (format &rest args) (if stack-network-display-debug-messages (message format args))) #+end_src Here is the planned interface: #+begin_src emacs-lisp :tangle elisp/stack-network-mode-navtest-buffer.test -*- stack-network -*- 4 unread inbox items 1 unread notification Favorites............................................................. TeX, LaTeX, and Friends tex.sx StackOverflow stackoverflow StackApps stackapps Mathematics math.sx Other Sites........................................................... #+end_src (While not functional, this file is intended to produce a buffer suitable for navigation testing. It should be de-tangled before release.) A mode hook is provided for customizability. I am not sure that this hook is automatically run or not per =special-mode=. (TODO) #+begin_src emacs-lisp :tangle elisp/stack-network-mode.el (defvar stack-network-mode-hook nil) #+end_src *** Default Keymap - Note taken on [2013-02-28 Thu 14:13] \\ Issues with this are in the process of being [[http://stackoverflow.com/questions/15124824/][resolved]]. Currently, keybindings do not act as expected, e.g. pressing =n= will not call =stack-network-next-site=. - n :: next site - p :: previous site - RET :: enter site into [[*Question%20Browse%20Mode:%20%3Dstack-question-browse-mode%3D][Question Browse Mode]] - u :: profile summary (stack-profile-mode ?) - C-u u :: profile summary for user - i :: goto [[*Inbox%20Mode:%20%3Dstack-inbox-mode%3D][Inbox Mode]] #+begin_src emacs-lisp :tangle elisp/stack-network-mode.el (defvar stack-network-mode-map (let ((map (make-keymap))) (define-key map "n" 'stack-network-next-site) (define-key map "p" 'stack-network-previous-site) (define-key map "," 'stack-network-move-site-up) (define-key map "." 'stack-network-move-site-down) (define-key map "j" 'stack-network-jump-to-bookmarks) (define-key map "\C-m" 'stack-network-do-enter-site) ; ret (define-key map "o" 'stack-network-do-enter-site) (define-key map "u" 'stack-network-do-profile-summary) (define-key map "\C-uu" 'stack-network-do-profile-summary-for-user) (define-key map "i" 'stack-network-do-inbox) (define-key map "b" 'stack-network-toggle-bookmark) (define-key map "\C-i" 'stack-network-display-details) ; tab map) "Keymap for Stack Exchange: Network Browser major mode") #+end_src *** Navigation The primary methods of navigation =-next-site=, =-previous-site=, and =-enter-site=. The first two of these three do only what makes sense: they move point up and down the list of available sites. #+begin_src emacs-lisp :tangle elisp/stack-network-mode.el (defun stack-network-next-site () "Move to the next site in the list." (interactive) (stack-network-debug "in next site") (next-line)) (defun stack-network-previous-site () "Move to the previous site in the list." (interactive) (stack-network-debug "in prev site") (previous-line)) (defun stack-network-do-enter-site () "Enter the site at point in another buffer." (interactive) (message "I have no idea what I'm doing") (stack-exchange-question-browse-mode (stack-network-get-site-under-point))) #+end_src If you look at the defintion of =-do-enter-site=, you will notice that the heretofore undefined =-get-site-under-point= is used. As you may have guessed, the purpose of this function is to obtain the 'string representation' of the Stack Exchange site upon which point currently rests. There is as of yet no method of retrieving this dynamically, so a definition-style a-list is made at the very top (=stack-mode.el=) to facilitate easy use. #+begin_src emacs-lisp :tangle elisp/stack-mode.el (defvar stack-exchange-api-key-to-site-alist ; define ) #+end_src *** Conclusion Network mode is the highest-level mode available within =stack-mode=. Its primary purpose is to be a dispatcher for other commands. Since many users reside on exactly one StackExchange site, =stack-mode= should be configurable to support this. #+begin_src emacs-lisp :tangle elisp/stack-network-mode.el (define-derived-mode stack-network-mode special-mode "SX-Network" "Major mode for navigating and organizing sites on the Stack Exchange Network.") (provide 'stack-network-mode) ;; stack-network-mode.el ends here #+end_src ** Question Browse Mode: =stack-question-browse-mode= - Note taken on [2013-02-27 Wed 15:28] \\ Upvoting and downvoting could easily be supported in this mode, but shouldn't be. How can you possibly upvote or downvote something just be reading the title? This mode is buffer-read-only. Font Lock - green :: answered question - bold red :: open bounty - bold :: unanswered Sorting and Filtering - should be able to sort Green font-lock for answered questions, red for unanswered. Question starring, dispatcher-y feel. Display statistical information on top: - if point is on a question, display the user who asked it, their reputation, the last revisor, their reputation, tags, views, answers, accept status, and votes. (The following example is from [[http://tex.stackexchange.com/questions/83970/auctex-preview-latex-and-ghostscript-emacs][one of my own questions]] retrieved [2013-02-27 Wed 15:36].) #+begin_example Full title: AUCTeX, preview-latex, and Ghostscript (Emacs) | Asker: vermiculus (572) Bounty: 50 Answers: 1 (Accepted) Active: [2013-02-27 Wed 15:44] Tags: emacs auctex preview ghostscript #+end_example - if point is not on a question, display site trends in general #+begin_example Site: TeX, LaTeX, and Friends Users: 400 Unanswered: 15 (0.003) #+end_example - users is 200 rep or more - the number beside unanswered is a float in [0, 1] - 0 :: all questions are answered - 1 :: no questions are answered *** Default Keymap - n :: next question (move point down) - p :: previous question - RET :: enter question ([[*Question%20Detail%20Mode:%20%3Dstack-question-detail-mode%3D][Question Detail Mode]]) - s :: star a question - A :: ask question ([[*Ask%20Mode:%20%3Dstack-ask-mode%3D][Ask Mode]]) - q :: go back to [[*Network%20Mode:%20%3Dstack-network-mode%3D][Network Mode]] - m :: switch to meta ** Question Detail Mode: =stack-question-detail-mode= Outline-mode-like question voting, comment voting/flagging This mode is buffer-read-only. The question and each answer are top-level nodes. *** Default Keymap - = :: upvote question/answer - - :: downvote - s :: star - C-u s :: save offline as an =org= node. (The archive file is kept in .emacs.d) ** Comment Mode: =stack-comment-mode= Minor mode for use atop markdown-mode. Valid for Questions and Answers; just a small window that would open up below in comment-mode. Should support mentions. *** Default Keymap - C-c C-c :: Commit comment. - C-c C-k :: Cancel comment. - TAB :: Expand username, if possible (must be after =@=) ** Ask Mode: =stack-ask-mode= A minor mode atop =markdown-mode=, adding support for tagging. Ask a question. ** Inbox Mode: =stack-inbox-mode= yeah. View notifications and stuff.