Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -145,7 +145,7 @@ /// \brief Whether the token text contains newlines (escaped or not). bool IsMultiline = false; - /// \brief Indicates that this is the first token. + /// \brief Indicates that this is the first token of the file. bool IsFirst = false; /// \brief Whether there must be a line break before this token. Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -690,10 +690,24 @@ } LineType parsePreprocessorDirective() { + bool IsFirstToken = CurrentToken->IsFirst; LineType Type = LT_PreprocessorDirective; next(); if (!CurrentToken) return Type; + + if (Style.Language == FormatStyle::LK_JavaScript && IsFirstToken) { + // JavaScript files can contain shebang lines of the form: + // #!/usr/bin/env node + // Treat these like C++ #include directives. + while (CurrentToken) { + // Tokens cannot be comments here. + CurrentToken->Type = TT_ImplicitStringLiteral; + next(); + } + return LT_ImportStatement; + } + if (CurrentToken->Tok.is(tok::numeric_constant)) { CurrentToken->SpacesRequiredBefore = 1; return Type; 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