import * as _ from 'lodash' ; import * as moment from 'moment' ; import * as vscode from 'vscode' ; import Config from '../../config' ; import Consts from '../../consts' ; import Utils from '../../utils' ; import Item from './item' ; class Todo extends Item { _lineNextText ; get lineNextText ( ) { if ( ! _ . isUndefined ( this . _lineNextText ) ) return this . _lineNextText ; return this . _lineNextText = ( this . line ? this . line . text : this . text ) ; } set lineNextText ( val ) { this . _lineNextText = val ; } makeEdit ( ) { return Utils . editor . edits . makeDiff ( this . line . text , this . lineNextText , this . line . lineNumber ) ; } makeStatus ( state ) { const status = { box : false , done : false , cancelled : false , other : false } ; status [ state ] = true ; return status ; } getStatus ( ) { const box = this . isBox ( ) , done = ! box && this . isDone ( ) , cancelled = ! box && ! done && this . isCancelled ( ) , other = ! box && ! done && ! cancelled ; return { box , done , cancelled , other } ; } setStatus ( is , was = this . getStatus ( ) ) { if ( _ . isEqual ( is , was ) ) return ; if ( was . other && ! is . other ) { this . create ( ) ; } if ( ! was . other && is . other ) { this . unfinish ( ) ; this . unstart ( ) ; this . uncreate ( ) ; } if ( ( was . done || was . cancelled ) && is . box ) { this . unfinish ( ) ; } if ( ( ( was . box || was . other ) && ( is . done || is . cancelled ) ) || ( was . cancelled && is . done ) || ( was . done && is . cancelled ) ) { this . finish ( is . done ) ; } } getTag ( re ) { const match = this . lineNextText . match ( re ) ; return match && match [ 0 ] ; } addTag ( tag ) { this . lineNextText = `${ _ . trimEnd ( this . lineNextText ) } ${ tag }` ; } removeTag ( tagRegex ) { if ( ! this . hasTag ( tagRegex ) ) return ; this . lineNextText = _ . trimEnd ( this . lineNextText . replace ( tagRegex , '' ) ) ; } replaceTag ( tagRegex , tag ) { this . removeTag ( tagRegex ) ; this . addTag ( tag ) ; } hasTag ( tagRegex ) { return Item . is ( this . lineNextText , tagRegex ) ; } create ( ) { if ( Config . getKey ( 'timekeeping.created.enabled' ) ) { if ( Config . getKey ( 'timekeeping.created.time' ) ) { const date = moment ( ) , format = Config . getKey ( 'timekeeping.created.format' ) , time = date . format ( format ) , tag = `@created(${ time })` ; this . addTag ( tag ) ; } else { const tag = '@created' ; this . addTag ( tag ) ; } } } uncreate ( ) { this . removeTag ( Consts . regexes . tagCreated ) ; } toggleStart ( ) { if ( this . hasTag ( Consts . regexes . tagStarted ) ) { this . unstart ( ) ; } else { this . start ( ) ; } } start ( ) { if ( Config . getKey ( 'timekeeping.started.time' ) ) { const date = moment ( ) , format = Config . getKey ( 'timekeeping.started.format' ) , time = date . format ( format ) , tag = `@started(${ time })` ; this . replaceTag ( Consts . regexes . tagStarted , tag ) ; } else { const tag = '@started' ; this . replaceTag ( Consts . regexes . tagStarted , tag ) ; } } unstart ( ) { this . removeTag ( Consts . regexes . tagStarted ) ; } finish ( isPositive ? ) { isPositive = _ . isBoolean ( isPositive ) ? isPositive : this . isDone ( ) ; const started = this . getTag ( Consts . regexes . tagStarted ) ; if ( started || Config . getKey ( 'timekeeping.finished.enabled' ) || Consts . symbols . box === ( isPositive ? Consts . symbols . done : Consts . symbols . cancelled ) ) { this . unfinish ( ) ; if ( Config . getKey ( 'timekeeping.finished.time' ) ) { const finishedDate = moment ( ) , finishedFormat = Config . getKey ( 'timekeeping.finished.format' ) , finishedTime = finishedDate . format ( finishedFormat ) , finishedTag = `@${ isPositive ? 'done' : 'cancelled' }(${ finishedTime })` ; this . addTag ( finishedTag ) ; } else { const finishedTag = `@${ isPositive ? 'done' : 'cancelled' }` ; this . addTag ( finishedTag ) ; } if ( Config . getKey ( 'timekeeping.elapsed.enabled' ) && started ) { const startedFormat = Config . getKey ( 'timekeeping.started.format' ) , startedMoment = moment ( started , startedFormat ) , startedDate = new Date ( startedMoment . valueOf ( ) ) , elapsedFormat = Config . getKey ( 'timekeeping.elapsed.format' ) , time = Utils . time . diff ( new Date ( ) , startedDate , elapsedFormat ) , elapsedTag = `@${ isPositive ? 'lasted' : 'wasted' }(${ time })` ; this . addTag ( elapsedTag ) ; } } } unfinish ( ) { this . removeTag ( Consts . regexes . tagFinished ) ; this . removeTag ( Consts . regexes . tagElapsed ) ; } setSymbol ( symbol ) { const match = this . lineNextText . match ( Consts . regexes . todoSymbol ) , firstChar = this . lineNextText . match ( /\S/ ) , startIndex = match ? match [ 0 ] . indexOf ( match [ 1 ] ) : ( firstChar ? firstChar . index : this . lineNextText . length ) , endIndex = match ? match [ 0 ] . length : startIndex ; this . lineNextText = `${ this . lineNextText . substring ( 0 , startIndex ) }${ symbol ? `${ symbol } ` : '' }${ this . lineNextText . substring ( endIndex ) }` ; } setSymbolAndState ( symbol , state ) { const prevStatus = this . getStatus ( ) ; this . setSymbol ( symbol ) ; const nextStatus = this . makeStatus ( state ) ; this . setStatus ( nextStatus , prevStatus ) ; } toggleBox ( force = ! this . isBox ( ) ) { const symbol = force ? Consts . symbols . box : '' , state = force ? 'box' : 'other' ; this . setSymbolAndState ( symbol , state ) ; } box ( ) { this . toggleBox ( true ) ; } unbox ( ) { this . toggleBox ( false ) ; } toggleDone ( force = ! this . isDone ( ) ) { const symbol = force ? Consts . symbols . done : Consts . symbols . box , state = force ? 'done' : 'box' ; this . setSymbolAndState ( symbol , state ) ; } done ( ) { this . toggleDone ( true ) ; } undone ( ) { this . toggleDone ( false ) ; } toggleCancelled ( force = ! this . isCancelled ( ) ) { const symbol = force ? Consts . symbols . cancelled : Consts . symbols . box , state = force ? 'cancelled' : 'box' ; this . setSymbolAndState ( symbol , state ) ; } cancelled ( ) { this . toggleCancelled ( true ) ; } uncancelled ( ) { this . toggleCancelled ( false ) ; } isBox ( ) { return Item . is ( this . text , Consts . regexes . todoBox ) ; } isDone ( ) { return Item . is ( this . text , Consts . regexes . todoDone ) ; } isCancelled ( ) { return Item . is ( this . text , Consts . regexes . todoCancelled ) ; } isFinished ( ) { return this . isDone ( ) || this . isCancelled ( ) ; } static is ( str ) { return super . is ( str , Consts . regexes . todo ) ; } } export default Todo ;