public function handleMessage(PhutilConsoleMessage $message)
 {
     $data = $message->getData();
     $type = $message->getType();
     switch ($type) {
         case PhutilConsoleMessage::TYPE_CONFIRM:
             $ok = phutil_console_confirm($data['prompt'], !$data['default']);
             return $this->buildMessage(PhutilConsoleMessage::TYPE_INPUT, $ok);
         case PhutilConsoleMessage::TYPE_PROMPT:
             $response = phutil_console_prompt($data['prompt'], idx($data, 'history'));
             return $this->buildMessage(PhutilConsoleMessage::TYPE_INPUT, $response);
         case PhutilConsoleMessage::TYPE_OUT:
             $this->writeText(STDOUT, $data);
             return null;
         case PhutilConsoleMessage::TYPE_ERR:
             $this->writeText(STDERR, $data);
             return null;
         case PhutilConsoleMessage::TYPE_LOG:
             if ($this->enableLog) {
                 $this->writeText(STDERR, $data);
             }
             return null;
         case PhutilConsoleMessage::TYPE_ENABLED:
             switch ($data['which']) {
                 case PhutilConsoleMessage::TYPE_LOG:
                     $enabled = $this->enableLog;
                     break;
                 default:
                     $enabled = true;
                     break;
             }
             return $this->buildMessage(PhutilConsoleMessage::TYPE_IS_ENABLED, $enabled);
         case PhutilConsoleMessage::TYPE_TTY:
         case PhutilConsoleMessage::TYPE_COLS:
             switch ($data['which']) {
                 case PhutilConsoleMessage::TYPE_OUT:
                     $which = STDOUT;
                     break;
                 case PhutilConsoleMessage::TYPE_ERR:
                     $which = STDERR;
                     break;
             }
             switch ($type) {
                 case PhutilConsoleMessage::TYPE_TTY:
                     if (function_exists('posix_isatty')) {
                         $is_a_tty = posix_isatty($which);
                     } else {
                         $is_a_tty = null;
                     }
                     return $this->buildMessage(PhutilConsoleMessage::TYPE_IS_TTY, $is_a_tty);
                 case PhutilConsoleMessage::TYPE_COLS:
                     // TODO: This is an approximation which might not be perfectly
                     // accurate.
                     $width = phutil_console_get_terminal_width();
                     return $this->buildMessage(PhutilConsoleMessage::TYPE_COL_WIDTH, $width);
             }
             break;
         default:
             if ($this->handler) {
                 return call_user_func($this->handler, $message);
             } else {
                 throw new Exception(pht("Received unknown console message of type '%s'.", $type));
             }
     }
 }
 public function run()
 {
     $output = array();
     $status = $this->getArgument('status');
     $owner = $this->getArgument('owner');
     $order = $this->getArgument('order');
     $limit = $this->getArgument('limit');
     $this->tasks = $this->loadManiphestTasks($status == 'all' ? 'any' : $status, $owner ? $this->findOwnerPhid($owner) : $this->getUserPHID(), $order, $limit);
     if (!$this->tasks) {
         echo "No tasks found.\n";
         return 0;
     }
     $task_rows = array();
     foreach ($this->tasks as $task) {
         $output = array();
         // Render the "T123" column.
         $task_id = "T" . $task['id'];
         $formatted_task_id = phutil_console_format('**%s**', $task_id);
         $output['id'] = array('text' => $formatted_task_id, 'len' => phutil_utf8_console_strlen($task_id));
         // Render the "Title" column.
         $formatted_title = rtrim($task['title']);
         $output['title'] = array('text' => $formatted_title, 'len' => phutil_utf8_console_strlen($formatted_title));
         // Render the "Priority" column.
         switch ($task['priority']) {
             case 'Needs Triage':
                 $color = 'magenta';
                 break;
             case 'Unbreak Now!':
                 $color = 'red';
                 break;
             case 'High':
                 $color = 'yellow';
                 break;
             case 'Normal':
                 $color = 'green';
                 break;
             case 'Low':
                 $color = 'blue';
                 break;
             case 'Wishlist':
                 $color = 'cyan';
                 break;
             default:
                 $color = 'white';
                 break;
         }
         $formatted_priority = phutil_console_format("<bg:{$color}> </bg> %s", $task['priority']);
         $output['priority'] = array('text' => $formatted_priority, 'len' => phutil_utf8_console_strlen($task['priority']) + 2);
         // Render the "Status" column.
         if ($task['status']) {
             $status_text = 'Closed';
             $status_color = 'red';
         } else {
             $status_text = 'Open';
             $status_color = 'green';
         }
         $formatted_status = phutil_console_format("<bg:{$status_color}> </bg> %s", $status_text);
         $output['status'] = array('text' => $formatted_status, 'len' => phutil_utf8_console_strlen('status') + 2);
         $task_rows[] = $output;
     }
     // Find the longest string in each column.
     $col_size = array();
     foreach ($task_rows as $row) {
         foreach ($row as $key => $col) {
             if (empty($col_size[$key])) {
                 $col_size[$key] = 0;
             }
             $col_size[$key] = max($col_size[$key], $col['len']);
         }
     }
     // Determine the terminal width. If we can't figure it out, assume 80.
     $width = nonempty(phutil_console_get_terminal_width(), 80);
     // We're going to clip the titles so they'll all fit in one line on the
     // terminal. Figure out where to clip them.
     $padding_between_columns = 4;
     $clip_title_at = max(16, $width - ($col_size['id'] + $col_size['priority'] + $col_size['status'] + $padding_between_columns * 3));
     $col_size['title'] = min($col_size['title'], $clip_title_at);
     foreach ($task_rows as $key => $cols) {
         $new_title = phutil_utf8_shorten($cols['title']['text'], $clip_title_at);
         $task_rows[$key]['title']['len'] = phutil_utf8_console_strlen($new_title);
         $task_rows[$key]['title']['text'] = $new_title;
     }
     $table = array();
     foreach ($task_rows as $row) {
         $trow = array();
         foreach ($row as $col => $cell) {
             $text = $cell['text'];
             $pad_len = $col_size[$col] - $cell['len'];
             if ($pad_len) {
                 $text .= str_repeat(' ', $pad_len);
             }
             $trow[] = $text;
         }
         $table[] = implode(str_repeat(' ', $padding_between_columns), $trow);
     }
     $table = implode("\n", $table) . "\n";
     echo $table;
 }