-
Notifications
You must be signed in to change notification settings - Fork 435
Update renderer-blank line after imports, delete blank line from docs #1782
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update renderer-blank line after imports, delete blank line from docs #1782
Conversation
…m docs Motivation: We want to have a blank line between the imports and the generated code and no blank line between the docs and the protocols/methods declarations. Modifications: Updated the TextBasedRenderer and the tests. Result: The generated code will have the right format.
| renderImport(`import`, isLast: isLast) | ||
| if isLast { | ||
| writer.nextLineAppendsToLastLine() | ||
| writer.writeLine("\n") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is the right approach as it special cases the rendering of imports. I think adding a CodeBlockItem for an empty line is probably the better way to do this because it's more flexible: you can then use it in other places like between declarations, for example.
| commentString = string | ||
| } | ||
| if prefix.isEmpty { | ||
| while commentString.hasSuffix("\n") { commentString.removeLast() } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where are the problematic extra new lines coming from with the preformatted comments? Modifying it here doesn't seem like the right thing to do, they are after all preformatted and we're re-formatting them...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw that they are stored in the "Location" objects that are created by protoc, so I wasn't sure where it would be best to do the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if we have a proto that's like:
// Some comments about Foo
service Foo {
}and get the comments using protoSourceComments(), do we get "// Some comments about Foo", "// Some comments about Foo\n" or "// Some comments about Foo\n\n" or something else?
It's not clear to me where the extra newline is coming from but I don't think we should reformat pre-formatted comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, we get "// Some comments about Foo\n"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, so how come when we render it we get the extra new line? Does the writer add a newline?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The writeLine() function of the renderer that is used sets a property "nextWriteAppendsToLastLine" to false at the end, this way the new line to be rendered is on the next line not the current one (which is the blank one in our case)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that's the problem, I think the issue is that writeLine just appends a single String to an array of lines in the writer which are eventually joined by newlines. In other words the writer adds in new lines between each "line" added by writeLine.
However when we render the preformatted comments we append the whole block as a single line. The code below - for rendering the other comments - is different: it splits the lines and writes each separately. This lets the writer deal with adding new lines.
I think we should do the same but when the last line is empty (i.e. the text ended in "\n" before it was split) we should discard that empty line.
| } else { | ||
| // A blank line that should be dropped - pre formatted documentation | ||
| // might contain such lines. | ||
| return nil | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is right, for example I could give the following preformatted comment:
// Foo
// Bar
and this would render as
// Foo
// Bar
which isn't correct.
We only want to drop the line if it's empty and it has no prefix and it's the last line. We should also have tests for this.
| static func emptyLine() -> Self { | ||
| CodeBlock(item: .emptyLine) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a static var:
| static func emptyLine() -> Self { | |
| CodeBlock(item: .emptyLine) | |
| } | |
| static var emptyLine: Self { | |
| CodeBlock(item: .emptyLine) | |
| } |
| func renderComment(_ comment: Comment) { | ||
| let prefix: String | ||
| let commentString: String | ||
| var commentString: String |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this should still be a let
| switch description { | ||
| case .declaration(let declaration): renderDeclaration(declaration) | ||
| case .expression(let expression): renderExpression(expression) | ||
| case .emptyLine: writer.nextLineOnNewLine() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some tests for this?
| /// Sets a flag on the writer so that the next call to `writeLine` starts on a new line. | ||
| /// | ||
| /// Safe to call repeatedly, it gets reset by `writeLine`. | ||
| func nextLineOnNewLine() { nextWriteAppendsToLastLine = false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd probably bias towards modifying nextLineAppendsToLastLine as we reduce the number of functions:
func nextLineAppendsToLastLine(_ append: Bool = true) { ... }|
|
||
| func translate(from codeGenerationRequest: CodeGenerationRequest) throws -> [CodeBlock] { | ||
| var codeBlocks: [CodeBlock] = [] | ||
| var codeBlocks: [CodeBlock] = [.emptyLine()] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be done by the caller instead. If you take this translator in isolation it's strange for it to return code blocks which start with an empty line.
| rendersAs: #""" | ||
|
|
||
| """# |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the test tests what you think it tests 🙂
The last line of a multiline string literal doesn't include a line break. As the line is otherwise empty it means that the multiline string in your test is really just an empty string. (You should be able to replace it with "" and the test would be the same.)
A multiline string literal includes all of the lines between its opening and closing quotation marks. The string begins on the first line after the opening quotation marks (""") and ends on the line before the closing quotation marks, which means that neither of the strings below start or end with a line break:
let singleLineString = "These are the same." let multilineString = """ These are the same. """
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW the correct test here should be rendering as "\n"
glbrntt
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy with this fix but a couple of small things need sorting first
| /// the last stored line instead of starting a new line. | ||
| /// the last stored line or starts on a new line. | ||
| /// | ||
| /// Safe to call repeatedly, it gets reset by `writeLine`. | ||
| func nextLineAppendsToLastLine() { nextWriteAppendsToLastLine = true } | ||
| func nextLineAppendsToLastLine(_ append: Bool = true) { nextWriteAppendsToLastLine = append } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can undo these changes since they're no longer necessary
| func testSameGeneratedNameServicesSameNamespaceError() throws { | ||
| let serviceA = ServiceDescriptor( | ||
| documentation: "Documentation for AService", | ||
| documentation: "/// Documentation for AService\n", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the service or method docs should be pre-formatted. We should only pre-format the leading trivia at the top of the proto file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are also extracted from the Location objects associated with the methods and services, so they are pre-formatted. I remember we've decided to have all the docs / comments from the proto file as pre-formatted and only the ones we generate as .docs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we decided that only the copyright header would be pre-formatted because those are the only docs we should copy verbatim. I should've picked this up in the PR which added it.
The other docs are formatted by protoSourceComments() which inserts the leading ///, which isn't actually necessary as the renderer can (and should) do it instead.
Either way we can just leave it as is for now, it's not particularly important.
…grpc#1782) Motivation: We want to have a blank line between the imports and the generated code and no blank line between the docs and the protocols/methods declarations. Modifications: Updated the TextBasedRenderer and the tests. Result: The generated code will have the right format.
Motivation:
We want to have a blank line between the imports and the generated code and no blank line between the docs and the protocols/methods declarations.
Modifications:
Updated the TextBasedRenderer and the tests.
Result:
The generated code will have the right format.