aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Blott2015-01-23 17:19:19 +0000
committerStephen Blott2015-01-24 06:48:00 +0000
commit0f96b6bf07c02704272a90a017ee14661dca8ce0 (patch)
tree256c156ddd617a1a207100abaafe77942fae7f6c
parente1b7b0a963490b0991d72a0143f489e0bc1e8096 (diff)
downloadvimium-0f96b6bf07c02704272a90a017ee14661dca8ce0.tar.bz2
Visual/edit modes: miscellaneous improvements.
-rw-r--r--content_scripts/mode_visual_edit.coffee106
-rw-r--r--content_scripts/scroller.coffee34
-rw-r--r--lib/handler_stack.coffee3
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