From 1e75afbe396127808ea72d2626f564fcd58e4a32 Mon Sep 17 00:00:00 2001 From: Simon Lydell Date: Sun, 25 Jan 2015 18:23:35 +0100 Subject: [PATCH] Fix #60: Give the same hint to links with the same URL This results in fewer hints, which results in shorter hints overall. 'vf' is excluded so that everything still is focusable. `` links, which are commonly used as buttons, are excluded since clicking them likely does different things via JavaScript. `` links are also excluded, mainly because `` is a commonly used as buttons as well. --- extension/lib/commands.coffee | 22 +++++++++++++++++++--- extension/lib/hints.coffee | 20 +++++++++++++++++--- extension/lib/marker.coffee | 1 + extension/lib/modes.coffee | 10 +++++----- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/extension/lib/commands.coffee b/extension/lib/commands.coffee index 35fd7db..c111434 100644 --- a/extension/lib/commands.coffee +++ b/extension/lib/commands.coffee @@ -249,8 +249,22 @@ command_close_tab = (vim, event, count) -> command_restore_tab = (vim, event, count) -> vim.rootWindow.undoCloseTab() for [1..count] +# Combine links with the same href. +combine = (hrefs, marker) -> + if marker.type == 'link' + { href } = marker.element + if href of hrefs + parent = hrefs[href] + marker.parent = parent + parent.weight += marker.weight + parent.numChildren++ + else + hrefs[href] = marker + return marker + # Follow links, focus text inputs and click buttons with hint markers. command_follow = (vim, event, count) -> + hrefs = {} filter = (element, getElementShape) -> document = element.ownerDocument isXUL = (document instanceof XULDocument) @@ -300,7 +314,7 @@ command_follow = (vim, event, count) -> type = 'clickable' return unless type return unless shape = getElementShape(element) - return new Marker(element, shape, {semantic, type}) + return combine(hrefs, new Marker(element, shape, {semantic, type})) callback = (marker) -> { element } = marker @@ -328,10 +342,11 @@ command_follow_multiple = (vim, event) -> # Follow links in a new background tab with hint markers. command_follow_in_tab = (vim, event, count, inBackground = true) -> + hrefs = {} filter = (element, getElementShape) -> return unless isProperLink(element) return unless shape = getElementShape(element) - return new Marker(element, shape, {semantic: true}) + return combine(hrefs, new Marker(element, shape, {semantic: true})) callback = (marker) -> last = (count == 1) @@ -350,6 +365,7 @@ command_follow_in_focused_tab = (vim, event, count) -> # Copy the URL or text of a markable element to the system clipboard. command_marker_yank = (vim) -> + hrefs = {} filter = (element, getElementShape) -> type = switch when isProperLink(element) then 'link' @@ -357,7 +373,7 @@ command_marker_yank = (vim) -> when isContentEditable(element) then 'contenteditable' return unless type return unless shape = getElementShape(element) - return new Marker(element, shape, {semantic: true, type}) + return combine(hrefs, new Marker(element, shape, {semantic: true, type})) callback = (marker) -> { element } = marker diff --git a/extension/lib/hints.coffee b/extension/lib/hints.coffee index 92e8a77..3932675 100644 --- a/extension/lib/hints.coffee +++ b/extension/lib/hints.coffee @@ -45,9 +45,9 @@ injectHints = (rootWindow, window, filter) -> height } - groups = {semantic: [], unsemantic: []} + groups = {semantic: [], unsemantic: [], combined: []} createMarkers(window, viewport, groups, filter) - { semantic, unsemantic } = groups + { semantic, unsemantic, combined } = groups markers = semantic.concat(unsemantic) return [[], null] if markers.length == 0 @@ -60,6 +60,8 @@ injectHints = (rootWindow, window, filter) -> markers.sort((a, b) -> a.weight - b.weight) for marker in markers when marker not instanceof huffman.BranchPoint marker.markerElement.style.zIndex = zIndex++ + # Add `z-index` space for all the children of the marker (usually 0). + zIndex += marker.numChildren # The `markers` passed to this function have been sorted by `setZIndexes` in # advance, so we can skip sorting in the `huffman.createTree` function. @@ -83,6 +85,16 @@ injectHints = (rootWindow, window, filter) -> tree = createHuffmanTree(semantic) tree.assignCodeWords(hintChars, (marker, hint) -> marker.setHint(hint)) + # Markers for links with the same href can be combined to use the same hint. + # They should all have the same `z-index` (because they all have the same + # combined weight), but in case any of them cover another they still get a + # unique `z-index` (space for this was added in `setZIndexes`). + for marker in combined + { parent } = marker + marker.markerElement.style.zIndex = parent.markerElement.style.zIndex++ + marker.setHint(parent.hint) + markers.push(combined...) + container = rootWindow.document.createElement('box') container.classList.add('VimFxMarkersContainer') rootWindow.gBrowser.mCurrentBrowser.parentNode.appendChild(container) @@ -108,7 +120,9 @@ createMarkers = (window, viewport, groups, filter, parents = []) -> localGetElementShape = getElementShape.bind(null, window, viewport, parents) for element in utils.getAllElements(document) continue unless marker = filter(element, localGetElementShape) - if marker.semantic + if marker.parent + groups.combined.push(marker) + else if marker.semantic groups.semantic.push(marker) else groups.unsemantic.push(marker) diff --git a/extension/lib/marker.coffee b/extension/lib/marker.coffee index 63616e5..98b2534 100644 --- a/extension/lib/marker.coffee +++ b/extension/lib/marker.coffee @@ -26,6 +26,7 @@ class Marker @markerElement = document.createElement('div') @markerElement.classList.add('VimFxHintMarker') @weight = @elementShape.area + @numChildren = 0 reset: -> @setHint(@hint) diff --git a/extension/lib/modes.coffee b/extension/lib/modes.coffee index f67c4f4..3a43b2b 100644 --- a/extension/lib/modes.coffee +++ b/extension/lib/modes.coffee @@ -181,18 +181,18 @@ exports['hints'] = else if keyStr not in utils.getHintChars() return true - matchedMarker = null + matchedMarkers = [] for marker in markers when marker.hintIndex == storage.numEnteredChars match = marker.matchHintChar(keyStr) marker.hide() unless match if marker.isMatched() marker.markMatched(true) - matchedMarker = marker - if matchedMarker - again = callback(matchedMarker) + matchedMarkers.push(marker) + if matchedMarkers.length > 0 + again = callback(matchedMarkers[0]) if again vim.rootWindow.setTimeout((-> - matchedMarker.markMatched(false) + marker.markMatched(false) for marker in matchedMarkers ), @timeout) marker.reset() for marker in markers storage.numEnteredChars = 0 -- 2.39.3