diff options
Diffstat (limited to 'content_scripts')
| -rw-r--r-- | content_scripts/mode_insert.coffee | 3 | ||||
| -rw-r--r-- | content_scripts/mode_visual_edit.coffee | 139 |
2 files changed, 85 insertions, 57 deletions
diff --git a/content_scripts/mode_insert.coffee b/content_scripts/mode_insert.coffee index 741f36cd..bee2ce58 100644 --- a/content_scripts/mode_insert.coffee +++ b/content_scripts/mode_insert.coffee @@ -45,7 +45,8 @@ class InsertMode extends Mode # We can't rely on focus and blur events arriving in the expected order. When the active element # changes, we might get "focus" before "blur". We track the active element in @insertModeLock, and # exit only when that element blurs. - @exit event, target if @insertModeLock and target == @insertModeLock + # We don't exit is we're running under edit mode. Edit mode itself will handles that case. + @exit event, target if @insertModeLock and target == @insertModeLock and not @options.editModeParent "focus": (event) => @alwaysContinueBubbling => if @insertModeLock != event.target and DomUtils.isFocusable event.target @activateOnElement event.target diff --git a/content_scripts/mode_visual_edit.coffee b/content_scripts/mode_visual_edit.coffee index 3659c7ba..1271a7fe 100644 --- a/content_scripts/mode_visual_edit.coffee +++ b/content_scripts/mode_visual_edit.coffee @@ -1,7 +1,4 @@ -enterInsertMode = -> - new InsertMode { badge: "I", blurOnEscape: false } - # This prevents printable characters from being passed through to underlying page. It should, however, allow # through chrome keyboard shortcuts. It's a backstop for all of the modes following. class SuppressPrintable extends Mode @@ -120,6 +117,10 @@ class Movement extends MaintainCount moveForwardWord: (direction) -> @runMovement movement for movement in [ "forward word", "forward word", "backward word" ] + collapseSelection: -> + if 0 < @selection.toString().length + @selection[if @getDirection() == backward then "collapseToEnd" else "collapseToStart"]() + movements: "l": "forward character" "h": "backward character" @@ -219,16 +220,6 @@ class Movement extends MaintainCount @exit() @yankedText - exit: (event, target) -> - super event, target - # unless @options.underEditMode - # if document.activeElement and DomUtils.isEditable document.activeElement - # document.activeElement.blur() - unless event?.type == "keydown" and KeyboardUtils.isEscape event - if 0 < @selection.toString().length - @selection[if @getDirection() == backward then "collapseToEnd" else "collapseToStart"]() - @copy @yankedText if @yankedText - # Select a lexical entity, such as a word, a line, or a sentence. The argument should be a movement target, # such as "word" or "lineboundary". selectLexicalEntity: (entity) -> @@ -270,15 +261,20 @@ class Movement extends MaintainCount class VisualMode extends Movement constructor: (options = {}) -> @selection = window.getSelection() - switch @selection.type - when "None" - unless @establishInitialSelection() - HUD.showForDuration "Create a selection before entering visual mode.", 2500 - return - when "Caret" - # Try to start with a visible selection. - @moveInDirection(forward) or @moveInDirection backward unless options.underEditMode - @scrollIntoView() if @selection.type == "Range" + + if options.initialRange + @selection.removeAllRanges() + @selection.addRange options.initialRange + else + switch @selection.type + when "None" + unless @establishInitialSelection() + HUD.showForDuration "Create a selection before entering visual mode.", 2500 + return + when "Caret" + # Try to start with a visible selection. + @moveInDirection(forward) or @moveInDirection backward unless options.editModeParent + @scrollIntoView() if @selection.type == "Range" defaults = name: "visual" @@ -289,15 +285,15 @@ class VisualMode extends Movement super extend defaults, options extend @commands, - "V": -> new VisualLineMode extend @options, initialRange: @selection.getRangeAt(0).cloneRange() + "V": -> new VisualLineMode initialRange: @selection.getRangeAt(0).cloneRange() "y": -> # Special case: "yy" (the first from edit mode, and now the second). @selectLexicalEntity "lineboundary" if @options.yYanksLine and @keyPressCount == 1 @yank() - if @options.underEditMode and not @options.oneMovementOnly + if @options.editModeParent and not @options.oneMovementOnly extend @commands, - "c": -> @yank(); enterInsertMode() + "c": -> @yank deleteFromDocument: true; @options.editModeParent.enterInsertMode() "x": -> @yank deleteFromDocument: true "d": -> # Special case: "dd" (the first from edit mode, and now the second). @@ -311,7 +307,7 @@ class VisualMode extends Movement for entity in [ "word", "sentence", "paragraph" ] do (entity) => @movements[entity.charAt 0] = -> @selectLexicalEntity entity - unless @options.underEditMode + unless @options.editModeParent @installFindMode() # Grab the initial clipboard contents. We'll try to keep them intact until we get an explicit yank. @@ -328,6 +324,26 @@ class VisualMode extends Movement copy: (text) -> super @clipboardContents = text + exit: (event, target) -> + if @options.editModeParent + if event?.type == "keydown" and KeyboardUtils.isEscape event + # Return to a caret for edit mode. + @collapseSelection() + + @collapseSelection() if @yankedText + + unless @options.editModeParent + # Don't leave the user in insert mode just because they happen to have selected text within an input + # element. + if document.activeElement and DomUtils.isEditable document.activeElement + document.activeElement.blur() + + super event, target + # Copying the yanked text to the clipboard must be the very last thing we do, because other operations + # (like collapsing the selection) interfere with the clipboard. + @copy @yankedText if @yankedText + + installFindMode: -> previousFindRange = null @@ -386,9 +402,6 @@ class VisualLineMode extends VisualMode options.name = "visual/line" super options unless @selection?.type == "None" - if options.initialRange - @selection.removeAllRanges() - @selection.addRange options.initialRange @selectLexicalEntity "lineboundary" handleMovementKeyChar: (keyChar) -> @@ -409,39 +422,46 @@ class EditMode extends Movement super extend defaults, options extend @commands, - "i": enterInsertMode - "a": enterInsertMode - "A": => @runMovement "forward lineboundary"; enterInsertMode() - "o": => @openLine forward - "O": => @openLine backward - "p": => @pasteClipboard forward - "P": => @pasteClipboard backward - "v": -> new VisualMode underEditMode: true + "i": -> @enterInsertMode() + "a": -> @enterInsertMode() + "A": -> @runMovement "forward lineboundary"; @enterInsertMode() + "o": -> @openLine forward + "O": -> @openLine backward + "p": -> @pasteClipboard forward + "P": -> @pasteClipboard backward + "v": -> @launchSubMode VisualMode "Y": -> @enterVisualMode runMovement: "Y" "x": -> @enterVisualMode runMovement: "h", deleteFromDocument: true - "y": => @enterVisualMode yYanksLine: true - "d": => @enterVisualMode deleteFromDocument: true, dYanksLine: true - "c": => @enterVisualMode deleteFromDocument: true, onYank: enterInsertMode + "y": -> @enterVisualMode yYanksLine: true + "d": -> @enterVisualMode deleteFromDocument: true, dYanksLine: true + "c": -> @enterVisualMode deleteFromDocument: true, onYank: => @enterInsertMode() - "D": => @enterVisualMode runMovement: "$", deleteFromDocument: true - "C": => @enterVisualMode runMovement: "$", deleteFromDocument: true, onYank: enterInsertMode + "D": -> @enterVisualMode runMovement: "$", deleteFromDocument: true + "C": -> @enterVisualMode runMovement: "$", deleteFromDocument: true, onYank: => @enterInsertMode() # Disabled as potentially confusing. # # If the input is empty, then enter insert mode immediately # unless @element.isContentEditable # if @element.value.trim() == "" - # enterInsertMode() + # @enterInsertMode() # HUD.showForDuration "Input empty, entered insert mode directly.", 3500 enterVisualMode: (options = {}) -> defaults = badge: "" - underEditMode: true initialCount: @countPrefix oneMovementOnly: true - new VisualMode extend defaults, options @countPrefix = "" + @launchSubMode VisualMode, extend defaults, options + + enterInsertMode: () -> + @launchSubMode InsertMode, badge: "I", blurOnEscape: false + + launchSubMode: (mode, options = {}) -> + @lastSubMode = + mode: mode + instance: new mode extend options, editModeParent: @ pasteClipboard: (direction) -> @paste (text) => @@ -449,7 +469,7 @@ class EditMode extends Movement openLine: (direction) -> @runMovement "#{direction} lineboundary" - enterInsertMode() + @enterInsertMode() DomUtils.simulateTextEntry @element, "\n" @runMovement "backward character" if direction == backward @@ -471,35 +491,42 @@ class EditMode extends Movement locked = false exit: (event, target) -> - super() + super event, target + + lastSubMode = + if @lastSubMode?.instance.modeIsActive + @lastSubMode.instance.exit event, target + @lastSubMode + if event?.type == "keydown" and KeyboardUtils.isEscape event if target? and DomUtils.isDOMDescendant @element, target @element.blur() + if event?.type == "blur" - new BlurredEditMode @options + new SuspendedEditMode @options, lastSubMode # In edit mode, the input blurs if the user changes tabs or clicks outside of the element. In the former # case, the user expects to remain in edit mode. In the latter case, they may just be copying some text with -# the mouse/Ctrl-C, and again they expect to remain in edit mode when they return. BlurredEditMode monitors +# the mouse/Ctrl-C, and again they expect to remain in edit mode when they return. SuspendedEditMode monitors # various events and tries to either exit completely or re-enter edit mode as appropriate. -class BlurredEditMode extends Mode - constructor: (originalOptions) -> +class SuspendedEditMode extends Mode + constructor: (editModeOptions, lastSubMode = null) -> super - name: "blurred-edit" - singleton: originalOptions.singleton + name: "suspended-edit" + singleton: editModeOptions.singleton @push _name: "#{@id}/focus" focus: (event) => @alwaysContinueBubbling => - if event?.target == originalOptions.singleton + if event?.target == editModeOptions.singleton console.log "#{@id}: reactivating edit mode" if @debug - new EditMode originalOptions + editMode = new EditMode editModeOptions + editMode.launchSubMode lastSubMode.mode, lastSubMode.instance.options if lastSubMode keypress: (event) => @alwaysContinueBubbling => @exit() unless event.metaKey or event.ctrlKey or event.altKey - root = exports ? window root.VisualMode = VisualMode root.VisualLineMode = VisualLineMode |
