mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Here I'm trying to add some simple formatting based on the token kind, aiming mainly to keep the implementation short for now. This approach won't generalize to arbitrary structures (e.g., it doesn't discern between braces for a function body and a struct literal). I think we'll probably want to build a parse tree and associate parse kinds with tokens in order to format, additionally doing something less linear. But my essential goal at present is to just get a proof-of-concept that the basics can yield something that looks okay.
111 lines
2.9 KiB
C++
111 lines
2.9 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/format/formatter.h"
|
|
|
|
namespace Carbon::Format {
|
|
|
|
auto Formatter::Run() -> bool {
|
|
if (tokens_->has_errors()) {
|
|
// TODO: Error recovery.
|
|
return false;
|
|
}
|
|
|
|
auto comments = tokens_->comments();
|
|
auto comment_it = comments.begin();
|
|
|
|
// If there are no tokens or comments, format as empty.
|
|
if (tokens_->size() == 0 && comment_it == comments.end()) {
|
|
*out_ << "\n";
|
|
return true;
|
|
}
|
|
|
|
for (auto token : tokens_->tokens()) {
|
|
auto token_kind = tokens_->GetKind(token);
|
|
|
|
while (comment_it != comments.end() &&
|
|
tokens_->IsAfterComment(token, *comment_it)) {
|
|
RequireEmptyLine();
|
|
PrepareForSpacedContent();
|
|
// TODO: We do need to adjust the indent of multi-line comments.
|
|
*out_ << tokens_->GetCommentText(*comment_it);
|
|
// Comment text includes a terminating newline, so just update the state.
|
|
line_state_ = LineState::Empty;
|
|
++comment_it;
|
|
}
|
|
|
|
switch (token_kind) {
|
|
case Lex::TokenKind::FileStart:
|
|
break;
|
|
|
|
case Lex::TokenKind::FileEnd:
|
|
RequireEmptyLine();
|
|
break;
|
|
|
|
case Lex::TokenKind::OpenCurlyBrace:
|
|
PrepareForSpacedContent();
|
|
*out_ << "{";
|
|
// Check for `{}`.
|
|
if (NextToken(token) != tokens_->GetMatchedClosingToken(token)) {
|
|
RequireEmptyLine();
|
|
}
|
|
indent_ += 2;
|
|
break;
|
|
|
|
case Lex::TokenKind::CloseCurlyBrace:
|
|
indent_ -= 2;
|
|
PrepareForPackedContent();
|
|
*out_ << "}";
|
|
RequireEmptyLine();
|
|
break;
|
|
|
|
case Lex::TokenKind::Semi:
|
|
PrepareForPackedContent();
|
|
*out_ << ";";
|
|
RequireEmptyLine();
|
|
break;
|
|
|
|
default:
|
|
if (token_kind.IsOneOf(
|
|
{Lex::TokenKind::CloseParen, Lex::TokenKind::Colon,
|
|
Lex::TokenKind::ColonExclaim, Lex::TokenKind::Comma})) {
|
|
PrepareForPackedContent();
|
|
} else {
|
|
PrepareForSpacedContent();
|
|
}
|
|
*out_ << tokens_->GetTokenText(token);
|
|
line_state_ = token_kind.is_opening_symbol()
|
|
? LineState::HasSeparator
|
|
: LineState::NeedsSeparator;
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
auto Formatter::PrepareForPackedContent() -> void {
|
|
if (line_state_ == LineState::Empty) {
|
|
out_->indent(indent_);
|
|
line_state_ = LineState::HasSeparator;
|
|
}
|
|
}
|
|
|
|
auto Formatter::RequireEmptyLine() -> void {
|
|
if (line_state_ != LineState::Empty) {
|
|
*out_ << "\n";
|
|
line_state_ = LineState::Empty;
|
|
}
|
|
}
|
|
|
|
auto Formatter::PrepareForSpacedContent() -> void {
|
|
if (line_state_ == LineState::NeedsSeparator) {
|
|
*out_ << " ";
|
|
line_state_ = LineState::HasSeparator;
|
|
} else {
|
|
PrepareForPackedContent();
|
|
}
|
|
}
|
|
|
|
} // namespace Carbon::Format
|