158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// found in the LICENSE file.
458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "extensions/browser/file_highlighter.h"
658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include <stack>
858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/strings/utf_string_conversions.h"
1058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "base/values.h"
1158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)namespace extensions {
1358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)namespace {
1558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Keys for a highlighted dictionary.
1758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)const char kBeforeHighlightKey[] = "beforeHighlight";
1858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)const char kHighlightKey[] = "highlight";
1958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)const char kAfterHighlightKey[] = "afterHighlight";
2058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Increment |index| to the position of the next quote ('"') in |str|, skipping
2258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// over any escaped quotes. If no next quote is found, |index| is set to
2358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// std::string::npos. Assumes |index| currently points to a quote.
2458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void QuoteIncrement(const std::string& str, size_t* index) {
2558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  size_t i = *index + 1;  // Skip over the first quote.
2658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  bool found = false;
2758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  while (!found && i < str.size()) {
2858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (str[i] == '\\')
2958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      i += 2;  // if we find an escaped character, skip it.
3058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    else if (str[i] == '"')
3158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      found = true;
3258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    else
3358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      ++i;
3458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
3558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  *index = found ? i : std::string::npos;
3658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
3758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
3858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Increment |index| by one if the next character is not a comment. Increment
3958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// index until the end of the comment if it is a comment.
4058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void CommentSafeIncrement(const std::string& str, size_t* index) {
4158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  size_t i = *index;
4258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (str[i] == '/' && i + 1 < str.size()) {
4358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // Eat a single-line comment.
4458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (str[i + 1] == '/') {
4558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      i += 2;  // Eat the '//'.
4658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      while (i < str.size() && str[i] != '\n' && str[i] != '\r')
4758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        ++i;
4858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    } else if (str[i + 1] == '*') {  // Eat a multi-line comment.
4958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      i += 3;  // Advance to the first possible comment end.
5058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      while (i < str.size() && !(str[i - 1] == '*' && str[i] == '/'))
5158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        ++i;
5258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    }
5358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
5458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  *index = i + 1;
5558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
5658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
5758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Increment index until the end of the current "chunk"; a "chunk" is a JSON-
5858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// style list, object, or string literal, without exceeding |end|. Assumes
5958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// |index| currently points to a chunk's starting character ('{', '[', or '"').
6058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void ChunkIncrement(const std::string& str, size_t* index, size_t end) {
6158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  char c = str[*index];
6258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::stack<char> stack;
6358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  do {
6458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (c == '"')
6558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      QuoteIncrement(str, index);
6658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    else if (c == '[')
6758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      stack.push(']');
6858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    else if (c == '{')
6958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      stack.push('}');
7058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    else if (!stack.empty() && c == stack.top())
7158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      stack.pop();
7258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    CommentSafeIncrement(str, index);
7358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    c = str[*index];
7458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  } while (!stack.empty() && *index < end);
7558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
7658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
7758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}  // namespace
7858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
7958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)FileHighlighter::FileHighlighter(const std::string& contents)
8058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    : contents_(contents), start_(0u), end_(contents_.size()) {
8158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
8258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
8358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)FileHighlighter::~FileHighlighter() {
8458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
8558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
8658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)std::string FileHighlighter::GetBeforeFeature() const {
8758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return contents_.substr(0, start_);
8858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
8958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
9058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)std::string FileHighlighter::GetFeature() const {
9158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return contents_.substr(start_, end_ - start_);
9258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
9358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
9458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)std::string FileHighlighter::GetAfterFeature() const {
9558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return contents_.substr(end_);
9658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
9758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
9858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void FileHighlighter::SetHighlightedRegions(base::DictionaryValue* dict) const {
9958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::string before_feature = GetBeforeFeature();
10058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!before_feature.empty())
10158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    dict->SetString(kBeforeHighlightKey, base::UTF8ToUTF16(before_feature));
10258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
10358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::string feature = GetFeature();
10458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!feature.empty())
10558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    dict->SetString(kHighlightKey, base::UTF8ToUTF16(feature));
10658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
10758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::string after_feature = GetAfterFeature();
10858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!after_feature.empty())
10958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    dict->SetString(kAfterHighlightKey, base::UTF8ToUTF16(after_feature));
11058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
11158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
11258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)ManifestHighlighter::ManifestHighlighter(const std::string& manifest,
11358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                         const std::string& key,
11458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                         const std::string& specific)
11558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    : FileHighlighter(manifest) {
11658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  start_ = contents_.find('{') + 1;
11758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  end_ = contents_.rfind('}');
11858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  Parse(key, specific);
11958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
12058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
12158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)ManifestHighlighter::~ManifestHighlighter() {
12258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
12358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
12458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
12558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void ManifestHighlighter::Parse(const std::string& key,
12658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                const std::string& specific) {
12758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // First, try to find the bounds of the full key.
12858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (FindBounds(key, true) /* enforce at top level */ ) {
12958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // If we succeed, and we have a specific location, find the bounds of the
13058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // specific.
13158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (!specific.empty())
13258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      FindBounds(specific, false /* don't enforce at top level */ );
13358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
13458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // We may have found trailing whitespace. Don't use base::TrimWhitespace,
13558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // because we want to keep any whitespace we find - just not highlight it.
13658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    size_t trim = contents_.find_last_not_of(" \t\n\r", end_ - 1);
13758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (trim < end_ && trim > start_)
13858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      end_ = trim + 1;
13958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  } else {
14058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // If we fail, then we set start to end so that the highlighted portion is
14158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // empty.
14258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    start_ = end_;
14358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
14458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
14558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
14658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)bool ManifestHighlighter::FindBounds(const std::string& feature,
14758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                     bool enforce_at_top_level) {
14858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  char c = '\0';
14958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  while (start_ < end_) {
15058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    c = contents_[start_];
15158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (c == '"') {
15258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      // The feature may be quoted.
15358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      size_t quote_end = start_;
15458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      QuoteIncrement(contents_, &quote_end);
15558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      if (contents_.substr(start_ + 1, quote_end - 1 - start_) == feature) {
15658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        FindBoundsEnd(feature, quote_end + 1);
15758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        return true;
15858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      } else {
15958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        // If it's not the feature, then we can skip the quoted section.
16058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        start_ = quote_end + 1;
16158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      }
16258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    } else if (contents_.substr(start_, feature.size()) == feature) {
16358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        FindBoundsEnd(feature, start_ + feature.size() + 1);
16458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        return true;
16558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    } else if (enforce_at_top_level && (c == '{' || c == '[')) {
16658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      // If we don't have to be at the top level, then we can skip any chunks
16758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      // we find.
16858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      ChunkIncrement(contents_, &start_, end_);
16958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    } else {
17058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      CommentSafeIncrement(contents_, &start_);
17158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    }
17258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
17358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return false;
17458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
17558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
17658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void ManifestHighlighter::FindBoundsEnd(const std::string& feature,
17758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                        size_t local_start) {
17858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  char c = '\0';
17958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  while (local_start < end_) {
18058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    c = contents_[local_start];
18158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // We're done when we find a terminating character (i.e., either a comma or
18258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // an ending bracket.
18358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (c == ',' || c == '}' || c == ']') {
18458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      end_ = local_start;
18558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      return;
18658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    }
18758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // We can skip any chunks we find, since we are looking for the end of the
18858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    // current feature, and don't want to go any deeper.
18958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (c == '"' || c == '{' || c == '[')
19058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      ChunkIncrement(contents_, &local_start, end_);
19158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    else
19258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      CommentSafeIncrement(contents_, &local_start);
19358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
19458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
19558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
19658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)SourceHighlighter::SourceHighlighter(const std::string& contents,
19758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                     size_t line_number)
19858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    : FileHighlighter(contents) {
19958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  Parse(line_number);
20058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
20158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
20258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)SourceHighlighter::~SourceHighlighter() {
20358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
20458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
20558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void SourceHighlighter::Parse(size_t line_number) {
20658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // If line 0 is requested, highlight nothing.
20758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (line_number == 0) {
20858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    start_ = contents_.size();
20958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return;
21058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
21158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
21258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  for (size_t i = 1; i < line_number; ++i) {
21358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    start_ = contents_.find('\n', start_);
21458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (start_ == std::string::npos)
21558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      break;
21658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    start_ += 1;
21758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
21858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
21958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  end_ = contents_.find('\n', start_);
22058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
22158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // If we went off the end of the string (i.e., the line number was invalid),
22258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // then move start and end to the end of the string, so that the highlighted
22358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // portion is empty.
22458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  start_ = start_ == std::string::npos ? contents_.size() : start_;
22558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  end_ = end_ == std::string::npos ? contents_.size() : end_;
22658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
22758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
22858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}  // namespace extensions
229