/** * Use at your OWN RISK. */ let INFO = <> teramako MPL 1.1/GPL 2.0/LGPL 2.1

For adding Panorama supports. This plugin makes the default feature to not switch to the other group suddenly. And add some mappings and commands for Parnorama.

Use at your OWN RISK. This pluin overwrite many mappings, some commands, and some completions.

New Mappings and Commands

g@ countg@

Switch to AppTab.

If the current tab is already AppTab, switch to the next AppTab.

]]> count<C-S-n>

Switch to next group.

Caution: cannot switch to empty group.

]]> count<C-S-p>

Switch to previous group.

:mkgroup :mkg :mkgroup! GroupName

Create new tab group named GroupName. And then, switch to the group.

If specified !, move the current tab to the group.

:stash :stashtogroup :stashtogroup! GroupName

Stash the current tab to GroupName.

Caution: connnot stash AppTab (pinned tab)

:switchgroup :swg :switchgroup GroupName :swg GroupName :countswitchgroup :countswg

Switch group to GroupName

; /** * @method selectVisible {{{ * 現在表示されているタブでの絶対/相対位置によるタブ選択を行う * (tabs.select() だと全タブが対象となる) * @param {String} spec * @param {Boolean} wrap */ function selectVisible (spec, wrap) { if (spec === void(0) || spec === "") return; let tabs = gBrowser.visibleTabs; let index; if (typeof spec === "number" || /^\d+$/.test(spec)) { index = parseInt(spec, 10); } else if (spec === "$") { index = tabs.length - 1; } else if (/^[+-]\d+$/.test(spec)) { index = tabs.indexOf(gBrowser.mCurrentTab) + parseInt(spec, 10); } else { return; } let length = tabs.length; if (index > length - 1) index = wrap ? index % length : length - 1; else if (index < 0) index = wrap ? index % length + length : 0; gBrowser.mTabContainer.selectedItem = tabs[index]; } // }}} /** * @method switchTo {{{ * tabs.switchTo 相当の関数 * @param {String} buffer * @param {Boolean} allowNonUnique * @param {Number} count * @param {Boolean} reverse */ function switchTo (buffer, allowNonUnique, count, reverse) { if (buffer == "") return null; if (buffer != null) { tabs._lastBufferSwitchArgs = buffer; tabs._lastBufferSwitchSpecial = allowNonUnique; } else { buffer = this._lastBufferSwitchArgs; if (allowNonUnique === void(0) || allowNonUnique === null) allowNonUnique = tabs._lastBufferSwitchSpecial; } if (buffer == "#") { tabs.selectAlternateTab(); return; } let tab = searchTab(buffer); if (tab) { tabs.select(tab._tPos, false); return; } if (!count || count < 1) count = 1; reverse = !!reverse; m = []; let lowerBuffer = buffer.toLowerCase(); let first = tabs.index() + (reverse ? 0 : 1); let length = config.tabbrowser.browsers.length; for (let [i, ] in tabs.browsers) { let index = (i + first) % length; let browser = config.tabbrowser.browsers[index]; let url, title; if ("__SS_restoreState" in browser) { let entry = browser.__SS_data.entries[0]; url = entry.url; title = entry.title; } else { url = browser.contentDocument.location.href; title = browser.contentDocument.title; } title = title.toLowerCase(); if (url == buffer) { tabs.select(index, false); return; } if (url.indexOf(buffer) >= 0 || title.indexOf(lowerBuffer) >= 0) m.push(index); } if (m.length == 0) liberator.echoerr("E94: No matching buffer for " + buffer); else if (m.length > 1 && !allowNonUnique) liberator.echoerr("E93: More than one match for " + buffer); else { if (reverse) { index = m.length - count; while (index < 0) index + m.length; } else { index = (count - 1) % m.length; } tabs.select(m[index], false); } } // }}} /** * @method searchTab {{{ * @param {String} buffer * - "{Number}:" * - "{GroupName} {Number}:" * @return {Element|null} */ function searchTab (buffer) { if (buffer == "#") { if (tabs.alternate != null && tabs.getTab() != tabs.alternate) return tabs.alternate; return null; } let m = buffer.match(/^(\d+):?/); if (m) return tabs.getTab(parseInt(m[1], 10) -1); m = buffer.match(/^(.+?)\s+(\d+):?/); if (m) { let [, groupName, tabNum] = m; tabNum = parseInt(tabNum, 10); let group = getGroupByName(groupName)[0]; if (!group) return null; let tabItem = group.getChild(tabNum -1); if (!tabItem) return null; return tabItem.tab; } return null; } // }}} let TV = window.TabView; /** * @type {Window} TabView._window {{{ */ this.__defineGetter__("tabView", function() { if (TV && TV._window && TV._window.GroupItems) { delete this.tabView; this.tabView = TV._window; return TV._window; } else { let wating = true; TV._initFrame(function(){ wating = false; }) while (wating) liberator.threadYield(false, true); return this.tabView; } }); // }}} /** * @type {Array} Array of AppTabs */ this.__defineGetter__("appTabs", function() gBrowser.visibleTabs.filter(function(t) t.pinned)); /** * @method createGroup {{{ * @param {String} name GroupName * @return {GroupItem} */ function createGroup (name) { let pageBounds = tabView.Items.getPageBounds(); pageBounds.inset(20, 20); let box = new tabView.Rect(pageBounds); box.width = 50; box.height = 50; let group = new tabView.GroupItem([], { bounds: box, title: name }); if (name && group.$title.hasClass("defaultName")) group.$title.removeClass("defaultName"); return group; } // }}} /** * @param {String|Number} name GroupName or GroupId * @return {GroupItem[]} */ function getGroupByName (name) { if (typeof name === "number") return tabView.GroupItems.groupItems.filter(function(g) g.id == name); return tabView.GroupItems.groupItems.filter(function(g) g.getTitle() == name); } /** * @param {Element} tab * @param {GroupItem|Number} group GroupItem object or group id */ function tabMoveToGroup (tab, group) { let id = (typeof group == "object") ? group.id : group; tabView.GroupItems.moveTabToGroupItem(tab, id); } /** * @method switchToGroup {{{ * @param {String|Number} spec * @param {Boolean} wrap */ function switchToGroup (spec, wrap) { const GI = tabView.GroupItems let current = GI.getActiveGroupItem() || GI.getActiveOrphanTab(); let groupsAndOrphans = GI.groupItems.concat(GI.getOrphanedTabs()); let offset = 1, relative = false, index; if (typeof spec === "number") index = parseInt(spec, 10); else if (/^[+-]\d+$/.test(spec)) { let buf = parseInt(spec, 10); index = groupsAndOrphans.indexOf(current) + buf; offset = buf >= 0 ? 1 : -1; relative = true; } else if (spec != "") { if (/^\d+$/.test(spec)) spec = parseInt(spec, 10); let targetGroup = getGroupByName(spec)[0]; if (targetGroup) index = groupsAndOrphans.indexOf(targetGroup); else { liberator.echoerr("No such group: " + spec); return; } } else { return; } let length = groupsAndOrphans.length; let apps = appTabs; function groupSwitch (index, wrap) { if (index > length - 1) index = wrap ? index % length : length - 1; else if (index < 0) index = wrap ? index % length + length : 0; let target = groupsAndOrphans[index], group = null; if (target instanceof tabView.GroupItem) { group = target; target = target.getActiveTab() || target.getChild(0); } if (target) { gBrowser.mTabContainer.selectedItem = target.tab; } else if (group && apps.length != 0) { GI.setActiveGroupItem(group); tabView.UI.goToTab(tabs.getTab(0)); } else if (relative) { groupSwitch(index + offset, true); } else { liberator.echoerr("Cannot switch to " + spec); return; } } groupSwitch(index, wrap); } // }}} // ============================================================================ // Mappings {{{ // ============================================================================ /** * {count}g@ select {count} of AppTab * g@ select AppTab, * if already selected, select the next AppTab */ mappings.add([modes.NORMAL], ["g@"], "Go to AppTab", function (count) { let apps = appTabs; let i = 0; if (count != null) i = count; else { let currentTab = tabs.getTab(); if (currentTab.pinned) i = apps.indexOf(currentTab) + 1; i %= apps.length; } if (apps[i]) selectVisible(i); }, { count: true }); /** * Switch to next group */ mappings.add([modes.NORMAL], [""], "switch to next group", function (count) { switchToGroup("+" + (count || 1), true); }, { count: true }); /** * Switch to previous group */ mappings.add([modes.NORMAL], [""], "switch to previous group", function (count) { switchToGroup("-" + (count || 1), true); }, { count: true }); // overwrite 'g0", 'g^' mappings.getDefault(modes.NORMAL, "g0").action = function (count) { selectVisible(0); }; // overwrite 'g$' mappings.getDefault(modes.NORMAL, "g$").action = function (count) { selectVisible("$"); }; // overwrite 'gt' mappings.getDefault(modes.NORMAL, "gt").action = function (count) { if (count != null) selectVisible(count - 1, false); else selectVisible("+1", true); }; // overwrite 'C-n', 'C-Tab', 'C-PageDown' mappings.getDefault(modes.NORMAL, "").action = function (count) { selectVisible("+" + (count || 1), true); }; // overwrite 'gT' mappings.getDefault(modes.NORMAL, "gT").action = function (count) { selectVisible("-" + (count || 1), true); } // overwrite 'b' mappings.getDefault(modes.NORMAL, "b").action = function (count) { if (count != null) selectVisible(count - 1); else commandline.open("", "buffer! ", modes.EX); } // }}} // ============================================================================ // Command {{{ // ============================================================================ /** * overwrite :ls :buffers */ let (cmd = commands.get("buffers")) { cmd.action = function (args) { completion.listCompleter("buffer", args.literalArg, null, args.bang); }; cmd.bang = true; } /** * overwrite :buffer */ let (cmd = commands.get("buffer")) { cmd.action = function (args) { let arg = args.literalArg; if (arg && args.count > 0) switchTo(arg, args.bang); else if (args.count > 0) switchTo(args.count.toString(), args.bang); else switchTo(arg, args.bang); }; cmd.completer = function (context) completion.buffer(context, true); } /** * make a group and switch to the group * if add ! (bang), take up the current tab to the group */ commands.addUserCommand(["mkg[roup]"], "create Group", function (args) { let groupName = args.literalArg; let group = createGroup(groupName); let currentTab = tabs.getTab(); if (args.bang) { if (!currentTab.pinned) TV.moveTabTo(currentTab, group.id); } let apps = appTabs, child = group.getChild(0); if (child) { tabView.GroupItems.setActiveGroupItem(group); tabView.UI.goToTab(child.tab); } else if (apps.length == 0) { group.newTab(); } else { tabView.GroupItems.setActiveGroupItem(group); tabView.UI.goToTab(currentTab.pinned ? currentTab : apps[apps.length - 1]); } }, { argCount: "1", bang: true, literal: 0, }, true); commands.addUserCommand(["switchgruop", "swg"], "Switch Group", function (args) { if (args.count > 0) { switchToGroup("+" + args.count, true); } else { switchToGroup(args.literalArg); } }, { argCount: "?", count: true, literal: 0, completer: function (context) completion.tabgroup(context, true), }, true); commands.addUserCommand(["stash[togroup]"], "Stash the current tab to other group", function (args) { let currentTab = tabs.getTab(); if (currentTab.pinned) { liberator.echoerr("Connot stash an AppTab"); return; } let groupName = args.literalArg; let group = getGroupByName(groupName)[0]; if (!group) { if (args.bang) { group = createGroup(groupName); } else { liberator.echoerr("No such group: " + groupName.quote() + ". if want create, add \"!\""); return; } } TV.moveTabTo(currentTab, group.id); } ,{ argCount: "1", bang: true, literal: 0, completer: function (context) completion.tabgroup(context, true), }, true); // }}} // ============================================================================ // Completion {{{ // ============================================================================ completion.tabgroup = function TabGroupCompleter (context, excludeActiveGroup) { const GI = tabView.GroupItems; let groupItems = GI.groupItems; if (excludeActiveGroup) { let activeGroup = GI.getActiveGroupItem(); groupItems = groupItems.filter(function(group) group.id != activeGroup.id); } context.title = ["TabGroup"]; context.completions = groupItems.map(function(group) { let title = group.getTitle(); let desc = [ "Title:", title || "(Untitled)", "TabNum:", group.getChildren().length, ].join(" "); if (!title) title = group.id; return [title, desc]; }); }; /** * overwite completion.buffer */ (function(TV, gBrowser) { const UNTITLE_LABEL = "(Untitled)"; function getIndicator (tab) { if (tab == gBrowser.mCurrentTab) return "%"; else if (tab == tabs.alternate) return "#"; return " "; } function getURLFromTab (tab) { if ("__SS_restoreState" in tab.linkedBrowser && "__SS_data" in tab.linkedBrowser) return tab.linkedBrowser.__SS_data.entries[0].url; return tab.linkedBrowser.contentDocument.location.href; } function generateVisibleTabs () { for (let [i, tab] in Iterator(gBrowser.visibleTabs)) { let indicator = getIndicator(tab), label = tab.label || UNTITLE_LABEL, url = getURLFromTab(tab), index = (tab._tPos + 1) + ": "; let item = { text: [ index + label, index + url], url: template.highlightURL(url), indicator: indicator + (tab.pinned ? "@" : ""), icon: tab.image || DEFAULT_FAVICON }; if (!tab.pinned && tab.tabItem && tab.tabItem.parent) { let groupName = tab.tabItem.parent.getTitle(); if (groupName) { let prefix = groupName + " " + (i + 1) + ": "; item.text.push(prefix + label); item.text.push(prefix + url); } } yield item; } } function generateGroupList (group, groupName) { for (let [i, tabItem] in Iterator(group.getChildren())) { let indicator = getIndicator(tabItem.tab); let index = (tabItem.tab._tPos + 1) + ": ", label = tabItem.tab.label || UNTITLE_LABEL, url = getURLFromTab(tabItem.tab); let item = { text: [index + label, index + url], url: template.highlightURL(url), indicator: indicator, icon: tabItem.tab.image || DEFAULT_FAVICON }; if (groupName) { index = groupName + " " + (i + 1) + ": "; item.text.push(index + label); item.text.push(index + url); } yield item; } } function generateOrphanedList (tabItems) { for (let [i, tabItem] in Iterator(tabItems)) { let indicator = getIndicator(tabItem.tab), index = (tabItem.tab._tPos + 1) + ": "; label = tabItem.tab.label || UNTITLE_LABEL, url = getURLFromTab(tabItem.tab); yield { text: [index + label, index + url], url: template.highlightURL(url), indicator: indicator, icon: tabItem.tab.image || DEFAULT_FAVICON }; } } completion.buffer = function bufferCompletion (context, all) { context.anchored = false; context.keys = { text: "text", description: "url", icon: "icon" }; context.compare = CompletionContext.Sort.number; let process = context.process[0]; context.process = [ function (item, text) <> {item.item.indicator} { process.call(this, item, text) } ]; context.title = ["Buffers"]; context.completions = [item for (item in generateVisibleTabs())]; if (!all) return; let self = this; TV._initFrame(function() { let groups = TV._window.GroupItems; let activeGroup = groups.getActiveGroupItem(); let activeGroupId = activeGroup === null ? null : activeGroup.id; for (let [i, group] in Iterator(groups.groupItems)) { if (group.id != activeGroupId) { let groupName = group.getTitle(); context.fork("GROUP_" + group.id, 0, self, function (context) { context.title = [groupName || UNTITLE_LABEL]; context.completions = [item for (item in generateGroupList(group, groupName))]; }); } } let orphanedTabs = [tabItem for ([, tabItem] in Iterator(groups.getOrphanedTabs())) if (tabItem.tab.hidden)]; if (orphanedTabs.length == 0) return; context.fork("__ORPHANED__", 0, self, function (context) { context.title = ["Orphaned"]; context.completions = [item for (item in generateOrphanedList(orphanedTabs))]; }); }); }; })(window.TabView, window.gBrowser); // }}} // vim: sw=2 ts=2 et fdm=marker: