-
Notifications
You must be signed in to change notification settings - Fork 173
Conversation
|
cc: @amrondonp, @swosti99 |
Co-authored-by: Raphael Koh <raph.koh@gmail.com>
Integrate space after if, with tests to formatter branch
…qsharp-compiler into raphael/vsc-formatter
swernli
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.
A few comments and questions, but overall I think this is a great start! Exciting to see the progress!
| * `operation Foo (q : Qubit, n : Int) : Bool` | ||
| */ | ||
| const argsRule = (code: string): string => { | ||
| const operationMatcher: RegExp = /operation\s*(\w+)\s*\((.*)\)\s*:\s*(\w+)/g; |
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 is great! Turns out that functions follow the same spacing rules, so you could have the same argsRule implementation for them. Maybe that means later all of this except the literal operation could be moved to a standard utility and both operation-rules.ts and a future function-rules.ts could refer to it? Alternatively, operation-rules.ts could be renamed to be more general and have both operation and function related rules in them, since they are almost identical.
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.
What's a generic umbrella term for operations and functions?
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.
That's a good question, and I don't think there really is one. But if the intent of the file is for checking operation and function declarations, maybe a decent name would be declaration-rules.ts? Then it could also handle user-defined type declarations (newtype as documented here). That file name change can be made in following PRs, no need to gate this PR on the name 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.
FYI, we've generally used "callable" to mean "function or operation."
|
Apologies for the delay on the review; I thought I had hit submit but had it sitting open in a browser instead. Great work so far! |
|
Looks like there was a failure in the testing of the VSCode extension. Is it possible something in these changes is showing up as a unit test when it shouldn't? |
|
Actually, looks like quite a few commits were added between my original review and now. I'm going to take a look back through the files again. Thanks for adding testing! |
| describe("space after if rule", () => { | ||
| it("adds space after if statement", () => { | ||
| const code = "if(1 == 1){H(qs[0]);}"; | ||
| const expectedCode = "if (1 == 1){H(qs[0]);}"; |
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.
Some of these test cases will need to be updated once other rules are implemented (such as requiring a space before and after open curly brace, ie if (1 == 1) { H(qs[0]); }. But that's ok to leave this as-is for now and expect those updates to be done by later rule authors.
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.
cc: @amrondonp
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 intention of this rule is only to handle the space after the if statement, We obviously need integration tests that test several rules at once but this specific rule only is concerned about the spacing after the if
swernli
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.
You'll need to make some build script changes to ensure these tests run as expected (and only when expected) in the pipeline.
| const formattedLines: vscode.TextEdit[] = lines | ||
| .map((line) => { | ||
| const formattedLine: string = formatter(line.text, rules); | ||
| return formattedLine !== line.text |
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.
Does this constrain all formatting rules to work one line at a time?
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.
Yup. I'm not sure how to match the whole document at once. Will definitely have to change this to handle multi-line matching.
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 could just go lines.join(""); counldn't we?
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.
It would be a bit more involved than that. We need to return vscode.TextEdit[] instead of just the final formatted string.
| * more examples in the unit tests | ||
| */ | ||
| export const spaceAfterIf: FormatRule = (code: string): string => { | ||
| return code.replace(/(^| +|;|})if[ \n]*/, "$1if "); |
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.
Using the * operator could make the regex match on zero instances of [ \n], such that variable names starting with if could be affected. Do you want + here to match [1, ∞) copies of [ \n]?
| return code.replace(/(^| +|;|})if[ \n]*/, "$1if "); | |
| return code.replace(/(^| +|;|})if[ \n]+/, "$1if "); |
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 also want to match none in the case of if(...) -> if (...). I would suggest the following
| return code.replace(/(^| +|;|})if[ \n]*/, "$1if "); | |
| return code.replace(/(^| +|;|})if[ |\n]*\(/, "$1if "); |
cc: @amrondonp who wrote this
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.
Good suggestion, @theRoughCode thanks!
| * `namespace Foo {}` | ||
| */ | ||
| export const namespaceRule: FormatRule = (code: string) => { | ||
| const namespaceMatcher: RegExp = /^\s*namespace\s*(\w+)\s*(\S|\S.*\S)\s*$/g; |
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.
Does \w+ catch dotted names like Microsoft.Quantum.Arrays?
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.
Good catch. What do you think of this:
/^\s*namespace\s+(\w+(?:\.\w+)*)\s*({|{.*})\s*/$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.
Please make a test for every edge case you encounter
| @@ -0,0 +1,98 @@ | |||
| import "mocha"; | |||
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 suggest a unit test to ensure that if occuring in the beginning, middle, or end of an identifier name doesn't get replaced.
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.
cc: @amrondonp
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.
Sure, Will do
This PR implements the skeleton for the VS Code formatter. We have 2 basic rules for now:
namespace. For example,namespace Microsoft.Quantum {}->namespace Microsoft.Quantum {}.operation Foo(q:Qubit, n :Int) : Bool->operation Foo (q : Qubit, n : Int) : Bool