diff --git a/ide/static/ide/css/ide.css b/ide/static/ide/css/ide.css
index c5017e42..ce5638fe 100644
--- a/ide/static/ide/css/ide.css
+++ b/ide/static/ide/css/ide.css
@@ -1136,8 +1136,25 @@ span.cm-autofilled-end {
text-align: left;
}
-#fuzzy-results > div {
+.fuzzy-subheader {
+ font-weight: bold;
+ color: #666;
+ font-variant: small-caps;
+ margin-left: 1em;
+}
+
+.fuzzy-hint {
+ opacity: 0.5;
+}
+.fuzzy-hint::before {
+ content: ' <';
+}
+.fuzzy-hint::after {
+ content: '>';
+}
+
+#fuzzy-results > div {
line-height: 28px;
cursor: pointer;
cursor: hand;
diff --git a/ide/static/ide/js/cloudpebble.js b/ide/static/ide/js/cloudpebble.js
index 306c0633..cc179401 100644
--- a/ide/static/ide/js/cloudpebble.js
+++ b/ide/static/ide/js/cloudpebble.js
@@ -15,6 +15,10 @@ CloudPebble.ProgressBar = (function() {
},
Hide: function() {
hide();
+ },
+ Error: function(msg) {
+ $('#progress-pane').find('.progress').addClass('progress-danger').removeClass('progress-striped')
+ .after($('
').text(msg).css({margin: 'auto', width: '300px'}));
}
};
})();
@@ -67,7 +71,6 @@ CloudPebble.Init = function() {
CloudPebble.Dependencies.Init();
CloudPebble.Documentation.Init();
CloudPebble.FuzzyPrompt.Init();
- CloudPebble.ProgressBar.Hide();
// Add source files.
$.each(data.source_files, function(index, value) {
@@ -86,8 +89,11 @@ CloudPebble.Init = function() {
$('.sdk3-only').hide();
}
return null;
+ }).then(function() {
+ CloudPebble.ProgressBar.Hide();
}).catch(function(err) {
- alert("Something went wrong:\n" + err.message);
+ CloudPebble.ProgressBar.Error(err);
+ throw err;
});
window.addEventListener('beforeunload', function(e) {
@@ -233,46 +239,3 @@ CloudPebble.Utils = {
return interpolate(ngettext("%s second", "%s seconds", n), [n]);
}
};
-
-CloudPebble.GlobalShortcuts = (function() {
- var make_shortcut_checker = function (command) {
- if (!(command.indexOf('-') > -1)) {
- command = _.findKey(CodeMirror.keyMap.default, _.partial(_.isEqual, command));
- }
- var split = command.split('-');
- var modifier = ({
- 'ctrl': 'ctrlKey',
- 'cmd': 'metaKey'
- })[split[0].toLowerCase()];
- return function (e) {
- return (e[modifier] && String.fromCharCode(e.keyCode) == split[1]);
- }
- };
-
-
- var global_shortcuts = {};
-
- $(document).keydown(function (e) {
- if (!e.isDefaultPrevented()) {
- _.each(global_shortcuts, function (shortcut) {
- if (shortcut.checker(e)) {
- shortcut.func(e);
- e.preventDefault();
- }
- });
- }
- });
-
- return {
- SetShortcutHandlers: function (shortcuts) {
- var new_shortcuts = _.mapObject(shortcuts, function (func, key) {
- return {
- checker: make_shortcut_checker(key),
- func: func
- };
-
- });
- _.extend(global_shortcuts, new_shortcuts);
- }
- }
-})();
diff --git a/ide/static/ide/js/compile.js b/ide/static/ide/js/compile.js
index 26ab6846..f51b422c 100644
--- a/ide/static/ide/js/compile.js
+++ b/ide/static/ide/js/compile.js
@@ -179,10 +179,9 @@ CloudPebble.Compile = (function() {
commands[gettext("Show Phone Logs")] = function() { show_app_logs(ConnectionType.Phone); };
commands[gettext("Show Emulator Logs")] = function() { show_app_logs(ConnectionType.Qemu); };
commands[gettext("Show Last Build Log")] = function() {show_build_log(mLastBuild.id)};
- commands[gettext("Compilation")] = function() { show_compile_pane();};
commands[gettext("Clear App Logs")] = function() { show_clear_logs_prompt(); };
commands[gettext("Take Screenshot")] = function() { take_screenshot(); };
- CloudPebble.FuzzyPrompt.AddCommands(commands);
+ CloudPebble.FuzzyPrompt.AddCommands(gettext('Actions'), commands);
SharedPebble.on('app_log', handle_app_log);
SharedPebble.on('phone_log', handle_phone_log);
diff --git a/ide/static/ide/js/dependencies.js b/ide/static/ide/js/dependencies.js
index b2c48c8c..7e0ed808 100644
--- a/ide/static/ide/js/dependencies.js
+++ b/ide/static/ide/js/dependencies.js
@@ -609,9 +609,6 @@ CloudPebble.Dependencies = (function() {
show_dependencies_pane();
},
Init: function() {
- var commands = {};
- commands[gettext("Dependencies")] = CloudPebble.Dependencies.Show;
- CloudPebble.FuzzyPrompt.AddCommands(commands);
dependencies_template = $('#dependencies-pane-template').remove().removeClass('hide');
alerts.init(dependencies_template);
diff --git a/ide/static/ide/js/editor.js b/ide/static/ide/js/editor.js
index ac4498a5..9e83c7f2 100644
--- a/ide/static/ide/js/editor.js
+++ b/ide/static/ide/js/editor.js
@@ -4,6 +4,7 @@ CloudPebble.Editor = (function() {
var unsaved_files = 0;
var is_fullscreen = false;
var resume_fullscreen = false;
+ var current_editor = null;
var add_source_file = function(file) {
CloudPebble.Sidebar.AddSourceFile(file, function() {
@@ -129,8 +130,9 @@ CloudPebble.Editor = (function() {
if(language_has_autocomplete && USER_SETTINGS.autocomplete === 2) {
settings.extraKeys = {'Ctrl-Space': 'autocomplete'};
}
+
if(language_has_autocomplete && USER_SETTINGS.autocomplete !== 0) {
- settings.extraKeys['Tab'] = function() {
+ settings.extraKeys['Tab'] = function selectNextArgument() {
var marks = code_mirror.getAllMarks();
var cursor = code_mirror.getCursor();
var closest = null;
@@ -163,7 +165,7 @@ CloudPebble.Editor = (function() {
if(USER_SETTINGS.use_spaces) {
var spaces = Array(settings.indentUnit + 1).join(' ');
var oldTab = settings.extraKeys['Tab'];
- settings.extraKeys['Tab'] = function(cm) {
+ settings.extraKeys['Tab'] = function indentMoreOrSelectNextArgument(cm) {
// If we already overrode tab, check that one.
if(oldTab) {
if(oldTab(cm) !== CodeMirror.Pass) {
@@ -213,12 +215,8 @@ CloudPebble.Editor = (function() {
return CodeMirror.Pass;
};
}
- settings.extraKeys['Cmd-/'] = function(cm) {
- CodeMirror.commands.toggleComment(cm);
- };
- settings.extraKeys['Ctrl-/'] = function(cm) {
- CodeMirror.commands.toggleComment(cm);
- };
+ settings.extraKeys['Ctrl-/'] = 'toggleComment';
+ settings.extraKeys['Cmd-/'] = 'toggleComment';
settings.gutters = ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'];
if(file_kind_in(['js', 'json'])) {
@@ -262,10 +260,9 @@ CloudPebble.Editor = (function() {
var help_shortcut = /Mac/.test(navigator.platform) ? 'Shift-Cmd-Ctrl-/' : 'Shift-Ctrl-Alt-/';
- settings.extraKeys[help_shortcut] = function(cm) {
+ settings.extraKeys[help_shortcut] = function lookupFunction(cm) {
var pos = cm.cursorCoords();
var token = code_mirror.getTokenAt(cm.getCursor());
-
create_popover(cm, token.string, pos.left, pos.top);
};
@@ -484,13 +481,22 @@ CloudPebble.Editor = (function() {
alert(gettext(interpolate("Failed to reload file. %s", [error])));
});
};
+
+ function set_save_shortcut() {
+ CloudPebble.GlobalShortcuts.SetShortcutHandlers({
+ "PlatformCmd-S": save
+ });
+ }
+ set_save_shortcut();
CloudPebble.Sidebar.SetActivePane(pane, {
id: 'source-' + file.id,
onRestore: function() {
+ current_editor = code_mirror;
code_mirror.refresh();
_.defer(function() { code_mirror.focus(); });
check_safe();
+ set_save_shortcut();
refresh_ib();
},
onSuspend: function() {
@@ -498,6 +504,7 @@ CloudPebble.Editor = (function() {
fullscreen(code_mirror, false);
resume_fullscreen = true;
}
+ current_editor = null;
},
onDestroy: function() {
if(!was_clean) {
@@ -522,7 +529,7 @@ CloudPebble.Editor = (function() {
CloudPebble.Sidebar.ClearIcon('source-' + file.id);
};
- var save = function() {
+ function save() {
// Make sure we're up to date with whatever changed in IB.
if(ib_showing) {
var content = code_mirror.getValue();
@@ -580,16 +587,12 @@ CloudPebble.Editor = (function() {
},
pattern)
};
+ code_mirror.show_rename_prompt = show_rename_prompt;
var ib_pane = $('#ui-editor-pane-template').clone().removeClass('hide').appendTo(pane).hide();
var ib_editor = new IB(ib_pane.find('.ui-canvas'), ib_pane.find('#ui-properties'), ib_pane.find('#ui-toolkit'), ib_pane.find('#ui-layer-list > div'));
var ib_showing = false;
- CloudPebble.GlobalShortcuts.SetShortcutHandlers({
- save: function() {
- save().catch(alert);
- }
- });
function toggle_ib() {
if(!ib_showing) {
@@ -765,13 +768,6 @@ CloudPebble.Editor = (function() {
fullscreen(code_mirror, true);
}
- var commands = {};
- commands[gettext('Rename File')] = function() {
- // We need to use a timeout because the rename prompt will conflict with the old prompt
- setTimeout(show_rename_prompt, 0);
- };
- CloudPebble.FuzzyPrompt.ReplaceCommands(commands);
-
// Tell Google
ga('send', 'event', 'file', 'open');
return code_mirror;
@@ -779,6 +775,7 @@ CloudPebble.Editor = (function() {
var error_box = $('
');
error_box.text(interpolate(gettext("Something went wrong: %s"), [error.message]));
CloudPebble.Sidebar.SetActivePane(error_box, {id: ''});
+ throw error;
}).finally(function() {
CloudPebble.ProgressBar.Hide();
});
@@ -796,7 +793,7 @@ CloudPebble.Editor = (function() {
cm.showHint({hint: CloudPebble.Editor.Autocomplete.complete, completeSingle: false});
};
CodeMirror.commands.save = function(cm) {
- cm.cloudpebble_save().catch(alert);;
+ cm.cloudpebble_save().catch(alert);
};
CodeMirror.commands.saveAll = function(cm) {
save_all().catch(alert);
@@ -823,14 +820,50 @@ CloudPebble.Editor = (function() {
edit_source_file(item.file);
}
});
- var commands = {};
+ var global_commands = {};
if (CloudPebble.ProjectProperties.is_runnable) {
- commands[gettext('Run')] = run;
+ global_commands[gettext('Run')] = run;
} else {
- commands[gettext('Build')] = run;
+ global_commands[gettext('Build')] = run;
}
+ var local_commands = {};
+ local_commands[gettext('Rename Source File')] = function() {
+ // We need to defer because the rename prompt will conflict with the old prompt
+ _.defer(function() {
+ current_editor.show_rename_prompt();
+ });
+ };
+
+ function neaten_command_name(str) {
+ var result = str.replace(/(\S)([A-Z])/g, '$1 $2').replace(/^./, function(str){ return str.toUpperCase(); });
+ result = result.replace('Doc ', 'Document ');
+ result = result.replace('Go ', 'Go To ');
+ result = result.replace('Del ', 'Delete ');
+ return result;
+ }
+
+ var codemirror_commands = [
+ {command: 'indentAuto', refocus: true},
+ {command: 'toggleComment', hint: 'Cmd-/ or Ctrl-/', refocus: true},
+ 'find', 'replace', 'indentMore', 'indentLess', 'save', 'saveAll'
+ ];
+
+ _.each(codemirror_commands, function(entry) {
+ var command = !!entry.command ? entry.command : entry;
+ var name = (!!entry.name ? entry.name : neaten_command_name(command));
+ local_commands[name] = function() {
+ current_editor.execCommand(command);
+ if (!!entry.refocus) current_editor.focus();
+ };
+ local_commands[name].hint = !!entry.hint ? entry.hint : CloudPebble.GlobalShortcuts.GetShortcutForCommand(command);
+ });
+
+ CloudPebble.FuzzyPrompt.AddCommands(gettext('File'), local_commands, function() {
+ return (!!current_editor);
+ });
+
- CloudPebble.FuzzyPrompt.AddCommands(commands);
+ CloudPebble.FuzzyPrompt.AddCommands(gettext('Actions'), global_commands);
}
diff --git a/ide/static/ide/js/fuzzyprompt.js b/ide/static/ide/js/fuzzyprompt.js
index 6b2cf063..8b3cea06 100644
--- a/ide/static/ide/js/fuzzyprompt.js
+++ b/ide/static/ide/js/fuzzyprompt.js
@@ -23,9 +23,8 @@ CloudPebble.FuzzyPrompt = (function() {
this.name = opts.name;
this.callback = opts.callback;
this.object = opts.object;
- this.id = opts.id;
this.menu_item = opts.menu_item;
- this.rank = opts.rank
+ this.subsection = opts.subsection;
};
var init = function() {
@@ -33,12 +32,20 @@ CloudPebble.FuzzyPrompt = (function() {
// Set up the fuzzy matcher
var options = {
caseSensitive: false,
- includeScore: false,
+ sortFn: function(a, b) {
+ return (a.score === b.score) ? a.item.name.localeCompare(b.item.name) : a.score - b.score;
+ },
shouldSort: true,
- threshold: 0.4,
+ threshold: 0.5,
location: 0,
- distance: 20,
- keys: ["name"]
+ distance: 40,
+ keys: [{
+ name: 'name',
+ weight: 0.8
+ }, {
+ name: 'subsection',
+ weight: 0.2
+ }]
};
fuse = new Fuse([], options);
@@ -49,21 +56,24 @@ CloudPebble.FuzzyPrompt = (function() {
var modifier = /Mac/.test(navigator.platform) ? 'metaKey' : 'ctrlKey';
// Register ctrl-p and ctrl-shift-p
- $(document).keydown(function(e) {
- if ((e[modifier]) && e.keyCode == 80) {
- e.preventDefault();
- if (e.shiftKey) {
- input.attr('placeholder', gettext("Enter Command"));
- show_prompt('commands');
- }
- else if (!e.shiftKey) {
+ CloudPebble.GlobalShortcuts.SetShortcutHandlers({
+ 'PlatformCmd-P': {
+ func: function() {
input.attr('placeholder', gettext("Search Files"));
show_prompt('files');
- }
+ },
+ name: gettext("Find File")
+ },
+ 'Shift-PlatformCmd-P': {
+ func: function() {
+ input.attr('placeholder', gettext("Enter Command"));
+ show_prompt('commands');
+ },
+ name: gettext("Find Action")
}
});
- prompt.keydown(function (e) {
+ prompt.keydown(function(e) {
// Ctrl-P to hide
if (opened && e[modifier] && e.keyCode == 80) {
hide_prompt(true);
@@ -103,11 +113,7 @@ CloudPebble.FuzzyPrompt = (function() {
// Then build the new results list
if (matches.length > 0) {
- _.each(matches, function(match, rank) {
- match.menu_item.appendTo(results);
- // The item's rank is its current position in the suggestion list.
- match.rank = rank;
- });
+ render_list(matches);
// Highlight the first item if the previously highlighted item disappears
// or the user has not been using the arrow keys
if (!manual || !(_.chain(matches).pluck('id')).contains(selected_id).value()) {
@@ -121,7 +127,7 @@ CloudPebble.FuzzyPrompt = (function() {
}
});
- prompt.on('shown.bs.modal', function () {
+ prompt.on('shown.bs.modal', function() {
input.focus();
input.val("");
});
@@ -148,7 +154,7 @@ CloudPebble.FuzzyPrompt = (function() {
var parts = input.val().split(":", 2);
if (parts[0].length == 0) {
if (_.isUndefined(parts[1]))
- return item_list;
+ return item_list;//_.sortBy(item_list, 'name');
else {
return _.where(item_list, {name: default_item});
}
@@ -185,39 +191,90 @@ CloudPebble.FuzzyPrompt = (function() {
opened = true;
prompt_kind = kind;
// Build up the list of files to search through
- var id = 0;
_.each(sources, function(source) {
if (source.kind == kind) {
- _.each(source.list_func(), function (object, name) {
- name = (!_.isFunction(object) && object.name ? object.name : name);
- var menu_item = $("
");
- menu_item.text(name).appendTo(results);
- // Set up the menu item handler
- (function () {
- var this_id = id;
- menu_item.on('click', function () {
- select_item(item_list[this_id]);
- });
- })();
+ if ((_.isFunction(source.should_show) && source.should_show()) || source.should_show === true) {
+ _.each(source.list_func(), function(object, name) {
+ name = (!_.isFunction(object) && object.name ? object.name : name);
+ var menu_item = $("
").append('
').text(name);
+ if (object.hint) {
+ menu_item.append($('').addClass('fuzzy-hint').text(object.hint));
+ }
- item_list.push(new Item({
- name: name,
- callback: source.callback,
- object: object,
- id: id,
- menu_item: menu_item,
- rank: id
- }));
- id++;
- });
+ item_list.push(new Item({
+ name: name,
+ callback: source.callback,
+ object: object,
+ menu_item: menu_item,
+ subsection: source.subsection
+ }));
+ id++;
+ });
+ }
}
});
+ item_list = _.sortBy(item_list, 'name');
+ var id = 0;
+ _.each(item_list, function(item) {
+ // Set up the menu item handler
+ (function() {
+ var this_id = id;
+ item.id = id;
+ item.rank = id;
+ item.menu_item.on('click', function() {
+ select_item(item_list[this_id]);
+ });
+ id += 1;
+ })();
+ });
+
fuse.set(item_list);
+ item_list = _.sortBy(item_list, 'name');
+ render_list(item_list);
// Select the current item by default, or the first item.
highlight_item(_.findWhere(item_list, {name: default_item}) || item_list[0]);
};
+ function render_list(item_list) {
+ var grouped = _.groupBy(item_list, 'subsection');
+ var sections = _.keys(grouped);
+ function add_items(current_rank, items) {
+ _.each(items, function(item) {
+ item.rank = current_rank;
+ results.append(item.menu_item);
+ current_rank +=1;
+ });
+ return current_rank;
+ }
+
+ if (sections.length === 1) {
+ if (sections[0] != 'undefined') {
+ results.append($('').text(sections[0]).addClass('fuzzy-subheader'));
+ }
+ add_items(0, item_list);
+ // results.append(_.pluck(item_list, 'menu_item'));
+ }
+ else {
+ var found = {};
+ var ordered = [];
+ var i = 0;
+ _.some(item_list, function(item) {
+ if (!_.has(found, item.subsection)) {
+ found[item.subsection] = true;
+ ordered.push(item.subsection);
+ i += 1;
+ }
+ if (i == sections.length) return true;
+ });
+ var rank = 0;
+ _.each(ordered, function(name) {
+ results.append($('
').text(name).addClass('fuzzy-subheader'));
+ rank = add_items(rank, grouped[name]);
+ });
+ }
+ }
+
/** Highlight an item in the suggestions list. If enter is hit, the highlighted item gets selected.
*
* @param {Item} item An item object.
@@ -254,7 +311,7 @@ CloudPebble.FuzzyPrompt = (function() {
opened = false;
prompt.modal('hide');
if (refocus) {
- setTimeout(function () {
+ setTimeout(function() {
$(previously_active).focus();
}, 1);
}
@@ -265,14 +322,6 @@ CloudPebble.FuzzyPrompt = (function() {
}
};
- var add_commands = function(commands) {
- sources.push({
- list_func: function() {return commands;},
- callback: function(func) {func();},
- kind: 'commands'
- });
- };
-
return {
/** Let fuzzy-prompt know the name of the currently open file/location to use as a default
* when nothing has been typed.
@@ -293,46 +342,36 @@ CloudPebble.FuzzyPrompt = (function() {
* @param {string} kind 'files' or 'commands'
* @param {Function} item_getter A function which should return a dict of items with string name keys
* @param {Function} select_callback A function to call when one of these items is selected
+ * @param {Function|Boolean} should_show Whether to actually show the commands. Either true, or a function which returns a boolean.
*/
- AddDataSource: function(kind, item_getter, select_callback) {
- sources.push({list_func: item_getter, callback: select_callback, kind: kind});
+ AddDataSource: function(kind, item_getter, select_callback, should_show) {
+ if (_.isUndefined(should_show)) should_show = true;
+ sources.push({
+ list_func: item_getter,
+ callback: select_callback,
+ kind: kind,
+ should_show: should_show
+ });
},
/** Add a set of commands
*
* @param {Object.} commands A dictionary of names->functions
+ * @param {Function|Boolean} should_show Whether to actually show the commands.
*/
- AddCommands: function(commands) {
- add_commands(commands);
- },
- /** Add a new set of commands, replacing any identically named commands
- *
- * @param {Object.} commands A dictionary of names->functions
- */
- ReplaceCommands: function(commands) {
- // For each new command, look through the data source for
- // commands with the same name. If we find any, replace their functions
- // with the new one.
- var all_replaced = [];
-
- _.each(commands, function(newfunc, compare_key) {
- _.each(sources, function (source) {
- if (source.kind == 'commands') {
- var source_commands = source.list_func();
- _.each(source_commands, function (func, key) {
- if (compare_key == key) {
- source_commands[key] = newfunc;
- // Keep track of the fact that this command was replaced
- all_replaced.push(key);
- }
- });
- source.list_func = function() {return source_commands};
- }
- });
+ AddCommands: function(subsection, commands, should_show) {
+ if (_.isUndefined(should_show)) should_show = true;
+ sources.push({
+ list_func: function() {
+ return commands;
+ },
+ callback: function(func) {
+ func();
+ },
+ kind: 'commands',
+ should_show: should_show,
+ subsection: subsection
});
-
- // At the end, we add any commands in the set which were new and note replacements.
- var filtered = _.omit(commands, all_replaced);
- add_commands(filtered);
+ // CloudPebble.FuzzyPrompt.AddDataSource('commands', , , should_show);
},
Init: function() {
init();
diff --git a/ide/static/ide/js/global_shortcuts.js b/ide/static/ide/js/global_shortcuts.js
new file mode 100644
index 00000000..dccf6163
--- /dev/null
+++ b/ide/static/ide/js/global_shortcuts.js
@@ -0,0 +1,75 @@
+CloudPebble.GlobalShortcuts = (function () {
+ var global_shortcuts = {};
+
+ // Build a full map of CodeMirror shortcuts by checking for the 'fallthrough' property
+ // and integrating any sub-keymaps of the main one.
+ var codemirror_full_keymap = _.clone(CodeMirror.keyMap[USER_SETTINGS.keybinds]);
+ if (codemirror_full_keymap.fallthrough) {
+ _.each(codemirror_full_keymap.fallthrough, function(sub_map) {
+ _.defaults(codemirror_full_keymap, CodeMirror.keyMap[sub_map]);
+ });
+ delete codemirror_full_keymap.fallthrough;
+ }
+
+ $(document).keydown(function (e) {
+ if (!e.isDefaultPrevented()) {
+ var shortcut = global_shortcuts[CodeMirror.keyName(e)];
+ if (shortcut) {
+ e.preventDefault();
+ shortcut.func(e);
+ }
+ }
+ });
+
+ function shortcut_for_command(command, commands) {
+ var look_through = _.isObject(commands) ? commands : codemirror_full_keymap;
+ // If the command is a name like "save", get they key-combo from CodeMirror
+ if (!(command.indexOf('-') > -1)) {
+ command = _.findKey(look_through, _.partial(_.isEqual, command));
+ }
+
+ // If any of the shortcut items are "platformcmd", convert them to 'Ctrl' or 'Cmd' depending on the platform.
+ function key_for_platform(name) {
+ if (name.toLowerCase() == "platformcmd") {
+ return /Mac/.test(navigator.platform) ? 'Cmd' : 'Ctrl'
+ } else return name;
+ }
+
+ if (!command) {
+ return null;
+ }
+ return command.split('-').map(key_for_platform).join('-');
+ }
+
+ return {
+ /** Add or replace global shortcuts
+ *
+ * @param {Object} shortcuts The keys of this object must be strings which represent keyboard shortcuts.
+ * They can Codemirror-compatible shortcut descriptors e.g. "Shift-Cmd-V", or they can reference CodeMirror
+ * commands such as "Save".
+ * The values should be objects which have a descriptive "name" property, and also either have a "func" property
+ * or be functions themselves. For example, a named function fully satisfies the requirements, as does an object
+ * such as {name: "My Function", func: my_anonymous_function}
+ */
+ SetShortcutHandlers: function (shortcuts) {
+ _.each(shortcuts, function (descriptor, key) {
+ var shortcut = shortcut_for_command(key);
+ if (shortcut) {
+ global_shortcuts[shortcut] = {
+ name: descriptor.name ? descriptor.name : key,
+ func: _.isFunction(descriptor) ? descriptor : descriptor.func
+ };
+ }
+ });
+ },
+ GetShortcuts: function() {
+ return global_shortcuts;
+ },
+ GetCodemirrorShortcuts: function() {
+ return codemirror_full_keymap;
+ },
+ GetShortcutForCommand: function(name, extras) {
+ return shortcut_for_command(name, extras);
+ }
+ }
+})();
diff --git a/ide/static/ide/js/resources.js b/ide/static/ide/js/resources.js
index 21263df1..d130dfc7 100644
--- a/ide/static/ide/js/resources.js
+++ b/ide/static/ide/js/resources.js
@@ -470,15 +470,28 @@ CloudPebble.Resources = (function() {
if(list_entry) {
list_entry.addClass('active');
}
-
+ function set_save_shortcut() {
+ CloudPebble.GlobalShortcuts.SetShortcutHandlers({
+ "PlatformCmd-S": save
+ });
+ }
+ set_save_shortcut();
CloudPebble.Sidebar.SetActivePane(pane, {
id: 'resource-' + resource.id,
- onRestore: _.partial(restore_pane, pane)
+ onRestore: function() {
+ restore_pane(pane);
+ set_save_shortcut();
+ },
+ onSuspend: function() {
+ CloudPebble.GlobalShortcuts.SetShortcutHandlers({
+ "PlatformCmd-S": function() {return false;}
+ });
+ }
});
pane.find('#edit-resource-type').val(resource.kind).attr('disabled', 'disabled');
pane.find('#edit-resource-type').change();
- var save = function(e) {
+ function save(e) {
if (e) e.preventDefault();
process_resource_form(form, false, resource.file_name, "/ide/project/" + PROJECT_ID + "/resource/" + resource.id + "/update").then(function(data) {
delete project_resources[resource.file_name];
@@ -764,9 +777,6 @@ CloudPebble.Resources = (function() {
}).init();
form.submit(save);
- CloudPebble.GlobalShortcuts.SetShortcutHandlers({
- save: save
- });
restore_pane(pane);
}).finally(function() {
diff --git a/ide/static/ide/js/settings.js b/ide/static/ide/js/settings.js
index 42b9e15f..6fc4d892 100644
--- a/ide/static/ide/js/settings.js
+++ b/ide/static/ide/js/settings.js
@@ -409,7 +409,8 @@ CloudPebble.Settings = (function() {
commands["GitHub"] = CloudPebble.GitHub.Show;
commands[gettext("Timeline")] = CloudPebble.Timeline.show;
commands[gettext("Add New Source File")] = CloudPebble.Editor.Create;
- CloudPebble.FuzzyPrompt.AddCommands(commands);
+ commands[gettext("Dependencies")] = CloudPebble.Dependencies.Show;
+ CloudPebble.FuzzyPrompt.AddCommands(gettext('Navigation'), commands);
settings_template = $('#settings-pane-template').remove().removeClass('hide');
},
AddResource: function(resource) {
diff --git a/ide/templates/ide/project.html b/ide/templates/ide/project.html
index 91e52f57..b6f1c6b9 100644
--- a/ide/templates/ide/project.html
+++ b/ide/templates/ide/project.html
@@ -575,6 +575,7 @@ {% trans 'Compass and Accelerometer' %}
+