├── .gitignore ├── Cask ├── README.md ├── docs └── _images │ └── native-fontification.png ├── sphinx-mode.el └── sphinx-src.el /.gitignore: -------------------------------------------------------------------------------- 1 | /.cask/ 2 | -------------------------------------------------------------------------------- /Cask: -------------------------------------------------------------------------------- 1 | (source melpa) 2 | (source gnu) 3 | 4 | (package "sphinx-mode" "0.1.0" 5 | "Minor mode providing sphinx support.") 6 | 7 | (depends-on "dash" "2.14.1") 8 | (depends-on "f" "0.20.0") 9 | 10 | (development 11 | (depends-on "buttercup") 12 | (depends-on "shut-up")) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sphinx-mode 2 | 3 | Minor mode providing sphinx support. 4 | 5 | # Usage 6 | 7 | In your `rst-mode` buffer call `M-x sphinx-mode` to add all the extra features. 8 | 9 | # Features 10 | 11 | * [x] Add fontification support 12 | * [ ] Add support for insertion of labels 13 | * [x] Add support for insertion of references (`:ref:`) (with a menu of available labels) 14 | * [x] Add support for jumping to a label/reference 15 | * [ ] Add footnote (`[#note]`) support 16 | * [ ] Add reference (`[ref]`) support 17 | 18 | ## Native code-block fontification 19 | 20 | Similar to `org-mode`, we provide native emacs fontification of code blocks, 21 | which can be tweaked by changing `sphinx-code-block-face`. 22 | 23 | ![fontification](docs/_images/native-fontification.png) 24 | 25 | ## Compile and view Sphinx projects 26 | 27 | In `rst-mode`, `rst-compile` prompts you to run `rst2html`, but Sphinx projects should be compiled by running `make` in the project directory. 28 | 29 | `sphinx-compile` (`C-c C-x C-c`) locates the directory containing the Makefile and runs `make $FORMAT`, prompting you for a `$FORMAT` in the minibuffer (default html). 30 | 31 | `sphinx-compile-and-view` (`C-c C-x C-v`) runs `sphinx-compile`, then opens with `xdg-open` the compiled version of the source file being visited by the current buffer. 32 | -------------------------------------------------------------------------------- /docs/_images/native-fontification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fuco1/sphinx-mode/038a9195b00636d38aa2fc3cf6cbff5cf84e0561/docs/_images/native-fontification.png -------------------------------------------------------------------------------- /sphinx-mode.el: -------------------------------------------------------------------------------- 1 | ;;; sphinx-mode.el --- Minor mode providing sphinx support. 2 | 3 | ;; Copyright (C) 2016 Matúš Goljer 4 | 5 | ;; Author: Matúš Goljer 6 | ;; Maintainer: Matúš Goljer 7 | ;; Created: 11th September 2016 8 | ;; Keywords: languages 9 | ;; Package-Requires: ((dash "2.14.1") (f "0.20")) 10 | 11 | ;; This program is free software; you can redistribute it and/or 12 | ;; modify it under the terms of the GNU General Public License 13 | ;; as published by the Free Software Foundation; either version 3 14 | ;; of the License, or (at your option) any later version. 15 | 16 | ;; This program is distributed in the hope that it will be useful, 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ;; GNU General Public License for more details. 20 | 21 | ;; You should have received a copy of the GNU General Public License 22 | ;; along with this program. If not, see . 23 | 24 | ;;; Commentary: 25 | 26 | ;;; Code: 27 | 28 | (require 'f) 29 | (require 'dash) 30 | (require 'sphinx-src) 31 | 32 | (defgroup sphinx () 33 | "Sphinx group." 34 | :group 'editing 35 | :prefix "sphinx-") 36 | 37 | (defface sphinx-code-block-face 38 | '((t (:inherit fixed-pitch))) 39 | "Face used for code blocks.") 40 | 41 | (defun sphinx-fontify-code-block (limit) 42 | "Fontify code blocks from point to LIMIT." 43 | (condition-case nil 44 | (while (re-search-forward "\.\. code-block:: \\(.*?\\)\n\n\\( +\\)" limit t) 45 | (let* ((block-start (match-end 0)) 46 | (block-highlight-start (match-beginning 2)) 47 | (lang (match-string 1)) 48 | (prefix (match-string 2)) 49 | (prefix-search (concat "^" prefix)) 50 | block-end) 51 | (while (and 52 | (< (forward-line) 1) 53 | (or (looking-at-p prefix-search) 54 | (looking-at-p "^$")))) 55 | (while (prog1 (looking-at-p "^$") 56 | (forward-line -1))) 57 | (forward-line) 58 | (setq block-end (1- (point))) 59 | (sphinx-src-font-lock-fontify-block lang block-start block-end) 60 | (add-face-text-property 61 | block-highlight-start block-end 62 | 'sphinx-code-block-face 'append))) 63 | (error nil))) 64 | 65 | 66 | (defun sphinx--get-refs-from-buffer (&optional buffer file-name) 67 | "Get all refs from BUFFER. 68 | 69 | If BUFFER is not given use the `current-buffer'." 70 | (setq buffer (current-buffer)) 71 | (let (re) 72 | (with-current-buffer buffer 73 | (save-excursion 74 | (save-restriction 75 | (widen) 76 | (goto-char (point-min)) 77 | (while (re-search-forward "^.. _\\(.*?\\):\\s-*\\(.*\\)?$" nil t) 78 | (push (list :name (match-string-no-properties 1) 79 | :ref (match-string-no-properties 2) 80 | :file (or (buffer-file-name) file-name) 81 | :point (point)) re))))) 82 | (nreverse re))) 83 | 84 | ;; TODO: add caching 85 | (defun sphinx--get-refs () 86 | "Get all available refs in the project." 87 | (let* ((root (locate-dominating-file (buffer-file-name) "conf.py")) 88 | (sources (f-entries root (lambda (file) (f-ext-p file "rst")) 'recursive)) 89 | (re)) 90 | (-each sources 91 | (lambda (source) 92 | (when (file-exists-p source) 93 | (if (get-file-buffer source) 94 | (with-current-buffer (find-file-noselect source) 95 | (push (sphinx--get-refs-from-buffer) re)) 96 | (with-temp-buffer 97 | (insert-file-contents-literally source) 98 | (push (sphinx--get-refs-from-buffer nil source) re)))))) 99 | (apply '-concat (nreverse re)))) 100 | 101 | (defun sphinx-insert-ref (ref &optional title) 102 | "Insert a REF with a TITLE." 103 | (interactive 104 | (let ((ref (completing-read 105 | "Ref: " (-map (lambda (r) 106 | (plist-get r :name)) 107 | (sphinx--get-refs))))) 108 | (list ref (read-from-minibuffer "Title: " nil nil nil nil ref)))) 109 | (insert (if (and (stringp title) 110 | (not (equal title ""))) 111 | (format ":ref:`%s<%s>`" title ref) 112 | (format ":ref:`%s`" ref)))) 113 | 114 | ;; TODO: add better default 115 | (defun sphinx-goto-ref (ref) 116 | (interactive 117 | (let* ((default (cond 118 | ((thing-at-point-looking-at "`\\(.*?\\)`") 119 | (match-string 1)) 120 | (t (symbol-at-point)))) 121 | (ref (completing-read 122 | (format "Ref [default %s]: " default) 123 | (-map (lambda (r) 124 | (plist-get r :name)) 125 | (sphinx--get-refs)) 126 | nil nil nil nil default))) 127 | (list ref))) 128 | (-when-let (target (--first (equal (plist-get it :name) ref) (sphinx--get-refs))) 129 | (find-file (plist-get target :file)) 130 | (goto-char (plist-get target :point)))) 131 | 132 | (defun sphinx-compile () 133 | "Run 'make' in project root directory and prompt user for target format. 134 | 135 | Return absolute path of compiled version of current source file. 136 | 137 | To see list of target formats, run 'make help' in a shell." 138 | (interactive) 139 | (let ((buffer-name-base (file-name-base (buffer-file-name))) 140 | (project-root-dir (locate-dominating-file buffer-file-name "Makefile")) 141 | (target-format (read-string "Make (default html): " nil nil "html" nil))) 142 | (let* ((file-rel-path (file-relative-name buffer-file-name project-root-dir)) 143 | (build-directory (expand-file-name (concat project-root-dir "_build/" target-format "/")))) 144 | (shell-command (concat "make -C " project-root-dir " " target-format)) 145 | (concat build-directory 146 | (file-name-directory file-rel-path) 147 | (car 148 | (file-name-all-completions 149 | (file-name-base file-rel-path) 150 | (concat build-directory (file-name-directory file-rel-path)))))))) 151 | 152 | (defun sphinx-compile-and-view () 153 | "Run ‘sphinx-compile’ and view compiled version of current source file with 'xdg-open'." 154 | (interactive) 155 | (shell-command (concat "xdg-open " (sphinx-compile)))) 156 | 157 | (defvar sphinx-mode-map 158 | (let ((map (make-sparse-keymap))) 159 | (define-key map (kbd "M-'") 'sphinx-goto-ref) 160 | (define-key map (kbd "C-c TAB") 'sphinx-insert-ref) 161 | (define-key map (kbd "C-c C-x C-c") 'sphinx-compile) 162 | (define-key map (kbd "C-c C-x C-v") 'sphinx-compile-and-view) 163 | map) 164 | "Sphinx-mode keymap.") 165 | 166 | ;;;###autoload 167 | (define-minor-mode sphinx-mode 168 | "Sphinx minor mode." 169 | :init-value nil 170 | :lighter "sphinx " 171 | :keymap 'sphinx-mode-map 172 | ;; add native fontification support 173 | (if sphinx-mode 174 | (font-lock-add-keywords nil '((sphinx-fontify-code-block))) 175 | (font-lock-remove-keywords nil '((sphinx-fontify-code-block))))) 176 | 177 | (provide 'sphinx-mode) 178 | ;;; sphinx-mode.el ends here 179 | -------------------------------------------------------------------------------- /sphinx-src.el: -------------------------------------------------------------------------------- 1 | ;;; sphinx-src.el --- Native code block support. 2 | 3 | ;; Copyright (C) 2016 Matúš Goljer 4 | 5 | ;; Author: Matúš Goljer 6 | ;; Maintainer: Matúš Goljer 7 | ;; Created: 11th September 2016 8 | ;; Keywords: languages 9 | 10 | ;; This program is free software; you can redistribute it and/or 11 | ;; modify it under the terms of the GNU General Public License 12 | ;; as published by the Free Software Foundation; either version 3 13 | ;; of the License, or (at your option) any later version. 14 | 15 | ;; This program is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with this program. If not, see . 22 | 23 | ;;; Commentary: 24 | 25 | ;;; Code: 26 | 27 | ;; This is copy-pasted from org-src.el 28 | (defcustom sphinx-src-lang-modes 29 | '(("ocaml" . tuareg) ("elisp" . emacs-lisp) ("ditaa" . artist) 30 | ("asymptote" . asy) ("dot" . fundamental) ("sqlite" . sql) 31 | ("calc" . fundamental) ("C" . c) ("cpp" . c++) ("C++" . c++) 32 | ("screen" . shell-script) ("shell" . sh) ("bash" . sh)) 33 | "Alist mapping languages to their major mode. 34 | The key is the language name, the value is the string that should 35 | be inserted as the name of the major mode. For many languages this is 36 | simple, but for language where this is not the case, this variable 37 | provides a way to simplify things on the user side. 38 | For example, there is no ocaml-mode in Emacs, but the mode to use is 39 | `tuareg-mode'." 40 | :group 'sphinx 41 | :type '(repeat 42 | (cons 43 | (string "Language name") 44 | (symbol "Major mode")))) 45 | 46 | ;; This is copy-pasted from org-src.el 47 | (defun sphinx-src--get-lang-mode (lang) 48 | "Return major mode that should be used for LANG. 49 | LANG is a string, and the returned major mode is a symbol." 50 | (intern 51 | (concat 52 | (let ((l (or (cdr (assoc lang sphinx-src-lang-modes)) lang))) 53 | (if (symbolp l) (symbol-name l) l)) 54 | "-mode"))) 55 | 56 | ;; This is copy-pasted from org-src.el 57 | (defun sphinx-src-font-lock-fontify-block (lang start end) 58 | "Fontify code block. 59 | 60 | LANG is the language used in the block. 61 | 62 | START and END specify the block position." 63 | (let ((lang-mode (sphinx-src--get-lang-mode lang))) 64 | (when (fboundp lang-mode) 65 | (let ((string (buffer-substring-no-properties start end)) 66 | (modified (buffer-modified-p)) 67 | (org-buffer (current-buffer)) pos next) 68 | (remove-text-properties start end '(face nil)) 69 | (with-current-buffer 70 | (get-buffer-create 71 | (concat " org-src-fontification:" (symbol-name lang-mode))) 72 | (delete-region (point-min) (point-max)) 73 | (insert string " ") ;; so there's a final property change 74 | (unless (eq major-mode lang-mode) (funcall lang-mode)) 75 | ;; Avoid `font-lock-ensure', which does not display fonts in 76 | ;; source block. 77 | (font-lock-fontify-buffer) 78 | (setq pos (point-min)) 79 | (while (setq next (next-single-property-change pos 'face)) 80 | (put-text-property 81 | (+ start (1- pos)) (1- (+ start next)) 'face 82 | (get-text-property pos 'face) org-buffer) 83 | (setq pos next))) 84 | (add-text-properties 85 | start end 86 | '(font-lock-fontified t fontified t font-lock-multiline t)) 87 | (set-buffer-modified-p modified))))) 88 | 89 | (provide 'sphinx-src) 90 | ;;; sphinx-src.el ends here 91 | --------------------------------------------------------------------------------