├── LICENSE ├── README.md ├── demo.gif └── htmlz.el /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Zeke Medley 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # htmlz-mode 2 | 3 | ![a demo of me using htmlz mode](demo.gif) 4 | 5 | ``` 6 | M-x htmlz-mode 7 | ``` 8 | 9 | This is a small, dead-simple, Emacs minor mode that shows a live 10 | preview of a html document as you work on it. This is inspired by the 11 | live preview option for the Brackets text editor. 12 | 13 | ## Usage 14 | 15 | Download the emacs lisp file and move it somewhere that your Emacs 16 | will see and evaluate it on startup. Then, run `M-x htmlz-mode` while 17 | editing an html file to open a live preview in your default browser. 18 | 19 | ## Why 20 | 21 | I made this because I occasionally find myself in the position of 22 | editing some html and wanting to see a simple live preview of it as I 23 | work. If you do this sort of thing seriously, I'd imagine that there 24 | are much better ways to manage this, but as a non web developer who 25 | occasionally needs to write some html I've found this to be quite 26 | pleasant. 27 | 28 | ## How it works 29 | 30 | This is my first Emacs Lisp program ever so it's likely to be less 31 | than ideal in many ways. I'd love feedback. 32 | 33 | In a nutshell, htmlz-mode works like this: 34 | 35 | 1. Create a temporary html file. 36 | 2. Start a websocket server in Emacs. 37 | 3. Place some Javascript in that file that opens a websocket 38 | connection with Emacs. 39 | 4. Open the temporary file in the default browser. 40 | 5. When the buffer contents change send the new buffer contents over 41 | the websocket connection from Emacs. 42 | 6. When a new websocket message is received in the browser, update the 43 | page contents with that message. 44 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xekez/htmlz-mode/013464fc7fc69d52519baf9ba40e20a0c3a031d5/demo.gif -------------------------------------------------------------------------------- /htmlz.el: -------------------------------------------------------------------------------- 1 | ;;; htmlz --- Simple real-time Emacs html preview 2 | 3 | ;; Copyright (C) 2020 Zeke Medley 4 | 5 | ;; Author: Zeke Medley 6 | ;; Keywords: html 7 | ;; Version: 0.1 8 | ;; URL: https://github.com/ZekeMedley/htmlz 9 | 10 | ;;; Commentary: 11 | ;; htmlz-mode is an Emacs mode that gives a live preview while you 12 | ;; edit html. There are other programs that do this and they are 13 | ;; reasonably good I'm sure, but they all seem a little more 14 | ;; complicated than I'd actually like. This doesn't have any 15 | ;; shortcuts or really add anything other than a live preview that 16 | ;; starts if you run M-x htmlz-mode from a buffer. 17 | 18 | ;; 19 | ;;; Code: 20 | 21 | (defun require-package (package) 22 | "Install given PACKAGE if it was not installed before." 23 | (if (package-installed-p package) 24 | t 25 | (progn 26 | (unless (assoc package package-archive-contents) 27 | (package-refresh-contents)) 28 | (package-install package)))) 29 | 30 | (defun htmlz-init-dependencies () 31 | "Initialize htmlz dependencies." 32 | (require-package 'websocket) 33 | (require 'package) 34 | (require 'browse-url) 35 | (require 'websocket)) 36 | 37 | (defvar htmlz-opened-websocket nil 38 | "The currently open websocket.") 39 | (defvar htmlz-the-server nil 40 | "The current websocket server. Created after the client opens a websocket connection with us.") 41 | 42 | (defun htmlz-get-current-extension () 43 | "Gets the file extension for the current buffer." 44 | (car (reverse (split-string (buffer-file-name) "\\.")))) 45 | 46 | (defun htmlz-get-current-dir () 47 | "Gets the directory for the current buffer." 48 | (file-name-directory (buffer-file-name))) 49 | 50 | (defun htmlz-get-filename () 51 | "Gets the filename for the current buffer." 52 | (concat (htmlz-get-current-dir) "~htmlz-tmp." (htmlz-get-current-extension))) 53 | 54 | (defun htmlz-init-file () 55 | "Create a file for htmlz to load." 56 | (write-region " 57 | " 67 | nil (htmlz-get-filename) 68 | nil 'quiet)) 69 | 70 | (defun htmlz-open-file () 71 | "Opens the htmlz file in the default browser." 72 | (browse-url (htmlz-get-filename))) 73 | 74 | (defun htmlz-init-server () 75 | "Initialize the htmlz websocket server." 76 | (if htmlz-the-server 77 | (htmlz-close-server)) 78 | (setq htmlz-the-server 79 | (websocket-server 80 | 3000 81 | :host 'local 82 | :on-open (lambda (ws) (setq htmlz-opened-websocket ws)) 83 | :on-close (lambda (ws) (setq htmlz-opened-websocket nil))))) 84 | 85 | (defun htmlz-send-buffer-contents () 86 | "Sends the contents of the current buffer to the browser." 87 | (if htmlz-opened-websocket 88 | (websocket-send-text htmlz-opened-websocket (buffer-string)) 89 | (message "error: no open websocket connection"))) 90 | 91 | (defun htmlz-close-server () 92 | "Closes our websocket server." 93 | (websocket-server-close htmlz-the-server)) 94 | 95 | (defun htmlz-start () 96 | "Startup htmlz." 97 | (htmlz-init-dependencies) 98 | (htmlz-init-file) 99 | (htmlz-init-server) 100 | (htmlz-open-file) 101 | (add-hook 'post-command-hook 'htmlz-send-buffer-contents nil 'local)) 102 | 103 | (defun htmlz-finish () 104 | "Clean up htmlz." 105 | (htmlz-close-server) 106 | (delete-file (htmlz-get-filename)) 107 | (remove-hook 'post-command-hook 'htmlz-send-buffer-contents 'local)) 108 | 109 | (define-minor-mode htmlz-mode 110 | "The htmlz minor mode" 111 | :lighter " htmlz" 112 | ; This is executed after the macro has toggled the mode. This means 113 | ; that the mode not being on implies that it has been turned off and 114 | ; the mode being on implies that it has been turned on. 115 | (if (not htmlz-mode) 116 | (htmlz-finish) 117 | (htmlz-start))) 118 | 119 | (provide 'htmlz-mode) 120 | ;;; htmlz.el ends here 121 | --------------------------------------------------------------------------------