| 1 |
;; processing-mode.el |
| 2 |
|
| 3 |
;; Processing.org language based on Java mode. Adds keyword |
| 4 |
;; highlighting for all recognized Processing language functions. |
| 5 |
;; Allows compilation of buffers and "sketches" from within Emacs but |
| 6 |
;; only for more recent versions of Processing. |
| 7 |
|
| 8 |
;; Copyright (C) 2008, 2009 Rudolf Olah <omouse@gmail.com> |
| 9 |
|
| 10 |
;; This program is free software: you can redistribute it and/or modify |
| 11 |
;; it under the terms of the GNU General Public License as published by |
| 12 |
;; the Free Software Foundation, either version 3 of the License, or |
| 13 |
;; (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 <http://www.gnu.org/licenses/>. |
| 22 |
|
| 23 |
(eval-when-compile |
| 24 |
(require 'compile) |
| 25 |
(require 'cl)) |
| 26 |
|
| 27 |
(define-derived-mode processing-mode |
| 28 |
java-mode "Processing" |
| 29 |
"Major mode for Processing. |
| 30 |
\\{java-mode-map}") |
| 31 |
|
| 32 |
(defvar processing-location nil |
| 33 |
"The directory where Processing can be found. Assumes you have |
| 34 |
downloaded the standalone package.") |
| 35 |
|
| 36 |
(defconst processing-platform |
| 37 |
(cond ((string= system-type "gnu/linux") |
| 38 |
"linux") |
| 39 |
((or (string= system-type "darwin") (string= system-type "macos")) |
| 40 |
"macosx") |
| 41 |
((or (string= system-type "ms-dos") (string= system-type "windows-nt") |
| 42 |
(string= system-type "cygwin")) |
| 43 |
"windows")) |
| 44 |
"The platform that Processing is running on. It can be `linux', `macosx' or `windows'.") |
| 45 |
|
| 46 |
;; Functions |
| 47 |
(defun make-java-classpath (&rest args) |
| 48 |
"Returns a string that is a Java CLASSPATH. Each arg is a |
| 49 |
folder containing .class files or a .jar. The delimiter is based |
| 50 |
on the platform, with MS Windows using \";\", and other platforms |
| 51 |
using \":\"." |
| 52 |
(reduce (lambda (x y) (concat x (if (string= processing-platform "windows") ";" ":") |
| 53 |
y)) |
| 54 |
args)) |
| 55 |
|
| 56 |
(defvar processing-import-libraries |
| 57 |
'((minim "jl1.0" "mp3spi1.9.4" "tritonus_share" "tritonus_aos" |
| 58 |
"minim-spi" "minim" "jsminim")) |
| 59 |
"An alist of library names and the JAR (Java ARchive) files |
| 60 |
required for their use. Each element looks like (library-name |
| 61 |
&REST jar-files) where library-name is a SYMBOL and jar-files are |
| 62 |
strings.") |
| 63 |
|
| 64 |
(defun processing-import-library (library-name) |
| 65 |
"Generates a STRING that is a Java classpath. The paths are |
| 66 |
constructed from the REST of the library found in the alist |
| 67 |
``processing-import-libraries''. The suffix \".jar\" is added to |
| 68 |
each string." |
| 69 |
(make-java-classpath (mapcar (lambda (x) (expand-file-name (concat processing-location |
| 70 |
"libraries/" |
| 71 |
(symbol-name library-name) |
| 72 |
"/library/" x ".jar"))) |
| 73 |
(rest (assoc library-name processing-import-libraries))))) |
| 74 |
|
| 75 |
(defun processing-read-libraries (sketch-dir) |
| 76 |
"Returns a LIST of SYMBOLS that are the names of Processing |
| 77 |
libraries. This list if stored in the \"libraries_required.txt\" |
| 78 |
file found in the Processing sketch directory ``sketch-dir''. |
| 79 |
|
| 80 |
If the file does not exist, it is assumed that there are no |
| 81 |
libraries required and NIL is returned. |
| 82 |
|
| 83 |
A general error is signalled if the library is not supported. |
| 84 |
Check the variable ``processing-import-libraries'' to see which |
| 85 |
libraries are supported." |
| 86 |
(let ((file-name (expand-file-name (concat sketch-dir "libraries_required.txt")))) |
| 87 |
(if (and (file-exists-p file-name) (file-readable-p file-name)) |
| 88 |
(let ((temp-buf (generate-new-buffer "libraries-required-by-sketch"))) |
| 89 |
(set-buffer temp-buf) |
| 90 |
(insert-file-contents file-name) |
| 91 |
(unwind-protect |
| 92 |
(loop while (< (point) (buffer-size)) |
| 93 |
for library-name = (read temp-buf) then (read-temp-buf) |
| 94 |
if (assoc library-name processing-import-libraries) |
| 95 |
collect library-name |
| 96 |
else do (error "Processing-mode does not know how to handle the library %s" |
| 97 |
library-name) |
| 98 |
do (forward-line)) |
| 99 |
(kill-buffer temp-buf))) |
| 100 |
nil))) |
| 101 |
|
| 102 |
(defun processing-make-compile-command (sketch-dir output-dir cmd &optional platform) |
| 103 |
"Returns a string which is the compile-command for Processing |
| 104 |
sketches, targetting the sketch files found in ``sketch-dir'', |
| 105 |
with the output being stored in ``output-dir''. The command flag |
| 106 |
that is executed on the sketch depends on the type of ``cmd''. |
| 107 |
|
| 108 |
Valid types of commands are: |
| 109 |
|
| 110 |
- \"preprocess\" |
| 111 |
- \"build\" |
| 112 |
- \"run\" |
| 113 |
- \"present\" |
| 114 |
- \"export-applet\" |
| 115 |
- \"export-application\" |
| 116 |
|
| 117 |
When ``cmd'' is set to \"export-application\", the ``platform'' |
| 118 |
must be set to one of \"windows\", \"macosx\", or \"linux\". If |
| 119 |
no platform is selected, the default platform that Emacs is |
| 120 |
running on will be selected." |
| 121 |
(concat (file-name-as-directory processing-location) |
| 122 |
"java/bin/java -classpath \"" |
| 123 |
(apply 'make-java-classpath |
| 124 |
(mapcar (lambda (x) (expand-file-name (concat processing-location x))) |
| 125 |
'("java/lib/rt.jar" |
| 126 |
"java/lib/tools.jar" |
| 127 |
"lib/antlr.jar" "lib/core.jar" |
| 128 |
"lib/ecj.jar" "lib/jna.jar" |
| 129 |
"lib/pde.jar"))) |
| 130 |
"\" processing.app.Commander" |
| 131 |
" --sketch=\"" (expand-file-name sketch-dir) |
| 132 |
"\" --output=\"" (expand-file-name output-dir) |
| 133 |
;; Remove this comment when Processing implements the --preferences=??? command-line option. |
| 134 |
;;"\" --preferences=\"" (expand-file-name "~/.processing/preferences.txt") |
| 135 |
"\" --" cmd |
| 136 |
(if (string= cmd "export-application") |
| 137 |
(concat " --platform=" |
| 138 |
(if platform platform (processing-platform)))))) |
| 139 |
|
| 140 |
(defun processing-commander (sketch-dir output-dir cmd &optional platform) |
| 141 |
"Runs the Processing compiler, using a compile-command |
| 142 |
constructed using the ``processing-make-compile-command'' |
| 143 |
function." |
| 144 |
(let ((compilation-error-regexp-alist '(processing))) |
| 145 |
(compile (processing-make-compile-command sketch-dir output-dir cmd platform)))) |
| 146 |
|
| 147 |
(defun processing-sketch-compile (&optional cmd) |
| 148 |
"Runs the Processing Commander application with the current |
| 149 |
buffer. The output directory is the sub-directory ``output'' |
| 150 |
which will be found in the parent directory of the buffer file." |
| 151 |
(interactive) |
| 152 |
;; TODO: Add support for temporary sketches |
| 153 |
(let ((sketch-dir (file-name-directory buffer-file-name))) |
| 154 |
(processing-commander sketch-dir (concat sketch-dir "output") (if cmd cmd "run")))) |
| 155 |
|
| 156 |
(defun processing-sketch-present () |
| 157 |
(interactive) |
| 158 |
(processing-sketch-compile "present")) |
| 159 |
|
| 160 |
(defun processing-sketch-build () |
| 161 |
"Runs the build command for a Processing sketch. Processing |
| 162 |
will process the sketch into .java files and then compile them |
| 163 |
into .class files." |
| 164 |
(interactive) |
| 165 |
(processing-sketch-compile "build")) |
| 166 |
|
| 167 |
(defun processing-export-application () |
| 168 |
"Turns the Processing sketch into a Java application. Assumes |
| 169 |
that the platform target is whatever platform Emacs is running |
| 170 |
on." |
| 171 |
t) |
| 172 |
|
| 173 |
;; Add hook so that when processing-mode is loaded, the local variable |
| 174 |
;; 'compile-command is set. |
| 175 |
(add-hook 'processing-mode-hook |
| 176 |
(lambda () |
| 177 |
(let ((sketch-dir (file-name-directory buffer-file-name))) |
| 178 |
(set (make-local-variable 'compile-command) |
| 179 |
(processing-make-compile-command sketch-dir |
| 180 |
(concat sketch-dir "output") |
| 181 |
"build"))))) |
| 182 |
|
| 183 |
;; Key bindings |
| 184 |
(define-key processing-mode-map "\C-c\C-r" 'processing-sketch-compile) |
| 185 |
(define-key processing-mode-map "\C-c\C-p" 'processing-sketch-present) |
| 186 |
(define-key processing-mode-map "\C-c\C-b" 'processing-sketch-build) |
| 187 |
|
| 188 |
;; Regular expressions |
| 189 |
;; Compilation |
| 190 |
(pushnew |
| 191 |
;; Mode name, REGEXP FILE LINE COLUMN TYPE HYPERLINK HIGHLIGHT |
| 192 |
'(processing "^\\([[:alnum:]]+.pde\\):\\([0-9]+\\):\\([0-9]+\\):.*$" |
| 193 |
1 2 3) |
| 194 |
compilation-error-regexp-alist-alist) |
| 195 |
|
| 196 |
;; Font-lock, keywords |
| 197 |
(defconst processing-font-lock-keywords-1 |
| 198 |
(eval-when-compile |
| 199 |
`( ;; Shape functions |
| 200 |
(,(concat |
| 201 |
(regexp-opt '("triangle" "line" "arc" "point" "quad" "ellipse" |
| 202 |
"rect" "curve" "bezier") |
| 203 |
t) |
| 204 |
"(") 1 font-lock-function-name-face t) |
| 205 |
(,(concat |
| 206 |
(regexp-opt '("strokeWeight" "smooth" "strokeJoin" "noSmooth" |
| 207 |
"ellipseMode" "rectMode" "background" "stroke") |
| 208 |
t) |
| 209 |
"(") 1 font-lock-doc-face t) |
| 210 |
(,(regexp-opt '("width" "height" "frameRate" "frameCount" "noCursor()" "cursor()") |
| 211 |
t) |
| 212 |
. font-lock-constant-face) |
| 213 |
(,(concat "screen." (regexp-opt '("width" "height") t)) |
| 214 |
1 font-lock-constant-face t) |
| 215 |
)) |
| 216 |
"Subdued level highlighting for Processing mode.") |
| 217 |
|
| 218 |
;;(defconst processing-font-lock-keywords-2 |
| 219 |
;; (append processing-font-lock-keywords-1 |
| 220 |
;; (eval-when-compile |
| 221 |
;; `( |
| 222 |
|
| 223 |
(defvar processing-font-lock-keywords processing-font-lock-keywords-1 |
| 224 |
"Default expressions to highlight in Processing mode.") |
| 225 |
|
| 226 |
;; YASnippets |
| 227 |
(if (fboundp 'yas/minor-mode) |
| 228 |
(progn |
| 229 |
(require 'yasnippet) |
| 230 |
(message "processing-mode: defining YASnippets") |
| 231 |
(yas/define-snippets |
| 232 |
'processing-mode |
| 233 |
'( |
| 234 |
;; (key template name condition) |
| 235 |
("tri" "triangle(${x1}, ${y1}, ${x2}, ${y2}, ${x3}, ${y3});" |
| 236 |
"triangle" nil) |
| 237 |
("l(" "line(${x1}, ${y1}, ${x2}, ${y2});" "line 2d" nil) |
| 238 |
("l(.3d" "line(${x1}, ${y1}, ${z1}, ${x2}, ${y2}, ${z2});" "line 3d" nil) |
| 239 |
("arc" "arc(${x}, ${y}, ${width}, ${height}, ${start}, ${stop});" "arc" nil) |
| 240 |
("p(" "point(${x}, ${y});" "point 2d" nil) |
| 241 |
("p(.3d" "point(${x}, ${y}, ${z});" "point 3d" nil) |
| 242 |
("quad" "quad(${x1}, ${y1}, ${x2}, ${y2}, ${x3}, ${y3}, ${x4}, ${y4});" |
| 243 |
"quad" nil) |
| 244 |
("ell" "ellipse(${x}, ${y}, ${width}, ${height});" "ellipse" nil) |
| 245 |
("rect" "rect(${x}, ${y}, ${width}, ${height});" "rect" nil) |
| 246 |
|
| 247 |
;; Color Setting |
| 248 |
("background" "background(${gray_or_color_or_hex});" "background .." nil) |
| 249 |
("background.ca" "background(${gray_or_color_or_hex}, ${alpha});" |
| 250 |
"background .. alpha" nil) |
| 251 |
("background.rgb" "background(${red}, ${green}, ${blue});" "background RGB" nil) |
| 252 |
("background.rgba" "background(${red}, ${green}, ${blue}, ${alpha});" |
| 253 |
"background RGBA" nil) |
| 254 |
("colorm" "colorMode(${RGB_or_HSV});" "color mode" nil) |
| 255 |
("colorm.r" "colorMode(${RGB_or_HSV}, ${range});" "color mode range" nil) |
| 256 |
("colorm.rgb" "colorMode(${RGB_or_HSV}, ${range1}, ${range2}, ${range3});" |
| 257 |
"color mode RGB/HSV range" nil) |
| 258 |
("colorm.rgba" "colorMode(${RGB_or_HSV}, ${range1}, ${range2}, ${range3}, ${range4});" |
| 259 |
"color mode RGB/HSV, A range" nil) |
| 260 |
("stroke" "stroke(${gray_or_color_or_hex});" "stroke .." nil) |
| 261 |
("stroke.ca" "stroke(${gray_or_color_or_hex}, ${alpha});" "stroke .. alpha" nil) |
| 262 |
("stroke.rgb" "stroke(${red}, ${green}, ${blue});" "stroke RGB" nil) |
| 263 |
("stroke.rgba" "stroke(${red}, ${green}, ${blue}, ${alpha});" "stroke RGBA" nil) |
| 264 |
("fill" "fill(${gray_or_color_or_hex});" "fill .." nil) |
| 265 |
("fill.ca" "fill(${gray_or_color_or_hex}, ${alpha});" "fill .. alpha" nil) |
| 266 |
("fill.rgb" "fill(${red}, ${green}, ${blue});" "fill RGB" nil) |
| 267 |
("fill.rgba" "fill(${red}, ${green}, ${blue}, ${alpha});" "fill RGBA" nil) |
| 268 |
) |
| 269 |
'java-mode) |
| 270 |
t) |
| 271 |
(progn |
| 272 |
(message "processing-mode: YASnippets not installed. Not defining any snippets.") |
| 273 |
nil)) |
| 274 |
|
| 275 |
(provide 'processing-mode) |