From 9a5e25a4326d6a60d87fd960715a82c262619032 Mon Sep 17 00:00:00 2001 From: Bent Cardan Date: Tue, 28 May 2013 07:28:56 -0400 Subject: [PATCH] mode for Jade and Stylus jade and stylus rock! drop that mode in! --- mode/jade-mode/README.md | 88 +++++++++++++++++++++ mode/jade-mode/example.jade | 27 +++++++ mode/jade-mode/jade-mode.el | 116 +++++++++++++++++++++++++++ mode/jade-mode/stylus-mode.el | 113 ++++++++++++++++++++++++++ mode/jade-mode/sws-mode.el | 144 ++++++++++++++++++++++++++++++++++ 5 files changed, 488 insertions(+) create mode 100644 mode/jade-mode/README.md create mode 100644 mode/jade-mode/example.jade create mode 100644 mode/jade-mode/jade-mode.el create mode 100644 mode/jade-mode/stylus-mode.el create mode 100644 mode/jade-mode/sws-mode.el diff --git a/mode/jade-mode/README.md b/mode/jade-mode/README.md new file mode 100644 index 0000000000..c8dd93dee8 --- /dev/null +++ b/mode/jade-mode/README.md @@ -0,0 +1,88 @@ +# sws-mode + +## major mode for jade-mode and stylus-mode + +__S__ignificant __W__hitespace __M__ode. Because Jade and Stylus are both 'significant white space' languages, sws-mode can be used to edit both types of files + +Lines can be indented or un-indented (is that a word?) with tab, shift-tab respectively. Indentation will look at the proceeding line and not indent more than 1 level of nesting below it. + + html + body + #container + .content + ^ + |----cursor anywhere on this line except at beginning of text, press tab or S-tab + + html + body + #container + .content + ^ + |---- cursor moves to beginning of text...once cursor is at beginning of text, press tab + + html + body + #container + .content + ^ + |---- now line is maximum indented, press tab again + + html + body + #container + .content + ^ + |---- line moves to minimum indentation level (no indentation) + +Regions can be indentend in a similar way; however, this is still buggy... + +Since jade and stylus nesting is somewhat related to sexp layout I hope to have sexp related selection & manipulation working in the future. See `jade-highlight-sexp` for an example + +## key bindings + + - [tab] if region is active, do 'smart indent' on region. otherwise, move cursor to beginning of line text. If cursor already at beginning of line text, do 'smart indent' on line. + - [shift-tab] if region is active, do 'smart dedent' on region. otherwise, move cursor to beginning of line text. If cursor already at beginning of line text, do 'smart dedent' on line. + + +### jade-mode + +Emacs major mode for [jade template](http://github.com/visionmedia/jade) highlighting. This mode extends sws-mode to provide some rudimentary syntax highlighting. + +Still in very early stages. Some simple highlighting of tags, ids, and classes works; however, it highlights incorrectly into the javascript code and plain text code as well. + + +I would like to get the highlighting working better. Also note javascript highlighting with either js2-mode or espresso-mode should be possible...I'm a major major-mode writing noob so it'll take a while. + +### stylus-mode +I'm not sure yet how to highlight .styl files, so for now, just use sws-mode when editing stylus mode. + +## Installation instructions + +Copy the jade-mode.el and sws-mode.el to some directory on your computer. I put mine under `~/code/jade-mode` and sym-link the jade-mode folder into `~/.emacs.d/vendor/`. You can just as easily put the folder itself under '~/.emacs.d/vendor/` + +Add the following lines to any of your initialization files + + (add-to-list 'load-path "~/.emacs.d/vendor/jade-mode") + (require 'sws-mode) + (require 'jade-mode) + (add-to-list 'auto-mode-alist '("\\.styl$" . sws-mode)) + (add-to-list 'auto-mode-alist '("\\.jade$" . jade-mode)) + +### Flymake support + +if you want to add flymake support for jade files: + + (defun flymake-jade-init () + (let* ((temp-file (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-intemp)) + (local-file (file-relative-name + temp-file + (file-name-directory buffer-file-name))) + (arglist (list local-file))) + (list "jade" arglist))) + (setq flymake-err-line-patterns + (cons '("\\(.*\\): \\(.+\\):\\([[:digit:]]+\\)$" + 2 3 nil 1) + flymake-err-line-patterns)) + (add-to-list 'flymake-allowed-file-name-masks + '("\\.jade\\'" flymake-jade-init)) diff --git a/mode/jade-mode/example.jade b/mode/jade-mode/example.jade new file mode 100644 index 0000000000..1a11995841 --- /dev/null +++ b/mode/jade-mode/example.jade @@ -0,0 +1,27 @@ +!!! +html(lang="en") + head + title My page + body.bp + #container + #header + h1.page-title= My.awesome. page #is awesome# + #nav + ul#nav_list something sweet + li + #content + - if (youAreUsingJade) + p You are amazing + - else + p Get on it! + form + input(type: "text", name='user[name]', readonly: true, disabled) + #footer + #copywrite-text= locals + + + + + + + diff --git a/mode/jade-mode/jade-mode.el b/mode/jade-mode/jade-mode.el new file mode 100644 index 0000000000..bfa9db4b6b --- /dev/null +++ b/mode/jade-mode/jade-mode.el @@ -0,0 +1,116 @@ +;;; jade-mode.el --- Major mode for editing .jade files +;;; +;;; URL: https://github.com/brianc/jade-mode +;;; Author: Brian M. Carlson and other contributors +;;; Package-Requires: ((sws-mode "0")) +;;; +;;; copied from http://xahlee.org/emacs/elisp_syntax_coloring.html +(require 'font-lock) +(require 'sws-mode) + +(defun jade-debug (string &rest args) + "Prints a debug message" + (apply 'message (append (list string) args))) + +(defmacro jade-line-as-string () + "Returns the current line as a string." + `(buffer-substring (point-at-bol) (point-at-eol))) + + +(defun jade-empty-line-p () + "If line is empty or not." + (= (point-at-eol) (point-at-bol))) + +(defun jade-blank-line-p () + "If line contains only spaces." + (string-match-p "^[ ]*$" (jade-line-as-string))) + +;; command to comment/uncomment text +(defun jade-comment-dwim (arg) + "Comment or uncomment current line or region in a smart way. +For detail, see `comment-dwim'." + (interactive "*P") + (require 'newcomment) + (let ( + (comment-start "//") (comment-end "") + ) + (comment-dwim arg))) + +(defconst jade-keywords + (eval-when-compile + (regexp-opt + '("if" "else" "for" "in" "each" "case" "when" "default" "block" "extends" + "include" "yield" "mixin") 'words)) + "Jade keywords.") + +(defvar jade-font-lock-keywords + `((,"!!!\\|doctype\\( ?[A-Za-z0-9\-\_]*\\)?" 0 font-lock-comment-face) ;; doctype + (,jade-keywords . font-lock-keyword-face) ;; keywords + (,"#\\(\\w\\|_\\|-\\)*" . font-lock-variable-name-face) ;; id + (,"\\(?:^[ {2,}]*\\(?:[a-z0-9_:\\-]*\\)\\)?\\(#[A-Za-z0-9\-\_]*[^ ]\\)" 1 font-lock-variable-name-face) ;; id + (,"\\(?:^[ {2,}]*\\(?:[a-z0-9_:\\-]*\\)\\)?\\(\\.[A-Za-z0-9\-\_]*\\)" 1 font-lock-type-face) ;; class name + (,"^[ {2,}]*[a-z0-9_:\\-]*" 0 font-lock-function-name-face))) ;; tag name + +;; syntax table +(defvar jade-syntax-table + (let ((syn-table (make-syntax-table))) + (modify-syntax-entry ?\/ ". 12b" syn-table) + (modify-syntax-entry ?\n "> b" syn-table) + (modify-syntax-entry ?' "\"" syn-table) + syn-table) + "Syntax table for `jade-mode'.") + +(defun jade-region-for-sexp () + "Selects the current sexp as the region" + (interactive) + (beginning-of-line) + (let ((ci (current-indentation))) + (push-mark nil nil t) + (while (> (jade-next-line-indent) ci) + (next-line) + (end-of-line)))) + +(defvar jade-mode-map (make-sparse-keymap)) +;;defer to sws-mode +;;(define-key jade-mode-map [S-tab] 'jade-unindent-line) + +;; mode declaration +;;;###autoload +(define-derived-mode jade-mode sws-mode + "Jade" + "Major mode for editing jade node.js templates" + :syntax-table jade-syntax-table + + (setq tab-width 2) + + (setq mode-name "Jade") + (setq major-mode 'jade-mode) + + ;; comment syntax + (set (make-local-variable 'comment-start) "// ") + + ;; default tab width + (setq sws-tab-width 2) + (make-local-variable 'indent-line-function) + (setq indent-line-function 'sws-indent-line) + (make-local-variable 'indent-region-function) + + (setq indent-region-function 'sws-indent-region) + + (setq indent-tabs-mode nil) + + ;; keymap + (use-local-map jade-mode-map) + + ;; modify the keymap + (define-key jade-mode-map [remap comment-dwim] 'jade-comment-dwim) + + ;; highlight syntax + (setq font-lock-defaults '(jade-font-lock-keywords))) + + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.jade$" . jade-mode)) + +(provide 'jade-mode) +;;; jade-mode.el ends here diff --git a/mode/jade-mode/stylus-mode.el b/mode/jade-mode/stylus-mode.el new file mode 100644 index 0000000000..46cc196249 --- /dev/null +++ b/mode/jade-mode/stylus-mode.el @@ -0,0 +1,113 @@ +;;; stylus-mode.el --- Major mode for editing .jade files +;;; +;;; URL: https://github.com/brianc/jade-mode +;;; Author: Brian M. Carlson and other contributors +;;; Package-Requires: ((sws-mode "0")) +;;; +;;; copied from http://xahlee.org/emacs/elisp_syntax_coloring.html +(require 'font-lock) +(require 'sws-mode) + +(defun stylus-debug (string &rest args) + "Prints a debug message" + (apply 'message (append (list string) args))) + +(defmacro stylus-line-as-string () + "Returns the current line as a string." + `(buffer-substring (point-at-bol) (point-at-eol))) + + +(defun stylus-empty-line-p () + "If line is empty or not." + (= (point-at-eol) (point-at-bol))) + +(defun stylus-blank-line-p () + "If line contains only spaces." + (string-match-p "^[ ]*$" (stylus-line-as-string))) + +(defconst stylus-colours + (eval-when-compile + (regexp-opt + '("black" "silver" "gray" "white" "maroon" "red" + "purple" "fuchsia" "green" "lime" "olive" "yellow" "navy" + "blue" "teal" "aqua"))) + "Stylus keywords.") + +(defconst stylus-keywords + (eval-when-compile + (regexp-opt + '("return" "if" "else" "unless" "for" "in" "true" "false"))) + "Stylus keywords.") + +(defvar stylus-font-lock-keywords + `( + (,"^[ {2,}]+[a-z0-9_:\\-]+[ ]" 0 font-lock-variable-name-face) + (,"\\(::?\\(root\\|nth-child\\|nth-last-child\\|nth-of-type\\|nth-last-of-type\\|first-child\\|last-child\\|first-of-type\\|last-of-type\\|only-child\\|only-of-type\\|empty\\|link\\|visited\\|active\\|hover\\|focus\\|target\\|lang\\|enabled\\|disabled\\|checked\\|not\\)\\)*" . font-lock-type-face) ;; pseudoSelectors + (,(concat "[^_$]?\\<\\(" stylus-colours "\\)\\>[^_]?") + 0 font-lock-constant-face) + (,(concat "[^_$]?\\<\\(" stylus-keywords "\\)\\>[^_]?") + 0 font-lock-keyword-face) + (,"\\([.0-9]+:?\\(em\\|ex\\|px\\|mm\\|cm\\|in\\|pt\\|pc\\|deg\\|rad\\|grad\\|ms\\|s\\|Hz\\|kHz\\|rem\\|%\\)\\)" 0 font-lock-constant-face) + (,"#\\w+" 0 font-lock-keyword-face) + (,"$\\w+" 0 font-lock-variable-name-face) + )) + +(defvar stylus-syntax-table + (let ((syntable (make-syntax-table))) + (modify-syntax-entry ?\/ ". 124b" syntable) + (modify-syntax-entry ?* ". 23" syntable) + (modify-syntax-entry ?\n "> b" syntable) + (modify-syntax-entry ?' "\"" syntable) + syntable) + "Syntax table for `stylus-mode'.") + +(defun stylus-region-for-sexp () + "Selects the current sexp as the region" + (interactive) + (beginning-of-line) + (let ((ci (current-indentation))) + (push-mark nil nil t) + (while (> (stylus-next-line-indent) ci) + (next-line) + (end-of-line)))) + +(defvar stylus-mode-map (make-sparse-keymap)) +;;defer to sws-mode +;;(define-key stylus-mode-map [S-tab] 'stylus-unindent-line) + +;; mode declaration +;;;###autoload +(define-derived-mode stylus-mode sws-mode + "Stylus" + "Major mode for editing stylus node.js templates" + (setq tab-width 2) + + (setq mode-name "Stylus") + (setq major-mode 'stylus-mode) + + ;; syntax table + (set-syntax-table stylus-syntax-table) + + ;; highlight syntax + (setq font-lock-defaults '(stylus-font-lock-keywords)) + + ;; comments + (set (make-local-variable 'comment-start) "//") + (set (make-local-variable 'comment-end) "") + + ;; default tab width + (setq sws-tab-width 2) + (make-local-variable 'indent-line-function) + (setq indent-line-function 'sws-indent-line) + (make-local-variable 'indent-region-function) + + (setq indent-region-function 'sws-indent-region) + + ;; keymap + (use-local-map stylus-mode-map)) + +;;;###autoload +(add-to-list 'auto-mode-alist '("\\.styl$" . stylus-mode)) + +(provide 'stylus-mode) +;;; stylus-mode.el ends here diff --git a/mode/jade-mode/sws-mode.el b/mode/jade-mode/sws-mode.el new file mode 100644 index 0000000000..094211490b --- /dev/null +++ b/mode/jade-mode/sws-mode.el @@ -0,0 +1,144 @@ +;;; sws-mode.el --- (S)ignificant (W)hite(S)pace mode +;;; +;;; URL: https://github.com/brianc/jade-mode +;;; Author: Brian M. Carlson and other contributors +;;; +(require 'font-lock) + +(defvar sws-tab-width 2) + +(defmacro sws-line-as-string () + "Returns the current line as a string." + `(buffer-substring (point-at-bol) (point-at-eol))) + +(defun sws-previous-indentation () + "Gets indentation of previous line" + (save-excursion + (forward-line -1) + (if (bobp) 0 + (progn + (while (and (looking-at "^[ \t]*$") (not (bobp))) (forward-line -1)) + (current-indentation))))) + +(defun sws-max-indent () + "Calculates max indentation" + (+ (sws-previous-indentation) sws-tab-width)) + +(defun sws-empty-line-p () + "If line is completely empty" + (= (point-at-bol) (point-at-eol))) + +(defun sws-point-to-bot () + "Moves point to beginning of text" + (beginning-of-line-text)) + +(defun sws-do-indent-line () + "Performs line indentation" + ;;if we are not tabbed out past max indent + (if (sws-empty-line-p) + (indent-to (sws-max-indent)) + (if (< (current-indentation) (sws-max-indent)) + (indent-to (+ (current-indentation) sws-tab-width)) + ;; if at max indent move text to beginning of line + (progn + (beginning-of-line) + (delete-horizontal-space))))) + +(defun sws-indent-line () + "Indents current line" + (interactive) + (if (eq this-command 'indent-for-tab-command) + (if mark-active + (sws-indent-region (region-beginning) (region-end)) + (if (sws-at-bot-p) + (sws-do-indent-line) + (sws-point-to-bot))) + (indent-to (sws-previous-indentation)))) + +(defun sws-at-bol-p () + "If point is at beginning of line" + (interactive) + (= (point) (point-at-bol))) + +(defun sws-at-bot-p () + "If point is at beginning of text" + (= (point) (+ (current-indentation) (point-at-bol)))) + +(defun sws-print-line-number () + "Prints line number" + (sws-print-num (point))) + +(defun sws-print-num (arg) + "Prints line number" + (message (number-to-string arg))) + +(defun sws-indent-to (num) + "Force indentation to level including those below current level" + (save-excursion + (beginning-of-line) + (delete-horizontal-space) + (indent-to num))) + +(defun sws-move-region (begin end prog) + "Moves left is dir is null, otherwise right. prog is '+ or '-" + (save-excursion + (let (first-indent indent-diff) + (goto-char begin) + (setq first-indent (current-indentation)) + (sws-indent-to + (funcall prog first-indent sws-tab-width)) + (setq indent-diff (- (current-indentation) first-indent)) + ;; move other lines based on movement of first line + (while (< (point) end) + (forward-line 1) + (if (< (point) end) + (sws-indent-to (+ (current-indentation) indent-diff))))))) + +(defun sws-indent-region (begin end) + "Indents the selected region" + (interactive) + (sws-move-region begin end '+)) + + +(defun sws-dendent-line () + "De-indents current line" + (interactive) + (if mark-active + (sws-move-region (region-beginning) (region-end) '-) + (if (sws-at-bol-p) + (progn + (message "at mother fucking bol") + (delete-horizontal-space) + (indent-to (sws-max-indent))) + (let ((ci (current-indentation))) + (beginning-of-line) + (delete-horizontal-space) + (indent-to (- ci sws-tab-width)))))) + +(defvar sws-mode-map (make-sparse-keymap)) +(define-key sws-mode-map [S-tab] 'sws-dendent-line) +(define-key sws-mode-map [backtab] 'sws-dendent-line) + +;;;###autoload +(define-derived-mode sws-mode fundamental-mode + "sws" + "Major mode for editing significant whitespace files" + (kill-all-local-variables) + + ;; default tab width + (setq sws-tab-width 2) + (make-local-variable 'indent-line-function) + (setq indent-line-function 'sws-indent-line) + (make-local-variable 'indent-region-function) + + (setq indent-region-function 'sws-indent-region) + + ;; TODO needed? + (setq indent-tabs-mode nil) + + ;; keymap + (use-local-map sws-mode-map) + (setq major-mode 'sws-mode)) + +(provide 'sws-mode) +;;; sws-mode.el ends here