diff --git a/emacs b/emacs index 2246f18..e166eeb 100644 --- a/emacs +++ b/emacs @@ -140,6 +140,10 @@ (require 'mojo) +;; enable Mojo for CSS, HTML, JS, and JSON files within a Mojo project +;; root. Did I forget anything? +(mojo-setup-mode-hooks 'css-mode-hook 'js2-mode-hook 'espresso-mode-hook 'html-mode-hook) + ;;;;;;;;;;;;;;;; ;; javascript ;; diff --git a/emacs.d/mojo.el b/emacs.d/mojo.el index 523942b..15f66e1 100644 --- a/emacs.d/mojo.el +++ b/emacs.d/mojo.el @@ -1,99 +1,225 @@ -;;; mojo.el --- Interactive functions to aid the development of webOS apps -;; 2009-12-01 08:29:25 -(defconst mojo-version "0.9.6") +;;; mojo.el --- Interactive functions for webOS development +;; 2009-12-04 15:44:24 +(defconst mojo-version "0.9.9") (require 'json) ;; Copyright (c)2008 Jonathan Arkell. (by)(nc)(sa) Some rights reserved. ;; 2009 Sami Samhuri -;; +;; ;; Authors: Jonathan Arkell ;; Sami Samhuri -;; +;; ;; Latest version is available on github: ;; http://github.com/samsonjs/mojo.el -;; -;; With sufficient interest mojo.el will get its own repo. - +;; ;; This file is not part of GNU Emacs. - +;; ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation version 2. - +;; ;; This program is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. - +;; ;; For a copy of the GNU General Public License, search the Internet, ;; or write to the Free Software Foundation, Inc., 59 Temple Place, ;; Suite 330, Boston, MA 02111-1307 USA + ;;; Commentary: (defgroup mojo '() - "Interactive functions to aid the development of webOS apps. + "Interactive functions for webOS development. -This package is in early beta. I am open to any contributions or +This package is in beta. I am open to any contributions or ideas. Send me a pull request on github if you hack on mojo.el.") - + ;;; Installation: +;; +;; +;; 1. Put json.el and mojo.el somewhere in your load-path. +;; (Use M-x show-variable RET load-path to see what your load path is.) ;; -;; 1. Put json.el and mojo.el somewhere in your load-path. -;; (Use M-x show-variable RET load-path to see what your load path is.) +;; => http://github.com/samsonjs/mojo.el -- mojo.el and json.el +;; => http://edward.oconnor.cx/2006/03/json.el -- official json.el +;; +;; 2. Add this to your Emacs init file: (require 'mojo) +;; +;; 3. Enable mojo-mode for modes that you use for webOS, e.g.: +;; +;; (mojo-setup-mode-hooks 'css-mode-hook 'js2-mode-hook +;; 'espresso-mode-hook 'html-mode-hook) +;; +;; * Note that this does not simply enable mojo-mode for these types +;; wholesale, but instead only enables mojo-mode when it finds that +;; the file is also under a Mojo project root (using mojo-project-p). ;; -;; 2. Add this to your Emacs init file: (require 'mojo) +;; 4. Make sure you customize the variables: +;; mojo-project-directory, mojo-sdk-directory and mojo-build-directory +;; (Use M-x customize-group RET mojo RET) ;; -;; 3. Make sure you customize the variables: -;; mojo-project-directory, mojo-sdk-directory and mojo-build-directory -;; (Use M-x customize-group RET mojo RET) +;; (optional but recommended) ;; -;; (optional) +;; 5. js2-mode for JavaScript and espresso-mode for JSON. ;; -;; 4. I recommend that you define a few keyboard shortcuts in your Emacs init -;; file. Maybe something like this: +;; => http://code.google.com/p/js2-mode/ +;; => http://www.nongnu.org/espresso/ +;; ;; -;; (global-set-key [f2] mojo-generate-scene) -;; (global-set-key [f3] mojo-emulate) -;; (global-set-key [f4] mojo-package) -;; (global-set-key [f5] mojo-package-install-and-inspect) +;; That's it! You've got the most powerful Mojo development environment. ;; +;; Check COMMANDS for a list of all interactive commands and key bindings. + + ;;; Commands: ;; -;; Below are complete command list: -;; -;; `mojo-generate' -;; Generate a new Mojo application in the `mojo-project-directory'. -;; `mojo-generate-scene' -;; Generate a new Mojo scene for the application in `mojo-root'. -;; `mojo-emulate' +;; Default key bindings: +;; +;; * C-c C-c a -- mojo-switch-to-assistant +;; * C-c C-c i -- mojo-switch-to-appinfo +;; * C-c C-c I -- mojo-switch-to-index +;; * C-c C-c n -- mojo-switch-to-next-view +;; * C-c C-c s -- mojo-switch-to-sources +;; * C-c C-c S -- mojo-switch-to-stylesheet +;; * C-c C-c v -- mojo-switch-to-view +;; * C-c C-c SPC -- mojo-switch-file-dwim +;; * C-c C-c C-e -- mojo-emulate +;; * C-c C-c C-p -- mojo-package +;; * C-c C-c C-r -- mojo-package-install-and-inspect +;; * C-c C-c C-s -- mojo-generate-scene +;; * C-c C-c C-t -- mojo-toggle-target +;; +;; +;; The complete command list: +;; +;; Code generation +;; --------------- +;; +;; mojo-generate +;; Generate a new Mojo application in the mojo-project-directory. +;; +;; mojo-generate-scene +;; Generate a new Mojo scene for the application found by mojo-root. +;; (a.k.a. the current application) +;; +;; +;; Packaging and device/emulator interactions +;; ------------------------------------------ +;; +;; mojo-emulate ;; Launch the palm emulator. -;; `mojo-package' -;; Package the current application. -;; `mojo-install' -;; Install the specified package for the current application. +;; +;; mojo-package +;; Package the specified application (defaults to current app id). +;; +;; mojo-install +;; Install the specified package (defaults to current app id). ;; The emulator needs to be running. -;; `mojo-list' +;; +;; mojo-list ;; List all installed packages. -;; `mojo-delete' -;; Remove application named APP-NAME. -;; `mojo-launch' -;; Launch the current application in an emulator. -;; `mojo-close' -;; Close launched application. -;; `mojo-inspect' -;; Run the dom inspector on the current application. -;; `mojo-hard-reset' +;; +;; mojo-delete +;; Remove the specified application. (defaults to current app id) +;; +;; mojo-launch +;; Launch the specified application in the emulator. (defaults to +;; current app id) +;; +;; mojo-close +;; Close specified application. (defaults to current app id) +;; +;; mojo-inspect +;; Run the dom inspector on the specified application. (defaults to +;; current app id) +;; +;; mojo-hard-reset ;; Perform a hard reset, clearing all data. -;; `mojo-package-install-and-launch' +;; +;; mojo-package-install-and-launch ;; Package, install, and launch the current app. -;; `mojo-package-install-and-inspect' +;; +;; mojo-package-install-and-inspect ;; Package, install, and launch the current app for inspection. -;; `mojo-target-device' -;; Set the target to a USB device. -;; `mojo-target-emulator' -;; Set the target to the emulator. +;; +;; mojo-target-device +;; Set the target device to USB. +;; +;; mojo-target-emulator +;; Set the target device to the emulator. +;; +;; mojo-toggle-target +;; Automatically change the target device from 'usb' to 'tcp' and vice +;; versa. +;; +;; +;; Quickly switch buffers +;; ---------------------- +;; +;; mojo-switch-to-assistant +;; Switch to the corresponding assistant from any view file. +;; +;; mojo-switch-to-view +;; Switch to the main view from an assistant. +;; +;; mojo-switch-to-next-view +;; Switch to the next view file, alphabetically. Wraps around at the +;; end. +;; +;; mojo-switch-to-appinfo +;; Switch to the appinfo.json file. +;; +;; mojo-switch-to-sources +;; Switch to the sources.json file. +;; +;; mojo-switch-to-index +;; Switch to the root index.html file. +;; +;; mojo-switch-to-stylesheet +;; Switch to the main stylesheet. +;; +;; +;; Manage framework_config.json +;; ---------------------------- +;; +;; mojo-debugging-enabled-p +;; See if debugging is enabled. +;; +;; mojo-log-events-p +;; See if event logging is enabled. +;; +;; mojo-timing-enabled-p +;; See if timing is enabled. +;; +;; mojo-use-native-json-parser-p +;; See if the native JSON parser is used (if available). +;; +;; mojo-log-level +;; See the log level. +;; +;; mojo-html-escaped-in-templates-p +;; See if HTML is escaped in templates. +;; +;; mojo-set-debugging-enabled +;; Enable or disable debugging. +;; +;; mojo-set-log-events +;; Enable or disable event logging. +;; +;; mojo-set-timing-enabled +;; Enable or disable timing. +;; +;; mojo-set-use-native-json-parser +;; Enable or disable use of the native JSON parser. +;; +;; mojo-set-log-level +;; Set the log level. +;; +;; mojo-set-escape-html-in-templates +;; Enable or disable escaping of HTML in templates. ;;; Customizable Options: ;; @@ -114,6 +240,103 @@ ideas. Send me a pull request on github if you hack on mojo.el.") ;; Run Mojo in debug mode. Assumed true while in such an early version. ;; default = t +;; CHANGELOG +;; ========= +;; +;; sjs 2009-12-04 +;; v 0.9.9 (bug fix) +;; +;; - Fixed the value of *mojo-switch-suffixes*, which was confusing +;; mojo-switch-file-dwim. +;; +;; sjs 2009-12-04 +;; v 0.9.8 (framework_config.json support) +;; +;; - Implemented support for framework_config.json. Functions for +;; both reading and writing all known values. +;; +;; sjs 2009-12-03 +;; v 0.9.7 (hooks) +;; +;; - Added mojo-setup-mode-hooks which adds +;; mojo-maybe-enable-minor-mode to the specified mode hooks. +;; When the hooked modes are activated on files under a Mojo +;; project root also activate mojo-mode. +;; +;; sjs 2009-12-01 +;; v 0.9.6 (minor mode) +;; +;; - Created mojo-mode, a minor mode with keybindings. +;; +;; - Prefixed *all* functions with "mojo-". +;; +;; sjs 2009-11-24 +;; v 0.9.5 (bug fix) +;; +;; - Now that I have a real Palm Pre device I was able to test +;; device support. Turns out I couldn't (easily) target the +;; device because mojo-target-device was not interactive. +;; Whoops. +;; +;; sjs 2009-11-22 +;; v 0.9.4 launch emulator if needed +;; +;; - Commands that use the emulator launch it if necessary and wait +;; till it is fully booted before running commands. +;; +;; sjs 2009-11-21 +;; v 0.9.3 (one more bug fix for today) +;; +;; - Don't pass -d switch to commands that don't accept it. +;; +;; sjs 2009-11-21 +;; v 0.9.2 (bug fixes) +;; +;; - reading json files no longer messes up your buffer history. +;; +;; - app list completion works now (caching bug) +;; +;; sjs 2009-11-21 +;; v 0.9.1 +;; +;; - Added mojo-package-install-and-launch. +;; +;; - New variable for specifying whether commands target the device +;; or emulator, *mojo-target*. Set it to 'usb' for a real device +;; and 'tcp' for the emulator. Defaults to 'tcp'. To set the +;; default target you can use the convenience functions +;; mojo-target-device and mojo-target-emulator. +;; +;; sjs 2009-11-20 +;; v 0.9 +;; +;; - Automatically find Mojo project root by searching upwards for +;; appinfo.json. +;; +;; - Added command for generating new scenes, mojo-generate-scene. +;; +;; - mojo-package now operates only on the current project. +;; +;; - Parse appinfo.json to get version, used for installing & +;; launching with less interaction. +;; +;; - mojo-install, mojo-launch, mojo-inspect, and mojo-delete still +;; read in arguments but have the current project/app as the +;; default values. +;; +;; - New convenience method: mojo-package-install-and-inspect +;; +;; This function only operates on the active app and does not +;; read in any input. +;; +;; - Remembered filenames and app ids are cleared when the Mojo +;; project root changes. (DWIM) +;; +;; - Parse output of `palm-install --list` for app id completion. +;; App id completion was ported from cheat.el. +;; +;; v 0.2 - Fixed some minor bugs +;; v 0.1 - Initial release ;;; Code: @@ -133,19 +356,19 @@ ideas. Send me a pull request on github if you hack on mojo.el.") Keybindings are: - * C-c a -- \\[mojo-switch-to-assistant] - * C-c i -- \\[mojo-switch-to-appinfo] - * C-c I -- \\[mojo-switch-to-index] - * C-c n -- \\[mojo-switch-to-next-view] - * C-c s -- \\[mojo-switch-to-sources] - * C-c S -- \\[mojo-switch-to-stylesheet] - * C-c v -- \\[mojo-switch-to-view] - * C-c SPC -- \\[mojo-switch-file-dwim] - * C-c C-e -- \\[mojo-emulate] - * C-c C-p -- \\[mojo-package] - * C-c C-r -- \\[mojo-package-install-and-inspect] - * C-c C-s -- \\[mojo-generate-scene] - * C-c C-t -- \\[mojo-toggle-target] + * C-c C-c a -- \\[mojo-switch-to-assistant] + * C-c C-c i -- \\[mojo-switch-to-appinfo] + * C-c C-c I -- \\[mojo-switch-to-index] + * C-c C-c n -- \\[mojo-switch-to-next-view] + * C-c C-c s -- \\[mojo-switch-to-sources] + * C-c C-c S -- \\[mojo-switch-to-stylesheet] + * C-c C-c v -- \\[mojo-switch-to-view] + * C-c C-c SPC -- \\[mojo-switch-file-dwim] + * C-c C-c C-e -- \\[mojo-emulate] + * C-c C-c C-p -- \\[mojo-package] + * C-c C-c C-r -- \\[mojo-package-install-and-inspect] + * C-c C-c C-s -- \\[mojo-generate-scene] + * C-c C-c C-t -- \\[mojo-toggle-target] See the source code mojo.el or the README file for a list of all of the interactive commands." @@ -155,19 +378,19 @@ ideas. Send me a pull request on github if you hack on mojo.el.") :lighter "-Mojo" ;; The minor mode bindings. :keymap - '(("\C-ca" . mojo-switch-to-assistant) - ("\C-ci" . mojo-switch-to-appinfo) - ("\C-cI" . mojo-switch-to-index) - ("\C-cn" . mojo-switch-to-next-view) - ("\C-cs" . mojo-switch-to-sources) - ("\C-cS" . mojo-switch-to-stylesheet) - ("\C-cv" . mojo-switch-to-view) - ("\C-c " . mojo-switch-file-dwim) - ("\C-c\C-e" . mojo-emulate) - ("\C-c\C-p" . mojo-package) - ("\C-c\C-r" . mojo-package-install-and-inspect) - ("\C-c\C-s" . mojo-generate-scene) - ("\C-c\C-t" . mojo-toggle-target)) + '(("\C-c\C-ca" . mojo-switch-to-assistant) + ("\C-c\C-ci" . mojo-switch-to-appinfo) + ("\C-c\C-cI" . mojo-switch-to-index) + ("\C-c\C-cn" . mojo-switch-to-next-view) + ("\C-c\C-cs" . mojo-switch-to-sources) + ("\C-c\C-cS" . mojo-switch-to-stylesheet) + ("\C-c\C-cv" . mojo-switch-to-view) + ("\C-c\C-c " . mojo-switch-file-dwim) + ("\C-c\C-c\C-e" . mojo-emulate) + ("\C-c\C-c\C-p" . mojo-package) + ("\C-c\C-c\C-r" . mojo-package-install-and-inspect) + ("\C-c\C-c\C-s" . mojo-generate-scene) + ("\C-c\C-c\C-t" . mojo-toggle-target)) :group 'mojo) (defcustom mojo-sdk-directory @@ -197,7 +420,17 @@ this work." "Run Mojo in debug mode. Assumed true while in such an early version." :type 'boolean :group 'mojo) - + +;; Call this from your emacs config file with the modes you want to hook. +(defun mojo-setup-mode-hooks (&rest hooks) + "Add `mojo-maybe-enable-minor-mode' to the specified mode hooks." + (dolist (hook hooks) + (add-hook hook 'mojo-maybe-enable-minor-mode))) + +(defun mojo-maybe-enable-minor-mode () + "Enable `mojo-mode' when the current buffer belongs to a Mojo project." + (when (mojo-project-p) + (mojo-mode))) ;;* interactive generate (defun mojo-generate (title directory) @@ -221,7 +454,7 @@ DIRECTORY is the directory where the files are stored." NAME is the name of the scene." (interactive "sScene Name: \n") - (let ((mojo-dir (mojo-root))) + (let ((mojo-dir (mojo-root t))) (mojo-cmd "palm-generate" (list "-t" "new_scene" "-p" (format "name=%s" name) mojo-dir)) (find-file (format "%s/app/assistants/%s-assistant.js" mojo-dir name)) @@ -239,11 +472,11 @@ NAME is the name of the scene." "Package the current application into `MOJO-BUILD-DIRECTORY'." (interactive) (mojo-cmd "palm-package" (list "-o" (expand-file-name mojo-build-directory) - (mojo-root)))) + (mojo-root t)))) ;;* interactive (defun mojo-install () - "Install the package named by `MOJO-PACKAGE-FILENAME'. The emulator needs to be running." + "Install the package named by `MOJO-PACKAGE-FILENAME'. The emulator is started if needed." (interactive) (mojo-ensure-emulator-is-running) (mojo-cmd-with-target "palm-install" (list (expand-file-name (mojo-read-package-filename)))) @@ -258,7 +491,7 @@ NAME is the name of the scene." ;;* interactive (defun mojo-delete () - "Remove the current application using `MOJO-APP-ID'." + "Remove an application." (interactive) (mojo-ensure-emulator-is-running) (mojo-cmd-with-target "palm-install" (list "-r" (mojo-read-app-id))) @@ -280,14 +513,14 @@ NAME is the name of the scene." ;;* interactive (defun mojo-launch () - "Launch the current application in the emulator." + "Launch the current application in the emulator, and the emulator if necessary.." (interactive) (mojo-ensure-emulator-is-running) (mojo-cmd-with-target "palm-launch" (list (mojo-read-app-id)))) ;;* interactive (defun mojo-close () - "Close launched application." + "Close an application." (interactive) (mojo-ensure-emulator-is-running) (mojo-cmd-with-target "palm-launch" (list "-c" (mojo-read-app-id)))) @@ -348,8 +581,7 @@ Sets `*mojo-target*' to \"tcp\"." ;;* interactive (defun mojo-toggle-target () - "Automatically change the target device from 'usb' to 'tcp' and -vice versa." + "Automatically change the target device from 'usb' to 'tcp' and vice versa." (interactive) (if (string= "usb" *mojo-target*) (mojo-target-emulator) @@ -359,16 +591,16 @@ vice versa." ;; Some support functions that grok the basics of a Mojo project. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defun mojo-drop-last-path-component (path) - "Get the head of a path by dropping the last component." +(defun mojo-parent-directory (path) + "Get the parent directory, i.e. the head of a path by dropping the last component." (if (< (length path) 2) path (substring path 0 (- (length path) - (length (mojo-last-path-component path)) + (length (mojo-filename path)) 1)))) ;; subtract one more for the trailing slash -(defun mojo-last-path-component (path) - "Get the tail of a path, i.e. the last component." +(defun mojo-filename (path) + "Get the filename from a path, i.e. the last component, or tail." (if (< (length path) 2) path (let ((start -2)) @@ -379,32 +611,34 @@ vice versa." (defvar *mojo-last-root* "" "Last Mojo root found by `MOJO-ROOT'.") -(defun mojo-root () - "Find a Mojo project's root directory starting with `DEFAULT-DIRECTORY'." - (let ((last-component (mojo-last-path-component default-directory)) +(defun mojo-root (&optional ask) + "Find a Mojo project's root directory starting with `DEFAULT-DIRECTORY'. + If ASK is non-nil and no root was found, ask the user for a directory." + (let ((last-component (mojo-filename default-directory)) (dir-prefix default-directory)) ;; remove last path element until we find appinfo.json (while (and (not (file-exists-p (concat dir-prefix "/appinfo.json"))) (not (< (length dir-prefix) 2))) - (setq last-component (mojo-last-path-component dir-prefix)) - (setq dir-prefix (mojo-drop-last-path-component dir-prefix))) + (setq last-component (mojo-filename dir-prefix)) + (setq dir-prefix (mojo-parent-directory dir-prefix))) ;; If no Mojo root found, ask for a directory. - (if (< (length dir-prefix) 2) - (setq dir-prefix (mojo-read-root))) + (when (and ask (< (length dir-prefix) 2)) + (setq dir-prefix (mojo-read-root))) ;; Invalidate cached values when changing projects. - (if (or (mojo-blank *mojo-last-root*) - (not (string= dir-prefix *mojo-last-root*))) - (progn - (setq *mojo-last-root* dir-prefix) - (setq *mojo-package-filename* nil) - (setq *mojo-app-id* nil))) + (when (or (mojo-blank *mojo-last-root*) + (not (string= dir-prefix *mojo-last-root*))) + (setq *mojo-last-root* dir-prefix) + (setq *mojo-package-filename* nil) + (setq *mojo-app-id* nil) + (setq *mojo-appinfo* nil)) dir-prefix)) -;; foolproof? +;; foolproof enough? don't want false positives. (defun mojo-project-p () + "Return T if the current buffer belongs to a Mojo project, otherwise NIL." (and (file-exists-p (concat (mojo-root) "/appinfo.json")) (file-exists-p (concat (mojo-root) "/sources.json")) (file-exists-p (concat (mojo-root) "/app")) @@ -413,34 +647,52 @@ vice versa." (defun mojo-read-json-file (filename) "Parse the JSON in FILENAME and return the result." (save-excursion - (let ((origbuffer (current-buffer)) - (filebuffer (find-file-noselect filename))) + (let* ((origbuffer (current-buffer)) + (path (concat (mojo-root) "/" filename)) + (filebuffer (find-file-noselect path))) (set-buffer filebuffer) (let ((text (buffer-string))) (switch-to-buffer origbuffer) (json-read-from-string text))))) -(defun mojo-app-version () - "Parse the project version from the appinfo.json file in `MOJO-ROOT'." +(defun mojo-write-json-file (filename content) + "Convert CONTENT to JSON and write to FILENAME in `mojo-root'." + (save-excursion + (let* ((origbuffer (current-buffer)) + (path (concat (mojo-root) "/" filename)) + (filebuffer (find-file-noselect path))) + (set-buffer filebuffer) + (erase-buffer) + (insert (json-encode content) "\n") + (save-buffer) + (switch-to-buffer origbuffer)))) + +(defvar *mojo-appinfo* nil + "Last structure read from appinfo.json.") + +(defun mojo-appinfo () + "Get the contents of appinfo.json. Last read appinfo is cached." (when (mojo-project-p) - (let ((appinfo (mojo-read-json-file (concat (mojo-root) "/appinfo.json")))) - (cdr (assoc 'version appinfo))))) + (or *mojo-appinfo* + (setq *mojo-appinfo* (mojo-read-json-file "appinfo.json"))))) + +(defun mojo-app-version () + "Parse the project version from appinfo.json." + (when (mojo-project-p) + (cdr (assoc 'version (mojo-appinfo))))) (defun mojo-app-id () - "Parse the project id from the appinfo.json file in `MOJO-ROOT'." + "Parse the project id from appinfo.json." (when (mojo-project-p) - (let ((appinfo (mojo-read-json-file (concat (mojo-root) "/appinfo.json")))) - (cdr (assoc 'id appinfo))))) + (cdr (assoc 'id (mojo-appinfo))))) (defun mojo-app-title () "Parse the project title from appinfo.json." (when (mojo-project-p) - (let ((appinfo (mojo-read-json-file (concat (mojo-root) "/appinfo.json")))) - (cdr (assoc 'title appinfo))))) + (cdr (assoc 'title (mojo-appinfo))))) (defun mojo-informal-app-id () - "Parse the project title from appinfo.json and remove all non alphanumeric - characters." + "Parse the project title from appinfo.json and remove all non alphanumeric characters." (let ((title (downcase (mojo-app-title)))) (replace-regexp-in-string "[^a-z0-9]" "" title))) @@ -450,6 +702,123 @@ vice versa." (mojo-app-id) (mojo-app-version))) +(defun mojo-framework-config () + "Get the contents of framework_config.json." + (when (mojo-project-p) + (and (file-exists-p (concat (mojo-root) "/framework_config.json")) + (mojo-read-json-file "framework_config.json")))) + +(defun mojo-write-framework-config (config) + "Set the contents of framework_config.json to the JSON representation of CONFIG." + (when (mojo-project-p) + (mojo-write-json-file "framework_config.json" config))) + +(defun mojo-framework-config-value (key) + "Retrieve a value from framework_config.json." + (when (and (mojo-project-p) + (mojo-framework-config)) + (cdr (assoc key (mojo-framework-config))))) + +(defun mojo-framework-config-boolean (key) + "Retrieve the value of a boolean in framework_config.json." + (string= t (mojo-framework-config-value key))) + +;;* interactive +(defun mojo-debugging-enabled-p () + (interactive) + "Determine if debugging is enabled for the current Mojo project." + (print (mojo-framework-config-boolean 'debuggingEnabled))) + +;;* interactive +(defun mojo-log-events-p () + (interactive) + "Determine if event logging is enabled for the current Mojo project." + (print (mojo-framework-config-boolean 'logEvents))) + +;;* interactive +(defun mojo-timing-enabled-p () + (interactive) + "Determine if timing is enabled for the current Mojo project." + (print (mojo-framework-config-boolean 'timingEnabled))) + +;;* interactive +(defun mojo-use-native-json-parser-p () + (interactive) + "Determine if the native JSON parser is enabled for the current Mojo project." + (print (mojo-framework-config-boolean 'useNativeJSONParser))) + +;;* interactive +(defun mojo-log-level () + "The log level for the current Mojo project." + (interactive) + (when (and (mojo-project-p) + (mojo-framework-config)) + (print (cdr (assoc 'logLevel (mojo-framework-config)))))) + +;;* interactive +(defun mojo-escape-html-in-templates-p () + "Return T if HTML is escaped in templates, NIL otherwise." + (interactive) + (print (mojo-framework-config-boolean 'escapeHTMLInTemplates))) + + +(defun mojo-change-framework-config-value (key value) + "Set the value for a key in framework_config.json." + (interactive) + (when (mojo-project-p) + (let ((config (mojo-framework-config))) + (unless config (setq config (list))) + (setq config (assq-delete-all key config)) + (mojo-write-framework-config (cons (cons key (or value :json-false)) + config))))) + +;;* interactive +(defun mojo-set-debugging-enabled (enabled) + "Enable debugging if ENABLED is t, or disable if it is nil or :json-false." + (interactive "XEnable debugging? (t or nil) ") + (mojo-change-framework-config-value 'debuggingEnabled enabled)) + +;;* interactive +(defun mojo-toggle-debugging () + "If debugging is enabled then disable it, and vice versa." + (interactive) + (mojo-change-framework-config-value 'debuggingEnabled (not (mojo-debugging-enabled-p)))) + +;;* interactive +(defun mojo-set-log-level (level) + "Set the log level to the integer LEVEL. + +Mojo.Log.LOG_LEVEL_INFO is 10 +Mojo.Log.LOG_LEVEL_ERROR is 30" + (interactive "nLog level: ") + (mojo-change-framework-config-value 'logLevel level)) + +;;* interactive +(defun mojo-set-escape-html-in-templates (enabled) + "Escape HTML if ENABLED is t, don't escape HTML if it is nil or :json-false." + (interactive "XEscape HTML in templates? (t or nil) ") + (mojo-change-framework-config-value 'escapeHTMLInTemplates enabled)) + +;;* interactive +(defun mojo-set-log-events (enabled) + "Turn event logging on if ENABLED is t, or off if it is nil or :json-false." + (interactive "XLog events? (t or nil) ") + (mojo-change-framework-config-value 'debuggingEnabled enabled)) + +;;* interactive +(defun mojo-set-timing-enabled (enabled) + "Enable timing if ENABLED is t, or disable timing if it is nil or :json-false." + (interactive "XEnable timing? (t or nil) ") + (mojo-change-framework-config-value 'timingEnabled enabled)) + +;;* interactive +(defun mojo-set-use-native-json-parser (enabled) + "Use the native JSON parser if ENABLED is t, or not if it is nil or :json-false." + (interactive "XUse native JSON parser? (t or nil) ") + (mojo-change-framework-config-value 'useNativeJSONParser enabled)) + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; app listing and completion ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -497,25 +866,22 @@ vice versa." (= 0 (length thing))))) (defun mojo-read-root () - "Get the path to a Mojo application, prompting with completion and - history." + "Get the path to a Mojo application, prompting with completion and history." (read-file-name "Mojo project: " (expand-file-name (concat mojo-project-directory "/")))) (defun mojo-read-package-filename () - "Get the filename of a packaged application, prompting with completion and - history. + "Get the filename of a packaged application, prompting with completion and history. The app id is stored in *mojo-package-filename* unless it was blank." (let* ((default (or *mojo-package-filename* (mojo-package-filename))) (package (read-file-name (format "Package file (default: %s): " default) (concat mojo-build-directory "/") default t))) - (setq *mojo-package-filename* (mojo-last-path-component package)) + (setq *mojo-package-filename* (mojo-filename package)) (expand-file-name package))) (defun mojo-read-app-id (&optional prompt) - "Get the id of an existing application, prompting with completion and - history. + "Get the id of an existing application, prompting with completion and history. The app id is stored in *mojo-app-id* unless it was blank." (let* ((default (or *mojo-app-id* (mojo-app-id))) @@ -562,8 +928,7 @@ The app id is stored in *mojo-app-id* unless it was blank." (nreverse apps))) (defun mojo-app-cache-stale-p () - "Non-nil if the cache in `MOJO-APP-CACHE-FILE' is more than - *mojo-app-cache-ttl* seconds old. + "Non-nil if the cache in `MOJO-APP-CACHE-FILE' is more than *mojo-app-cache-ttl* seconds old. If the cache file does not exist then it is considered stale." (or (null (file-exists-p (mojo-app-cache-file))) @@ -589,47 +954,64 @@ If the cache file does not exist then it is considered stale." '(("-assistant.js" . mojo-switch-to-view) ("-scene.html" . mojo-switch-to-assistant) (".html" . mojo-switch-to-next-view) - ("" . mojo-switch-to-appinfo))) + ("" . mojo-switch-to-appinfo)) + "Suffixes of files that we can guess where to switch.") ;;* interactive (defun mojo-switch-to-appinfo () + "Switch to appinfo.json." (interactive) - (find-file (concat (mojo-root) "/appinfo.json"))) + (when (mojo-project-p) + (find-file (concat (mojo-root) "/appinfo.json")))) ;;* interactive (defun mojo-switch-to-index () + "Switch to index.html." (interactive) - (find-file (concat (mojo-root) "/index.html"))) + (when (mojo-project-p) + (find-file (concat (mojo-root) "/index.html")))) ;;* interactive (defun mojo-switch-to-sources () + "Switch to sources.json." (interactive) - (find-file (concat (mojo-root) "/sources.json"))) + (when (mojo-project-p) + (find-file (concat (mojo-root) "/sources.json")))) ;;* interactive (defun mojo-switch-to-stylesheet () + "Switch to the main CSS stylesheet, or the first one." (interactive) - (let* ((stylesheet-dir (concat (mojo-root) "/stylesheets")) - (path (concat stylesheet-dir "/" - (mojo-informal-app-id) ".css"))) - (when (not (file-exists-p path)) - (setq path (car (mojo-filter-paths (directory-files stylesheet-dir t))))) - (find-file path))) + (when (mojo-project-p) + (let* ((stylesheet-dir (concat (mojo-root) "/stylesheets")) + (path (concat stylesheet-dir "/" + (mojo-informal-app-id) ".css"))) + (when (not (file-exists-p path)) + (setq path (car (mojo-filter-paths (directory-files stylesheet-dir t))))) + (find-file path)))) + +(defun mojo-parent-directory-name (path) + "The parent directory's basename." + (mojo-filename (mojo-parent-directory path))) (defun mojo-scene-name-from-assistant () + "The scene name of the assistant being edited." (let ((path (buffer-file-name))) - (substring (mojo-last-path-component path) 0 (- 0 (length "-assistant.js"))))) + (and (string= "assistants" (mojo-parent-directory-name path)) + (substring (mojo-filename path) 0 (- 0 (length "-assistant.js")))))) (defun mojo-scene-name-from-view () - (mojo-last-path-component (mojo-drop-last-path-component (buffer-file-name)))) + "The scene name of the view being edited." + (let ((path (buffer-file-name))) + (and (string= "views" (mojo-parent-directory-name (mojo-parent-directory path))) + (mojo-parent-directory-name path)))) ;;* interactive (defun mojo-switch-file-dwim () - "Determine if the current buffer is visiting a file with known - relationships. Try to find the 'best' choice and switch to it. + "Attempt to intelligently find a related file and switch to it. - This does what I (sjs) mean by default, so change - *mojo-switch-suffixes* if you want different behaviour." +This does what I (sjs) mean by default, so change *mojo-switch-suffixes* +if you want different behaviour." (interactive) (let* ((path (buffer-file-name)) (suffixes (copy-list *mojo-switch-suffixes*)) @@ -645,6 +1027,7 @@ If the cache file does not exist then it is considered stale." ;;* interactive (defun mojo-switch-to-view () + "Switch to the corresponding main view from an assistant." (interactive) (when (mojo-project-p) (let ((scene-name (mojo-scene-name-from-assistant))) @@ -653,12 +1036,14 @@ If the cache file does not exist then it is considered stale." scene-name "-scene.html"))))) (defun mojo-ignored-path (path) - (let ((filename (mojo-last-path-component path))) - (or (string= (substring filename 0 1) ".") - (and (string= (substring filename 0 1) "#") + "Paths that we don't want to look at when walking directories." + (let ((filename (mojo-filename path))) + (or (string= (substring filename 0 1) ".") ;; "." and ".." and hidden files + (and (string= (substring filename 0 1) "#") ;; emacs recovery files (string= (substring filename -1) "#"))))) (defun mojo-filter-paths (all-paths) + "Filter out unimportant paths from a list of paths." (let ((wanted-paths (list))) (dolist (path all-paths wanted-paths) (unless (mojo-ignored-path path) @@ -666,6 +1051,7 @@ If the cache file does not exist then it is considered stale." (reverse wanted-paths))) (defun mojo-index (elem list) + "Return the position of ELEM in LIST." (catch 'break (let ((index 0)) (dolist (path list index) @@ -675,6 +1061,7 @@ If the cache file does not exist then it is considered stale." ;;* interactive (defun mojo-switch-to-next-view () + "Switch to the next view in this scene, alphabetically. Wrap around at the end." (interactive) (when (mojo-project-p) (let* ((scene-name (mojo-scene-name-from-view)) @@ -687,6 +1074,7 @@ If the cache file does not exist then it is considered stale." ;;* interactive (defun mojo-switch-to-assistant () + "Swtich to the corresponding assistant from a view." (interactive) (let ((scene-name (mojo-scene-name-from-view))) (when (and (mojo-project-p) @@ -710,8 +1098,8 @@ URL is the luna url, and DATA is the data." (mojo-cmd "luna-send" (list "-n" "1" url data))) (defvar *mojo-target* "tcp" - "Used to specify the target platform, \"usb\" for the device - and \"tcp\" for the emulator. Deaults to \"tcp\".") + "Used to specify the target platform, \"usb\" for the device and \"tcp\" +for the emulator. Deaults to \"tcp\".") (defun mojo-emulator-running-p () "Determine if the webOS emulator is running or not. @@ -720,8 +1108,9 @@ This command only works on Unix-like systems." (= 0 (shell-command "ps x | fgrep 'Palm SDK' | fgrep -v fgrep >/dev/null 2>&1"))) (defun mojo-emulator-responsive-p () - "Determine if the webOS emulator is able to respond to commands yet - (i.e. if it's done booting)." + "Determine if the webOS emulator is able to respond to commands yet. + +(i.e. if it's done booting)." (= 0 (shell-command "palm-install -d tcp --list >/dev/null 2>&1"))) (defun mojo-path-to-cmd (cmd)