Index: lib/Format/FormatTokenLexer.h =================================================================== --- lib/Format/FormatTokenLexer.h +++ lib/Format/FormatTokenLexer.h @@ -55,6 +55,8 @@ void tryParseTemplateString(); + void tryParseShebangLine(); + bool tryMerge_TMacro(); bool tryMergeConflictMarkers(); Index: lib/Format/FormatTokenLexer.cpp =================================================================== --- lib/Format/FormatTokenLexer.cpp +++ lib/Format/FormatTokenLexer.cpp @@ -20,6 +20,10 @@ #include "clang/Format/Format.h" #include "llvm/Support/Regex.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "format-formatter" + namespace clang { namespace format { @@ -50,8 +54,10 @@ if (Style.Language == FormatStyle::LK_JavaScript) { tryParseJSRegexLiteral(); tryParseTemplateString(); + tryParseShebangLine(); } tryMergePreviousTokens(); + if (Tokens.back()->NewlinesBefore > 0 || Tokens.back()->IsMultiline) FirstInLineIndex = Tokens.size() - 1; } while (Tokens.back()->Tok.isNot(tok::eof)); @@ -265,6 +271,31 @@ resetLexer(SourceMgr.getFileOffset(Lex->getSourceLocation(Offset + 1))); } +void FormatTokenLexer::tryParseShebangLine() { + if (Tokens.size() > 1) + // Must be the first token of the file. + return; + FormatToken *HashToken = Tokens.back(); + if (!HashToken->is(tok::hash) || HashToken->TokenText != "#") + return; + + // 'Manually' lex ahead in the current file buffer. + const char *Offset = Lex->getBufferLocation(); + const char *ShebangBegin = Offset - HashToken->TokenText.size(); // at "#" + for (; Offset != Lex->getBuffer().end() && *Offset != '\n';) + ++Offset; + + // ShebangText does *not* include the terminating '\n' so that the comment + // gets properly terminated. + StringRef ShebangText(ShebangBegin, Offset - ShebangBegin); + HashToken->Type = TT_Unknown; + HashToken->Tok.setKind(tok::comment); + HashToken->TokenText = ShebangText; + HashToken->ColumnWidth = ShebangText.size(); + + resetLexer(SourceMgr.getFileOffset(Lex->getSourceLocation(Offset))); +} + bool FormatTokenLexer::tryMerge_TMacro() { if (Tokens.size() < 4) return false; Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -1272,5 +1272,12 @@ verifyFormat("var x = 'foo';", LeaveQuotes); } +TEST_F(FormatTestJS, SupportShebangLines) { + verifyFormat("#!/usr/bin/env node\n" + "var x = hello();", + "#!/usr/bin/env node\n" + "var x = hello();"); +} + } // end namespace tooling } // end namespace clang