diff options
| author | Stephen Blott | 2015-01-23 17:19:19 +0000 | 
|---|---|---|
| committer | Stephen Blott | 2015-01-24 06:48:00 +0000 | 
| commit | 0f96b6bf07c02704272a90a017ee14661dca8ce0 (patch) | |
| tree | 256c156ddd617a1a207100abaafe77942fae7f6c | |
| parent | e1b7b0a963490b0991d72a0143f489e0bc1e8096 (diff) | |
| download | vimium-0f96b6bf07c02704272a90a017ee14661dca8ce0.tar.bz2 | |
Visual/edit modes: miscellaneous improvements.
| -rw-r--r-- | content_scripts/mode_visual_edit.coffee | 106 | ||||
| -rw-r--r-- | content_scripts/scroller.coffee | 34 | ||||
| -rw-r--r-- | lib/handler_stack.coffee | 3 | 
3 files changed, 90 insertions, 53 deletions
| diff --git a/content_scripts/mode_visual_edit.coffee b/content_scripts/mode_visual_edit.coffee index e2e9c5fe..d32ae24c 100644 --- a/content_scripts/mode_visual_edit.coffee +++ b/content_scripts/mode_visual_edit.coffee @@ -20,7 +20,7 @@ class SuppressPrintable extends Mode      # This is pushed onto the handler stack before calling super().  Therefore, it ends up underneath (or      # after) all of the other handlers associated with the mode.      @suppressPrintableHandlerId = handlerStack.push -      _name: "movement/suppress-printable" +      _name: "#{@id}/suppress-printable"        keydown: handler        keypress: handler        keyup: handler @@ -31,17 +31,20 @@ class SuppressPrintable extends Mode  # This watches keyboard events and maintains @countPrefix as number keys and other keys are pressed.  class MaintainCount extends SuppressPrintable    constructor: (options) -> -    @countPrefix = "" +    @countPrefix = options.initialCount || ""      super options      @push -      _name: "movement/maintain-count" +      _name: "#{@id}/maintain-count"        keypress: (event) =>          @alwaysContinueBubbling =>            unless event.metaKey or event.ctrlKey or event.altKey              keyChar = String.fromCharCode event.charCode              @countPrefix =                if keyChar and keyChar.length == 1 and "0" <= keyChar <= "9" +                if @options.initialCount +                  @countPrefix = "" +                  delete @options.initialCount                  @countPrefix + keyChar                else                  "" @@ -112,17 +115,16 @@ class Movement extends MaintainCount      "k": "backward line"      "e": "forward word"      "b": "backward word" -    "B": "backward word" +    "w": -> @moveForwardWord()      ")": "forward sentence"      "(": "backward sentence"      "}": "forward paragraph"      "{": "backward paragraph"      "$": "forward lineboundary"      "0": "backward lineboundary" -    "w": -> @moveForwardWord() -    "o": -> @swapFocusAndAnchor()      "G": "forward documentboundary"      "gg": "backward documentboundary" +    "o": -> @swapFocusAndAnchor()    constructor: (options) ->      @movements = extend {}, @movements @@ -130,25 +132,37 @@ class Movement extends MaintainCount      @alterMethod = options.alterMethod || "extend"      @keyQueue = ""      @yankedText = "" -    super extend options, +    super extend options + +    @push +      _name: "#{@id}/keypress"        keypress: (event) => -        @alwaysContinueBubbling => -          unless event.metaKey or event.ctrlKey or event.altKey -            @keyQueue += String.fromCharCode event.charCode -            @keyQueue = @keyQueue.slice Math.max 0, @keyQueue.length - 3 -            for keyChar in (@keyQueue[i..] for i in [0...@keyQueue.length]) -              if @movements[keyChar] or @commands[keyChar] -                @keyQueue = "" -                if @commands[keyChar] -                  @commands[keyChar].call @ -                else if @movements[keyChar] -                  @selection = window.getSelection() -                  @runCountPrefixTimes => -                    switch typeof @movements[keyChar] -                      when "string" then @runMovement @movements[keyChar] -                      when "function" then @movements[keyChar].call @ -                  @scrollIntoView() -                break +        unless event.metaKey or event.ctrlKey or event.altKey +          @keyQueue += String.fromCharCode event.charCode +          # We allow at most three characters for a command or movement mapping. +          @keyQueue = @keyQueue.slice Math.max 0, @keyQueue.length - 3 +          # Try each possible multi-character keyChar sequence, from longest to shortest. +          for keyChar in (@keyQueue[i..] for i in [0...@keyQueue.length]) +            if @movements[keyChar] or @commands[keyChar] +              @keyQueue = "" +              @selection = window.getSelection() + +              if @commands[keyChar] +                @commands[keyChar].call @ +                @scrollIntoView() +                return @suppressEvent + +              else if @movements[keyChar] +                @runCountPrefixTimes => +                  switch typeof @movements[keyChar] +                    when "string" then @runMovement @movements[keyChar] +                    when "function" then @movements[keyChar].call @ +                @scrollIntoView() +                if @options.singleMovement +                  @yank() +                  return @suppressEvent + +        @continueBubbling      # Aliases.      @movements.B = @movements.b @@ -156,6 +170,7 @@ class Movement extends MaintainCount    yank: (args = {}) ->      @yankedText = text = window.getSelection().toString() +    console.log "yank:", text      @selection.deleteFromDocument() if args.deleteFromDocument      @selection[if @getDirection() == backward then "collapseToEnd" else "collapseToStart"]()      @yankedText @@ -166,6 +181,9 @@ class Movement extends MaintainCount        @swapFocusAndAnchor()      @lastYankedLine = @yank() +  enterInsertMode: -> +    new InsertMode { badge: "I", blurOnEscape: false } +    # Adapted from: http://roysharon.com/blog/37.    # I have no idea how this works (smblott, 2015/1/22).    # The intention is to find the element containing the focus.  That's the element we need to scroll into @@ -224,28 +242,31 @@ class VisualMode extends Movement      if @options.underEditMode        extend @commands,          "d": => @yank deleteFromDocument: true +        "c": => @yank deleteFromDocument: true; @enterInsertMode()    yank: (args...) ->      text = super args... -    unless @options.underEditMode -      length = text.length -      text = text.replace /\s+/g, " " -      text = text[...12] + "..." if 15 < length -      HUD.showForDuration "Yanked #{length} character#{if length == 1 then "" else "s"}: \"#{text}\".", 2500 +    length = text.length +    text = text.replace /\s+/g, " " +    text = text[...12] + "..." if 15 < length +    HUD.showForDuration "Yanked #{length} character#{if length == 1 then "" else "s"}: \"#{text}\".", 2500      @exit()    exit: (event) ->      super() -    if @options.underEditMode -      direction = @getDirection() -      @selection[if direction == backward then "collapseToEnd" else "collapseToStart"]() -    else +    unless @options.underEditMode        if document.activeElement and DomUtils.isEditable document.activeElement          document.activeElement.blur() -    # Now we set the clipboard.  No operations which maniplulate the selection should follow this. -    console.log "yank:", @yankedText.length, @yankedText +    # Now set the clipboard.  No operations which maniplulate the selection should follow this.      chrome.runtime.sendMessage { handler: "copyToClipboard", data: @yankedText } if @yankedText +class VisualModeForEdit extends VisualMode +  constructor: (options = {}) -> +    super extend options, underEditMode: true + +  exit: (args...) -> +    @selection[if @getDirection() == backward then "collapseToEnd" else "collapseToStart"]() +    super args...  class EditMode extends Movement    constructor: (options = {}) -> @@ -266,11 +287,15 @@ class EditMode extends Movement        "O": => @openLine backward        "p": => @pasteClipboard forward        "P": => @pasteClipboard backward -      "v": -> new VisualMode underEditMode: true -      "yy": => @withRangeSelection => @yankLine() +      "v": -> new VisualModeForEdit +      "Y": => @withRangeSelection => @yankLine() +      "y": => +        new VisualModeForEdit +          singleMovement: true +          initialCount: @countPrefix -    # Aliases. -    @commands.Y = @commands.yy +    # # Aliases. +    # @commands.Y = @commands.yy    pasteClipboard: (direction) ->      text = Clipboard.paste @element @@ -287,9 +312,6 @@ class EditMode extends Movement      DomUtils.simulateTextEntry @element, "\n"      @runMovement "backward character" if direction == backward -  enterInsertMode: -> -    new InsertMode { badge: "I", blurOnEscape: false } -    withRangeSelection: (func) ->      @alterMethod = "extend"      func.call @ diff --git a/content_scripts/scroller.coffee b/content_scripts/scroller.coffee index 43fad87e..f84dce8e 100644 --- a/content_scripts/scroller.coffee +++ b/content_scripts/scroller.coffee @@ -246,26 +246,38 @@ Scroller =      amount = getDimension(element,direction,pos) - element[scrollProperties[direction].axisName]      CoreScroller.scroll element, direction, amount +  # FIXME(smblott). Implement scroll in the "x" dimension.    scrollIntoView: (element) ->      activatedElement ||= document.body and firstScrollableElement()      rect = element.getBoundingClientRect() +    direction = "y"      if rect.top < 0 -      CoreScroller.scroll activatedElement, "y", rect.top - 50, false +      amount = rect.top - 10 +      element = findScrollableElement element, direction, amount, 1 +      CoreScroller.scroll element, direction, amount, false      else if window.innerHeight < rect.bottom -      CoreScroller.scroll activatedElement, "y", 50 + rect.bottom - window.innerHeight, false +      amount = rect.bottom - window.innerHeight + 10 +      element = findScrollableElement element, direction, amount, 1 +      CoreScroller.scroll element, direction, amount, false    scrollToPosition: (element, top, left) -> -    padding = 20 -    bottom = top + padding -    right = left + padding +    activatedElement ||= document.body and firstScrollableElement() + +    # Scroll down, "y". +    amount = top + 20 - (element.clientHeight + element.scrollTop) +    CoreScroller.scroll element, "y", amount, false if 0 < amount + +    # Scroll up, "y". +    amount = top - (element.scrollTop) - 5 +    CoreScroller.scroll element, "y", amount, false if amount < 0 -    element.scrollTop = top if top <= element.scrollTop -    element.scrollLeft = left if left <= element.scrollLeft +    # Scroll down, "x". +    amount = left + 20 - (element.clientWidth + element.scrollLeft) +    CoreScroller.scroll element, "x", amount, false if 0 < amount -    if element.scrollTop + element.clientHeight <= bottom -      element.scrollTop = bottom - element.clientHeight -    if element.scrollLeft + element.clientWidth <= right -      element.scrollLeft = right - element.clientWidth +    # Scroll up, "x". +    amount = left - (element.scrollLeft) - 5 +    CoreScroller.scroll element, "x", amount, false if amount < 0  root = exports ? window  root.Scroller = Scroller diff --git a/lib/handler_stack.coffee b/lib/handler_stack.coffee index 9630759c..8faec088 100644 --- a/lib/handler_stack.coffee +++ b/lib/handler_stack.coffee @@ -54,6 +54,8 @@ class HandlerStack          return true if result == @stopBubblingAndTrue          return false if result == @stopBubblingAndFalse          return @bubbleEvent type, event if result == @restartBubbling +      else +        @logResult eventNumber, type, event, handler, "skip" if @debug      true    remove: (id = @currentId) -> @@ -92,6 +94,7 @@ class HandlerStack          when @stopBubblingAndTrue then "stop/true"          when @stopBubblingAndFalse then "stop/false"          when @restartBubbling then "rebubble" +        when "skip" then "skip"          when true then "continue"      label ||= if result then "continue/truthy" else "suppress"      console.log "#{eventNumber}", type, handler._name, label | 
