//===--- Trivia.cpp - Swift Syntax Trivia Implementation ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/Syntax/RawSyntax.h"
#include "swift/Syntax/Trivia.h"

using namespace swift;
using namespace swift::syntax;

namespace {
void printRepeated(llvm::raw_ostream &OS, char c, size_t Count) {
  for (decltype(Count) i = 0; i < Count; ++i)
    OS << c;
}

void escapeNewlines(std::string &S) {
  size_t Index = 0;
  while (true) {
    Index = S.find("\n", Index);
    if (Index == std::string::npos)
      break;
    S.erase(Index, 1);
    S.insert(Index, "\\n");
    Index += 3;
  }
}
} // end anonymous namespace

void TriviaPiece::dump(llvm::raw_ostream &OS, unsigned Indent) const {
  printRepeated(OS, ' ', Indent);
  OS << "(trivia ";

  switch (Kind) {
  case TriviaKind::Space:
    OS << "space " << Count;
    break;
  case TriviaKind::Tab:
    OS << "tab " << Count;
    break;
  case TriviaKind::VerticalTab:
    OS << "vertical_tab " << Count;
    break;
  case TriviaKind::Formfeed:
    OS << "form_feed " << Count;
    break;
  case TriviaKind::Newline:
    OS << "newline " << Count;
    break;
  case TriviaKind::LineComment:
    OS << "line_comment" << Text.str();
    break;
  case TriviaKind::BlockComment: {
    // Make this fit a little more nicely with indentation
    // and the lispy nature of the dump by escaping the newlines.
    auto s = Text.str().str();
    escapeNewlines(s);
    OS << "block_comment" << Text.str();
    break;
  }
  case TriviaKind::DocLineComment:
    OS << "doc_line_comment" << Text.str();
    break;
  case TriviaKind::DocBlockComment: {
    // Make this fit a little more nicely with indentation
    // and the lispy nature of the dump by escaping the newlines.
    auto s = Text.str().str();
    escapeNewlines(s);
    OS << "doc_block_comment" << Text.str();
    break;
  }
  case TriviaKind::Backtick:
    OS << "backtick " << Count;
    break;
  }
  OS << ')';
}

void TriviaPiece::accumulateAbsolutePosition(AbsolutePosition &Pos) const {
  switch (Kind) {
  case TriviaKind::LineComment:
  case TriviaKind::BlockComment:
  case TriviaKind::DocBlockComment:
  case TriviaKind::DocLineComment:
    Pos.addText(Text.str());
    break;
  case TriviaKind::Newline:
    Pos.addNewlines(Count);
    break;
  case TriviaKind::Space:
  case TriviaKind::Backtick:
  case TriviaKind::Tab:
  case TriviaKind::VerticalTab:
  case TriviaKind::Formfeed:
    Pos.addColumns(Count);
    break;
  }
}

void TriviaPiece::print(llvm::raw_ostream &OS) const {
  switch (Kind) {
  case TriviaKind::Space:
    printRepeated(OS, ' ', Count);
    break;
  case TriviaKind::Tab:
    printRepeated(OS, '\t', Count);
    break;
  case TriviaKind::VerticalTab:
    printRepeated(OS, '\v', Count);
    break;
  case TriviaKind::Formfeed:
    printRepeated(OS, '\f', Count);
    break;
  case TriviaKind::Newline:
    printRepeated(OS, '\n', Count);
    break;
  case TriviaKind::LineComment:
  case TriviaKind::BlockComment:
  case TriviaKind::DocLineComment:
  case TriviaKind::DocBlockComment:
    OS << Text.str();
    break;
  case TriviaKind::Backtick:
    printRepeated(OS, '`', Count);
    break;
  }
}

#pragma mark - Trivia collection

Trivia Trivia::appending(const Trivia &Other) const {
  auto NewPieces = Pieces;
  std::copy(Other.begin(), Other.end(), std::back_inserter(NewPieces));
  return { NewPieces };
}

void Trivia::dump(llvm::raw_ostream &OS, unsigned Indent) const {
  for (const auto &Piece : Pieces) {
    Piece.dump(OS, Indent);
  }
}

void Trivia::dump() const {
  dump(llvm::errs());
}

void Trivia::print(llvm::raw_ostream &OS) const {
  for (const auto &Piece : Pieces) {
    Piece.print(OS);
  }
}

TriviaList::const_iterator Trivia::find(const TriviaKind DesiredKind) const {
  return std::find_if(Pieces.begin(), Pieces.end(),
                      [=](const TriviaPiece &Piece) -> bool {
                        return Piece.Kind == DesiredKind;
                      });
}

Trivia Trivia::operator+(const Trivia &Other) const {
  auto NewPieces = Pieces;
  std::copy(Other.Pieces.begin(), Other.Pieces.end(),
            std::back_inserter(NewPieces));
  return { NewPieces };
}
