September 1st Release
Pre-release
8.0.0-beta-001
Changed
=,>,<,%and%%cannot start a line at the column of their left-hand side: there the parser reads=,>and<as the=of a binding, and reads%and%%inside a quotation as a splice. Fantomas already kept them off that column, but did so with the layout that keeps the right-hand side beside the operator whenever it is short enough to fit, which needs the page width and a column count to predict and moves a0around when an unrelated part of the line changes length. It now follows the rule proposed in fsharp/fslang-design#836: the expression fits on one line, or it does not and the operator stays with the left-hand side while the right-hand side moves one level in, or the left-hand side itself spans several lines, in which case the operator and the right-hand side each take a line of their own.fsharp_multiline_bracket_style = stroustrupstill overrides this for a right-hand side that opens a bracket, which keeps hugging the operator. This is the first beta of8.0.0: the style guide for this is not settled yet, so give it a try and report back on the design issue if something reads worse than what it replaces. #3434
Special thanks to @nojaf!
August 29th Release
Pre-release
8.0.0-alpha-026
Changed
fsharp_multiline_bracket_style = stroustrupnow applies to object expressions, so{stays on the line that opens the binding andnew T withmoves below it. Up to now object expressions were printed by thealignedbranch whatever the setting said, which left{ new T withon one line and made stroustrup the only bracket style that did not reach every bracket it names. The Microsoft style guide writes the form out under "Formatting object expressions" and Fantomas now produces it character for character. This was left alone in the belief that the layout was an offside error waiting to happen; it is not, and the compiler accepts it in a binding, a list item, a function argument, a lambda body, a match clause and a nestedletalike. An object expression under stroustrup now lands where a record already landed in each of those positions.aligned, which is the default since8.0.0-alpha-002, andcrampedare untouched. #2990fsharp_multiline_bracket_style = stroustrupnow survives a binding whose signature broke across lines, and a match clause underfsharp_experimental_keep_indent_in_branch. Both were printed by branches written before stroustrup existed and never given a stroustrup-aware counterpart, so the setting was quietly overruled in each: aletwhose parameters wrapped left its=alone on a line and indented the bracket below it, and a match clause under keep-indent did the same below the arrow. They now read= {and| _ -> {, with the items one level in and the closing bracket back at the column the binding or the clause started from, which is what the same code already produced when the signature fitted on one line or the setting was off. This reaches every bracket the setting names, so records, update records, anonymous records, anonymous structs, lists and arrays move with the object expressions of the entry above rather than behind them. The closing bracket of a match clause stays two columns right of the bar, because on the bar's own column the|of the next clause no longer parses. #3450
Special thanks to @nojaf!
August 28th Release
Pre-release
8.0.0-alpha-025
Fixed
- A
!line of a.fantomasignoretakes a path back out again even when a line above it matched the folder holding that path, sosub/*followed by!sub/keepformats and checks the files undersub/keep. Since8.0.0-alpha-016a folder an ignore file names is never opened, which is what a folder pattern should mean and is not what it can mean here: closingsubdecides thatsub/keepis not there, and the line that would have taken it back out is never reached. The files were not reported as ignored, they were never found, so--checkpassed over a scope smaller than the ignore file describes anddoctor, which asks about one file and answers correctly, disagreed with the run about that same file. An ignore file that negates anything now leaves every folder open and asks about the files inside one at a time, which is what every version up to8.0.0-alpha-015did. An ignore file with no!line in it still closes the folders it names. #3447
Special thanks to @nojaf!
August 28th Release
Pre-release
8.0.0-alpha-024
Fixed
- The dots of a chain that breaks are indented past the head of the chain, instead of landing on the head's own column or to the left of it. A dot on the head's column reads as a new item rather than as the chain continuing, and inside a parenthesis the parser refuses the output outright:
let v = ((someObject.First(a).Second(b).Third(c)))at two-space indentation produced code Fantomas would not accept from itself, so the file was left unformatted with a bug report on the console. The dots now step one indent level at a time until they clear the head, which keeps every column a multiple of the indent size; a chain whose dots already cleared its head is untouched. This replaces a branch that anchored a parenthesised chain on its opening parenthesis, written when only a real dot chain reached it. Since8.0.0-alpha-014a dotted long identifier is a chain too, so it also fired for the likes ofSeq.map, which never breaks, and moved a lambda written behind such a call three columns for no reason. #3445
Special thanks to @nojaf!
August 28th Release
Pre-release
8.0.0-alpha-023
Fixed
- An operator that starts with a star keeps a space on either side of it inside the parentheses wherever its name is written, so
val inline ( *. ): ...andabstract member ( *. ): ...survive formatting. Onlylet ( *. ) a b = ...was spaced before; avalin a signature file and an abstract member in either kind of file came out as(*.), which opens a block comment and swallows the rest of the file. Multiplication is unaffected, because(*)is a token the lexer knows. #3443
Special thanks to @nojaf!
August 27th Release
Pre-release
8.0.0-alpha-022
Added
- Extension members on tuple types,
type (int * int) with ...andtype struct (int * int) with ..., format instead of failing. The parser accepts them since dotnet/fsharp#19602, and a type definition's name is now any type in the Oak tree rather than only an identifier. The tuple prints as written; no layout decision was added for it. #3436
Changed
- Update FCS to 'Rotate [<return: X>] attributes during binding normalization', commit 74ec4f7df70717a162d6ffd23007603cf298fb8b #3436
Special thanks to @nojaf!
August 27th Release
Pre-release
8.0.0-alpha-021
Fixed
- A call in the middle of a chain keeps its parenthesis against the member name when its lambda argument no longer fits, and breaks behind it instead. Up to now the whole argument moved down a line,
(and all, which is what the last call of a chain does, but for an earlier one it changes what the code means:a.Foo (x).Bar()passes(x).Bar()toFoorather than callingBaron the result. With a call behind it the compiler rejected the result with "This argument expression needs parentheses"; with a plain member behind it, such as.Value, nothing warned at all and the member quietly became part of the argument. Hanging the parameters under(funwould keep the parenthesis in place too, and the F# style guide rules that out, because the column they hang from is the length of the member name. The closing)answers tofsharp_multi_line_lambda_closing_newlineas it did before, and the last call of a chain is untouched, since nothing follows it to be swallowed. #3432
Special thanks to @nojaf!
August 27th Release
Pre-release
8.0.0-alpha-020
Fixed
- A list or an array on the right of
=,>,<,%or%%is indented from the binding again, solet a = b = [ ... ]breaks tob = [with its items one level in and the]back at the column ofb. Since8.0.0-alpha-003both the items and the closing bracket sat a further level to the right. The right-hand side of these operators is indented so that a comment between the operator and that side lands below the operator, which is what #2944 asked for, and so that the lines under a chain or an application are not read as a continuation of the left-hand side. A list or an array needs neither: its bracket says where the right-hand side begins and indents its items from the left-hand side by itself, so indenting it again pushed the items and the closing bracket a level too far. This concernsaligned, which is the default, andstroustrup.crampedlines the items up under the opening bracket, so the indent never reached them and nothing changes there. Right-hand sides that open a bracket in some other way, such asseq { },<@ @>,begin ... endandnew T(...), still carry the extra level; where a no-break operator should put what follows it is a wider question than this fix. #3428
Special thanks to @nojaf!
August 26th Release
Pre-release
8.0.0-alpha-019
Fixed
- A call on a constant receiver keeps its parenthesis tight, so
"yow".Substring (0, 3)and3L.ToString ()come out as"yow".Substring(0, 3)and3L.ToString().8.0.0-alpha-018gave a constant receiver a space it was never meant to have. The rule there says a call keeps the space only when the whole thing being called is a plain dotted name, and the comment agreed at fslang-design#648 lists"yow".Substring(0, 3)under "a receiver that is not a name", beside(f x)and[ 1; 2 ]. The alpha carved constants back out on the reasoning that a constant is atomic rather than bracketed, which went past what was agreed and was not deliberate. A constant is a value, the rule asks for a name, so it goes tight. A type parameter really is a name, so'T.set_StaticProperty (3)is unaffected. #3426
Special thanks to @nojaf!
August 26th Release
Pre-release
8.0.0-alpha-018
Added
doctornames the.fantomasignoreabove the one that governs a file, when a pattern in it would have skipped that file. Fantomas reads the nearest ignore file at or above a file and no other, so a pattern written at the root of a repository has no effect on a folder that has an ignore file of its own beside it. Up to now the report named the ignore file that did decide and said nothing about the one that did not, which answers "which file" and leaves "then why did mine not" for the reader to work out. It quotes the pattern that would have matched, with its line number, the way it already quotes the line that decided.--jsoncarries every ignore file above the governing one undershadowed, each withwouldIgnoreand the lines of it that match, whether or not it would have decided anything. #3423
Changed
- The settings that ask for a space before the parenthesis of a call,
fsharp_space_before_uppercase_invocationandfsharp_space_before_lowercase_invocation, now get a say only when the whole thing being called is a plain dotted name. Put a call, an index, a bracketed receiver, or a type application anywhere in it, and the parenthesis stays tight whatever the settings ask for. On default settingsxs.map(fun a -> a + 1).filter (fun a -> a > 1)becomesxs.map(fun a -> a + 1).filter(fun a -> a > 1), which is the report this started from, andFoo().bar ()andmyList.[7].someFunction (arg)lose their space for the same reason. A plain dotted name is untouched however long it gets, soList.map (f)andFantomas.FCS.Text.Range.unionRanges (r1, r2)keep the space they had, and so is a literal or type parameter receiver, since"yow".Substring (0, 3)and'T.set_StaticProperty (3)are atomic rather than bracketed. Agreed at fslang-design#648 and written up under Formatting chain expressions. #3425 - A type application now makes a call tight with no chain in sight, which is the part of the change above most likely to catch you out, because it reaches ordinary generic calls rather than fluent code. On default settings
unbox<bool> (value),f<int> (x),List.map<int> (f)andjsOptions<Vis.Options> (fun o -> ...)all lose their space, sincefsharp_space_before_lowercase_invocationistrueand each of those is a lower-case name carrying type arguments. An upper-case generic call such asDictionary<string, int>(x)was already tight by default and only moves iffsharp_space_before_uppercase_invocationis on. Fantomas does not add parentheses, so the far more common form without them,unbox<int> obj, is left exactly as written and never comes into it. #3425
Fixed
- More expression forms count as open-ended, so a record, list, array or tuple that holds one of them in a non-last position stays multiline instead of collapsing onto a line that means something else. #3279 settled
fun,if,matchandtry, and left the forms that wrap one of those or end in one:lazy,yield,return,do,assertandfixed, an assignment such asx.P <- fun y -> y, andlet x = 1 in body.{ A = 1; B = lazy fun x -> x; C = 3 }reads back as a lambda that swallowedC = 3. A wrapper around something that closes on its own,lazy aorx.P <- 1, collapses as it always did. When this happens, why, and how to get the single line back is written up under Open-ended expressions. #3424
Special thanks to @nojaf!