//===--- iwyu_lexer_utils.cc - clang-lexer utilities for iwyu -------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "iwyu_lexer_utils.h"
#include "iwyu_globals.h"
#include "iwyu_port.h"  // for CHECK_

#include <string>

#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Token.h"

using clang::SourceLocation;
using clang::SourceManager;
using clang::Token;
using llvm::StringRef;
using std::string;

namespace include_what_you_use {

bool LineHasText(SourceLocation source_location, StringRef text) {
  const StringRef data =
      GetSourceTextUntilEndOfLine(source_location, DefaultDataGetter());
  return data.find(text) != StringRef::npos;
}

// SourceManagerCharacterDataGetter method implementations.
SourceManagerCharacterDataGetter::SourceManagerCharacterDataGetter(
    const SourceManager& source_manager)
    : source_manager_(source_manager) {
}

const char* SourceManagerCharacterDataGetter::GetCharacterData(
    SourceLocation loc) const {
  bool invalid;
  const char* data = source_manager_.getCharacterData(loc, &invalid);
  CHECK_(!invalid);
  CHECK_(data);
  return data;
}

StringRef GetSourceTextUntilEndOfLine(
    SourceLocation start_loc, const CharacterDataGetterInterface& data_getter) {
  const char* data = data_getter.GetCharacterData(start_loc);
  const char* line_end = strchr(data, '\n');
  if (!line_end)
    return data;
  return StringRef(data, line_end - data);
}

SourceLocation GetLocationAfter(
    SourceLocation start_loc, const string& needle,
    const CharacterDataGetterInterface& data_getter) {
  CHECK_(start_loc.isValid() && "GetLocationAfter takes only valid locations");
  const char* data = data_getter.GetCharacterData(start_loc);
  const char* needle_loc = strstr(data, needle.c_str());
  if (!needle_loc)
    return SourceLocation();   // invalid source location
  return start_loc.getLocWithOffset(needle_loc - data + needle.length());
}

string GetIncludeNameAsWritten(
    SourceLocation include_loc,
    const CharacterDataGetterInterface& data_getter) {
  const string data = GetSourceTextUntilEndOfLine(include_loc, data_getter).str();
  if (data.empty())
    return data;
  string::size_type endpos = string::npos;
  if (data[0] == '<') {
    endpos = data.find('>', 1);
  } else if (data[0] == '"') {
    endpos = data.find('"', 1);
  } else {
    CHECK_UNREACHABLE_("Unexpected token being #included");
  }
  CHECK_(endpos != string::npos && "No end-character found for #include");
  return data.substr(0, endpos+1);
}

// Get the text of a given token.
string GetTokenText(const Token& token,
                    const CharacterDataGetterInterface& data_getter) {
  const char* text = data_getter.GetCharacterData(token.getLocation());
  return string(text, token.getLength());
}

}  // namespace include_what_you_use
