import { Position , Selection , Range , TextDocument , TextEditor , TextEditorRevealType } from 'vscode' ; import { Motion , Motions } from './motions' ; import { Mode , IController , DeleteRegister } from './common' ; export abstract class Operator { public abstract runNormalMode ( ctrl , ed , repeatCount , args ) ; public abstract runVisualMode ( ctrl , ed , args ) ; protected doc ( ed ) { return ed . document ; } protected pos ( ed ) { return ed . selection . active ; } protected sel ( ed ) { return ed . selection ; } protected setPosReveal ( ed , line , char ) { ed . selection = new Selection ( new Position ( line , char ) , new Position ( line , char ) ) ; ed . revealRange ( ed . selection , TextEditorRevealType . Default ) ; } protected delete ( ctrl , ed , isWholeLine , range ) { ctrl . setDeleteRegister ( new DeleteRegister ( isWholeLine , ed . document . getText ( range ) ) ) ; ed . edit ( ( builder ) => { builder . delete ( range ) ; } ) ; } } abstract class OperatorWithNoArgs extends Operator { public runNormalMode ( ctrl , ed , repeatCount , args ) { this . _run ( ctrl , ed ) ; return true ; } public runVisualMode ( ctrl , ed , args ) { this . _run ( ctrl , ed ) ; return true ; } protected abstract _run ( ctrl , ed ) ; } class InsertOperator extends OperatorWithNoArgs { protected _run ( ctrl , ed ) { ctrl . setMode ( Mode . INSERT ) ; } } class AppendOperator extends OperatorWithNoArgs { protected _run ( ctrl , ed ) { const newPos = Motions . RightMotion . run ( this . doc ( ed ) , this . pos ( ed ) , ctrl . motionState ) ; this . setPosReveal ( ed , newPos . line , newPos . character ) ; ctrl . setMode ( Mode . INSERT ) ; } } class AppendEndOfLineOperator extends OperatorWithNoArgs { protected _run ( ctrl , ed ) { const newPos = Motions . EndOfLine . run ( this . doc ( ed ) , this . pos ( ed ) , ctrl . motionState ) ; this . setPosReveal ( ed , newPos . line , newPos . character ) ; ctrl . setMode ( Mode . INSERT ) ; } } class VisualOperator extends OperatorWithNoArgs { protected _run ( ctrl , ed ) { ctrl . motionState . anchor = this . pos ( ed ) ; ctrl . setVisual ( true ) ; } } class DeleteCharUnderCursorOperator extends Operator { public runNormalMode ( ctrl , ed , repeatCount , args ) { const to = Motions . NextCharacter . repeat ( repeatCount > 1 , repeatCount ) . run ( this . doc ( ed ) , this . pos ( ed ) , ctrl . motionState ) ; const from = this . pos ( ed ) ; this . delete ( ctrl , ed , false , new Range ( from . line , from . character , to . line , to . character ) ) ; return true ; } public runVisualMode ( ctrl , ed , args ) { const sel = this . sel ( ed ) ; this . delete ( ctrl , ed , false , sel ) ; return true ; } } class DeleteLineOperator extends Operator { public runNormalMode ( ctrl , ed , repeatCount , args ) { const pos = this . pos ( ed ) ; const doc = this . doc ( ed ) ; let fromLine = pos . line ; let fromCharacter = 0 ; let toLine = fromLine + repeatCount ; let toCharacter = 0 ; if ( toLine >= doc . lineCount - 1 ) { toLine = doc . lineCount - 1 ; toCharacter = doc . lineAt ( toLine ) . text . length ; if ( fromLine > 0 ) { fromLine = fromLine - 1 ; fromCharacter = doc . lineAt ( fromLine ) . text . length ; } } this . delete ( ctrl , ed , true , new Range ( fromLine , fromCharacter , toLine , toCharacter ) ) ; return true ; } public runVisualMode ( ctrl , ed , args ) { const sel = this . sel ( ed ) ; this . delete ( ctrl , ed , false , sel ) ; return true ; } } abstract class OperatorWithMotion extends Operator { public runNormalMode ( ctrl , ed , repeatCount , args ) { const motion = ctrl . findMotion ( args ) ; if ( ! motion ) { if ( ctrl . isMotionPrefix ( args ) ) { return false ; } return true ; } return this . _runNormalMode ( ctrl , ed , motion . repeat ( repeatCount > 1 , repeatCount ) ) ; } protected abstract _runNormalMode ( ctrl , ed , motion ) ; } class DeleteToOperator extends OperatorWithMotion { public runNormalMode ( ctrl , ed , repeatCount , args ) { if ( args === 'd' ) { return Operators . DeleteLine . runNormalMode ( ctrl , ed , repeatCount , args ) ; } return super . runNormalMode ( ctrl , ed , repeatCount , args ) ; } protected _runNormalMode ( ctrl , ed , motion ) { const to = motion . run ( this . doc ( ed ) , this . pos ( ed ) , ctrl . motionState ) ; const from = this . pos ( ed ) ; this . delete ( ctrl , ed , false , new Range ( from . line , from . character , to . line , to . character ) ) ; return true ; } public runVisualMode ( ctrl , ed , args ) { const sel = this . sel ( ed ) ; this . delete ( ctrl , ed , false , sel ) ; return true ; } } class PutOperator extends Operator { public runNormalMode ( ctrl , ed , repeatCount , args ) { const register = ctrl . getDeleteRegister ( ) ; if ( ! register ) { return true ; } let str = repeatString ( register . content , repeatCount ) ; const pos = this . pos ( ed ) ; if ( ! register . isWholeLine ) { ed . edit ( ( builder ) => { builder . insert ( new Position ( pos . line , pos . character + 1 ) , str ) ; } ) ; return true ; } const doc = this . doc ( ed ) ; let insertLine = pos . line + 1 ; let insertCharacter = 0 ; if ( insertLine >= doc . lineCount ) { insertLine = doc . lineCount - 1 ; insertCharacter = doc . lineAt ( insertLine ) . text . length ; str = '\n' + str ; } ed . edit ( ( builder ) => { builder . insert ( new Position ( insertLine , insertCharacter ) , str ) ; } ) ; return true ; } public runVisualMode ( ctrl , ed , args ) { const register = ctrl . getDeleteRegister ( ) ; if ( ! register ) { return false ; } const str = register . content ; const sel = this . sel ( ed ) ; ed . edit ( ( builder ) => { builder . replace ( sel , str ) ; } ) ; return true ; } } class ReplaceOperator extends Operator { public runNormalMode ( ctrl , ed , repeatCount , args ) { if ( args . length === 0 ) { return false ; } const doc = this . doc ( ed ) ; const pos = this . pos ( ed ) ; const toCharacter = pos . character + repeatCount ; if ( toCharacter > doc . lineAt ( pos ) . text . length ) { return true ; } ed . edit ( ( builder ) => { builder . replace ( new Range ( pos . line , pos . character , pos . line , toCharacter ) , repeatString ( args , repeatCount ) ) ; } ) ; return true ; } public runVisualMode ( ctrl , ed , args ) { if ( args . length === 0 ) { return false ; } const doc = this . doc ( ed ) ; const sel = this . sel ( ed ) ; const srcString = doc . getText ( sel ) ; let dstString = '' ; for ( let i = 0 ; i < srcString . length ; i ++ ) { const ch = srcString . charAt ( i ) ; if ( ch === '\r' || ch === '\n' ) { dstString += ch ; } else { dstString += args ; } } ed . edit ( ( builder ) => { builder . replace ( sel , dstString ) ; } ) ; return true ; } } class ReplaceModeOperator extends Operator { public runNormalMode ( ctrl , ed , repeatCount , args ) { ctrl . setMode ( Mode . REPLACE ) ; return true ; } public runVisualMode ( ctrl , ed , args ) { this . delete ( ctrl , ed , false , this . sel ( ed ) ) ; ctrl . setMode ( Mode . INSERT ) ; return true ; } } class ChangeOperator extends OperatorWithMotion { protected _runNormalMode ( ctrl , ed , motion ) { const to = motion . run ( this . doc ( ed ) , this . pos ( ed ) , ctrl . motionState ) ; const from = this . pos ( ed ) ; this . delete ( ctrl , ed , false , new Range ( from . line , from . character , to . line , to . character ) ) ; ctrl . setMode ( Mode . INSERT ) ; return true ; } public runVisualMode ( ctrl , ed , args ) { const sel = this . sel ( ed ) ; this . delete ( ctrl , ed , false , sel ) ; ctrl . setMode ( Mode . INSERT ) ; return true ; } } function repeatString ( str , repeatCount ) { let result = '' ; for ( let i = 0 ; i < repeatCount ; i ++ ) { result += str ; } return result ; } export const Operators = { Insert : new InsertOperator ( ) , Visual : new VisualOperator ( ) , Append : new AppendOperator ( ) , AppendEndOfLine : new AppendEndOfLineOperator ( ) , DeleteCharUnderCursor : new DeleteCharUnderCursorOperator ( ) , DeleteTo : new DeleteToOperator ( ) , DeleteLine : new DeleteLineOperator ( ) , Put : new PutOperator ( ) , Replace : new ReplaceOperator ( ) , Change : new ChangeOperator ( ) , ReplaceMode : new ReplaceModeOperator ( ) , } ;