visionmedia/pomo

View on GitHub
bin/pomo

Summary

Maintainability
Test Coverage
#!/usr/bin/env ruby
# coding: utf-8

require 'commander/import'
require 'pomo'

program :version, Pomo::VERSION

program :description, "Pomodoro time management.

    pomo provides a unified task selection api which can be used
    with most of the commands. Commands with [task] or [task ...]
    in their synopsis accept only single or both single and multiple
    task selection, respectively.

    Single task selection:
    n          : selects a single task by index : Ex: pomo remove 1
    first      : selects the first task         : Ex: pomo remove first
    last       : selects the last task          : Ex: pomo remove last

    Multiple task selection:
    [n ]+      : selects several tasks by index : Ex: pomo remove 2 8 1
    [n..n]+    : selects a range of tasks       : Ex: pomo remove 5..9 11..14
    [n..-n]+   : selects a range of tasks       : Ex: pomo remove 2..-1
    [api ]+    : selects several tasks by api   : Ex: pomo remove first last
    complete   : selects complete tasks         : Ex: pomo remove complete
    incomplete : selects incomplete tasks       : Ex: pomo remove incomplete
    all        : selects all tasks              : Ex: pomo remove all"

program :help_formatter, :compact

program :int_message, "\nTerminated pomo" \
                      "\n  * previously running tasks not marked as complete" \
                      "\n  * manually complete a task with `$ pomo complete <task>`"

list = Pomo::List.new

default_command :list

command :initconfig do |c|
  c.syntax      = 'pomo initconfig [options]'
  c.description = 'Initialize default configuration'
  c.example 'Configure with notification center, no progress bar, and tmux integration', 'pomo initconfig --notifier notification_center --no-progress --tmux'

  c.option '--notifier <lib>', String, 'Specify notificaiton library: `notification_center`, `libnotify`, `growl`, `quicksilver`'
  c.option '--[no-]progress'         , 'Run with progress bar'
  c.option '--[no-]tmux'             , 'Refresh tmux status bar on timer change'
  c.option '--[no-]force'            , 'force overwrite of existing config file'
  c.action do |args, options|
    Pomo::Configuration.save(options.__hash__)
  end
end

command :init do |c|
  c.syntax      = 'pomo init [options]'
  c.description = 'Initialize pomo in the current directory'

  c.action do |args, options|
    config = Pomo::Configuration.load
    Pomo::List.new :init => true
    say 'Initialized at `./.pomo`'
    say '  - Any commands run while in this directory will reference this file for tasks'
    say '  - To remove simply execute `rm .pomo`'
  end
end

command :start do |c|
  c.syntax      = 'pomo start [task] [options]'
  c.summary     = 'Start a task'
  c.description = 'Start a task, given the task [task] or the first task'
  c.example 'Start the first incomplete task', 'pomo start'
  c.example 'Start the first task'           , 'pomo start 0'
  c.example 'Start the first task'           , 'pomo start first'
  c.example 'Start the fifth task'           , 'pomo start 5'

  c.option '--notifier <lib>', String, 'Specify notificaiton library: `notification_center`, `libnotify`, `growl`, `quicksilver`'
  c.option '--[no-]progress'         , 'Run with progress bar'
  c.option '--[no-]tmux'             , 'Refresh tmux status bar on timer change'
  c.action do |args, options|
    abort 'a task is already running' if list.running

    config = Pomo::Configuration.load(options.__hash__)
    args = ['incomplete'] if args.empty?
    list.find(*args) do |task|
      abort 'task already completed' if task.complete?
      task.start(config, :progress => options.progress, :list => list)
      break
    end
  end
end

command :import do |c|
  c.syntax      = 'pomo import <user> <project> [issue_number]'
  c.summary     = 'Import Github issue(s)'
  c.description = 'Import Github project issues which have not yet been closed'
  c.example 'Import all open Github issues from "visionmedia/pomo"', 'pomo import visionmedia pomo'
  c.example 'Import Github issue #3 from "visionmedia/pomo"'       , 'pomo import visionmedia pomo 3'

  c.action do |args, options|
    config = Pomo::Configuration.load
    user = args.shift or raise('Github <user> is required')
    project = args.shift or raise('Github <project> is required')
    number = args.shift

    say "Importing issues from https://github.com/#{user}/#{project}"
    tasks = Pomo::GithubTask.import(user, project, number)
    tasks.each do |task|
      list << task
      say "  - Added #{task}"
    end
    list.save
  end
end

command :add do |c|
  c.syntax      = 'pomo add <task> [options]'
  c.summary     = 'Add a task'
  c.description = 'Add a task to the current list of tasks'
  c.example 'Adds the task "fix IE styling issues"', 'pomo add "fix IE styling issues"'
  c.example 'Add a task with 60 minute limit'      , 'pomo add "create executable" --length 60'

  c.option '-d', '--description string', 'Add verbose task description'
  c.option '-l', '--length minutes', Integer, 'Change the default length in minutes'
  c.action do |args, options|
    config = Pomo::Configuration.load
    task = Pomo::Task.new(args.shift, options.__hash__)
    list << task
    list.save
    say "  - Added #{task}"
  end
end

command :edit do |c|
  c.syntax      = 'pomo edit [task ...] [options]'
  c.summary     = 'Edit task(s)'
  c.description = 'Edit the given task(s) or the first task'
  c.example 'Changes the description for the first task'           , 'pomo edit first -d "fix IE styling issues"'
  c.example 'Changes the description and length for the third task', 'pomo edit 3 -d "fix IE styling issues" -l 60'
  c.example 'Changes the length of several tasks'                  , 'pomo edit 1..5 -l 10'

  c.option '-n', '--name string', 'Change the task name'
  c.option '-d', '--description string', 'Change the task description'
  c.option '-l', '--length minutes', Integer, 'Change the task length'
  c.action do |args, options|
    config = Pomo::Configuration.load
    list.find(*args) do |task|
      options.__hash__.each do |key, value|
        task.send :"#{key}=", value
      end
      say "  - Updated #{task}"
    end
    list.save
  end
end

command :break do |c|
  c.syntax      = 'pomo break [length] [options]'
  c.summary     = 'Start a break'
  c.description = 'Take a break, defaults to 5 minutes or [length] or --length'
  c.example 'Take a five minute break', 'pomo break'
  c.example 'Take a 30 minute break'  , 'pomo break 30'
  c.example 'Take a 30 minute break'  , 'pomo break --length 30'

  c.option '-l', '--length minutes', Integer, 'Change the default length in minutes'
  c.option '--notifier <lib>', String, 'Specify notificaiton library: `notification_center`, `libnotify`, `growl`, `quicksilver`'
  c.option '--[no-]progress'         , 'Run with progress bar'
  c.option '--[no-]tmux'             , 'Refresh tmux status bar on timer change'
  c.action do |args, options|
    options.default :length => args.first ? args.first.to_i : 5

    config = Pomo::Configuration.load(options.__hash__)
    task = Pomo::Break.new('Break time', options.__hash__)
    task.start(config, :progress => options.progress)
  end
end

command :remove do |c|
  c.syntax      = 'pomo [remove|rm] [task ...] [options]'
  c.summary     = 'Remove task(s)'
  c.description = 'Remove task(s) or the first task'
  c.example 'Remove the first task'           , 'pomo remove first'
  c.example 'Remove the fifth task'           , 'pomo rm 5'
  c.example 'Remove the fifth and second task', 'pomo rm 5 2'
  c.example 'Remove a range of tasks'         , 'pomo rm 2..6'
  c.example 'Remove all but the first task'   , 'pomo rm 2..-1'
  c.example 'Remove all tasks'                , 'pomo rm all'

  c.action do |args, options|
    config = Pomo::Configuration.load
    list.find(*args) do |task|
      list.tasks -= [task]
      say "  - Removed #{task}"
    end
    list.save
  end
end
alias_command :rm, :remove
alias_command :clear, :remove, 'all'

command :copy do |c|
  c.syntax      = 'pomo [copy|cp] [task...]'
  c.summary     = 'Copy task(s)'
  c.description = 'Copy task(s) to the end of the current list as an incomplete task'
  c.example 'Copy first task'               , 'pomo copy first'
  c.example 'Copy the fourth task'          , 'pomo cp 4'
  c.example 'Copy the fifth and second task', 'pomo cp 5 2'
  c.example 'Copy a range of tasks'         , 'pomo cp 2..6'
  c.example 'Copy all but the first task'   , 'pomo cp 2..-1'
  c.example 'Copy all tasks'                , 'pomo cp all'

  c.action do |args, options|
    config = Pomo::Configuration.load
    list.find(*args) do |task|
      dup_task = Marshal::load(Marshal.dump(task))
      dup_task.complete = false
      list.tasks << dup_task
      say "  - Copied #{task}"
    end
    list.save
  end
end
alias_command :cp, :copy

command :move do |c|
  c.syntax      = 'pomo move [from] [to]'
  c.summary     = 'Move a task'
  c.description = 'Move a task to a different position in the current list'
  c.example 'Move task 4 to position 2'       , 'pomo move 4 2'
  c.example 'Move last task to first position', 'pomo mv last first'

  c.action do |args, options|
    config = Pomo::Configuration.load
    from = args.shift or raise('<from> is required')
    to = args.shift or raise('<to> is required')
    list.find(from) do |task|
      list.find(to) do |other_task|
        list.move(from, to)
        say "  - Moved #{task} from position #{from} to #{to}"
      end
    end
    list.save
  end
end
alias_command :mv, :move

# TODO remove view alias in 3.0 release
command :show do |c|
  c.syntax      = 'pomo show [task ...] [options]'
  c.summary     = 'Show verbose task information'
  c.description = 'Show verbose information for the given task(s) or the first task'
  c.example 'Show the first task', 'pomo show first'
  c.example 'Show the last task' , 'pomo show last'
  c.example 'Show the fifth task', 'pomo show 5'

  c.action do |args, options|
    config = Pomo::Configuration.load
    list.find(*args) do |task|
      say "\n"
      format = "%15s : %s\n"
      task.verbose_output(format)
    end
    say "\n"
  end
end
alias_command :view, :show

command :complete do |c|
  c.syntax      = 'pomo complete [task ...] [options]'
  c.summary     = 'Mark task(s) as completed'
  c.description = 'Mark the given task(s) or the first task to complete'
  c.example 'Mark first task as complete', 'pomo complete first'
  c.example 'Mark last task as complete' , 'pomo complete last'
  c.example 'Mark fifth task as complete', 'pomo complete 5'

  c.action do |args, options|
    config = Pomo::Configuration.load
    list.find(*args) do |task|
      task.complete = true
      say "  - Completed #{task}"
    end
    list.save
  end
end

command :incomplete do |c|
  c.syntax      = 'pomo incomplete [task ...] [options]'
  c.summary     = 'Mark task(s) as incompleted'
  c.description = 'Mark the given task(s) or the first task as not completed'
  c.example 'Mark first task as not completed', 'pomo incomplete first'
  c.example 'Mark last task as not completed' , 'pomo incomplete last'
  c.example 'Mark fifth task as not completed', 'pomo incomplete 5'

  c.action do |args, options|
    config = Pomo::Configuration.load
    list.find(*args) do |task|
      task.complete = false
      say "  - #{task} marked incomplete"
    end
    list.save
  end
end

command :list do |c|
  c.syntax      = 'pomo [list|ls] [options]'
  c.description = 'List tasks'
  c.example 'List incomplete tasks', 'pomo list'
  c.example 'List all tasks', 'pomo list --all'
  c.example 'List completed tasks', 'pomo list --complete'

  c.option '-a', '--all', 'List all tasks'
  c.option '-c', '--complete', 'List only completed tasks'
  c.action do |args, options|
    abort "invalid command. See 'pomo help' for more information" unless args.empty?

    config = Pomo::Configuration.load
    total = 0
    list.tasks.each_with_index do |task, i|
      next if task.complete? && !options.all && !options.complete
      next if !task.complete? && options.complete

      say ' %s %2d. %-50s : %d minutes' % [task.complete? ? '✓' : ' ', i, task.to_s, task.length]
      total += task.length
    end
    say ' ' * 60 + '%d minutes' % total
  end
end
alias_command :ls, :list