From 0ebcd7b435465f942c85adfee18c75df11c7a2d0 Mon Sep 17 00:00:00 2001 From: Joseph Atkins-Turkish Date: Fri, 6 May 2016 16:39:42 -0700 Subject: [PATCH 1/4] Improved global shortcut handler and made fuzzyprompt use it too. --- ide/static/ide/js/cloudpebble.js | 43 ---------------------- ide/static/ide/js/editor.js | 13 +++---- ide/static/ide/js/fuzzyprompt.js | 21 ++++++----- ide/static/ide/js/global_shortcuts.js | 53 +++++++++++++++++++++++++++ ide/templates/ide/project.html | 1 + 5 files changed, 72 insertions(+), 59 deletions(-) create mode 100644 ide/static/ide/js/global_shortcuts.js diff --git a/ide/static/ide/js/cloudpebble.js b/ide/static/ide/js/cloudpebble.js index 3fd0f93a..c26ccb79 100644 --- a/ide/static/ide/js/cloudpebble.js +++ b/ide/static/ide/js/cloudpebble.js @@ -184,46 +184,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/editor.js b/ide/static/ide/js/editor.js index cb746b54..dde7cb26 100644 --- a/ide/static/ide/js/editor.js +++ b/ide/static/ide/js/editor.js @@ -97,7 +97,7 @@ CloudPebble.Editor = (function() { settings.extraKeys = {'Ctrl-Space': 'autocomplete'}; } if(!is_js && 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; @@ -130,7 +130,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) { @@ -180,10 +180,10 @@ CloudPebble.Editor = (function() { return CodeMirror.Pass; }; } - settings.extraKeys['Cmd-/'] = function(cm) { + settings.extraKeys['Cmd-/'] = function toggleComment(cm) { CodeMirror.commands.toggleComment(cm); }; - settings.extraKeys['Ctrl-/'] = function(cm) { + settings.extraKeys['Ctrl-/'] = function toggleComment(cm) { CodeMirror.commands.toggleComment(cm); }; if(is_js) { @@ -227,10 +227,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); }; @@ -733,7 +732,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); diff --git a/ide/static/ide/js/fuzzyprompt.js b/ide/static/ide/js/fuzzyprompt.js index 6b2cf063..9545bd84 100644 --- a/ide/static/ide/js/fuzzyprompt.js +++ b/ide/static/ide/js/fuzzyprompt.js @@ -49,17 +49,20 @@ 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") } }); diff --git a/ide/static/ide/js/global_shortcuts.js b/ide/static/ide/js/global_shortcuts.js new file mode 100644 index 00000000..625be878 --- /dev/null +++ b/ide/static/ide/js/global_shortcuts.js @@ -0,0 +1,53 @@ +CloudPebble.GlobalShortcuts = (function () { + var global_shortcuts = {}; + + $(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) { + // If the command is a name like "save", get they key-combo from CodeMirror + if (!(command.indexOf('-') > -1)) { + command = _.findKey(CodeMirror.keyMap.default, _.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; + } + + 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); + global_shortcuts[shortcut] = { + name: descriptor.name ? descriptor.name : key, + func: _.isFunction(descriptor) ? descriptor : descriptor.func + }; + }); + }, + GetShortcuts: function() { + return global_shortcuts; + } + } +})(); diff --git a/ide/templates/ide/project.html b/ide/templates/ide/project.html index c80dd6b9..d2a0e3e9 100644 --- a/ide/templates/ide/project.html +++ b/ide/templates/ide/project.html @@ -503,6 +503,7 @@

{% trans 'Compass and Accelerometer' %}

+ From d8bbb30954ea953fcc102615b7647eec4ea593c6 Mon Sep 17 00:00:00 2001 From: Joseph Atkins-Turkish Date: Thu, 1 Sep 2016 21:15:42 -0700 Subject: [PATCH 2/4] Nicer cloudpebble.js error handling --- ide/static/ide/js/cloudpebble.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ide/static/ide/js/cloudpebble.js b/ide/static/ide/js/cloudpebble.js index 0de2a5c0..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) { From 0330e609aa634af7b209e3e423012f2029407b66 Mon Sep 17 00:00:00 2001 From: Joseph Atkins-Turkish Date: Thu, 1 Sep 2016 21:17:52 -0700 Subject: [PATCH 3/4] Fuzzyprompt subsections! --- ide/static/ide/css/ide.css | 22 +++ ide/static/ide/js/compile.js | 3 +- ide/static/ide/js/dependencies.js | 3 - ide/static/ide/js/editor.js | 83 ++++++++--- ide/static/ide/js/fuzzyprompt.js | 196 +++++++++++++++----------- ide/static/ide/js/global_shortcuts.js | 35 +++-- ide/static/ide/js/settings.js | 3 +- 7 files changed, 228 insertions(+), 117 deletions(-) diff --git a/ide/static/ide/css/ide.css b/ide/static/ide/css/ide.css index c5017e42..22b40e89 100644 --- a/ide/static/ide/css/ide.css +++ b/ide/static/ide/css/ide.css @@ -1136,6 +1136,28 @@ span.cm-autofilled-end { text-align: left; } +.fuzzy-subheader { + /*background-color: #222;*/ + font-weight: bold; + color: #555; + font-variant: small-caps; + margin-left: 1em; + +} +.fuzzy-subheader::before { + /*content: '-- ';*/ +} + +.fuzzy-hint { + opacity: 0.5; +} +.fuzzy-hint::before { + content: ' <'; +} +.fuzzy-hint::after { + content: '>'; +} + #fuzzy-results > div { line-height: 28px; 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 1f8e8698..9a46216b 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() { @@ -214,12 +215,8 @@ CloudPebble.Editor = (function() { return CodeMirror.Pass; }; } - settings.extraKeys['Cmd-/'] = function toggleComment(cm) { - CodeMirror.commands.toggleComment(cm); - }; - settings.extraKeys['Ctrl-/'] = function toggleComment(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'])) { @@ -243,6 +240,7 @@ CloudPebble.Editor = (function() { code_mirror.on('shown', function() { is_autocompleting = true; }); + current_editor = code_mirror; // The browser should probably do this without our help. Sometimes Safari doesn't. $(document).click(function(e) { @@ -580,16 +578,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 +759,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; @@ -823,14 +810,66 @@ 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' + ]; + + _.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); + }); + + // local_commands[gettext('Auto Indent')] = function() { + // current_editor.execCommand('indentAuto'); + // current_editor.focus(); + // }; + // local_commands[gettext('Auto Indent')].hint = CloudPebble.GlobalShortcuts.GetShortcutForCommand('indentAuto'); + // local_commands[gettext('Find')] = function() { + // current_editor.execCommand('find'); + // }; + // local_commands[gettext('Find')].hint = CloudPebble.GlobalShortcuts.GetShortcutForCommand('find'); + + + CloudPebble.FuzzyPrompt.AddCommands(gettext('File'), local_commands, function() { + return (!!current_editor); + }); + + CloudPebble.GlobalShortcuts.SetShortcutHandlers({ + save: function() { + save().catch(alert); + } + }); - 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 9545bd84..c1388523 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) : 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); @@ -51,14 +58,14 @@ CloudPebble.FuzzyPrompt = (function() { // Register ctrl-p and ctrl-shift-p CloudPebble.GlobalShortcuts.SetShortcutHandlers({ 'PlatformCmd-P': { - func: function () { + func: function() { input.attr('placeholder', gettext("Search Files")); show_prompt('files'); }, name: gettext("Find File") }, 'Shift-PlatformCmd-P': { - func: function () { + func: function() { input.attr('placeholder', gettext("Enter Command")); show_prompt('commands'); }, @@ -66,7 +73,7 @@ CloudPebble.FuzzyPrompt = (function() { } }); - prompt.keydown(function (e) { + prompt.keydown(function(e) { // Ctrl-P to hide if (opened && e[modifier] && e.keyCode == 80) { hide_prompt(true); @@ -106,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()) { @@ -124,7 +127,7 @@ CloudPebble.FuzzyPrompt = (function() { } }); - prompt.on('shown.bs.modal', function () { + prompt.on('shown.bs.modal', function() { input.focus(); input.val(""); }); @@ -151,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}); } @@ -188,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. @@ -257,7 +311,7 @@ CloudPebble.FuzzyPrompt = (function() { opened = false; prompt.modal('hide'); if (refocus) { - setTimeout(function () { + setTimeout(function() { $(previously_active).focus(); }, 1); } @@ -268,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. @@ -296,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 index 4ec9e34e..dccf6163 100644 --- a/ide/static/ide/js/global_shortcuts.js +++ b/ide/static/ide/js/global_shortcuts.js @@ -1,7 +1,15 @@ CloudPebble.GlobalShortcuts = (function () { var global_shortcuts = {}; - var keymap = _.extend({}, CodeMirror.keyMap.basic, CodeMirror.keyMap.default); + // 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()) { @@ -13,10 +21,11 @@ CloudPebble.GlobalShortcuts = (function () { } }); - function shortcut_for_command(command) { + 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(keymap, _.partial(_.isEqual, command)); + 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. @@ -26,6 +35,9 @@ CloudPebble.GlobalShortcuts = (function () { } else return name; } + if (!command) { + return null; + } return command.split('-').map(key_for_platform).join('-'); } @@ -42,17 +54,22 @@ CloudPebble.GlobalShortcuts = (function () { SetShortcutHandlers: function (shortcuts) { _.each(shortcuts, function (descriptor, key) { var shortcut = shortcut_for_command(key); - global_shortcuts[shortcut] = { - name: descriptor.name ? descriptor.name : key, - func: _.isFunction(descriptor) ? descriptor : descriptor.func - }; + if (shortcut) { + global_shortcuts[shortcut] = { + name: descriptor.name ? descriptor.name : key, + func: _.isFunction(descriptor) ? descriptor : descriptor.func + }; + } }); }, GetShortcuts: function() { return global_shortcuts; }, - GetShortcutForCommand: function(name) { - return shortcut_for_command(name); + GetCodemirrorShortcuts: function() { + return codemirror_full_keymap; + }, + GetShortcutForCommand: function(name, extras) { + return shortcut_for_command(name, extras); } } })(); 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) { From f20b8f1a572b2c6a6e06b86cb6c2d6d391a24466 Mon Sep 17 00:00:00 2001 From: Joseph Atkins-Turkish Date: Fri, 2 Sep 2016 14:39:57 -0700 Subject: [PATCH 4/4] Cleanup and fixes --- ide/static/ide/css/ide.css | 7 +------ ide/static/ide/js/editor.js | 32 +++++++++++++------------------- ide/static/ide/js/fuzzyprompt.js | 2 +- ide/static/ide/js/resources.js | 22 ++++++++++++++++------ 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/ide/static/ide/css/ide.css b/ide/static/ide/css/ide.css index 22b40e89..ce5638fe 100644 --- a/ide/static/ide/css/ide.css +++ b/ide/static/ide/css/ide.css @@ -1137,16 +1137,12 @@ span.cm-autofilled-end { } .fuzzy-subheader { - /*background-color: #222;*/ font-weight: bold; - color: #555; + color: #666; font-variant: small-caps; margin-left: 1em; } -.fuzzy-subheader::before { - /*content: '-- ';*/ -} .fuzzy-hint { opacity: 0.5; @@ -1159,7 +1155,6 @@ span.cm-autofilled-end { } #fuzzy-results > div { - line-height: 28px; cursor: pointer; cursor: hand; diff --git a/ide/static/ide/js/editor.js b/ide/static/ide/js/editor.js index 9a46216b..9e83c7f2 100644 --- a/ide/static/ide/js/editor.js +++ b/ide/static/ide/js/editor.js @@ -240,7 +240,6 @@ CloudPebble.Editor = (function() { code_mirror.on('shown', function() { is_autocompleting = true; }); - current_editor = code_mirror; // The browser should probably do this without our help. Sometimes Safari doesn't. $(document).click(function(e) { @@ -482,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() { @@ -496,6 +504,7 @@ CloudPebble.Editor = (function() { fullscreen(code_mirror, false); resume_fullscreen = true; } + current_editor = null; }, onDestroy: function() { if(!was_clean) { @@ -520,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(); @@ -766,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(); }); @@ -835,7 +845,7 @@ CloudPebble.Editor = (function() { var codemirror_commands = [ {command: 'indentAuto', refocus: true}, {command: 'toggleComment', hint: 'Cmd-/ or Ctrl-/', refocus: true}, - 'find', 'replace', 'indentMore', 'indentLess' + 'find', 'replace', 'indentMore', 'indentLess', 'save', 'saveAll' ]; _.each(codemirror_commands, function(entry) { @@ -848,26 +858,10 @@ CloudPebble.Editor = (function() { local_commands[name].hint = !!entry.hint ? entry.hint : CloudPebble.GlobalShortcuts.GetShortcutForCommand(command); }); - // local_commands[gettext('Auto Indent')] = function() { - // current_editor.execCommand('indentAuto'); - // current_editor.focus(); - // }; - // local_commands[gettext('Auto Indent')].hint = CloudPebble.GlobalShortcuts.GetShortcutForCommand('indentAuto'); - // local_commands[gettext('Find')] = function() { - // current_editor.execCommand('find'); - // }; - // local_commands[gettext('Find')].hint = CloudPebble.GlobalShortcuts.GetShortcutForCommand('find'); - - CloudPebble.FuzzyPrompt.AddCommands(gettext('File'), local_commands, function() { return (!!current_editor); }); - CloudPebble.GlobalShortcuts.SetShortcutHandlers({ - save: function() { - save().catch(alert); - } - }); CloudPebble.FuzzyPrompt.AddCommands(gettext('Actions'), global_commands); diff --git a/ide/static/ide/js/fuzzyprompt.js b/ide/static/ide/js/fuzzyprompt.js index c1388523..8b3cea06 100644 --- a/ide/static/ide/js/fuzzyprompt.js +++ b/ide/static/ide/js/fuzzyprompt.js @@ -33,7 +33,7 @@ CloudPebble.FuzzyPrompt = (function() { var options = { caseSensitive: false, sortFn: function(a, b) { - return (a.score === b.score) ? a.item.name.localeCompare(b) : a.score - b.score; + return (a.score === b.score) ? a.item.name.localeCompare(b.item.name) : a.score - b.score; }, shouldSort: true, threshold: 0.5, 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() {