<commit_before><?php

final class DiffusionBrowseController extends DiffusionController {

  private $lintCommit;
  private $lintMessages;
  private $coverage;

  public function shouldAllowPublic() {
    return true;
  }

  public function handleRequest(AphrontRequest $request) {
    $response = $this->loadDiffusionContext();
    if ($response) {
      return $response;
    }

    $drequest = $this->getDiffusionRequest();

    // Figure out if we're browsing a directory, a file, or a search result
    // list.

    $grep = $request->getStr('grep');
    $find = $request->getStr('find');
    if (strlen($grep) || strlen($find)) {
      return $this->browseSearch();
    }

    $pager = id(new PHUIPagerView())
      ->readFromRequest($request);

    $results = DiffusionBrowseResultSet::newFromConduit(
      $this->callConduitWithDiffusionRequest(
        'diffusion.browsequery',
        array(
          'path' => $drequest->getPath(),
          'commit' => $drequest->getStableCommit(),
          'offset' => $pager->getOffset(),
          'limit' => $pager->getPageSize() + 1,
        )));

    $reason = $results->getReasonForEmptyResultSet();
    $is_file = ($reason == DiffusionBrowseResultSet::REASON_IS_FILE);

    if ($is_file) {
      return $this->browseFile($results);
    } else {
      $paths = $results->getPaths();
      $paths = $pager->sliceResults($paths);
      $results->setPaths($paths);

      return $this->browseDirectory($results, $pager);
    }
  }

  private function browseSearch() {

    $drequest = $this->getDiffusionRequest();
    $header = $this->buildHeaderView($drequest);

    $search_form = $this->renderSearchForm();
    $search_results = $this->renderSearchResults();

    $search_form = id(new PHUIObjectBoxView())
      ->setHeaderText(pht('Search'))
      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
      ->appendChild($search_form);

    $crumbs = $this->buildCrumbs(
      array(
        'branch' => true,
        'path'   => true,
        'view'   => 'browse',
      ));
    $crumbs->setBorder(true);

    $view = id(new PHUITwoColumnView())
      ->setHeader($header)
      ->setFooter(array(
          $search_form,
          $search_results,
        ));

    return $this->newPage()
      ->setTitle(
        array(
          nonempty(basename($drequest->getPath()), '/'),
          $drequest->getRepository()->getDisplayName(),
        ))
      ->setCrumbs($crumbs)
      ->appendChild($view);
  }

  private function browseFile() {
    $viewer = $this->getViewer();
    $request = $this->getRequest();
    $drequest = $this->getDiffusionRequest();
    $repository = $drequest->getRepository();

    $before = $request->getStr('before');
    if ($before) {
      return $this->buildBeforeResponse($before);
    }

    $path = $drequest->getPath();

    $blame_key = PhabricatorDiffusionBlameSetting::SETTINGKEY;
    $color_key = PhabricatorDiffusionColorSetting::SETTINGKEY;

    $show_blame = $request->getBool(
      'blame',
      $viewer->getUserSetting($blame_key));

    $show_color = $request->getBool(
      'color',
      $viewer->getUserSetting($color_key));

    $view = $request->getStr('view');
    if ($request->isFormPost() && $view != 'raw' && $viewer->isLoggedIn()) {
      $preferences = PhabricatorUserPreferences::loadUserPreferences($viewer);

      $editor = id(new PhabricatorUserPreferencesEditor())
        ->setActor($viewer)
        ->setContentSourceFromRequest($request)
        ->setContinueOnNoEffect(true)
        ->setContinueOnMissingFields(true);

      $xactions = array();
      $xactions[] = $preferences->newTransaction($blame_key, $show_blame);
      $xactions[] = $preferences->newTransaction($color_key, $show_color);
      $editor->applyTransactions($preferences, $xactions);

      $uri = $request->getRequestURI()
        ->alter('blame', null)
        ->alter('color', null);

      return id(new AphrontRedirectResponse())->setURI($uri);
    }

    // We need the blame information if blame is on and we're building plain
    // text, or blame is on and this is an Ajax request. If blame is on and
    // this is a colorized request, we don't show blame at first (we ajax it
    // in afterward) so we don't need to query for it.
    $needs_blame = ($show_blame && !$show_color) ||
                   ($show_blame && $request->isAjax());

    $params = array(
      'commit' => $drequest->getCommit(),
      'path' => $drequest->getPath(),
    );

    $byte_limit = null;
    if ($view !== 'raw') {
      $byte_limit = PhabricatorFileStorageEngine::getChunkThreshold();
      $time_limit = 10;

      $params += array(
        'timeout' => $time_limit,
        'byteLimit' => $byte_limit,
      );
    }

    $response = $this->callConduitWithDiffusionRequest(
      'diffusion.filecontentquery',
      $params);

    $hit_byte_limit = $response['tooHuge'];
    $hit_time_limit = $response['tooSlow'];

    $file_phid = $response['filePHID'];
    if ($hit_byte_limit) {
      $corpus = $this->buildErrorCorpus(
        pht(
          'This file is larger than %s byte(s), and too large to display '.
          'in the web UI.',
          phutil_format_bytes($byte_limit)));
    } else if ($hit_time_limit) {
      $corpus = $this->buildErrorCorpus(
        pht(
          'This file took too long to load from the repository (more than '.
          '%s second(s)).',
          new PhutilNumber($time_limit)));
    } else {
      $file = id(new PhabricatorFileQuery())
        ->setViewer($viewer)
        ->withPHIDs(array($file_phid))
        ->executeOne();
      if (!$file) {
        throw new Exception(pht('Failed to load content file!'));
      }

      if ($view === 'raw') {
        return $file->getRedirectResponse();
      }

      $data = $file->loadFileData();

      $ref = $this->getGitLFSRef($repository, $data);
      if ($ref) {
        if ($view == 'git-lfs') {
          $file = $this->loadGitLFSFile($ref);

          // Rename the file locally so we generate a better vanity URI for
          // it. In storage, it just has a name like "lfs-13f9a94c0923...",
          // since we don't get any hints about possible human-readable names
          // at upload time.
          $basename = basename($drequest->getPath());
          $file->makeEphemeral();
          $file->setName($basename);

          return $file->getRedirectResponse();
        } else {
          $corpus = $this->buildGitLFSCorpus($ref);
        }
      } else if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) {
        $file_uri = $file->getBestURI();

        if ($file->isViewableImage()) {
          $corpus = $this->buildImageCorpus($file_uri);
        } else {
          $corpus = $this->buildBinaryCorpus($file_uri, $data);
        }
      } else {
        $this->loadLintMessages();
        $this->coverage = $drequest->loadCoverage();

        // Build the content of the file.
        $corpus = $this->buildCorpus(
          $show_blame,
          $show_color,
          $data,
          $needs_blame,
          $drequest,
          $path,
          $data);
      }
    }

    if ($request->isAjax()) {
      return id(new AphrontAjaxResponse())->setContent($corpus);
    }

    require_celerity_resource('diffusion-source-css');

    // Render the page.
    $view = $this->buildCurtain($drequest);
    $curtain = $this->enrichCurtain(
      $view,
      $drequest,
      $show_blame,
      $show_color);

    $properties = $this->buildPropertyView($drequest);
    $header = $this->buildHeaderView($drequest);
    $header->setHeaderIcon('fa-file-code-o');

    $content = array();

    $follow  = $request->getStr('follow');
    if ($follow) {
      $notice = new PHUIInfoView();
      $notice->setSeverity(PHUIInfoView::SEVERITY_WARNING);
      $notice->setTitle(pht('Unable to Continue'));
      switch ($follow) {
        case 'first':
          $notice->appendChild(
            pht(
              'Unable to continue tracing the history of this file because '.
              'this commit is the first commit in the repository.'));
          break;
        case 'created':
          $notice->appendChild(
            pht(
              'Unable to continue tracing the history of this file because '.
              'this commit created the file.'));
          break;
      }
      $content[] = $notice;
    }

    $renamed = $request->getStr('renamed');
    if ($renamed) {
      $notice = new PHUIInfoView();
      $notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
      $notice->setTitle(pht('File Renamed'));
      $notice->appendChild(
        pht(
          'File history passes through a rename from "%s" to "%s".',
          $drequest->getPath(),
          $renamed));
      $content[] = $notice;
    }

    $content[] = $corpus;
    $content[] = $this->buildOpenRevisions();

    $crumbs = $this->buildCrumbs(
      array(
        'branch' => true,
        'path'   => true,
        'view'   => 'browse',
      ));
    $crumbs->setBorder(true);

    $basename = basename($this->getDiffusionRequest()->getPath());

    $view = id(new PHUITwoColumnView())
      ->setHeader($header)
      ->setCurtain($curtain)
      ->setMainColumn(array(
        $content,
        ));

    if ($properties) {
      $view->addPropertySection(pht('Details'), $properties);
    }

    $title = array($basename, $repository->getDisplayName());

    return $this->newPage()
      ->setTitle($title)
      ->setCrumbs($crumbs)
      ->appendChild(
        array(
          $view,
      ));

  }

  public function browseDirectory(
    DiffusionBrowseResultSet $results,
    PHUIPagerView $pager) {

    $request = $this->getRequest();
    $drequest = $this->getDiffusionRequest();
    $repository = $drequest->getRepository();

    $reason = $results->getReasonForEmptyResultSet();

    $curtain = $this->buildCurtain($drequest);
    $details = $this->buildPropertyView($drequest);

    $header = $this->buildHeaderView($drequest);
    $header->setHeaderIcon('fa-folder-open');

    $search_form = $this->renderSearchForm();

    $empty_result = null;
    $browse_panel = null;
    if (!$results->isValidResults()) {
      $empty_result = new DiffusionEmptyResultView();
      $empty_result->setDiffusionRequest($drequest);
      $empty_result->setDiffusionBrowseResultSet($results);
      $empty_result->setView($request->getStr('view'));
    } else {
      $phids = array();
      foreach ($results->getPaths() as $result) {
        $data = $result->getLastCommitData();
        if ($data) {
          if ($data->getCommitDetail('authorPHID')) {
            $phids[$data->getCommitDetail('authorPHID')] = true;
          }
        }
      }

      $phids = array_keys($phids);
      $handles = $this->loadViewerHandles($phids);

      $browse_table = id(new DiffusionBrowseTableView())
        ->setDiffusionRequest($drequest)
        ->setHandles($handles)
        ->setPaths($results->getPaths())
        ->setUser($request->getUser());

      $browse_header = id(new PHUIHeaderView())
        ->setHeader(nonempty(basename($drequest->getPath()), '/'))
        ->setHeaderIcon('fa-folder-open');

      $browse_panel = id(new PHUIObjectBoxView())
        ->setHeader($browse_header)
        ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
        ->setTable($browse_table);

      $browse_panel->setShowHide(
        array(pht('Show Search')),
        pht('Hide Search'),
        $search_form,
        '#');
    }

    $open_revisions = $this->buildOpenRevisions();
    $readme = $this->renderDirectoryReadme($results);

    $crumbs = $this->buildCrumbs(
      array(
        'branch' => true,
        'path'   => true,
        'view'   => 'browse',
      ));

    $pager_box = $this->renderTablePagerBox($pager);
    $crumbs->setBorder(true);

    $view = id(new PHUITwoColumnView())
      ->setHeader($header)
      ->setCurtain($curtain)
      ->setMainColumn(array(
        $empty_result,
        $browse_panel,
      ))
      ->setFooter(array(
          $open_revisions,
          $readme,
          $pager_box,
      ));

    if ($details) {
      $view->addPropertySection(pht('Details'), $details);
    }

    return $this->newPage()
      ->setTitle(array(
          nonempty(basename($drequest->getPath()), '/'),
          $repository->getDisplayName(),
        ))
      ->setCrumbs($crumbs)
      ->appendChild(
        array(
          $view,
        ));
  }

  private function renderSearchResults() {
    $request = $this->getRequest();

    $drequest = $this->getDiffusionRequest();
    $repository = $drequest->getRepository();
    $results = array();

    $pager = id(new PHUIPagerView())
      ->readFromRequest($request);

    $search_mode = null;
    switch ($repository->getVersionControlSystem()) {
      case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
        $results = array();
        break;
      default:
        if (strlen($this->getRequest()->getStr('grep'))) {
          $search_mode = 'grep';
          $query_string = $request->getStr('grep');
          $results = $this->callConduitWithDiffusionRequest(
            'diffusion.searchquery',
            array(
              'grep' => $query_string,
              'commit' => $drequest->getStableCommit(),
              'path' => $drequest->getPath(),
              'limit' => $pager->getPageSize() + 1,
              'offset' => $pager->getOffset(),
            ));
        } else { // Filename search.
          $search_mode = 'find';
          $query_string = $request->getStr('find');
          $results = $this->callConduitWithDiffusionRequest(
            'diffusion.querypaths',
            array(
              'pattern' => $query_string,
              'commit' => $drequest->getStableCommit(),
              'path' => $drequest->getPath(),
              'limit' => $pager->getPageSize() + 1,
              'offset' => $pager->getOffset(),
            ));
        }
        break;
    }
    $results = $pager->sliceResults($results);

    if ($search_mode == 'grep') {
      $table = $this->renderGrepResults($results, $query_string);
      $header = pht(
        'File content matching "%s" under "%s"',
        $query_string,
        nonempty($drequest->getPath(), '/'));
    } else {
      $table = $this->renderFindResults($results);
      $header = pht(
        'Paths matching "%s" under "%s"',
        $query_string,
        nonempty($drequest->getPath(), '/'));
    }

    $box = id(new PHUIObjectBoxView())
      ->setHeaderText($header)
      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
      ->setTable($table);

    $pager_box = $this->renderTablePagerBox($pager);

    return array($box, $pager_box);
  }

  private function renderGrepResults(array $results, $pattern) {
    $drequest = $this->getDiffusionRequest();

    require_celerity_resource('phabricator-search-results-css');

    $rows = array();
    foreach ($results as $result) {
      list($path, $line, $string) = $result;

      $href = $drequest->generateURI(array(
        'action' => 'browse',
        'path' => $path,
        'line' => $line,
      ));

      $matches = null;
      $count = @preg_match_all(
        '('.$pattern.')u',
        $string,
        $matches,
        PREG_OFFSET_CAPTURE);

      if (!$count) {
        $output = ltrim($string);
      } else {
        $output = array();
        $cursor = 0;
        $length = strlen($string);
        foreach ($matches[0] as $match) {
          $offset = $match[1];
          if ($cursor != $offset) {
            $output[] = array(
              'text' => substr($string, $cursor, $offset),
              'highlight' => false,
            );
          }
          $output[] = array(
            'text' => $match[0],
            'highlight' => true,
          );
          $cursor = $offset + strlen($match[0]);
        }
        if ($cursor != $length) {
          $output[] = array(
            'text' => substr($string, $cursor),
            'highlight' => false,
          );
        }

        if ($output) {
          $output[0]['text'] =  ltrim($output[0]['text']);
        }

        foreach ($output as $key => $segment) {
          if ($segment['highlight']) {
            $output[$key] = phutil_tag('strong', array(), $segment['text']);
          } else {
            $output[$key] = $segment['text'];
          }
        }
      }

      $string = phutil_tag(
        'pre',
        array('class' => 'PhabricatorMonospaced phui-source-fragment'),
        $output);

      $path = Filesystem::readablePath($path, $drequest->getPath());

      $rows[] = array(
        phutil_tag('a', array('href' => $href), $path),
        $line,
        $string,
      );
    }

    $table = id(new AphrontTableView($rows))
      ->setClassName('remarkup-code')
      ->setHeaders(array(pht('Path'), pht('Line'), pht('String')))
      ->setColumnClasses(array('', 'n', 'wide'))
      ->setNoDataString(
        pht(
          'The pattern you searched for was not found in the content of any '.
          'files.'));

    return $table;
  }

  private function renderFindResults(array $results) {
    $drequest = $this->getDiffusionRequest();

    $rows = array();
    foreach ($results as $result) {
      $href = $drequest->generateURI(array(
        'action' => 'browse',
        'path' => $result,
      ));

      $readable = Filesystem::readablePath($result, $drequest->getPath());

      $rows[] = array(
        phutil_tag('a', array('href' => $href), $readable),
      );
    }

    $table = id(new AphrontTableView($rows))
      ->setHeaders(array(pht('Path')))
      ->setColumnClasses(array('wide'))
      ->setNoDataString(
        pht(
          'The pattern you searched for did not match the names of any '.
          'files.'));

    return $table;
  }

  private function loadLintMessages() {
    $drequest = $this->getDiffusionRequest();
    $branch = $drequest->loadBranch();

    if (!$branch || !$branch->getLintCommit()) {
      return;
    }

    $this->lintCommit = $branch->getLintCommit();

    $conn = id(new PhabricatorRepository())->establishConnection('r');

    $where = '';
    if ($drequest->getLint()) {
      $where = qsprintf(
        $conn,
        'AND code = %s',
        $drequest->getLint());
    }

    $this->lintMessages = queryfx_all(
      $conn,
      'SELECT * FROM %T WHERE branchID = %d %Q AND path = %s',
      PhabricatorRepository::TABLE_LINTMESSAGE,
      $branch->getID(),
      $where,
      '/'.$drequest->getPath());
  }

  private function buildCorpus(
    $show_blame,
    $show_color,
    $file_corpus,
    $needs_blame,
    DiffusionRequest $drequest,
    $path,
    $data) {

    $viewer = $this->getViewer();
    $blame_timeout = 15;
    $blame_failed = false;

    $highlight_limit = DifferentialChangesetParser::HIGHLIGHT_BYTE_LIMIT;
    $blame_limit = DifferentialChangesetParser::HIGHLIGHT_BYTE_LIMIT;
    $can_highlight = (strlen($file_corpus) <= $highlight_limit);
    $can_blame = (strlen($file_corpus) <= $blame_limit);

    if ($needs_blame && $can_blame) {
      $blame = $this->loadBlame($path, $drequest->getCommit(), $blame_timeout);
      list($blame_list, $blame_commits) = $blame;
      if ($blame_list === null) {
        $blame_failed = true;
        $blame_list = array();
      }
    } else {
      $blame_list = array();
      $blame_commits = array();
    }

    if (!$show_color) {
      $corpus = $this->renderPlaintextCorpus(
        $file_corpus,
        $blame_list,
        $blame_commits,
        $show_blame);
    } else {
      if ($can_highlight) {
        require_celerity_resource('syntax-highlighting-css');

        $highlighted = PhabricatorSyntaxHighlighter::highlightWithFilename(
          $path,
          $file_corpus);
        $lines = phutil_split_lines($highlighted);
      } else {
        $lines = phutil_split_lines($file_corpus);
      }

      $rows = $this->buildDisplayRows(
        $lines,
        $blame_list,
        $blame_commits,
        $show_blame,<commit_msg>(stable) Fix an XSS issue where Diffusion files exceeding the highlighting byte limit were not properly escaped<commit_after><?php

final class DiffusionBrowseController extends DiffusionController {

  private $lintCommit;
  private $lintMessages;
  private $coverage;

  public function shouldAllowPublic() {
    return true;
  }

  public function handleRequest(AphrontRequest $request) {
    $response = $this->loadDiffusionContext();
    if ($response) {
      return $response;
    }

    $drequest = $this->getDiffusionRequest();

    // Figure out if we're browsing a directory, a file, or a search result
    // list.

    $grep = $request->getStr('grep');
    $find = $request->getStr('find');
    if (strlen($grep) || strlen($find)) {
      return $this->browseSearch();
    }

    $pager = id(new PHUIPagerView())
      ->readFromRequest($request);

    $results = DiffusionBrowseResultSet::newFromConduit(
      $this->callConduitWithDiffusionRequest(
        'diffusion.browsequery',
        array(
          'path' => $drequest->getPath(),
          'commit' => $drequest->getStableCommit(),
          'offset' => $pager->getOffset(),
          'limit' => $pager->getPageSize() + 1,
        )));

    $reason = $results->getReasonForEmptyResultSet();
    $is_file = ($reason == DiffusionBrowseResultSet::REASON_IS_FILE);

    if ($is_file) {
      return $this->browseFile($results);
    } else {
      $paths = $results->getPaths();
      $paths = $pager->sliceResults($paths);
      $results->setPaths($paths);

      return $this->browseDirectory($results, $pager);
    }
  }

  private function browseSearch() {

    $drequest = $this->getDiffusionRequest();
    $header = $this->buildHeaderView($drequest);

    $search_form = $this->renderSearchForm();
    $search_results = $this->renderSearchResults();

    $search_form = id(new PHUIObjectBoxView())
      ->setHeaderText(pht('Search'))
      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
      ->appendChild($search_form);

    $crumbs = $this->buildCrumbs(
      array(
        'branch' => true,
        'path'   => true,
        'view'   => 'browse',
      ));
    $crumbs->setBorder(true);

    $view = id(new PHUITwoColumnView())
      ->setHeader($header)
      ->setFooter(array(
          $search_form,
          $search_results,
        ));

    return $this->newPage()
      ->setTitle(
        array(
          nonempty(basename($drequest->getPath()), '/'),
          $drequest->getRepository()->getDisplayName(),
        ))
      ->setCrumbs($crumbs)
      ->appendChild($view);
  }

  private function browseFile() {
    $viewer = $this->getViewer();
    $request = $this->getRequest();
    $drequest = $this->getDiffusionRequest();
    $repository = $drequest->getRepository();

    $before = $request->getStr('before');
    if ($before) {
      return $this->buildBeforeResponse($before);
    }

    $path = $drequest->getPath();

    $blame_key = PhabricatorDiffusionBlameSetting::SETTINGKEY;
    $color_key = PhabricatorDiffusionColorSetting::SETTINGKEY;

    $show_blame = $request->getBool(
      'blame',
      $viewer->getUserSetting($blame_key));

    $show_color = $request->getBool(
      'color',
      $viewer->getUserSetting($color_key));

    $view = $request->getStr('view');
    if ($request->isFormPost() && $view != 'raw' && $viewer->isLoggedIn()) {
      $preferences = PhabricatorUserPreferences::loadUserPreferences($viewer);

      $editor = id(new PhabricatorUserPreferencesEditor())
        ->setActor($viewer)
        ->setContentSourceFromRequest($request)
        ->setContinueOnNoEffect(true)
        ->setContinueOnMissingFields(true);

      $xactions = array();
      $xactions[] = $preferences->newTransaction($blame_key, $show_blame);
      $xactions[] = $preferences->newTransaction($color_key, $show_color);
      $editor->applyTransactions($preferences, $xactions);

      $uri = $request->getRequestURI()
        ->alter('blame', null)
        ->alter('color', null);

      return id(new AphrontRedirectResponse())->setURI($uri);
    }

    // We need the blame information if blame is on and we're building plain
    // text, or blame is on and this is an Ajax request. If blame is on and
    // this is a colorized request, we don't show blame at first (we ajax it
    // in afterward) so we don't need to query for it.
    $needs_blame = ($show_blame && !$show_color) ||
                   ($show_blame && $request->isAjax());

    $params = array(
      'commit' => $drequest->getCommit(),
      'path' => $drequest->getPath(),
    );

    $byte_limit = null;
    if ($view !== 'raw') {
      $byte_limit = PhabricatorFileStorageEngine::getChunkThreshold();
      $time_limit = 10;

      $params += array(
        'timeout' => $time_limit,
        'byteLimit' => $byte_limit,
      );
    }

    $response = $this->callConduitWithDiffusionRequest(
      'diffusion.filecontentquery',
      $params);

    $hit_byte_limit = $response['tooHuge'];
    $hit_time_limit = $response['tooSlow'];

    $file_phid = $response['filePHID'];
    if ($hit_byte_limit) {
      $corpus = $this->buildErrorCorpus(
        pht(
          'This file is larger than %s byte(s), and too large to display '.
          'in the web UI.',
          phutil_format_bytes($byte_limit)));
    } else if ($hit_time_limit) {
      $corpus = $this->buildErrorCorpus(
        pht(
          'This file took too long to load from the repository (more than '.
          '%s second(s)).',
          new PhutilNumber($time_limit)));
    } else {
      $file = id(new PhabricatorFileQuery())
        ->setViewer($viewer)
        ->withPHIDs(array($file_phid))
        ->executeOne();
      if (!$file) {
        throw new Exception(pht('Failed to load content file!'));
      }

      if ($view === 'raw') {
        return $file->getRedirectResponse();
      }

      $data = $file->loadFileData();

      $ref = $this->getGitLFSRef($repository, $data);
      if ($ref) {
        if ($view == 'git-lfs') {
          $file = $this->loadGitLFSFile($ref);

          // Rename the file locally so we generate a better vanity URI for
          // it. In storage, it just has a name like "lfs-13f9a94c0923...",
          // since we don't get any hints about possible human-readable names
          // at upload time.
          $basename = basename($drequest->getPath());
          $file->makeEphemeral();
          $file->setName($basename);

          return $file->getRedirectResponse();
        } else {
          $corpus = $this->buildGitLFSCorpus($ref);
        }
      } else if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) {
        $file_uri = $file->getBestURI();

        if ($file->isViewableImage()) {
          $corpus = $this->buildImageCorpus($file_uri);
        } else {
          $corpus = $this->buildBinaryCorpus($file_uri, $data);
        }
      } else {
        $this->loadLintMessages();
        $this->coverage = $drequest->loadCoverage();

        // Build the content of the file.
        $corpus = $this->buildCorpus(
          $show_blame,
          $show_color,
          $data,
          $needs_blame,
          $drequest,
          $path,
          $data);
      }
    }

    if ($request->isAjax()) {
      return id(new AphrontAjaxResponse())->setContent($corpus);
    }

    require_celerity_resource('diffusion-source-css');

    // Render the page.
    $view = $this->buildCurtain($drequest);
    $curtain = $this->enrichCurtain(
      $view,
      $drequest,
      $show_blame,
      $show_color);

    $properties = $this->buildPropertyView($drequest);
    $header = $this->buildHeaderView($drequest);
    $header->setHeaderIcon('fa-file-code-o');

    $content = array();

    $follow  = $request->getStr('follow');
    if ($follow) {
      $notice = new PHUIInfoView();
      $notice->setSeverity(PHUIInfoView::SEVERITY_WARNING);
      $notice->setTitle(pht('Unable to Continue'));
      switch ($follow) {
        case 'first':
          $notice->appendChild(
            pht(
              'Unable to continue tracing the history of this file because '.
              'this commit is the first commit in the repository.'));
          break;
        case 'created':
          $notice->appendChild(
            pht(
              'Unable to continue tracing the history of this file because '.
              'this commit created the file.'));
          break;
      }
      $content[] = $notice;
    }

    $renamed = $request->getStr('renamed');
    if ($renamed) {
      $notice = new PHUIInfoView();
      $notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
      $notice->setTitle(pht('File Renamed'));
      $notice->appendChild(
        pht(
          'File history passes through a rename from "%s" to "%s".',
          $drequest->getPath(),
          $renamed));
      $content[] = $notice;
    }

    $content[] = $corpus;
    $content[] = $this->buildOpenRevisions();

    $crumbs = $this->buildCrumbs(
      array(
        'branch' => true,
        'path'   => true,
        'view'   => 'browse',
      ));
    $crumbs->setBorder(true);

    $basename = basename($this->getDiffusionRequest()->getPath());

    $view = id(new PHUITwoColumnView())
      ->setHeader($header)
      ->setCurtain($curtain)
      ->setMainColumn(array(
        $content,
        ));

    if ($properties) {
      $view->addPropertySection(pht('Details'), $properties);
    }

    $title = array($basename, $repository->getDisplayName());

    return $this->newPage()
      ->setTitle($title)
      ->setCrumbs($crumbs)
      ->appendChild(
        array(
          $view,
      ));

  }

  public function browseDirectory(
    DiffusionBrowseResultSet $results,
    PHUIPagerView $pager) {

    $request = $this->getRequest();
    $drequest = $this->getDiffusionRequest();
    $repository = $drequest->getRepository();

    $reason = $results->getReasonForEmptyResultSet();

    $curtain = $this->buildCurtain($drequest);
    $details = $this->buildPropertyView($drequest);

    $header = $this->buildHeaderView($drequest);
    $header->setHeaderIcon('fa-folder-open');

    $search_form = $this->renderSearchForm();

    $empty_result = null;
    $browse_panel = null;
    if (!$results->isValidResults()) {
      $empty_result = new DiffusionEmptyResultView();
      $empty_result->setDiffusionRequest($drequest);
      $empty_result->setDiffusionBrowseResultSet($results);
      $empty_result->setView($request->getStr('view'));
    } else {
      $phids = array();
      foreach ($results->getPaths() as $result) {
        $data = $result->getLastCommitData();
        if ($data) {
          if ($data->getCommitDetail('authorPHID')) {
            $phids[$data->getCommitDetail('authorPHID')] = true;
          }
        }
      }

      $phids = array_keys($phids);
      $handles = $this->loadViewerHandles($phids);

      $browse_table = id(new DiffusionBrowseTableView())
        ->setDiffusionRequest($drequest)
        ->setHandles($handles)
        ->setPaths($results->getPaths())
        ->setUser($request->getUser());

      $browse_header = id(new PHUIHeaderView())
        ->setHeader(nonempty(basename($drequest->getPath()), '/'))
        ->setHeaderIcon('fa-folder-open');

      $browse_panel = id(new PHUIObjectBoxView())
        ->setHeader($browse_header)
        ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
        ->setTable($browse_table);

      $browse_panel->setShowHide(
        array(pht('Show Search')),
        pht('Hide Search'),
        $search_form,
        '#');
    }

    $open_revisions = $this->buildOpenRevisions();
    $readme = $this->renderDirectoryReadme($results);

    $crumbs = $this->buildCrumbs(
      array(
        'branch' => true,
        'path'   => true,
        'view'   => 'browse',
      ));

    $pager_box = $this->renderTablePagerBox($pager);
    $crumbs->setBorder(true);

    $view = id(new PHUITwoColumnView())
      ->setHeader($header)
      ->setCurtain($curtain)
      ->setMainColumn(array(
        $empty_result,
        $browse_panel,
      ))
      ->setFooter(array(
          $open_revisions,
          $readme,
          $pager_box,
      ));

    if ($details) {
      $view->addPropertySection(pht('Details'), $details);
    }

    return $this->newPage()
      ->setTitle(array(
          nonempty(basename($drequest->getPath()), '/'),
          $repository->getDisplayName(),
        ))
      ->setCrumbs($crumbs)
      ->appendChild(
        array(
          $view,
        ));
  }

  private function renderSearchResults() {
    $request = $this->getRequest();

    $drequest = $this->getDiffusionRequest();
    $repository = $drequest->getRepository();
    $results = array();

    $pager = id(new PHUIPagerView())
      ->readFromRequest($request);

    $search_mode = null;
    switch ($repository->getVersionControlSystem()) {
      case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
        $results = array();
        break;
      default:
        if (strlen($this->getRequest()->getStr('grep'))) {
          $search_mode = 'grep';
          $query_string = $request->getStr('grep');
          $results = $this->callConduitWithDiffusionRequest(
            'diffusion.searchquery',
            array(
              'grep' => $query_string,
              'commit' => $drequest->getStableCommit(),
              'path' => $drequest->getPath(),
              'limit' => $pager->getPageSize() + 1,
              'offset' => $pager->getOffset(),
            ));
        } else { // Filename search.
          $search_mode = 'find';
          $query_string = $request->getStr('find');
          $results = $this->callConduitWithDiffusionRequest(
            'diffusion.querypaths',
            array(
              'pattern' => $query_string,
              'commit' => $drequest->getStableCommit(),
              'path' => $drequest->getPath(),
              'limit' => $pager->getPageSize() + 1,
              'offset' => $pager->getOffset(),
            ));
        }
        break;
    }
    $results = $pager->sliceResults($results);

    if ($search_mode == 'grep') {
      $table = $this->renderGrepResults($results, $query_string);
      $header = pht(
        'File content matching "%s" under "%s"',
        $query_string,
        nonempty($drequest->getPath(), '/'));
    } else {
      $table = $this->renderFindResults($results);
      $header = pht(
        'Paths matching "%s" under "%s"',
        $query_string,
        nonempty($drequest->getPath(), '/'));
    }

    $box = id(new PHUIObjectBoxView())
      ->setHeaderText($header)
      ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
      ->setTable($table);

    $pager_box = $this->renderTablePagerBox($pager);

    return array($box, $pager_box);
  }

  private function renderGrepResults(array $results, $pattern) {
    $drequest = $this->getDiffusionRequest();

    require_celerity_resource('phabricator-search-results-css');

    $rows = array();
    foreach ($results as $result) {
      list($path, $line, $string) = $result;

      $href = $drequest->generateURI(array(
        'action' => 'browse',
        'path' => $path,
        'line' => $line,
      ));

      $matches = null;
      $count = @preg_match_all(
        '('.$pattern.')u',
        $string,
        $matches,
        PREG_OFFSET_CAPTURE);

      if (!$count) {
        $output = ltrim($string);
      } else {
        $output = array();
        $cursor = 0;
        $length = strlen($string);
        foreach ($matches[0] as $match) {
          $offset = $match[1];
          if ($cursor != $offset) {
            $output[] = array(
              'text' => substr($string, $cursor, $offset),
              'highlight' => false,
            );
          }
          $output[] = array(
            'text' => $match[0],
            'highlight' => true,
          );
          $cursor = $offset + strlen($match[0]);
        }
        if ($cursor != $length) {
          $output[] = array(
            'text' => substr($string, $cursor),
            'highlight' => false,
          );
        }

        if ($output) {
          $output[0]['text'] =  ltrim($output[0]['text']);
        }

        foreach ($output as $key => $segment) {
          if ($segment['highlight']) {
            $output[$key] = phutil_tag('strong', array(), $segment['text']);
          } else {
            $output[$key] = $segment['text'];
          }
        }
      }

      $string = phutil_tag(
        'pre',
        array('class' => 'PhabricatorMonospaced phui-source-fragment'),
        $output);

      $path = Filesystem::readablePath($path, $drequest->getPath());

      $rows[] = array(
        phutil_tag('a', array('href' => $href), $path),
        $line,
        $string,
      );
    }

    $table = id(new AphrontTableView($rows))
      ->setClassName('remarkup-code')
      ->setHeaders(array(pht('Path'), pht('Line'), pht('String')))
      ->setColumnClasses(array('', 'n', 'wide'))
      ->setNoDataString(
        pht(
          'The pattern you searched for was not found in the content of any '.
          'files.'));

    return $table;
  }

  private function renderFindResults(array $results) {
    $drequest = $this->getDiffusionRequest();

    $rows = array();
    foreach ($results as $result) {
      $href = $drequest->generateURI(array(
        'action' => 'browse',
        'path' => $result,
      ));

      $readable = Filesystem::readablePath($result, $drequest->getPath());

      $rows[] = array(
        phutil_tag('a', array('href' => $href), $readable),
      );
    }

    $table = id(new AphrontTableView($rows))
      ->setHeaders(array(pht('Path')))
      ->setColumnClasses(array('wide'))
      ->setNoDataString(
        pht(
          'The pattern you searched for did not match the names of any '.
          'files.'));

    return $table;
  }

  private function loadLintMessages() {
    $drequest = $this->getDiffusionRequest();
    $branch = $drequest->loadBranch();

    if (!$branch || !$branch->getLintCommit()) {
      return;
    }

    $this->lintCommit = $branch->getLintCommit();

    $conn = id(new PhabricatorRepository())->establishConnection('r');

    $where = '';
    if ($drequest->getLint()) {
      $where = qsprintf(
        $conn,
        'AND code = %s',
        $drequest->getLint());
    }

    $this->lintMessages = queryfx_all(
      $conn,
      'SELECT * FROM %T WHERE branchID = %d %Q AND path = %s',
      PhabricatorRepository::TABLE_LINTMESSAGE,
      $branch->getID(),
      $where,
      '/'.$drequest->getPath());
  }

  private function buildCorpus(
    $show_blame,
    $show_color,
    $file_corpus,
    $needs_blame,
    DiffusionRequest $drequest,
    $path,
    $data) {

    $viewer = $this->getViewer();
    $blame_timeout = 15;
    $blame_failed = false;

    $highlight_limit = DifferentialChangesetParser::HIGHLIGHT_BYTE_LIMIT;
    $blame_limit = DifferentialChangesetParser::HIGHLIGHT_BYTE_LIMIT;
    $can_highlight = (strlen($file_corpus) <= $highlight_limit);
    $can_blame = (strlen($file_corpus) <= $blame_limit);

    if ($needs_blame && $can_blame) {
      $blame = $this->loadBlame($path, $drequest->getCommit(), $blame_timeout);
      list($blame_list, $blame_commits) = $blame;
      if ($blame_list === null) {
        $blame_failed = true;
        $blame_list = array();
      }
    } else {
      $blame_list = array();
      $blame_commits = array();
    }

    if (!$show_color) {
      $corpus = $this->renderPlaintextCorpus(
        $file_corpus,
        $blame_list,
        $blame_commits,
        $show_blame);
    } else {
      require_celerity_resource('syntax-highlighting-css');

      if (!$can_highlight) {
        $highlighted = PhabricatorSyntaxHighlighter::highlightWithFilename(
          $path,
          $file_corpus);
      } else {
        // Highlight as plain text to escape the content properly.
        $highlighted = PhabricatorSyntaxHighlighter::highlightWithLanguage(
          'txt',
          $file_corpus);
      }

      $lines = phutil_split_lines($highlighted);

      $rows = $this->buildDisplayRows(
        $lines,
        $blame_list,
        $blame_commits,
        $show_blame,
{'fraction_non_alphanumeric': 0.15925519716835335, 'fraction_numerical': 0.0011466174784386062, 'mean_word_length': 2.4234149671473677, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 531, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1694421', 'n_tokens_mistral': 12702, 'n_tokens_neox': 12369, 'n_words': 2280}