[PATCH] D145435: [clang-format] Choose style (file) from within code for use in IDEs

2023-04-12 Thread bers via Phabricator via cfe-commits
bersbersbers added a comment.

Alright, thanks for your opinions. I will probably maintain this feature in a 
private working copy and continue to advertise it (compare, e.g., 
https://github.com/llvm/llvm-project/issues/60917 and 
https://stackoverflow.com/q/20540017), hoping to convince more people that this 
feature should be implemented publicly :) Until then, thank you all for your 
consideration!


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D145435/new/

https://reviews.llvm.org/D145435

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D145435: Choose style (file) from within code for use in IDEs

2023-03-19 Thread bers via Phabricator via cfe-commits
bersbersbers added a comment.

That remaining failing unit test (`Flang.Driver::code-gen-rv64.f90`) appears to 
be independent of my diff, as it appears in other pipelines as well, compare 
https://buildkite.com/llvm-project/premerge-checks/builds/141883#0186f8c7-4ac2-43c4-a83d-d101ed0ebf65/6-13797


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D145435/new/

https://reviews.llvm.org/D145435

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D145435: Choose style (file) from within code for use in IDEs

2023-03-19 Thread bers via Phabricator via cfe-commits
bersbersbers updated this revision to Diff 506357.
bersbersbers added a comment.

Fix unit tests, add unit tests


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D145435/new/

https://reviews.llvm.org/D145435

Files:
  clang/docs/ClangFormatStyleOptions.rst
  clang/include/clang/Format/Format.h
  clang/lib/Format/BreakableToken.cpp
  clang/lib/Format/BreakableToken.h
  clang/lib/Format/ContinuationIndenter.cpp
  clang/lib/Format/Format.cpp
  clang/lib/Format/UnwrappedLineParser.cpp
  clang/unittests/Format/ConfigParseTest.cpp

Index: clang/unittests/Format/ConfigParseTest.cpp
===
--- clang/unittests/Format/ConfigParseTest.cpp
+++ clang/unittests/Format/ConfigParseTest.cpp
@@ -1262,6 +1262,22 @@
 "none", "", );
   ASSERT_TRUE(static_cast(Style9));
   ASSERT_EQ(*Style9, SubSubStyle);
+
+  // Test 10.1: use style from style comment in code file.
+  ASSERT_TRUE(
+  FS.addFile("comment.cpp", 0, llvm::MemoryBuffer::getMemBuffer("")));
+  auto Style10 = getStyle("file", "comment.cpp", "LLVM",
+  "   // clang-format style=Google", );
+  ASSERT_TRUE((bool)Style10);
+  ASSERT_EQ(*Style10, getGoogleStyle());
+
+  // Test 10.2: don't use commented style comment.
+  ASSERT_TRUE(
+  FS.addFile("comment.cpp", 0, llvm::MemoryBuffer::getMemBuffer("")));
+  Style10 = getStyle("file", "comment.cpp", "LLVM",
+ "// // clang-format style=Google", );
+  ASSERT_TRUE((bool)Style10);
+  ASSERT_EQ(*Style10, getLLVMStyle());
 }
 
 TEST(ConfigParseTest, GetStyleOfSpecificFile) {
Index: clang/lib/Format/UnwrappedLineParser.cpp
===
--- clang/lib/Format/UnwrappedLineParser.cpp
+++ clang/lib/Format/UnwrappedLineParser.cpp
@@ -4292,6 +4292,10 @@
   if (CommentPragmasRegex.match(IndentContent))
 return false;
 
+  // If comment is a style comment, treat as separate section.
+  if (IndentContent.ltrim().startswith(clang::format::StyleComment))
+return false;
+
   // If Line starts with a line comment, then FormatTok continues the comment
   // section if its original column is greater or equal to the original start
   // column of the line.
Index: clang/lib/Format/Format.cpp
===
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -3741,6 +3741,29 @@
   if (!getPredefinedStyle(FallbackStyleName, Style.Language, ))
 return make_string_error("Invalid fallback style \"" + FallbackStyleName);
 
+  if (StyleName == "file") {
+// Read style from first style comment at start of line in the code.
+std::string Prefix{std::string("// ") + StyleComment};
+size_t PrefixPos = StringRef::npos;
+while ((PrefixPos = Code.find(Prefix, PrefixPos + 1)) != StringRef::npos) {
+  // For each prefix, locate respective start of line (SoL) and check text
+  // between prefix and start of line for non-whitespace (comments not at
+  // start of line up to whitespace may be quoted, commented out, ...).
+  if (const size_t SoL = Code.substr(0, PrefixPos).find_last_of("\r\n") + 1;
+  !Code.substr(SoL, PrefixPos - SoL).ltrim().empty()) {
+continue;
+  }
+
+  // Use remainder of line as `StyleName` as if passed by `-style=...`;
+  // if no end of line (EoL) found, use remainder of file.
+  const size_t StylePos = PrefixPos + Prefix.size();
+  const size_t EoL = Code.find_first_of("\r\n", StylePos);
+  const size_t StyleLen = EoL - (EoL == StringRef::npos ? 0 : StylePos);
+  StyleName = Code.substr(StylePos, StyleLen);
+  break;
+}
+  }
+
   llvm::SmallVector, 1>
   ChildFormatTextToApply;
 
Index: clang/lib/Format/ContinuationIndenter.cpp
===
--- clang/lib/Format/ContinuationIndenter.cpp
+++ clang/lib/Format/ContinuationIndenter.cpp
@@ -2192,7 +2192,7 @@
 }();
 if (!Style.ReflowComments ||
 CommentPragmasRegex.match(Current.TokenText.substr(2)) ||
-switchesFormatting(Current) || !RegularComments) {
+setsStyle(Current) || switchesFormatting(Current) || !RegularComments) {
   return nullptr;
 }
 return std::make_unique(
Index: clang/lib/Format/BreakableToken.h
===
--- clang/lib/Format/BreakableToken.h
+++ clang/lib/Format/BreakableToken.h
@@ -27,6 +27,10 @@
 namespace clang {
 namespace format {
 
+/// Checks if \p Token sets style, basically, // clang-format style=
+/// \p Token must be a line comment.
+bool setsStyle(const FormatToken );
+
 /// Checks if \p Token switches formatting, like /* clang-format off */.
 /// \p Token must be a comment.
 bool switchesFormatting(const FormatToken );
Index: clang/lib/Format/BreakableToken.cpp
===
--- 

[PATCH] D145435: Choose style (file) from within code for use in IDEs

2023-03-18 Thread bers via Phabricator via cfe-commits
bersbersbers updated this revision to Diff 506336.
bersbersbers added a comment.

Rebased patch against main branch (previous patch was against 15.0.7 tag)


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D145435/new/

https://reviews.llvm.org/D145435

Files:
  clang/docs/ClangFormatStyleOptions.rst
  clang/include/clang/Format/Format.h
  clang/lib/Format/BreakableToken.cpp
  clang/lib/Format/BreakableToken.h
  clang/lib/Format/ContinuationIndenter.cpp
  clang/lib/Format/Format.cpp
  clang/lib/Format/UnwrappedLineParser.cpp

Index: clang/lib/Format/UnwrappedLineParser.cpp
===
--- clang/lib/Format/UnwrappedLineParser.cpp
+++ clang/lib/Format/UnwrappedLineParser.cpp
@@ -4292,6 +4292,10 @@
   if (CommentPragmasRegex.match(IndentContent))
 return false;
 
+  // If comment is a style comment, treat as separate section.
+  if (IndentContent.ltrim().startswith(clang::format::StyleComment))
+return false;
+
   // If Line starts with a line comment, then FormatTok continues the comment
   // section if its original column is greater or equal to the original start
   // column of the line.
Index: clang/lib/Format/Format.cpp
===
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -3741,6 +3741,29 @@
   if (!getPredefinedStyle(FallbackStyleName, Style.Language, ))
 return make_string_error("Invalid fallback style \"" + FallbackStyleName);
 
+  if (StyleName == "file") {
+// Read style from first style comment at start of line in the code.
+std::string Prefix{std::string("// ") + StyleComment};
+size_t PrefixPos = StringRef::npos;
+while ((PrefixPos = Code.find(Prefix, PrefixPos + 1)) != StringRef::npos) {
+  // For each prefix, locate respective start of line (SoL) and check text
+  // between prefix and start of line for non-whitespace (comments not at
+  // start of line up to whitespace may be quoted, commented out, ...).
+  if (const size_t SoL = Code.substr(0, PrefixPos).find_last_of("\r\n") + 1;
+  !Code.substr(SoL, PrefixPos - SoL).ltrim().empty()) {
+continue;
+  }
+
+  // Use remainder of line as `StyleName` as if passed by `-style=...`;
+  // if no end of line (EoL) found, use remainder of file.
+  const size_t StylePos = PrefixPos + Prefix.size();
+  const size_t EoL = Code.find_first_of("\r\n", StylePos);
+  const size_t StyleLen = EoL - (EoL == StringRef::npos ? 0 : StylePos);
+  StyleName = Code.substr(StylePos, StyleLen);
+  break;
+}
+  }
+
   llvm::SmallVector, 1>
   ChildFormatTextToApply;
 
Index: clang/lib/Format/ContinuationIndenter.cpp
===
--- clang/lib/Format/ContinuationIndenter.cpp
+++ clang/lib/Format/ContinuationIndenter.cpp
@@ -2192,7 +2192,7 @@
 }();
 if (!Style.ReflowComments ||
 CommentPragmasRegex.match(Current.TokenText.substr(2)) ||
-switchesFormatting(Current) || !RegularComments) {
+setsStyle(Current) || switchesFormatting(Current) || !RegularComments) {
   return nullptr;
 }
 return std::make_unique(
Index: clang/lib/Format/BreakableToken.h
===
--- clang/lib/Format/BreakableToken.h
+++ clang/lib/Format/BreakableToken.h
@@ -27,6 +27,10 @@
 namespace clang {
 namespace format {
 
+/// Checks if \p Token sets style, basically, // clang-format style=
+/// \p Token must be a line comment.
+bool setsStyle(const FormatToken );
+
 /// Checks if \p Token switches formatting, like /* clang-format off */.
 /// \p Token must be a comment.
 bool switchesFormatting(const FormatToken );
Index: clang/lib/Format/BreakableToken.cpp
===
--- clang/lib/Format/BreakableToken.cpp
+++ clang/lib/Format/BreakableToken.cpp
@@ -216,6 +216,12 @@
   return BreakableToken::Split(StringRef::npos, 0);
 }
 
+bool setsStyle(const FormatToken ) {
+  assert(Token.is(TT_LineComment) && "style is set by line-comment token");
+  StringRef Content = Token.TokenText.substr(2).ltrim();
+  return Content.startswith(StyleComment);
+}
+
 bool switchesFormatting(const FormatToken ) {
   assert((Token.is(TT_BlockComment) || Token.is(TT_LineComment)) &&
  "formatting regions are switched by comment tokens");
@@ -339,7 +345,7 @@
   // Lines starting with '-', '-#', '+' or '*' are bulleted/numbered lists.
   bool hasSpecialMeaningPrefix = false;
   for (StringRef Prefix :
-   {"@", "TODO", "FIXME", "XXX", "-# ", "- ", "+ ", "* "}) {
+   {"@", "TODO", "FIXME", "XXX", "-# ", "- ", "+ ", "* ", StyleComment}) {
 if (Content.startswith(Prefix)) {
   hasSpecialMeaningPrefix = true;
   break;
@@ -743,6 +749,7 @@
 IndentContent = Lines[LineIndex].ltrim(Blanks).substr(1);
   return LineIndex > 0 && 

[PATCH] D145435: Choose style (file) from within code for use in IDEs

2023-03-15 Thread bers via Phabricator via cfe-commits
bersbersbers updated this revision to Diff 505615.
bersbersbers added a comment.

Made comment extraction more strict, made sure special comments not affected by 
formatting, added unit tests


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D145435/new/

https://reviews.llvm.org/D145435

Files:
  clang/docs/ClangFormatStyleOptions.rst
  clang/include/clang/Format/Format.h
  clang/lib/Format/BreakableToken.cpp
  clang/lib/Format/BreakableToken.h
  clang/lib/Format/ContinuationIndenter.cpp
  clang/lib/Format/Format.cpp
  clang/lib/Format/UnwrappedLineParser.cpp
  clang/test/Format/style-comment.cpp

Index: clang/test/Format/style-comment.cpp
===
--- /dev/null
+++ clang/test/Format/style-comment.cpp
@@ -0,0 +1,12 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s | clang-format | clang-format \
+// RUN:   | FileCheck -strict-whitespace %s
+
+// CHECK: //comment
+// comment
+// CHECK: // clang-format style={BasedOnStyle: LLVM, SpacesInLineCommentPrefix: {Maximum: 0}, PointerAlignment: Left, ReflowComments: true, ColumnLimit: 30}
+// clang-format style={BasedOnStyle: LLVM, SpacesInLineCommentPrefix: {Maximum: 0}, PointerAlignment: Left, ReflowComments: true, ColumnLimit: 30}
+// CHECK: //comment
+// comment
+
+// CHECK: {{^int\*\ i;}}
+ int   *  i  ;
Index: clang/lib/Format/UnwrappedLineParser.cpp
===
--- clang/lib/Format/UnwrappedLineParser.cpp
+++ clang/lib/Format/UnwrappedLineParser.cpp
@@ -4063,6 +4063,10 @@
   if (CommentPragmasRegex.match(IndentContent))
 return false;
 
+  // If comment is a style comment, treat as separate section.
+  if (IndentContent.ltrim().startswith(clang::format::StyleComment))
+return false;
+
   // If Line starts with a line comment, then FormatTok continues the comment
   // section if its original column is greater or equal to the original start
   // column of the line.
Index: clang/lib/Format/Format.cpp
===
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -3534,6 +3534,29 @@
   if (!getPredefinedStyle(FallbackStyleName, Style.Language, ))
 return make_string_error("Invalid fallback style \"" + FallbackStyleName);
 
+  if (StyleName == "file") {
+// Read style from first style comment at start of line in the code.
+std::string Prefix{std::string("// ") + StyleComment};
+size_t PrefixPos = StringRef::npos;
+while ((PrefixPos = Code.find(Prefix, PrefixPos + 1)) != StringRef::npos) {
+  // For each prefix, locate respective start of line (SoL) and check text
+  // between prefix and start of line for non-whitespace (comments not at
+  // start of line up to whitespace may be quoted, commented out, ...).
+  if (const size_t SoL = Code.substr(0, PrefixPos).find_last_of("\r\n") + 1;
+  !Code.substr(SoL, PrefixPos - SoL).ltrim().empty()) {
+continue;
+  }
+
+  // Use remainder of line as `StyleName` as if passed by `-style=...`;
+  // if no end of line (EoL) found, use remainder of file.
+  const size_t StylePos = PrefixPos + Prefix.size();
+  const size_t EoL = Code.find_first_of("\r\n", StylePos);
+  const size_t StyleLen = EoL - (EoL == StringRef::npos ? 0 : StylePos);
+  StyleName = Code.substr(StylePos, StyleLen);
+  break;
+}
+  }
+
   llvm::SmallVector, 1>
   ChildFormatTextToApply;
 
Index: clang/lib/Format/ContinuationIndenter.cpp
===
--- clang/lib/Format/ContinuationIndenter.cpp
+++ clang/lib/Format/ContinuationIndenter.cpp
@@ -2142,7 +2142,7 @@
 }();
 if (!Style.ReflowComments ||
 CommentPragmasRegex.match(Current.TokenText.substr(2)) ||
-switchesFormatting(Current) || !RegularComments) {
+setsStyle(Current) || switchesFormatting(Current) || !RegularComments) {
   return nullptr;
 }
 return std::make_unique(
Index: clang/lib/Format/BreakableToken.h
===
--- clang/lib/Format/BreakableToken.h
+++ clang/lib/Format/BreakableToken.h
@@ -27,6 +27,10 @@
 namespace clang {
 namespace format {
 
+/// Checks if \p Token sets style, basically, // clang-format style=
+/// \p Token must be a line comment.
+bool setsStyle(const FormatToken );
+
 /// Checks if \p Token switches formatting, like /* clang-format off */.
 /// \p Token must be a comment.
 bool switchesFormatting(const FormatToken );
Index: clang/lib/Format/BreakableToken.cpp
===
--- clang/lib/Format/BreakableToken.cpp
+++ clang/lib/Format/BreakableToken.cpp
@@ -216,6 +216,12 @@
   return BreakableToken::Split(StringRef::npos, 0);
 }
 
+bool setsStyle(const FormatToken ) {
+  assert(Token.is(TT_LineComment) && "style is set by line-comment token");
+  StringRef Content = 

[PATCH] D145435: Choose style (file) from within code for use in IDEs

2023-03-15 Thread bers via Phabricator via cfe-commits
bersbersbers marked an inline comment as done.
bersbersbers added a comment.

> The disadvantage is that it forces everyone else who needs to review or 
> maintain the file to deal with a style that's different from that of the 
> directory.

"different", yes, but as explained, not necessarily incompatible.

> Are there other tools that require their special comments to be at the top of 
> the file?

In a certain sense, yes: `clang-format` itself requires you to put 
`//clang-format off` comments *before* the code you want to turn it off for. So 
if you want to disable `clang-format` for some file, you would need to put that 
special comment near the top as well. So I would find it rather intuitive to 
have to put the style comment near the top. But I get your point, and chose not 
to implement any such restriction with the current revision.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D145435/new/

https://reviews.llvm.org/D145435

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D145435: Choose style (file) from within code for use in IDEs

2023-03-12 Thread bers via Phabricator via cfe-commits
bersbersbers added a comment.

In D145435#4179662 , @owenpan wrote:

> Are the settings above hypothetical or are they real-world use cases?

Real-world - actually, all of them.

> IMO, if people really want to have per-file configurations, they can do so 
> outside of clang-format, e.g. using grep in a shell script.

My main use case would be IDE integration - is there a way to achieve what you 
propose from within Visual Studio, for example? The beauty of the current setup 
(I believe) is that clang-format is pre-configured, so there is nothing to do 
except check out the code, and formatting works out of the box.

In D145435#4183507 , @owenpan wrote:

> It would add extra runtime (especially for large files).

That is a good point. It's probably an argument for restricting the search for 
that comment to the first N lines of the file, in which case the performance 
impact would be largely independent of file size. (I believe it would even be 
beneficial for this comment to be required to be placed near the top of the 
file, to reduce the amount of surprised users down the road.)

> We would also run the risk of regressions (though unlikely) if for whatever 
> reason an existing codebase has files starting with `// clang-format style=`.

If that is any indication, that is not the case on GitHub at least:
https://cs.github.com/?scopeName=All+repos==%22%2F%2F+clang-format+style%22


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D145435/new/

https://reviews.llvm.org/D145435

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D145435: Choose style (file) from within code for use in IDEs

2023-03-08 Thread bers via Phabricator via cfe-commits
bersbersbers added a comment.

All, thanks for considering my patch! I'll answer all messages here, and will 
work on the patch over the weekend most probably.

In D145435#4173875 , @owenpan wrote:

> Please refer to D125171#4167866  to 
> make the case that such a feature should be added.

I assume you refer to these requirements:

> Each new style option must ..
>
> - be used in a project of significant size (have dozens of contributors)

I am not sure those two apply fully, as my patch does not have a new option 
defining a style, but another option how to define a style. So it's more 
infrastructure than style IMHO, and it's hard to come up with projects using 
some infrastructure that does not exist yet :) Still, see below for a few 
argument on why one might want this.

> - have a publicly accessible style guide

I am not sure what a "style guide" might be in my case, and I'd be happy about 
any clarification. Might that simply be documentation?

> - have a person willing to contribute and maintain patches

That would be me :)

In D145435#4174661 , @MyDeveloperDay 
wrote:

> This is going to need unit tests

Agreed, will add some.

> A full context diff

I see - sorry for that, will add one with the next update.

In D145435#4176109 , 
@HazardyKnusperkeks wrote:

> I think this would be a great feature. But I also think that it needs more 
> documentation.

Thanks! May I ask for hints about what you would expect in addition? Something 
like an exact description of the syntax that the parser expects?

> You also need to make sure, that we don't change that comment, that is 
> `SpacesInLineCommentPrefix` and `ColumnLimit` should have no effect on the 
> comment.

That is a good point, thank you. This may actually be the hardest part (for 
me), but I'll give it a shot. I guess `SpacesInLineCommentPrefix` will also 
skip `// clang-format on/off`, so that should be simple to adapt. Let's see how 
hard the `ColumnLimit` thing is.

In D145435#4176279 , @owenpan wrote:

> I don't understand why anyone wants to do this. [...] If I lock down the 
> style for the files I created as this requested feature allows, what would 
> happen if others need to maintain these files down the road?

I see a number of settings where people might want this:

- Teams not (fully) agreeing on a common style, with different people using 
different styles (such as `BS_Allman` vs `BS_Attach`). Not saying that should 
be allowed in every project, but I can easily see how allowing it can avoid 
conflicts if users can just impose their own style on the files they maintain. 
(Note that this is already possible, but only by moving each user's file into a 
separate directory. The proposed patch will just make it easier.)
- If the previous point sounds like opening the code base for style chaos (in 
fact, your final question appears to suggest that a per-file style will always 
be incompatible with the directory style), note there can be very benign 
examples. A team may have agreed on a 120 column limit, but they have a file 
that is diffed very often and for which it makes sense to have an 80 column 
limit. With the proposed style comment, you can enforce that locally. Note that 
such a file does not go against the 120 column limit, so no one is breaking any 
agreed-upon rules, and no other maintainer should have a problem with that 
file. Another example is `MaxEmptyLinesToKeep`: a team may have agreed on 3 (or 
no limit at all), but one maintainer likes to keep their code vertically short 
at works using `MaxEmptyLinesToKeep: 1`. That file will still be compliant with 
the agreed-upon style.
- Code and style are self-contained in a single file. If you move a file from 
one directory to another, it will continue to use the same style, even if the 
two folders use different .clang-format files. (Not saying this has to be done, 
but it's nice to have the option.) If you email/scp/... the file to someone, 
the recipient knows the format and can easily format after making changes.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D145435/new/

https://reviews.llvm.org/D145435

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D145435: Choose style (file) from within code for use in IDEs

2023-03-06 Thread bers via Phabricator via cfe-commits
bersbersbers created this revision.
bersbersbers added a reviewer: owenpan.
bersbersbers created this object with visibility "All Users".
bersbersbers added a project: clang-format.
Herald added a project: All.
bersbersbers requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Address https://github.com/llvm/llvm-project/issues/60917


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D145435

Files:
  clang/docs/ClangFormatStyleOptions.rst
  clang/lib/Format/Format.cpp


Index: clang/lib/Format/Format.cpp
===
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -3534,6 +3534,25 @@
   if (!getPredefinedStyle(FallbackStyleName, Style.Language, ))
 return make_string_error("Invalid fallback style \"" + FallbackStyleName);
 
+  if (StyleName == "file") {
+// Read style from a style comment in the code.
+const char *Prefix{"// clang-format style="};
+const size_t PrefixPos = Code.find(Prefix);
+
+// Prefix found, and at start of file or preceded by white space?
+if (PrefixPos != StringRef::npos &&
+((PrefixPos == 0) || (Code.substr(PrefixPos - 1, 1).ltrim() == ""))) {
+
+  // Use remainder of line as `StyleName` as if passed by `-style=...`.
+  const size_t StylePos = PrefixPos + strlen(Prefix);
+  const size_t EndOfLine = Code.find_first_of("\r\n", StylePos);
+  if (EndOfLine == StringRef::npos)
+StyleName = Code.substr(StylePos);
+  else
+StyleName = Code.substr(StylePos, EndOfLine - StylePos);
+}
+  }
+
   llvm::SmallVector, 1>
   ChildFormatTextToApply;
 
Index: clang/docs/ClangFormatStyleOptions.rst
===
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -22,12 +22,15 @@
 Configuring Style with clang-format
 ===
 
-:program:`clang-format` supports two ways to provide custom style options:
-directly specify style configuration in the ``-style=`` command line option or
-use ``-style=file`` and put style configuration in the ``.clang-format`` or
+:program:`clang-format` supports three ways to provide custom style options:
+directly specify style configuration in the ``-style=`` command line option, or
+use ``-style=file`` and put style configuration either in a style comment
+(``// clang-format style=``) in each code file or in the ``.clang-format`` or
 ``_clang-format`` file in the project directory.
 
 When using ``-style=file``, :program:`clang-format` for each input file will
+locate the first ``// clang-format style=`` comment, if it exists, and use the
+remainder of that line as the style; if no such comment is found, it will then
 try to find the ``.clang-format`` file located in the closest parent directory
 of the input file. When the standard input is used, the search is started from
 the current directory.
@@ -100,6 +103,18 @@
 
   -style='{key1: value1, key2: value2, ...}'
 
+Similar syntax be used within the file, such as
+
+.. code-block:: console
+
+  // clang-format style={key1: value1, key2: value2, ...}
+
+or even
+
+.. code-block:: console
+
+  // clang-format style=file:
+
 
 Disabling Formatting on a Piece of Code
 ===


Index: clang/lib/Format/Format.cpp
===
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -3534,6 +3534,25 @@
   if (!getPredefinedStyle(FallbackStyleName, Style.Language, ))
 return make_string_error("Invalid fallback style \"" + FallbackStyleName);
 
+  if (StyleName == "file") {
+// Read style from a style comment in the code.
+const char *Prefix{"// clang-format style="};
+const size_t PrefixPos = Code.find(Prefix);
+
+// Prefix found, and at start of file or preceded by white space?
+if (PrefixPos != StringRef::npos &&
+((PrefixPos == 0) || (Code.substr(PrefixPos - 1, 1).ltrim() == ""))) {
+
+  // Use remainder of line as `StyleName` as if passed by `-style=...`.
+  const size_t StylePos = PrefixPos + strlen(Prefix);
+  const size_t EndOfLine = Code.find_first_of("\r\n", StylePos);
+  if (EndOfLine == StringRef::npos)
+StyleName = Code.substr(StylePos);
+  else
+StyleName = Code.substr(StylePos, EndOfLine - StylePos);
+}
+  }
+
   llvm::SmallVector, 1>
   ChildFormatTextToApply;
 
Index: clang/docs/ClangFormatStyleOptions.rst
===
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -22,12 +22,15 @@
 Configuring Style with clang-format
 ===
 
-:program:`clang-format` supports two ways to provide custom style options:
-directly specify style configuration in the ``-style=`` command line option or
-use