[UPDATED] mojo.el to 0.9.9 (bug fix for mojo-switch-file-dwim)

This commit is contained in:
Sami Samhuri 2009-12-04 16:35:33 -08:00
parent c5772f07dc
commit af79c9ef71
2 changed files with 546 additions and 153 deletions

4
emacs
View file

@ -140,6 +140,10 @@
(require 'mojo) (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 ;; ;; javascript ;;

View file

@ -1,99 +1,225 @@
;;; mojo.el --- Interactive functions to aid the development of webOS apps ;;; mojo.el --- Interactive functions for webOS development
;; 2009-12-01 08:29:25 ;; 2009-12-04 15:44:24
(defconst mojo-version "0.9.6") (defconst mojo-version "0.9.9")
(require 'json) (require 'json)
;; Copyright (c)2008 Jonathan Arkell. (by)(nc)(sa) Some rights reserved. ;; Copyright (c)2008 Jonathan Arkell. (by)(nc)(sa) Some rights reserved.
;; 2009 Sami Samhuri ;; 2009 Sami Samhuri
;; ;;
;; Authors: Jonathan Arkell <jonnay@jonnay.net> ;; Authors: Jonathan Arkell <jonnay@jonnay.net>
;; Sami Samhuri <sami.samhuri@gmail.com> ;; Sami Samhuri <sami.samhuri@gmail.com>
;; ;;
;; Latest version is available on github: ;; Latest version is available on github:
;; http://github.com/samsonjs/mojo.el ;; 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 file is not part of GNU Emacs.
;;
;; This program is free software; you can redistribute it and/or ;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as ;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation version 2. ;; published by the Free Software Foundation version 2.
;;
;; This program is distributed in the hope that it will be useful, but ;; This program is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details. ;; General Public License for more details.
;;
;; For a copy of the GNU General Public License, search the Internet, ;; For a copy of the GNU General Public License, search the Internet,
;; or write to the Free Software Foundation, Inc., 59 Temple Place, ;; or write to the Free Software Foundation, Inc., 59 Temple Place,
;; Suite 330, Boston, MA 02111-1307 USA ;; Suite 330, Boston, MA 02111-1307 USA
;;; Commentary: ;;; Commentary:
(defgroup mojo '() (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.") ideas. Send me a pull request on github if you hack on mojo.el.")
;;; Installation: ;;; 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. ;; => http://github.com/samsonjs/mojo.el -- mojo.el and json.el
;; (Use M-x show-variable RET load-path to see what your load path is.) ;; => 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: ;; (optional but recommended)
;; mojo-project-directory, mojo-sdk-directory and mojo-build-directory
;; (Use M-x customize-group RET mojo RET)
;; ;;
;; (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 ;; => http://code.google.com/p/js2-mode/
;; file. Maybe something like this: ;; => http://www.nongnu.org/espresso/
;;
;; ;;
;; (global-set-key [f2] mojo-generate-scene) ;; That's it! You've got the most powerful Mojo development environment.
;; (global-set-key [f3] mojo-emulate)
;; (global-set-key [f4] mojo-package)
;; (global-set-key [f5] mojo-package-install-and-inspect)
;; ;;
;; Check COMMANDS for a list of all interactive commands and key bindings.
;;; Commands: ;;; Commands:
;; ;;
;; Below are complete command list: ;; Default key bindings:
;; ;;
;; `mojo-generate' ;; * C-c C-c a -- mojo-switch-to-assistant
;; Generate a new Mojo application in the `mojo-project-directory'. ;; * C-c C-c i -- mojo-switch-to-appinfo
;; `mojo-generate-scene' ;; * C-c C-c I -- mojo-switch-to-index
;; Generate a new Mojo scene for the application in `mojo-root'. ;; * C-c C-c n -- mojo-switch-to-next-view
;; `mojo-emulate' ;; * 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. ;; Launch the palm emulator.
;; `mojo-package' ;;
;; Package the current application. ;; mojo-package
;; `mojo-install' ;; Package the specified application (defaults to current app id).
;; Install the specified package for the current application. ;;
;; mojo-install
;; Install the specified package (defaults to current app id).
;; The emulator needs to be running. ;; The emulator needs to be running.
;; `mojo-list' ;;
;; mojo-list
;; List all installed packages. ;; List all installed packages.
;; `mojo-delete' ;;
;; Remove application named APP-NAME. ;; mojo-delete
;; `mojo-launch' ;; Remove the specified application. (defaults to current app id)
;; Launch the current application in an emulator. ;;
;; `mojo-close' ;; mojo-launch
;; Close launched application. ;; Launch the specified application in the emulator. (defaults to
;; `mojo-inspect' ;; current app id)
;; Run the dom inspector on the current application. ;;
;; `mojo-hard-reset' ;; 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. ;; Perform a hard reset, clearing all data.
;; `mojo-package-install-and-launch' ;;
;; mojo-package-install-and-launch
;; Package, install, and launch the current app. ;; 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. ;; Package, install, and launch the current app for inspection.
;; `mojo-target-device' ;;
;; Set the target to a USB device. ;; mojo-target-device
;; `mojo-target-emulator' ;; Set the target device to USB.
;; Set the target to the emulator. ;;
;; 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: ;;; 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. ;; Run Mojo in debug mode. Assumed true while in such an early version.
;; default = t ;; 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: ;;; Code:
@ -133,19 +356,19 @@ ideas. Send me a pull request on github if you hack on mojo.el.")
Keybindings are: Keybindings are:
* C-c a -- \\[mojo-switch-to-assistant] * C-c C-c a -- \\[mojo-switch-to-assistant]
* C-c i -- \\[mojo-switch-to-appinfo] * C-c C-c i -- \\[mojo-switch-to-appinfo]
* C-c I -- \\[mojo-switch-to-index] * C-c C-c I -- \\[mojo-switch-to-index]
* C-c n -- \\[mojo-switch-to-next-view] * C-c C-c n -- \\[mojo-switch-to-next-view]
* C-c s -- \\[mojo-switch-to-sources] * C-c C-c s -- \\[mojo-switch-to-sources]
* C-c S -- \\[mojo-switch-to-stylesheet] * C-c C-c S -- \\[mojo-switch-to-stylesheet]
* C-c v -- \\[mojo-switch-to-view] * C-c C-c v -- \\[mojo-switch-to-view]
* C-c SPC -- \\[mojo-switch-file-dwim] * C-c C-c SPC -- \\[mojo-switch-file-dwim]
* C-c C-e -- \\[mojo-emulate] * C-c C-c C-e -- \\[mojo-emulate]
* C-c C-p -- \\[mojo-package] * C-c C-c C-p -- \\[mojo-package]
* C-c C-r -- \\[mojo-package-install-and-inspect] * C-c C-c C-r -- \\[mojo-package-install-and-inspect]
* C-c C-s -- \\[mojo-generate-scene] * C-c C-c C-s -- \\[mojo-generate-scene]
* C-c C-t -- \\[mojo-toggle-target] * C-c C-c C-t -- \\[mojo-toggle-target]
See the source code mojo.el or the README file for a list of See the source code mojo.el or the README file for a list of
all of the interactive commands." 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" :lighter "-Mojo"
;; The minor mode bindings. ;; The minor mode bindings.
:keymap :keymap
'(("\C-ca" . mojo-switch-to-assistant) '(("\C-c\C-ca" . mojo-switch-to-assistant)
("\C-ci" . mojo-switch-to-appinfo) ("\C-c\C-ci" . mojo-switch-to-appinfo)
("\C-cI" . mojo-switch-to-index) ("\C-c\C-cI" . mojo-switch-to-index)
("\C-cn" . mojo-switch-to-next-view) ("\C-c\C-cn" . mojo-switch-to-next-view)
("\C-cs" . mojo-switch-to-sources) ("\C-c\C-cs" . mojo-switch-to-sources)
("\C-cS" . mojo-switch-to-stylesheet) ("\C-c\C-cS" . mojo-switch-to-stylesheet)
("\C-cv" . mojo-switch-to-view) ("\C-c\C-cv" . mojo-switch-to-view)
("\C-c " . mojo-switch-file-dwim) ("\C-c\C-c " . mojo-switch-file-dwim)
("\C-c\C-e" . mojo-emulate) ("\C-c\C-c\C-e" . mojo-emulate)
("\C-c\C-p" . mojo-package) ("\C-c\C-c\C-p" . mojo-package)
("\C-c\C-r" . mojo-package-install-and-inspect) ("\C-c\C-c\C-r" . mojo-package-install-and-inspect)
("\C-c\C-s" . mojo-generate-scene) ("\C-c\C-c\C-s" . mojo-generate-scene)
("\C-c\C-t" . mojo-toggle-target)) ("\C-c\C-c\C-t" . mojo-toggle-target))
:group 'mojo) :group 'mojo)
(defcustom mojo-sdk-directory (defcustom mojo-sdk-directory
@ -197,7 +420,17 @@ this work."
"Run Mojo in debug mode. Assumed true while in such an early version." "Run Mojo in debug mode. Assumed true while in such an early version."
:type 'boolean :type 'boolean
:group 'mojo) :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 ;;* interactive generate
(defun mojo-generate (title directory) (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." NAME is the name of the scene."
(interactive "sScene Name: \n") (interactive "sScene Name: \n")
(let ((mojo-dir (mojo-root))) (let ((mojo-dir (mojo-root t)))
(mojo-cmd "palm-generate" (list "-t" "new_scene" (mojo-cmd "palm-generate" (list "-t" "new_scene"
"-p" (format "name=%s" name) mojo-dir)) "-p" (format "name=%s" name) mojo-dir))
(find-file (format "%s/app/assistants/%s-assistant.js" mojo-dir name)) (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'." "Package the current application into `MOJO-BUILD-DIRECTORY'."
(interactive) (interactive)
(mojo-cmd "palm-package" (list "-o" (expand-file-name mojo-build-directory) (mojo-cmd "palm-package" (list "-o" (expand-file-name mojo-build-directory)
(mojo-root)))) (mojo-root t))))
;;* interactive ;;* interactive
(defun mojo-install () (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) (interactive)
(mojo-ensure-emulator-is-running) (mojo-ensure-emulator-is-running)
(mojo-cmd-with-target "palm-install" (list (expand-file-name (mojo-read-package-filename)))) (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 ;;* interactive
(defun mojo-delete () (defun mojo-delete ()
"Remove the current application using `MOJO-APP-ID'." "Remove an application."
(interactive) (interactive)
(mojo-ensure-emulator-is-running) (mojo-ensure-emulator-is-running)
(mojo-cmd-with-target "palm-install" (list "-r" (mojo-read-app-id))) (mojo-cmd-with-target "palm-install" (list "-r" (mojo-read-app-id)))
@ -280,14 +513,14 @@ NAME is the name of the scene."
;;* interactive ;;* interactive
(defun mojo-launch () (defun mojo-launch ()
"Launch the current application in the emulator." "Launch the current application in the emulator, and the emulator if necessary.."
(interactive) (interactive)
(mojo-ensure-emulator-is-running) (mojo-ensure-emulator-is-running)
(mojo-cmd-with-target "palm-launch" (list (mojo-read-app-id)))) (mojo-cmd-with-target "palm-launch" (list (mojo-read-app-id))))
;;* interactive ;;* interactive
(defun mojo-close () (defun mojo-close ()
"Close launched application." "Close an application."
(interactive) (interactive)
(mojo-ensure-emulator-is-running) (mojo-ensure-emulator-is-running)
(mojo-cmd-with-target "palm-launch" (list "-c" (mojo-read-app-id)))) (mojo-cmd-with-target "palm-launch" (list "-c" (mojo-read-app-id))))
@ -348,8 +581,7 @@ Sets `*mojo-target*' to \"tcp\"."
;;* interactive ;;* interactive
(defun mojo-toggle-target () (defun mojo-toggle-target ()
"Automatically change the target device from 'usb' to 'tcp' and "Automatically change the target device from 'usb' to 'tcp' and vice versa."
vice versa."
(interactive) (interactive)
(if (string= "usb" *mojo-target*) (if (string= "usb" *mojo-target*)
(mojo-target-emulator) (mojo-target-emulator)
@ -359,16 +591,16 @@ vice versa."
;; Some support functions that grok the basics of a Mojo project. ;; ;; Some support functions that grok the basics of a Mojo project. ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun mojo-drop-last-path-component (path) (defun mojo-parent-directory (path)
"Get the head of a path by dropping the last component." "Get the parent directory, i.e. the head of a path by dropping the last component."
(if (< (length path) 2) (if (< (length path) 2)
path path
(substring path 0 (- (length path) (substring path 0 (- (length path)
(length (mojo-last-path-component path)) (length (mojo-filename path))
1)))) ;; subtract one more for the trailing slash 1)))) ;; subtract one more for the trailing slash
(defun mojo-last-path-component (path) (defun mojo-filename (path)
"Get the tail of a path, i.e. the last component." "Get the filename from a path, i.e. the last component, or tail."
(if (< (length path) 2) (if (< (length path) 2)
path path
(let ((start -2)) (let ((start -2))
@ -379,32 +611,34 @@ vice versa."
(defvar *mojo-last-root* "" (defvar *mojo-last-root* ""
"Last Mojo root found by `MOJO-ROOT'.") "Last Mojo root found by `MOJO-ROOT'.")
(defun mojo-root () (defun mojo-root (&optional ask)
"Find a Mojo project's root directory starting with `DEFAULT-DIRECTORY'." "Find a Mojo project's root directory starting with `DEFAULT-DIRECTORY'.
(let ((last-component (mojo-last-path-component 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)) (dir-prefix default-directory))
;; remove last path element until we find appinfo.json ;; remove last path element until we find appinfo.json
(while (and (not (file-exists-p (concat dir-prefix "/appinfo.json"))) (while (and (not (file-exists-p (concat dir-prefix "/appinfo.json")))
(not (< (length dir-prefix) 2))) (not (< (length dir-prefix) 2)))
(setq last-component (mojo-last-path-component dir-prefix)) (setq last-component (mojo-filename dir-prefix))
(setq dir-prefix (mojo-drop-last-path-component dir-prefix))) (setq dir-prefix (mojo-parent-directory dir-prefix)))
;; If no Mojo root found, ask for a directory. ;; If no Mojo root found, ask for a directory.
(if (< (length dir-prefix) 2) (when (and ask (< (length dir-prefix) 2))
(setq dir-prefix (mojo-read-root))) (setq dir-prefix (mojo-read-root)))
;; Invalidate cached values when changing projects. ;; Invalidate cached values when changing projects.
(if (or (mojo-blank *mojo-last-root*) (when (or (mojo-blank *mojo-last-root*)
(not (string= dir-prefix *mojo-last-root*))) (not (string= dir-prefix *mojo-last-root*)))
(progn (setq *mojo-last-root* dir-prefix)
(setq *mojo-last-root* dir-prefix) (setq *mojo-package-filename* nil)
(setq *mojo-package-filename* nil) (setq *mojo-app-id* nil)
(setq *mojo-app-id* nil))) (setq *mojo-appinfo* nil))
dir-prefix)) dir-prefix))
;; foolproof? ;; foolproof enough? don't want false positives.
(defun mojo-project-p () (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")) (and (file-exists-p (concat (mojo-root) "/appinfo.json"))
(file-exists-p (concat (mojo-root) "/sources.json")) (file-exists-p (concat (mojo-root) "/sources.json"))
(file-exists-p (concat (mojo-root) "/app")) (file-exists-p (concat (mojo-root) "/app"))
@ -413,34 +647,52 @@ vice versa."
(defun mojo-read-json-file (filename) (defun mojo-read-json-file (filename)
"Parse the JSON in FILENAME and return the result." "Parse the JSON in FILENAME and return the result."
(save-excursion (save-excursion
(let ((origbuffer (current-buffer)) (let* ((origbuffer (current-buffer))
(filebuffer (find-file-noselect filename))) (path (concat (mojo-root) "/" filename))
(filebuffer (find-file-noselect path)))
(set-buffer filebuffer) (set-buffer filebuffer)
(let ((text (buffer-string))) (let ((text (buffer-string)))
(switch-to-buffer origbuffer) (switch-to-buffer origbuffer)
(json-read-from-string text))))) (json-read-from-string text)))))
(defun mojo-app-version () (defun mojo-write-json-file (filename content)
"Parse the project version from the appinfo.json file in `MOJO-ROOT'." "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) (when (mojo-project-p)
(let ((appinfo (mojo-read-json-file (concat (mojo-root) "/appinfo.json")))) (or *mojo-appinfo*
(cdr (assoc 'version 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 () (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) (when (mojo-project-p)
(let ((appinfo (mojo-read-json-file (concat (mojo-root) "/appinfo.json")))) (cdr (assoc 'id (mojo-appinfo)))))
(cdr (assoc 'id appinfo)))))
(defun mojo-app-title () (defun mojo-app-title ()
"Parse the project title from appinfo.json." "Parse the project title from appinfo.json."
(when (mojo-project-p) (when (mojo-project-p)
(let ((appinfo (mojo-read-json-file (concat (mojo-root) "/appinfo.json")))) (cdr (assoc 'title (mojo-appinfo)))))
(cdr (assoc 'title appinfo)))))
(defun mojo-informal-app-id () (defun mojo-informal-app-id ()
"Parse the project title from appinfo.json and remove all non alphanumeric "Parse the project title from appinfo.json and remove all non alphanumeric characters."
characters."
(let ((title (downcase (mojo-app-title)))) (let ((title (downcase (mojo-app-title))))
(replace-regexp-in-string "[^a-z0-9]" "" title))) (replace-regexp-in-string "[^a-z0-9]" "" title)))
@ -450,6 +702,123 @@ vice versa."
(mojo-app-id) (mojo-app-version))) (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 ;;; ;;; app listing and completion ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -497,25 +866,22 @@ vice versa."
(= 0 (length thing))))) (= 0 (length thing)))))
(defun mojo-read-root () (defun mojo-read-root ()
"Get the path to a Mojo application, prompting with completion and "Get the path to a Mojo application, prompting with completion and history."
history."
(read-file-name "Mojo project: " (expand-file-name (concat mojo-project-directory "/")))) (read-file-name "Mojo project: " (expand-file-name (concat mojo-project-directory "/"))))
(defun mojo-read-package-filename () (defun mojo-read-package-filename ()
"Get the filename of a packaged application, prompting with completion and "Get the filename of a packaged application, prompting with completion and history.
history.
The app id is stored in *mojo-package-filename* unless it was blank." The app id is stored in *mojo-package-filename* unless it was blank."
(let* ((default (or *mojo-package-filename* (let* ((default (or *mojo-package-filename*
(mojo-package-filename))) (mojo-package-filename)))
(package (read-file-name (format "Package file (default: %s): " default) (package (read-file-name (format "Package file (default: %s): " default)
(concat mojo-build-directory "/") default t))) (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))) (expand-file-name package)))
(defun mojo-read-app-id (&optional prompt) (defun mojo-read-app-id (&optional prompt)
"Get the id of an existing application, prompting with completion and "Get the id of an existing application, prompting with completion and history.
history.
The app id is stored in *mojo-app-id* unless it was blank." The app id is stored in *mojo-app-id* unless it was blank."
(let* ((default (or *mojo-app-id* (mojo-app-id))) (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))) (nreverse apps)))
(defun mojo-app-cache-stale-p () (defun mojo-app-cache-stale-p ()
"Non-nil if the cache in `MOJO-APP-CACHE-FILE' is more than "Non-nil if the cache in `MOJO-APP-CACHE-FILE' is more than *mojo-app-cache-ttl* seconds old.
*mojo-app-cache-ttl* seconds old.
If the cache file does not exist then it is considered stale." If the cache file does not exist then it is considered stale."
(or (null (file-exists-p (mojo-app-cache-file))) (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) '(("-assistant.js" . mojo-switch-to-view)
("-scene.html" . mojo-switch-to-assistant) ("-scene.html" . mojo-switch-to-assistant)
(".html" . mojo-switch-to-next-view) (".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 ;;* interactive
(defun mojo-switch-to-appinfo () (defun mojo-switch-to-appinfo ()
"Switch to appinfo.json."
(interactive) (interactive)
(find-file (concat (mojo-root) "/appinfo.json"))) (when (mojo-project-p)
(find-file (concat (mojo-root) "/appinfo.json"))))
;;* interactive ;;* interactive
(defun mojo-switch-to-index () (defun mojo-switch-to-index ()
"Switch to index.html."
(interactive) (interactive)
(find-file (concat (mojo-root) "/index.html"))) (when (mojo-project-p)
(find-file (concat (mojo-root) "/index.html"))))
;;* interactive ;;* interactive
(defun mojo-switch-to-sources () (defun mojo-switch-to-sources ()
"Switch to sources.json."
(interactive) (interactive)
(find-file (concat (mojo-root) "/sources.json"))) (when (mojo-project-p)
(find-file (concat (mojo-root) "/sources.json"))))
;;* interactive ;;* interactive
(defun mojo-switch-to-stylesheet () (defun mojo-switch-to-stylesheet ()
"Switch to the main CSS stylesheet, or the first one."
(interactive) (interactive)
(let* ((stylesheet-dir (concat (mojo-root) "/stylesheets")) (when (mojo-project-p)
(path (concat stylesheet-dir "/" (let* ((stylesheet-dir (concat (mojo-root) "/stylesheets"))
(mojo-informal-app-id) ".css"))) (path (concat stylesheet-dir "/"
(when (not (file-exists-p path)) (mojo-informal-app-id) ".css")))
(setq path (car (mojo-filter-paths (directory-files stylesheet-dir t))))) (when (not (file-exists-p path))
(find-file 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 () (defun mojo-scene-name-from-assistant ()
"The scene name of the assistant being edited."
(let ((path (buffer-file-name))) (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 () (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 ;;* interactive
(defun mojo-switch-file-dwim () (defun mojo-switch-file-dwim ()
"Determine if the current buffer is visiting a file with known "Attempt to intelligently find a related file and switch to it.
relationships. Try to find the 'best' choice and switch to it.
This does what I (sjs) mean by default, so change This does what I (sjs) mean by default, so change *mojo-switch-suffixes*
*mojo-switch-suffixes* if you want different behaviour." if you want different behaviour."
(interactive) (interactive)
(let* ((path (buffer-file-name)) (let* ((path (buffer-file-name))
(suffixes (copy-list *mojo-switch-suffixes*)) (suffixes (copy-list *mojo-switch-suffixes*))
@ -645,6 +1027,7 @@ If the cache file does not exist then it is considered stale."
;;* interactive ;;* interactive
(defun mojo-switch-to-view () (defun mojo-switch-to-view ()
"Switch to the corresponding main view from an assistant."
(interactive) (interactive)
(when (mojo-project-p) (when (mojo-project-p)
(let ((scene-name (mojo-scene-name-from-assistant))) (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"))))) scene-name "-scene.html")))))
(defun mojo-ignored-path (path) (defun mojo-ignored-path (path)
(let ((filename (mojo-last-path-component path))) "Paths that we don't want to look at when walking directories."
(or (string= (substring filename 0 1) ".") (let ((filename (mojo-filename path)))
(and (string= (substring filename 0 1) "#") (or (string= (substring filename 0 1) ".") ;; "." and ".." and hidden files
(and (string= (substring filename 0 1) "#") ;; emacs recovery files
(string= (substring filename -1) "#"))))) (string= (substring filename -1) "#")))))
(defun mojo-filter-paths (all-paths) (defun mojo-filter-paths (all-paths)
"Filter out unimportant paths from a list of paths."
(let ((wanted-paths (list))) (let ((wanted-paths (list)))
(dolist (path all-paths wanted-paths) (dolist (path all-paths wanted-paths)
(unless (mojo-ignored-path path) (unless (mojo-ignored-path path)
@ -666,6 +1051,7 @@ If the cache file does not exist then it is considered stale."
(reverse wanted-paths))) (reverse wanted-paths)))
(defun mojo-index (elem list) (defun mojo-index (elem list)
"Return the position of ELEM in LIST."
(catch 'break (catch 'break
(let ((index 0)) (let ((index 0))
(dolist (path list index) (dolist (path list index)
@ -675,6 +1061,7 @@ If the cache file does not exist then it is considered stale."
;;* interactive ;;* interactive
(defun mojo-switch-to-next-view () (defun mojo-switch-to-next-view ()
"Switch to the next view in this scene, alphabetically. Wrap around at the end."
(interactive) (interactive)
(when (mojo-project-p) (when (mojo-project-p)
(let* ((scene-name (mojo-scene-name-from-view)) (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 ;;* interactive
(defun mojo-switch-to-assistant () (defun mojo-switch-to-assistant ()
"Swtich to the corresponding assistant from a view."
(interactive) (interactive)
(let ((scene-name (mojo-scene-name-from-view))) (let ((scene-name (mojo-scene-name-from-view)))
(when (and (mojo-project-p) (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))) (mojo-cmd "luna-send" (list "-n" "1" url data)))
(defvar *mojo-target* "tcp" (defvar *mojo-target* "tcp"
"Used to specify the target platform, \"usb\" for the device "Used to specify the target platform, \"usb\" for the device and \"tcp\"
and \"tcp\" for the emulator. Deaults to \"tcp\".") for the emulator. Deaults to \"tcp\".")
(defun mojo-emulator-running-p () (defun mojo-emulator-running-p ()
"Determine if the webOS emulator is running or not. "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"))) (= 0 (shell-command "ps x | fgrep 'Palm SDK' | fgrep -v fgrep >/dev/null 2>&1")))
(defun mojo-emulator-responsive-p () (defun mojo-emulator-responsive-p ()
"Determine if the webOS emulator is able to respond to commands yet "Determine if the webOS emulator is able to respond to commands yet.
(i.e. if it's done booting)."
(i.e. if it's done booting)."
(= 0 (shell-command "palm-install -d tcp --list >/dev/null 2>&1"))) (= 0 (shell-command "palm-install -d tcp --list >/dev/null 2>&1")))
(defun mojo-path-to-cmd (cmd) (defun mojo-path-to-cmd (cmd)