-
-
Notifications
You must be signed in to change notification settings - Fork 193
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
Feature request: more flexible record whitespace #453
Comments
Also, having the curly curly brace on the first field makes it a little more tedious when editing the record. e.g. when adding, removing or moving a field via copy-pasting. type Foo = {
Field : int
Field2 : int
Field244 : int
Field3 : int
} |
Original: Command.sub ["commit"; "ci"] {
opts = [
Opts.oneOf (Optional, [
Opt.value ["all"; "a"]
Opt.value ["interactive"]
Opt.value ["patch"; "p"]
])
// To commit you *must* give either message or file where
// the message is.
Opts.oneOf (Required, [
Opt.value ["file"; "F"]
Opt.value ["message"; "m"]
])
]
args = [
// You can give optional files to commit as positional
// arguments
Arg.infinite Optional "file"
]
commands = []
} Fantomas: Command.sub [ "commit"; "ci" ] { opts =
[ Opts.oneOf
(Optional,
[ Opt.value
[ "all"; "a" ]
Opt.value
[ "interactive" ]
Opt.value
[ "patch"; "p" ] ])
// To commit you *must* give either message or file where
// the message is.
Opts.oneOf
(Required,
[ Opt.value
[ "file"; "F" ]
Opt.value
[ "message";
"m" ] ]) ]
args =
[ // You can give optional files to commit as positional
// arguments
Arg.infinite Optional
"file" ]
commands = [] } I think too that Records should have newline configuration of sorts, that looks way too indented. |
To summary we have 3 possible variant of opening bracket: O1. Opening bracket on same line (current default)
O2. Opening bracket on separate line
O3. Opening bracket on same line
And 2 possible variant of closing bracket: C1. Closing bracket on same line (current default)
C2. Closing bracket on separate line
Same for list and arrays. I support adding these options (shared for records, lists, arrays) to Fantomas. Maybe:
|
@jindraivanek yeah! Looks much better now, updated to 3.0 preview. It still doesn't add newlines after brackets/square brackets. Different example, formatted by Fantomas:
It doesn't look horrible, in fact it's very readable but the amount of indentation looks a lot. |
@Ciantic Things like this are definitely a bug, multiline records should be on newline indented. I will create new issue for this. |
@jindraivanek Is there a separate issue created to track the feature request mentioned above to have an option to select if a new line return should be inserted before/after opening/closing brackets? (Right now this is a blocker for adoption in our team.) |
@jindraivanek Yes, that would cover our needs. Thanks! |
Hey @Smaug123 would this proposal work out for G-Research? |
@nojaf Thanks - that would work for us. |
Hey @Smaug123, I've been working on a new setting to facilitate an alternative style of formatting for records. This hasn't been really trivial and I'm not entirely sure yet I've covered all scenarios. It would be nice if some more code samples were posted here so I can add some more tests. |
I love this new "alternate braces" syntax and have some samples I believe Fantomas should format without any changes. E.g. these particular versions of If you want some more specific samples (e.g. more types/records/CEs or whatever), then please clarify your request: I have plenty of them here and there, and would be happy to share or prepare samples specifically for Fantomas tests. |
Hey @ForNeVeR , thanks for your comment. Let me clarify: Please point to the concrete lines in files, f.ex. https://github.com/codingteam/emulsion/blob/3502000a9302324a3abde2bd413befce9727c738/Emulsion/Xmpp/Types.fs#L7-L12 To set the correct expectations, the new setting would format this: type RoomInfo = {
RoomJid: JID
Nickname: string
} to type RoomInfo =
{
RoomJid: JID
Nickname: string
} I don't want to introduce too many different styles of formatting at the same time. Hope this helps. |
@nojaf I believe we should have an option to keep this style: type RoomInfo = {
RoomJid: JID
Nickname: string
} I've seen it in various projects before and it's clearly what people try to stick there. Could we have a union case instead of a boolean for this style setting? |
We could and I'm not against it. It would be nice if someone could sum up everything and prove the consistency between all of it. |
Okay, here're some samples (we could discuss them if you think something here is breaking the guidelines; I've made sure they all compile with no warnings in F# 4.7): open System
// Records
type Record = {
foo: int
bar: int64
xs: obj seq
}
let recordExpr = {
foo = 10
bar = 100L
xs = seq {
yield! (seq {
1
2
3
()
} : obj seq)
}
}
// Object expressions
let disposable = {
new IDisposable with
member _.Dispose() = ()
}
// List
let nestedList: obj list = [
1
2
3
[ // this case looks weird but seen rarely
1
2
3
]
]
// Nested CE
let sequence: obj seq = seq {
1
2
3
async {
Async.Start <| async {
return ()
}
return 10
}
}
let functionExpression = Func<_> (fun () ->
let x = 10
x + 5
) Please let me know if you need more. |
By contrast, G-Research's internal style guidelines suggest the following for that same block of code: open System
// Records
type Record =
{
foo: int
bar: int64
xs: obj seq
}
let recordExpr =
{
foo = 10
bar = 100L
xs = seq {
yield! (seq {
1
2
3
()
} : obj seq)
}
}
// Object expressions
let disposable =
{ new IDisposable with
member _.Dispose() = ()
}
// List
let nestedList: obj list =
[
1
2
3
[ // this case looks weird but seen rarely
1
2
3
]
]
// Nested CE
let sequence: obj seq = seq {
1
2
3
async {
Async.Start <| async {
return ()
}
return 10
}
}
let functionExpression = Func<_> (fun () ->
let x = 10
x + 5
) |
@nojaf If there's a version which contains your new work with records etc, I'd be very happy to run it over some of our internal codebase and see what happens. (As you know, it's quite hard to come up with examples unprompted, but very easy to see when something "looks wrong" in a large existing codebase!) |
Hello @Smaug123 & @ForNeVeR, thanks for the examples. I asked for samples related to records and got examples that change lists and computation expressions as well. So clearly we will have to find some middle ground on this. // List
let nestedList: obj list = [
1
2
3
[ // this case looks weird but seen rarely
1
2
3
]
] The inner list is formatted differently than the outer one. It is not consistent and the technical problem here is that when formatting the inner list we don't know that it is being printed inside the outer list. @Smaug123 // Nested CE
let sequence: obj seq = seq {
1
2
3
async {
Async.Start <| async {
return ()
}
return 10
}
}
let functionExpression = Func<_> (fun () ->
let x = 10
x + 5
) will be formatted to // Nested CE
let sequence: obj seq =
seq {
1
2
3
async {
Async.Start <| async { return () }
return 10
}
}
let functionExpression =
Func<_>(fun () ->
let x = 10
x + 5) I would not change this behaviour as it would end up being inconsistent with other parts of Fantomas. let meh () =
printfn "meh"
7 + 7 Please both give an example of records in a list and nested records, haven't seen those yet. |
And how would you format Elmish code like this: let navButtons =
[
div [Class "navbar-item navbar-social"]
[ navButton "twitter" "https://twitter.com/FableCompiler" "fab fa-twitter" ]
div [Class "navbar-item navbar-social"] [
navButton "gitter" "https://gitter.im/fable-compiler/Fable" "fas fa-comment-dots" ]
]
let menuItem label page currentPage dispatch=
a [
classList ["navbar-item", true; "is-active", page = currentPage]
Href (toHash page)
OnClick (fun _ -> dispatch ToggleBurger)
] [str label] how do these lists work in your style? |
Sure, I realise that what I consider to be "stylistically correct" might not even be consistent and I may have to change :) I think I'm fairly strongly attached to having a space after constructors like I've never used Elmish, but if I'm reading the code correctly, I think that the two most obvious-to-me renderings are: let navButtons =
[
div
[ Class "navbar-item navbar-social" ]
[ navButton "twitter" "https://twitter.com/FableCompiler" "fab fa-twitter" ]
div
[ Class "navbar-item navbar-social" ]
[ navButton "gitter" "https://gitter.im/fable-compiler/Fable" "fas fa-comment-dots" ]
]
let menuItem label page currentPage dispatch =
a
[
classList [ ("navbar-item", true) ; ("is-active", page = currentPage) ]
Href (toHash page)
OnClick (fun _ -> dispatch ToggleBurger)
]
[ str label ] or let navButtons =
[
div
[ Class "navbar-item navbar-social" ]
[
navButton
"twitter"
"https://twitter.com/FableCompiler"
"fab fa-twitter"
]
div
[ Class "navbar-item navbar-social" ]
[
navButton
"gitter"
"https://gitter.im/fable-compiler/Fable"
"fas fa-comment-dots"
]
]
let menuItem label page currentPage dispatch =
a
[
classList
[
"navbar-item", true
"is-active", page = currentPage
]
Href (toHash page)
OnClick (fun _ -> dispatch ToggleBurger)
]
[ str label ] Both of these are harmonious; perhaps the latter is better because of the multi-line I would note that code of this general form is very unlike code I've seen at G-Research, though, so maybe my idea of "correct" formatting is not good; I could probably count on one finger the number of times I've seen lists of such strange shapes constructed and passed into functions at the call site. |
@nojaf, I understand that this is potentially very big change, and that's why we're discussing it here; I'm okay with looking for some middle ground (i.e. what's possible and what isn't in the current Fantomas) for now. If you think that we better create a couple of more focused separate requests (e.g. for records, lists and CEs), I'll do it (and I'm not in position to require changes all over the place at once). Comments on your points:
While I disagree that it's different, I have no strong opinion on that example, because the situation is extremely rare in my sources. What would you propose on that?
This is one of the main points of this proposed code style: to avoid this additional indent. I believe we should do our best to preserve this particular "oddity".
type MyRecord = {
xxx: int
yyy: string
zzz: MyRecord option
}
let nested = {
xxx = 0
yyy = ""
zzz = Some {
xxx = 0
yyy = ""
zzz = None
}
}
let list = [
{
xxx = 0
yyy = ""
zzz = Some {
xxx = 0
yyy = ""
zzz = None
}
}
{
xxx = 0
yyy = ""
zzz = None
}
]
I found this code sample very unusual for my code base (probably just because I've never used Elmish before), and I have no strong opinion on how multiline curried function arguments should be laid out. But one of valid variants in my point of view would be this: let navButtons = [
div
[ Class "navbar-item navbar-social" ]
[ navButton "twitter" "https://twitter.com/FableCompiler" "fab fa-twitter" ]
div
[ Class "navbar-item navbar-social" ]
[ navButton "gitter" "https://gitter.im/fable-compiler/Fable" "fas fa-comment-dots" ]
]
let menuItem label page currentPage dispatch =
a
[ // "a [" would be cool but will create mess for the second argument, so I'll avoid this
classList [
("navbar-item", true) // here, using the newline is optional, but I'll use it for sake of the example
("is-active", page = currentPage)
]
Href (toHash page)
OnClick (fun _ -> dispatch ToggleBurger)
]
[ str label ] |
Sorry, I just realised I missed out a chunk of your request. ForNeVeR's example I would format as follows: type MyRecord =
{
xxx : int
yyy : string
zzz : MyRecord option
}
let nested =
{
xxx = 0
yyy = ""
zzz = // although I would always pipe this instead
Some
{
xxx = 0
yyy = ""
zzz = None
}
}
let list =
[
{
xxx = 0
yyy = ""
zzz =
Some
{
xxx = 0
yyy = ""
zzz = None
}
}
{
xxx = 0
yyy = ""
zzz = None
}
] |
Me and my colleagues would be super happy if there was an alternate placement of let expected =
(Event.Author "john",
[ Event.Revenue
(Event.RevenueId "123",
Event.Match
{ transactionId = 1337
amounts =
{ total = 2500000L
vat = 625000L } }) ])
let xml =
element "supplier-invoice-writeable"
[ element "foreign-id" [ text foreignId ]
element "invoice-date" [ text "2020-02-02" ]
element "accounts"
[ element "account"
[ element "account-nr" [ text "1234" ]
element "amount" [ text (String.fromInt64 amount) ] ]
element "account"
[ element "account-nr" [ text "5678" ]
element "amount" [ text (String.fromInt64 -amount) ] ] ] ] When trying to add a new field or list item in the above examples, we spend a lot of time trying to figure out after which |
The originally reported issue has been implemented and can be used by setting There were other ideas and suggestions here as well, please create new issues for them. |
@nojaf Awesome, thank you! ❤️ I split out two new issues: |
Issue created from fantomas-ui
G-Research's internal coding guidelines specify that if a record spans more than one line, it should be split one entry per line, and the braces must be symmetrical. The original source below conforms to G-Research's coding standards; Fantomas's output does not. Could we please have a config option for this? I know that the official F# style guidelines say that this layout is "unusual", but it is the completely standard layout for us.
Code
Result
Options
Fantomas Next - 3.0.0-7/20/2019
IndentSpaceNum
4
PageWidth
80
PreserveEndOfLine
false
SemicolonAtEndOfLine
false
SpaceBeforeArgument
true
SpaceBeforeColon
true
SpaceAfterComma
true
SpaceAfterSemicolon
true
IndentOnTryWith
false
ReorderOpenDeclaration
false
SpaceAroundDelimiter
true
StrictMode
true
The text was updated successfully, but these errors were encountered: