protected function renderContext(ArcanistLintMessage $message, array $line_data)
 {
     $lines_of_context = 3;
     $out = array();
     $num_lines = count($line_data);
     // make line numbers line up with array indexes
     array_unshift($line_data, '');
     $line_num = min($message->getLine(), $num_lines);
     $line_num = max(1, $line_num);
     // Print out preceding context before the impacted region.
     $cursor = max(1, $line_num - $lines_of_context);
     for (; $cursor < $line_num; $cursor++) {
         $out[] = $this->renderLine($cursor, $line_data[$cursor]);
     }
     $text = $message->getOriginalText();
     $start = $message->getChar() - 1;
     $patch = '';
     // Refine original and replacement text to eliminate start and end in common
     if ($message->isPatchable()) {
         $patch = $message->getReplacementText();
         $text_strlen = strlen($text);
         $patch_strlen = strlen($patch);
         $min_length = min($text_strlen, $patch_strlen);
         $same_at_front = 0;
         for ($ii = 0; $ii < $min_length; $ii++) {
             if ($text[$ii] !== $patch[$ii]) {
                 break;
             }
             $same_at_front++;
             $start++;
             if ($text[$ii] == "\n") {
                 $out[] = $this->renderLine($cursor, $line_data[$cursor]);
                 $cursor++;
                 $start = 0;
                 $line_num++;
             }
         }
         // deal with shorter string '     ' longer string '     a     '
         $min_length -= $same_at_front;
         // And check the end of the string
         $same_at_end = 0;
         for ($ii = 1; $ii <= $min_length; $ii++) {
             if ($text[$text_strlen - $ii] !== $patch[$patch_strlen - $ii]) {
                 break;
             }
             $same_at_end++;
         }
         $text = substr($text, $same_at_front, $text_strlen - $same_at_end - $same_at_front);
         $patch = substr($patch, $same_at_front, $patch_strlen - $same_at_end - $same_at_front);
     }
     // Print out the impacted region itself.
     $diff = $message->isPatchable() ? '-' : null;
     $text_lines = explode("\n", $text);
     $text_length = count($text_lines);
     $intraline = $text != '' || $start || !preg_match('/\\n$/', $patch);
     if ($intraline) {
         for (; $cursor < $line_num + $text_length; $cursor++) {
             $chevron = $cursor == $line_num;
             // We may not have any data if, e.g., the old file does not exist.
             $data = idx($line_data, $cursor, null);
             // Highlight the problem substring.
             $text_line = $text_lines[$cursor - $line_num];
             if (strlen($text_line)) {
                 $data = substr_replace($data, phutil_console_format('##%s##', $text_line), $cursor == $line_num ? $start : 0, strlen($text_line));
             }
             $out[] = $this->renderLine($cursor, $data, $chevron, $diff);
         }
     }
     // Print out replacement text.
     if ($message->isPatchable()) {
         // Strip trailing newlines, since "explode" will create an extra patch
         // line for these.
         if (strlen($patch) && $patch[strlen($patch) - 1] === "\n") {
             $patch = substr($patch, 0, -1);
         }
         $patch_lines = explode("\n", $patch);
         $patch_length = count($patch_lines);
         $patch_line = $patch_lines[0];
         $len = isset($text_lines[0]) ? strlen($text_lines[0]) : 0;
         $patched = phutil_console_format('##%s##', $patch_line);
         if ($intraline) {
             $patched = substr_replace($line_data[$line_num], $patched, $start, $len);
         }
         $out[] = $this->renderLine(null, $patched, false, '+');
         foreach (array_slice($patch_lines, 1) as $patch_line) {
             $out[] = $this->renderLine(null, phutil_console_format('##%s##', $patch_line), false, '+');
         }
     }
     $end = min($num_lines, $cursor + $lines_of_context);
     for (; $cursor < $end; $cursor++) {
         // If there is no original text, we didn't print out a chevron or any
         // highlighted text above, so print it out here. This allows messages
         // which don't have any original/replacement information to still
         // render with indicator chevrons.
         if ($text || $message->isPatchable()) {
             $chevron = false;
         } else {
             $chevron = $cursor == $line_num;
         }
         $out[] = $this->renderLine($cursor, $line_data[$cursor], $chevron);
         // With original text, we'll render the text highlighted above. If the
         // lint message only has a line/char offset there's nothing to
         // highlight, so print out a caret on the next line instead.
         if ($chevron && $message->getChar()) {
             $out[] = $this->renderCaret($message->getChar());
         }
     }
     $out[] = null;
     return implode("\n", $out);
 }