diff --git a/TODO b/TODO index bc2543dd..d8790d39 100644 --- a/TODO +++ b/TODO @@ -9,6 +9,7 @@ BUGS: - autoupdate does not work - multiple windows to not work at all, so :q will close the whole browser session, even when there are other windows which has tabs - reload/stop buttons don't update enabled state +- http://en.wikipedia.org/wiki/Portal:Current_events - 'f' shows more hints than 'F' (on left side of nav bar) FEATURES: 9 marks of a Location, [m a-zA-Z] to set it, [` a-zA-Z] to go there @@ -31,7 +32,7 @@ FEATURES: 6 autocommands (BrowserStart, BrowserQuit, TabClose, TabOpen, TabChanged, PageLoaded, any more?) 6 vim like mappings for caret mode and textboxes (i to start caret mode?) 6 pipe selected text/link/website to an external command -6 it would be nice to have :undo w/ tab completion support +6 it would be nice to have :(undo|back|forward) w/ tab completion support 6 macros (qq) 6 support firefox search engines, or at least make our search enginges user configurable 6 gf = view source? diff --git a/chrome/content/vimperator/commands.js b/chrome/content/vimperator/commands.js index 8aac5a6a..e49a78a4 100644 --- a/chrome/content/vimperator/commands.js +++ b/chrome/content/vimperator/commands.js @@ -107,7 +107,7 @@ var g_commands = [/*{{{*/ ["buffer", "b"], ["b[uffer]"], "Go to buffer number n. Full completion works.", - function (args) { tab_go(args.split(":")[0]); }, + buffer_switch, function (filter) {return get_buffer_completions(filter);} ], [ @@ -1250,6 +1250,35 @@ function stringToURLs(str) } } + // check for ./ and ../ (or even .../) to go to a file in the upper directory + if (urls[url].match(/^(\.$|\.\/\S*)/)) + { + var newLocation = getCurrentLocation(); + newLocation = newLocation.replace(/([\s\S]+)\/[^\/]*/, "$1"); + if(urls[url].match(/^\.(\/\S+)/)) + newLocation += urls[url].replace(/^\.(\/\S+)/, "$1"); + + urls[url] = newLocation; + } + else if (urls[url].match(/^(\.\.$|\.\.\/[\S]*)/)) + { + var newLocation = getCurrentLocation(); + newLocation = newLocation.replace(/([\s\S]+)\/[^\/]*/, "$1/../"); + if(urls[url].match(/^\.\.(\/\S+)/)) + newLocation += urls[url].replace(/^\.\.\/(\S+)/, "$1"); + + urls[url] = newLocation; + } + else if (urls[url].match(/^(\.\.\.$|\.\.\.\/[\S]*)/)) + { + var newLocation = getCurrentLocation(); + newLocation = newLocation.replace(/([\s\S]+):\/\/\/?(\S+?)\/\S*/, "$1://$2/"); + if(urls[url].match(/^\.\.\.(\/\S+)/)) + newLocation += urls[url].replace(/^\.\.\.\/(\S+)/, "$1"); + + urls[url] = newLocation; + } + /* if the string contains a space or does not contain any of: .:/ * open it with default searchengine */ if (urls[url].match(/\s+/) || urls[url].match(/\.|:|\//) == null) @@ -1519,6 +1548,19 @@ function bufshow(filter, in_comp_window) } } +function buffer_switch(string) +{ + var match; + if (match = string.match(/^(\d+):?/)) + return tab_go(match[1]); + for (var i = 0; i < getBrowser().browsers.length; i++) + { + var url = getBrowser().getBrowserAtIndex(i).contentDocument.location.href; + if (url == string) + return tab_go(i); + } +} + //toggles the buffer preview window function buffer_preview_toggle() { diff --git a/chrome/content/vimperator/completion.js b/chrome/content/vimperator/completion.js index 3e92c3de..7ac93a3c 100644 --- a/chrome/content/vimperator/completion.js +++ b/chrome/content/vimperator/completion.js @@ -24,6 +24,9 @@ const COMMAND_LINE_HISTORY_SIZE = 500; // 2nd element: help description var g_completions = new Array(); +// The completion substrings, used for showing the longest common match +var g_substrings = []; + var comp_tab_index = COMPLETION_UNINITIALIZED; // the index in the internal g_completions array var comp_tab_list_offset = 0; // how many items is the displayed list shifted from the internal tab index var comp_tab_startstring = ""; @@ -107,17 +110,24 @@ function completion_add_to_list(completion_item, at_beginning)/*{{{*/ /* select the next index, refill list if necessary * * changes 'comp_tab_index' */ -function completion_select_next_item()/*{{{*/ +function completion_select_next_item(has_list, has_full, has_longest)/*{{{*/ { - comp_tab_index++; + if (has_full) + comp_tab_index++; + has_list = has_list || (!completion_list.hidden && (has_full || has_longest)); if (comp_tab_index >= g_completions.length) /* wrap around */ { comp_tab_index = -1; - completion_list.selectedIndex = -1; + if (has_list && has_full) + completion_list.selectedIndex = -1; return; } - if (comp_tab_index == 0) // at the top of the list + if (has_full) + showStatusbarMessage(" match " + (comp_tab_index + 1).toString() + " of " + g_completions.length.toString() + " ", STATUSFIELD_PROGRESS); + if (!has_list) return; + + if (comp_tab_index < 1) // at the top of the list { completion_fill_list(0); comp_tab_list_offset = 0; @@ -135,50 +145,143 @@ function completion_select_next_item()/*{{{*/ comp_tab_list_offset++; } - listindex = comp_tab_index - comp_tab_list_offset; - completion_list.selectedIndex = listindex; - - showStatusbarMessage(" match " + (comp_tab_index+1).toString() + " of " + g_completions.length.toString() + " ", STATUSFIELD_PROGRESS); + if (has_full) + { + listindex = comp_tab_index - comp_tab_list_offset; + completion_list.selectedIndex = listindex; + } }/*}}}*/ /* select the previous index, refill list if necessary * * changes 'comp_tab_index' */ -function completion_select_previous_item()/*{{{*/ +function completion_select_previous_item(has_list, has_full, has_longest)/*{{{*/ { - comp_tab_index--; + if (has_full) + comp_tab_index--; + has_list = has_list || (!completion_list.hidden && (has_full || has_longest)); if (comp_tab_index == -1) { - completion_list.selectedIndex = -1; + if (has_list && has_full) + completion_list.selectedIndex = -1; return; } + if (has_full) + showStatusbarMessage("match " + (comp_tab_index+1).toString() + " of " + g_completions.length.toString(), STATUSFIELD_PROGRESS); + if (comp_tab_index < -1) // go to the end of the list { comp_tab_index = g_completions.length -1; + if (!has_list) return; completion_fill_list(g_completions.length - COMPLETION_MAXITEMS); comp_tab_list_offset = g_completions.length - COMPLETION_MAXITEMS;//COMPLETION_MAXITEMS - 1; if (comp_tab_list_offset < 0) comp_tab_list_offset = 0; } + if (!has_list) return; var listindex = comp_tab_index - comp_tab_list_offset; // only move the list, if there are still items we can move if (listindex < COMPLETION_CONTEXTLINES && comp_tab_list_offset > 0) { // for speed reason: just remove old item, and add new at the end of the list - var items = completion_list.getElementsByTagName("listitem"); - completion_list.removeChild(items[items.length-1]); - completion_add_to_list(g_completions[comp_tab_index - COMPLETION_CONTEXTLINES], true); + if (has_list) + { + var items = completion_list.getElementsByTagName("listitem"); + completion_list.removeChild(items[items.length-1]); + completion_add_to_list(g_completions[comp_tab_index - COMPLETION_CONTEXTLINES], true); + } comp_tab_list_offset--; } - listindex = comp_tab_index - comp_tab_list_offset; - completion_list.selectedIndex = listindex; - - showStatusbarMessage("item " + (comp_tab_index+1).toString() + " of " + g_completions.length.toString(), STATUSFIELD_PROGRESS); + if (has_full) + { + listindex = comp_tab_index - comp_tab_list_offset; + completion_list.selectedIndex = listindex; + } }/*}}}*/ +/* + * returns the longest common substring + * used for the 'longest' setting for wildmode + * + */ +function get_longest_substring()/*{{{*/ +{ + if (g_substrings.length == 0) + return ''; + var longest = g_substrings[0]; + for (var i = 1; i < g_substrings.length; i++) + { + if (g_substrings[i].length > longest.length) + longest = g_substrings[i]; + } + return longest; +}/*}}}*/ + +// list = [ [['com1', 'com2'], 'text'], [['com3', 'com4'], 'text'] ] +function build_longest_common_substring(list, filter)/*{{{*/ +{ + var filtered = []; + var filter_length = filter.length; + for (var i = 0; i < list.length; i++) + { + for (var j = 0; j < list[i][0].length; j++) + { + if (list[i][0][j].indexOf(filter) == -1) + continue; + if (g_substrings.length == 0) + { + var last_index = list[i][0][j].lastIndexOf(filter); + var length = list[i][0][j].length; + for (var k = list[i][0][j].indexOf(filter); k != -1 && k <= last_index; k = list[i][0][j].indexOf(filter, k + 1)) + { + for (var l = k + filter_length; l <= length; l++) + g_substrings.push(list[i][0][j].substring(k, l)); + } + } + else + { + g_substrings = g_substrings.filter(function($_) { + return list[i][0][j].indexOf($_) >= 0; + }); + } + filtered.push([list[i][0][j], list[i][1]]); + break; + } + } + return filtered; +}/*}}}*/ + +function build_longest_starting_substring(list, filter)/*{{{*/ +{ + var filtered = []; + var filter_length = filter.length; + for (var i = 0; i < list.length; i++) + { + for (var j = 0; j < list[i][0].length; j++) + { + if (list[i][0][j].indexOf(filter) != 0) + continue; + if (g_substrings.length == 0) + { + var length = list[i][0][j].length; + for (var k = filter_length; k <= length; k++) + g_substrings.push(list[i][0][j].substring(0, k)); + } + else + { + g_substrings = g_substrings.filter(function($_) { + return list[i][0][j].indexOf($_) == 0; + }); + } + filtered.push([list[i][0][j], list[i][1]]); + break; + } + } + return filtered; +}/*}}}*/ /* @@ -190,6 +293,7 @@ function completion_select_previous_item()/*{{{*/ function get_url_completions(filter)/*{{{*/ { g_completions = []; + g_substrings = []; var cpt = get_pref("complete"); // join all completion arrays together @@ -207,147 +311,63 @@ function get_url_completions(filter)/*{{{*/ }/*}}}*/ /* discard all entries in the 'urls' array, which don't match 'filter */ -function filter_url_array(urls, filter, use_regex)/*{{{*/ +function filter_url_array(urls, filter)/*{{{*/ { var filtered = []; // completions which don't match the url but just the description // list them add the end of the array var additional_completions = []; - var reg, reg2; - if (use_regex) + if (!filter) return urls.map(function($_) { + return [$_[0], $_[1]] + }); + var filter_length = filter.length; + /* + * Longest Common Subsequence + * This shouldn't use build_longest_common_substring + * for performance reasons, so as not to cycle through the urls twice + */ + for (var i = 0; i < urls.length; i++) { - reg = new RegExp("^" + filter, "i"); - reg2 = new RegExp(filter, "i"); - } - -outer: - for (var i = 0; i < urls.length; ++i) - { - if (filter != null && filter.length > 0) + if (urls[i][0].indexOf(filter) == -1) { - // first check if the filter matches in the main part of the URL - var bm = urls[i][0].replace(/^.*:\/\/(www)?/, ""); - bm = bm.replace(/\/.*$/, ""); - var tokens = bm.split("."); - for (var k = 0; k < tokens.length-1; k++) // -1 because we don't search the domain (like .com) + if (urls[i][1].indexOf(filter) != -1) + additional_completions.push([urls[i][0], urls[i][1] ]); + continue; + } + if (g_substrings.length == 0) // Build the substrings + { + var last_index = urls[i][0].lastIndexOf(filter); + var url_length = urls[i][0].length; + for (var k = urls[i][0].indexOf(filter); k != -1 && k <= last_index; k = urls[i][0].indexOf(filter, k + 1)) { - if (use_regex) - { - if(tokens[k].search(reg) != -1) - { - filtered.push([urls[i][0], urls[i][1] ]); - continue outer; - } - } - else - { - if (tokens[k].indexOf(filter) == 0) - { - filtered.push([urls[i][0], urls[i][1] ]); - continue outer; - } - } - } - - // otherwise check if the filter matches in the description of the bookmark - if (use_regex) - { - if(urls[i][0].search(reg2) == -1 && urls[i][1].search(reg2) == -1) - continue; - } - else - { - if (urls[i][0].indexOf(filter) == -1 && urls[i][1].indexOf(filter) == -1) - continue; + for (var l = k + filter_length; l <= url_length; l++) + g_substrings.push(urls[i][0].substring(k, l)); } } - additional_completions.push([urls[i][0], urls[i][1] ]); + else + { + g_substrings = g_substrings.filter(function($_) { + return urls[i][0].indexOf($_) >= 0; + }); + } + filtered.push([urls[i][0], urls[i][1]]); } + return filtered.concat(additional_completions); }/*}}}*/ -function get_file_completions(filter)/*{{{*/ -{ - g_completions = []; - var match = filter.match(/^(.*[\/\\])(.*?)$/); - var dir; - - if (!match || !(dir = match[1])) - return []; - - var compl = match[2] || ''; - var fd = fopen(dir, "<"); - if (!fd) - return []; - - var entries = fd.read(); - var delim = fd.path.length == 1 ? '' : (fd.path.search(/\\/) != -1) ? "\\" : "/"; - var reg = new RegExp("^" + fd.path + delim + compl); - entries.forEach(function(file) - { - if (file.path.search(reg) != -1) - g_completions.push([file.path, '']); - }); - - return g_completions; -}/*}}}*/ - function get_search_completions(filter)/*{{{*/ { - var search_completions = []; - - var reg = new RegExp("^" + filter, "i"); - for (var i = 0; i < g_searchengines.length; i++) - { - if (g_searchengines[i][0].search(reg) != -1) - search_completions.push([g_searchengines[i][0] + " ", g_searchengines[i][1] ]); - } - - return search_completions; + if (!filter) return g_searchengines.map(function($_) { + return [$_[0], $_[1]]; + }); + var mapped = g_searchengines.map(function($_) { + return [[$_[0]], $_[1]]; + }); + return build_longest_common_substring(mapped, filter); }/*}}}*/ -function get_bookmark_completions(filter)/*{{{*/ -{ - if (!bookmarks_loaded) - { - // update our bookmark cache - var RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService( Components.interfaces.nsIRDFService ); - var root = RDF.GetResource( "NC:BookmarksRoot" ); - bookmarks = []; // here getAllChildren will store the bookmarks - g_bookmarks = []; // also clear our bookmark cache - BookmarksUtils.getAllChildren(root, bookmarks); - for(var i = 0; i < bookmarks.length; i++) - { - if (bookmarks[i][0] && bookmarks[i][1]) - g_bookmarks.push([bookmarks[i][1].Value, bookmarks[i][0].Value ]); - } - bookmarks_loaded = true; - } - - return filter_url_array(g_bookmarks, filter, true); -}/*}}}*/ - -function get_help_completions(filter) -{ - var help_completions = []; - help_completions = help_completions.concat(get_command_completions(filter)); - help_completions = help_completions.concat(get_settings_completions(filter)); - for(var i = 0; i < g_mappings.length; i++) - { - for(var j = 0; j < g_mappings[i][0].length; j++) - { - //var re = new RegExp("^" + filter); - if (g_mappings[i][0][j].indexOf(filter) == 0) - { - help_completions.push([g_mappings[i][0][j], g_mappings[i][1]]); - break; // only add a command once - } - } - } - return help_completions; -} - function get_history_completions(filter)/*{{{*/ { var history = document.getElementById("hiddenHistoryTree"); @@ -398,39 +418,109 @@ function get_history_completions(filter)/*{{{*/ history_loaded = true; } - return filter_url_array(g_history, filter, true); + return filter_url_array(g_history, filter); +}/*}}}*/ + +function get_bookmark_completions(filter)/*{{{*/ +{ + if (!bookmarks_loaded) + { + // update our bookmark cache + var RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService( Components.interfaces.nsIRDFService ); + var root = RDF.GetResource( "NC:BookmarksRoot" ); + bookmarks = []; // here getAllChildren will store the bookmarks + g_bookmarks = []; // also clear our bookmark cache + BookmarksUtils.getAllChildren(root, bookmarks); + for(var i = 0; i < bookmarks.length; i++) + { + if (bookmarks[i][0] && bookmarks[i][1]) + g_bookmarks.push([bookmarks[i][1].Value, bookmarks[i][0].Value ]); + } + bookmarks_loaded = true; + } + + return filter_url_array(g_bookmarks, filter); +}/*}}}*/ + +function get_file_completions(filter)/*{{{*/ +{ + g_completions = []; + g_substrings = []; + var match = filter.match(/^(.*[\/\\])(.*?)$/); + var dir; + + if (!match || !(dir = match[1])) + return []; + + var compl = match[2] || ''; + var fd = fopen(dir, "<"); + if (!fd) + return []; + + var entries = fd.read(); + var delim = fd.path.length == 1 ? '' : (fd.path.search(/\\/) != -1) ? "\\" : "/"; + var new_filter = fd.path + delim + compl; + if (!filter) return entries.map(function($_) { + var path = $_.path; + if ($_.isDirectory()) path += '/'; + return [path, '']; + }); + var mapped = entries.map(function($_) { + var path = $_.path; + if ($_.isDirectory()) path += '/'; + return [[path], '']; + }); + + return g_completions = build_longest_starting_substring(mapped, filter); +}/*}}}*/ + +function get_help_completions(filter)/*{{{*/ +{ + var help_array = []; + g_substrings = []; + help_array = help_array.concat(g_commands); + help_array = help_array.concat(get_settings_completions(filter, true)); + help_array = help_array.concat(g_mappings); + if (!filter) return help_array.map(function($_) { + return [$_[0][0], $_[1]]; + }); + return build_longest_common_substring(help_array, filter); }/*}}}*/ function get_command_completions(filter)/*{{{*/ { g_completions = []; - for(var i=0; i it DOES hurt :( - //g_completions.sort(); - return g_completions; + g_substrings = []; + if (!filter) return g_completions = g_commands.map(function($_) { + return [$_[0][0], $_[1]]; + }); + return g_completions = build_longest_starting_substring(g_commands, filter); }/*}}}*/ -function get_settings_completions(filter)/*{{{*/ +function get_settings_completions(filter, unfiltered)/*{{{*/ { - settings_completions = []; + g_substrings = []; + var settings_completions = []; var no_mode = false; - if (filter.indexOf("no") == 0) // boolean option { no_mode = true; filter = filter.substr(2); } + if (unfiltered) return g_settings.filter(function($_) { + if (no_mode && $_[5] != "boolean") return false; + else return true; + }).map(function($_) { + return [$_[0], $_[1]]; + }); + if (!filter) return g_settings.filter(function($_) { + if (no_mode && $_[5] != "boolean") return false; + else return true; + }).map(function($_) { + return [$_[0][0], $_[1]]; + }); + + // check if filter ends with =, then complete current value else if(filter.length > 0 && filter.lastIndexOf("=") == filter.length -1) { @@ -449,32 +539,45 @@ function get_settings_completions(filter)/*{{{*/ return settings_completions; } - for(var i=0; i -1) + if (no_mode && g_settings[i][5] != "boolean") + continue; + + var prefix = no_mode ? 'no' : ''; + for (var j = 0; j < g_settings[i][0].length; j++) { - // for 'no'-mode only return bool options - if (no_mode) + if (g_settings[i][0][j].indexOf(filter) != 0) continue; + if (g_substrings.length == 0) { - if (g_settings[i][5] == "bool") - settings_completions.push(["no" + g_settings[i][0][0], g_settings[i][1]]); + var length = g_settings[i][0][j].length; + for (var k = filter_length; k <= length; k++) + g_substrings.push(prefix + g_settings[i][0][j].substring(0, k)); } else - settings_completions.push([g_settings[i][0][0], g_settings[i][1]]); + { + g_substrings = g_substrings.filter(function($_) { + return g_settings[i][0][j].indexOf($_) == 0; + }); + } + settings_completions.push([prefix + g_settings[i][0][j], g_settings[i][1]]); + break; } } return settings_completions; }/*}}}*/ -function get_buffer_completions(filter) +function get_buffer_completions(filter)/*{{{*/ { - var reg = new RegExp(filter,"i"); - items = new Array(); + g_substrings = []; + var items = []; var num = getBrowser().browsers.length; var title, url; - for(var i=0; i" + "History access can be quite slow for a large history. Vimperator maintains a cache to speed it up significantly on subsequent access.
"+ @@ -182,7 +182,7 @@ var g_settings = [/*{{{*/ null ], [ - ["showmode", "smd"], + ["showmode", "smd", "noshowmode", "nosmd"], ["showmode", "smd"], "Show the current mode in the command line", function(value) { set_pref("showmode", value); }, @@ -209,7 +209,7 @@ var g_settings = [/*{{{*/ null ], [ - ["usermode", "um"], + ["usermode", "um", "nousermode", "noum"], ["usermode", "um"], "Show current website with a minimal stylesheet to make it easily accessible
" + "Note that this is a local setting for now, later it may be split into a global and :setlocal part", @@ -223,12 +223,37 @@ var g_settings = [/*{{{*/ [ ["wildmode", "wim"], ["wildmode", "wim"], - "Define how command line completion works
" + - "Not implemented yet.", + "Defines how command line completion works
" + + "It is a comma-separated list of parts, where each part specifies" + + "what to do for each consecutive use of the completion key. The first part" + + "specifies the behavior for the first use of the completion key, the second part" + + "for the second use, etc.
" + + "These are the possible values for each part:
" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
''Complete only the first match
'full'Complete the next full match. After the last, the original string is used.
'longest'Complete till the longest common string.
'list'When more than one match, list all matches.
'list:full'When more than one match, list all matches and complete first match.
'list:longest'When more than one match, list all matches and complete till the longest common string.
" + + "When there is only a single match, it is fully completed regardless of the case.", function(value) { set_pref("wildmode", value); }, function() { return get_pref("wildmode"); }, "stringlist", - "full", + "list:full", + null, + null + ], + [ + ["wildsort", "wis", "nowildsort", "nowis"], + ["wildsort", "wis"], + "Defines whether command line completion is sorted
" + + "If you don't want a sorted completion list, set this to false.", + function(value) { set_pref("wildsort", value); }, + function() { return get_pref("wildsort"); }, + "boolean", + false, null, null ] diff --git a/chrome/content/vimperator/tags b/chrome/content/vimperator/tags index 4bf7b897..cbdf3361 100644 --- a/chrome/content/vimperator/tags +++ b/chrome/content/vimperator/tags @@ -5,6 +5,7 @@ !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ !_TAG_PROGRAM_VERSION 5.6 // addBookmark bookmarks.js /^function addBookmark(title, uri)$/;" f +addEventListeners vimperator.js /^function addEventListeners()$/;" f addMode commands.js /^function addMode(mode)$/;" f add_to_command_history completion.js /^function add_to_command_history(str)$/;" f beep commands.js /^function beep()$/;" f @@ -24,7 +25,7 @@ copyToClipboard commands.js /^function copyToClipboard(str)$/;" f createCursorPositionString vimperator.js /^function createCursorPositionString()$/;" f createHints hints.js /^ function createHints(win)$/;" f createProgressBar vimperator.js /^function createProgressBar(aProgress)$/;" f -cumulativeOffset commands.js /^function cumulativeOffset(element)$/;" f +cumulativeOffset help.js /^ function cumulativeOffset(element)$/;" f deleteBookmark bookmarks.js /^function deleteBookmark(url)$/;" f del_url_mark commands.js /^function del_url_mark(mark)$/;" f echo commands.js /^function echo(msg)$/;" f @@ -62,7 +63,7 @@ get_settings_completions completion.js /^function get_settings_completions(filte get_url_completions completion.js /^function get_url_completions(filter)\/*{{{*\/$/;" f get_url_mark commands.js /^function get_url_mark(mark)$/;" f hasMode commands.js /^function hasMode(mode)$/;" f -help commands.js /^function help(section, easter)$/;" f +help help.js /^function help(section, easter)$/;" f historyGoToBeginning commands.js /^function historyGoToBeginning()$/;" f historyGoToEnd commands.js /^function historyGoToEnd()$/;" f hit_a_hint hints.js /^function hit_a_hint()$/;" f @@ -75,8 +76,8 @@ keyToString vimperator.js /^function keyToString(event)$/;" f load_history completion.js /^function load_history()$/;" f LocalFile file.js /^function LocalFile(file, mode, perms, tmp)$/;" f logMessage vimperator.js /^function logMessage(msg)$/;" f -makeHelpString commands.js /^ function makeHelpString(commands, color, beg, end, func)$/;" f -makeSettingsHelpString commands.js /^ function makeSettingsHelpString(command)$/;" f +makeHelpString help.js /^ function makeHelpString(commands, color, beg, end, func)$/;" f +makeSettingsHelpString help.js /^ function makeSettingsHelpString(command)$/;" f multiliner commands.js /^function multiliner(line, prev_match, heredoc)$/;" f nsBrowserStatusHandler vimperator.js /^function nsBrowserStatusHandler() \/*{{{*\/$/;" f onCommandBarInput vimperator.js /^function onCommandBarInput(event)$/;" f diff --git a/chrome/content/vimperator/vimperator.js b/chrome/content/vimperator/vimperator.js index d1dfd17c..d231d3d9 100644 --- a/chrome/content/vimperator/vimperator.js +++ b/chrome/content/vimperator/vimperator.js @@ -45,6 +45,9 @@ var g_inputbuffer = ""; // here we store partial commands (e.g. 'g' if you want var g_count = -1; // the parsed integer of g_inputbuffer, or -1 if no count was given var g_bufshow = false; // keeps track if the preview window shows current buffers ('B') +// handles wildmode tab index +var wild_tab_index = 0; + // handles multi-line commands var prev_match = new Array(5); var heredoc = ''; @@ -548,8 +551,10 @@ function onCommandBarKeypress(evt)/*{{{*/ /* user pressed TAB to get completions of a command */ else if (evt.keyCode == KeyEvent.DOM_VK_TAB) { + var start_cmd = command; var match = tokenize_ex(command); var [count, cmd, special, args] = match; + var command = get_command(cmd); //always reset our completion history so up/down keys will start with new values comp_history_index = -1; @@ -559,7 +564,8 @@ function onCommandBarKeypress(evt)/*{{{*/ g_completions = []; comp_tab_index = -1; comp_tab_list_offset = 0; - comp_tab_startstring = command; + comp_tab_startstring = start_cmd; + wild_tab_index = 0; /* if there is no space between the command name and the cursor * then get completions of the command name @@ -570,10 +576,18 @@ function onCommandBarKeypress(evt)/*{{{*/ } else // dynamically get completions as specified in the g_commands array { - var command = get_command(cmd); if (command && command[4]) { g_completions = command[4].call(this, args); + // Sort the completion list + if (get_pref('wildsort')) + g_completions.sort(function(a, b) { + if (a[0] < b[0]) + return -1; + else if (a[0] > b[0]) + return 1; + else return 0; + }); } } } @@ -586,24 +600,45 @@ function onCommandBarKeypress(evt)/*{{{*/ if (g_completions.length == 0) beep(); + var wim = get_pref('wildmode').split(/,/); + var has_list = false; + var longest = false; + var full = false; + var wildtype = wim[wild_tab_index++] || wim[wim.length - 1]; + if (wildtype == 'list' || wildtype == 'list:full' || wildtype == 'list:longest') + has_list = true; + if (wildtype == 'longest' || wildtype == 'list:longest') + longest = true; + if (wildtype == 'full' || wildtype == 'list:full') + full = true; // show the list - completion_show_list(); + if (has_list) + completion_show_list(); if (evt.shiftKey) - completion_select_previous_item(); + completion_select_previous_item(has_list, full, longest); else - completion_select_next_item(); + completion_select_next_item(has_list, full, longest); //command_line.focus(); // workaraound only need for RICHlistbox - if (comp_tab_index == -1) // wrapped around matches, reset command line + if (comp_tab_index == -1 && !longest) // wrapped around matches, reset command line { - command_line.value = comp_tab_startstring; - completion_list.selectedIndex = -1; + if (full && g_completions.length > 1) + { + command_line.value = comp_tab_startstring; + completion_list.selectedIndex = -1; + } } else { - var compl = g_completions[comp_tab_index][0]; + if (longest && g_completions.length > 1) + var compl = get_longest_substring(); + if (full) + var compl = g_completions[comp_tab_index][0]; + if (g_completions.length == 1) + var compl = g_completions[0][0]; + if (compl) { /* if there is no space between the command name and the cursor @@ -617,6 +652,9 @@ function onCommandBarKeypress(evt)/*{{{*/ { command_line.value = ":" + (count ? count.toString() : "") + cmd + (special ? "!" : "") + " " + compl; + // Start a new completion in the next iteration. Useful for commands like :source + if (g_completions.length == 1 && !full) // RFC: perhaps the command can indicate whether the completion should be restarted + comp_tab_index = COMPLETION_UNINITIALIZED; } } } diff --git a/install.rdf b/install.rdf index e36fb970..de975f0f 100644 --- a/install.rdf +++ b/install.rdf @@ -5,7 +5,7 @@ vimperator@mozdev.org Vimperator - 0.3 + 0.4 Make Firefox work like Vim Martin Stubenschrott http://vimperator.mozdev.org