-
Notifications
You must be signed in to change notification settings - Fork 69
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
Implement BlueStyle #283
Comments
# Yes:
using A
using B
# No:
using A, B
# Yes:
using A
using B
# No:
using B
using A
# Yes:
using Example
Example.hello(x::Monster) = "Aargh! It's a Monster!"
Base.isreal(x::Ghost) = false
# No:
import Base: isreal
import Example: hello
hello(x::Monster) = "Aargh! It's a Monster!"
isreal(x::Ghost) = false By partially supported I mean: julia> str = """
import Example: hello
import Foo
""";
julia> print(format_text(str, import_to_using=true))
import Example: hello # <-- for BlueStyle should be `using Example: hello`, even though this can break code (see below)
using Foo: Foo
But I think that's fine for following BlueStyle, since the purpose of this rule is "to ensure that extension of a function is always explicit and on purpose", so we'd want CI to fail in this case. (I also think this is fine because I see JuliaFormatter as a great tool for "fixing up" code to follow the style guide you intended -- but realistically the code has to be written with some mind to that style in the first place -- not to entirely rewrite any possible given code (however far it deviates from the style guide) to have then be exactly as if rewritten by someone following the guide. If that makes sense....) |
julia> str = """
foo(x::Int64) = abs(x) + 3
foobar(array_data::AbstractArray{T}, item::T) where {T<:Int64} = T[
abs(x) * abs(item) + 3 for x in array_data
]
foobar(
array_data::AbstractArray{T},
item::T,
) where {T<:Int64} = T[abs(x) * abs(item) + 3 for x in array_data]
""";
julia> print(format_text(str; short_to_long_function_def=true))
foo(x::Int64) = abs(x) + 3
function foobar(array_data::AbstractArray{T}, item::T) where {T<:Int64}
T[abs(x) * abs(item) + 3 for x in array_data]
end
function foobar(array_data::AbstractArray{T}, item::T) where {T<:Int64}
T[abs(x) * abs(item) + 3 for x in array_data]
end
julia> str = """
function Foo(x, y)
new(x, y)
end
""";
julia> print(format_text(str; always_use_return=true))
function Foo(x, y)
return new(x, y)
end
|
julia> str = """
xy = foo(x; y = 3)
ab = foo(; a= 1, b= 2)
""";
julia> print(format_text(str; whitespace_in_kwargs=false))
xy = foo(x; y=3)
ab = foo(; a=1, b=2)
|
julia> str = """
spam( Ham{ T }( ), [ eggs ] )
""";
julia> print(format_text(str))
spam(Ham{T}(), [eggs])
julia> str = """
@show(x, y); x , y = y ;
""";
julia> print(format_text(str))
@show(x, y);
x, y = y;
julia> str = """
ham[1: 9]
ham[9 : -3: 1]
ham[lower : upper - 1]
ham[lower + offset:upper + offset]
""";
julia> print(format_text(str; whitespace_ops_in_indices=true))
ham[1:9]
ham[9:-3:1]
ham[lower:(upper - 1)]
ham[(lower + offset):(upper + offset)]
julia> str = """
x = 1
y = 2
long_variable = 30
""";
julia> print(format_text(str))
x = 1
y = 2
long_variable = 30
julia> str = """
i=j+1
submitted +=1
x^2<y
x->x>1
3!=2
1 : 2
1//2
""";
julia> print(format_text(str))
i = j + 1
submitted += 1
x^2 < y # <-- Nice!
x -> x > 1
3 != 2
1:2
1 // 2 # <-- BlueStyle would prefer 1//2 here, i think If we want |
Whitespace (continued)
julia> str = """
domath(x::Number) = x + 5
domath(x::Int) = x + 10
function domath(x::String)
return "A string is a one-dimensional extended object postulated in string theory."
end
dophilosophy() = "Why?"
""";
julia> print(format_text(str; remove_extra_newlines=true))
domath(x::Number) = x + 5
domath(x::Int) = x + 10
function domath(x::String)
return "A string is a one-dimensional extended object postulated in string theory."
end
dophilosophy() = "Why?"
julia> str = """
f(
a,
b,
)
constraint = conic_form!(SOCElemConstraint(temp2 + temp3,
temp2 - temp3, 2 * temp1),
unique_conic_forms)
""";
julia> print(format_text(str))
f(a, b)
constraint = conic_form!(
SOCElemConstraint(temp2 + temp3, temp2 - temp3, 2 * temp1),
unique_conic_forms,
) julia> str = """
f(
a,
b,
)
constraint = conic_form!(SOCElemConstraint(temp2 + temp3,
temp2 - temp3, 2 * temp1, "other reallllllly long arg"),
unique_conic_forms)
""";
julia> print(format_text(str))
f(a, b)
constraint = conic_form!(
SOCElemConstraint(
temp2 + temp3,
temp2 - temp3,
2 * temp1,
"other reallllllly long arg",
),
unique_conic_forms,
) (^ I'm so impressed by this) |
Whitespace (continued again)
|
First it places the RHS on the next line and if that's not gonna it lifts the function call
|
yah, either of these would be okay: quite_long_variable_that_takes_up_space = f(
"argument doesn't fit on that first line because now it's longer", 2
) quite_long_variable_that_takes_up_space = f(
"argument doesn't fit on that first line because now it's longer",
2,
) (maybe the second one is preferable, but crucially: the Is there a way to get that result already (I couldn't find one), or is it something we'd need to add for BlueStyle? Running quite_long_variable_that_takes_up_space = f(
"argument doesn't fit on that first line because now it's longer",
2,
) currently alters it and returns quite_long_variable_that_takes_up_space =
f("argument doesn't fit on that first line because now it's longer", 2) |
It would probably be |
Whiespace (continued again, again)
julia> str = """
arr1 = [
1,
2,
]
arr2 = [
"realllllllllllllllllllllllllllllly long one",
"realllllllllllllllllllllllllllllly long two"
]
tup = (
1,
)
""";
julia> print(format_text(str))
arr1 = [1, 2]
arr2 = [
"realllllllllllllllllllllllllllllly long one",
"realllllllllllllllllllllllllllllly long two",
]
tup = (1,)
# Yes:
str2 = """
hello
world!
"""
# No:
str2 = """
hello
world!
"""
str2 =
"""
hello
world!
"""
julia> str = """
function foo(bar::Int64, baz::Int64)
return bar + baz
end
function foo(bar::In64, baz::Int64)
return bar + baz
end
""";
julia> print(format_text(str; remove_extra_newlines=true))
function foo(bar::Int64, baz::Int64)
return bar + baz
end
function foo(bar::In64, baz::Int64)
return bar + baz
end should both become: function foo(bar::In64, baz::Int64)
return bar + baz
end |
julia> str = """
xy = (x = 1, y = 2)
""";
julia> print(format_text(str, whitespace_in_kwargs=false))
xy = (x=1, y=2)
julia> str = """
xy = (; x = 1, y = 2)
""";
julia> print(format_text(str, whitespace_in_kwargs=false))
xy = (; x=1, y=2) # <-- should be `xy = (x=1, y=2)`, under current guidance, apparently
|
julia> str = ".1";
julia> print(format_text(str))
0.1
julia> str = "2.";
julia> print(format_text(str))
2.0 julia> str = "3.f0";
julia> print(format_text(str))
3.f0 Desired output is julia> 3.f0
3.0f0 |
# Yes:
foobar = foo == 2 ? bar : baz
# No:
foobar = foo == 2 ?
bar :
baz Currently supported for the case where it can fit on one line julia> str = """
foobar = foo == 2 ?
bar :
baz
""";
julia> print(format_text(str))
foobar = foo == 2 ? bar : baz But i think BlueStyle prefers # Yes:
foobar = if some_big_long_thing * 10_000 == 2
bar
else
another_big_long_thing * 10^300 / this_things_here
end
# No:
foobar = some_big_long_thing * 10_000 == 2 ?
bar :
another_big_long_thing * 10^300 / this_things_here whereas JuliaFormatter goes to: julia> str = """
foobar = some_big_long_thing * 10_000 == 2 ?
bar :
another_big_long_thing * 10^300 / this_things_here
""";
julia> print(format_text(str))
foobar = some_big_long_thing * 10_000 == 2 ? bar :
another_big_long_thing * 10^300 / this_things_here
# Yes
foobar = if foo == 2
bar
elseif foo == 3
qux
else
baz
end
# No
foobar = foo == 2 ? bar : foo == 3 ? qux : baz |
julia> str = """
for i = 1:10
#...
end
[foo(x) for x ∈ xs]
""";
julia> print(format_text(str, always_for_in=true))
for i in 1:10
#...
end
[foo(x) for x in xs] |
Before I write this bit out, @domluna can JuliaFormatter move or edit comments? looooooooong_variable = 10_000_000 # make this quite a big number so it takes up some space in the line to # make this quite a big number so it takes up some space in the line
looooooooong_variable = 10_000_000 ? |
Right now it just indents and dedents comments inline comments like these are represented as their own node type so it probably wouldn't be too difficult to do this, I'm guessing it would depend on how general or niche this transform is, meaning is this done in all situations or only a handful? |
in cases where the inline comment would take the line length past the 92 char limit. Here are the guidelines for comments:
# Yes
# make this quite a big number so it takes up some space in the line
looooooooong_variable = 10_000_000
# No
looooooooong_variable = 10_000_000 # make this quite a big number so it takes up some space in the line
# Yes:
# Number of nodes to predict. Again, an issue with the workflow order.
# Should be updated after data is fetched.
p = 1
# No:
p = 1 # Number of nodes to predict. Again, an issue with the workflow order.
# Should be updated after data is fetched. |
this case might be a pain to implement. I would be more comfortable leaving this up to the user. |
Yeah, I think that's fair enough. Let's leave this one out :) |
By the way, the white-space rules for function definitions in BlueStyle allow quite a lot of flexibility. In particular, it supports the idea you proposed here, Dom: jrevels/YASGuide#16 (comment)) Right now JuliaFormatter does this, which is consistent with BlueStyle julia> str = """
function long_name_of_function_because_i_am_writing_an_example(
arg1, arg2, arg3, arg4, arg5, arg6,
)
# code
end
""";
julia> print(format_text(str))
function long_name_of_function_because_i_am_writing_an_example(
arg1,
arg2,
arg3,
arg4,
arg5,
arg6,
)
# code
end but the original code actually fit with in the line limit (once all the args were on that second line) julia> print(format_test(str; style=BlueStyle()) # hypothetical
function long_name_of_function_because_i_am_writing_an_example(
arg1, arg2, arg3, arg4, arg5, arg6,
)
# code
end But then have the current behaviour if it'd go beyond the line limits to have all the args on one line e.g. julia> str = """
function long_name_of_function_because_i_am_writing_an_example(
arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12
)
# code
end
""";
julia> print(format_text(str))
function long_name_of_function_because_i_am_writing_an_example(
arg1,
arg2,
arg3,
arg4,
arg5,
arg6,
arg7,
arg8,
arg9,
arg10,
arg11,
arg12,
)
# code
end |
@nickrobinson251 which ones are higher priority than others? I'm thinking it may be better to implement these incrementally instead of doing 1 big PR |
Yeah, that sounds better to me too.
I'd say top three priorities, in order, are
for context:
3 is high priority based on this being the most common style reminder made in code-reviews on codebases following BlueStyle (at least the most common that the Formatter doesn't already help with, and based on my memory of code reviews), so first on the list of feature that'd be nice to have, i think. |
@nickrobinson251 does this also apply to short function definitions? |
Since we have "Only use short-form function definitions when they fit on a single line", this should never come up for short-form functions (i.e. if we're wondering how to put a short-form function over multiple lines, we should instead make it a long-form function) |
Using v0.9.1, this code gets changed to something not quite right (from https://github.com/invenia/LibPQ.jl/pull/197/files/0e908dfb097273fe1eb87050c4f0d99d071232ed#r494597229) julia> str = """
Dict(
"options" => join(
imap(Iterators.filter(keep_option, connection_options)) do (k, v)
"-c k=(show_option(v))"
end,
" ",
),
)
""";
julia> println(format_text(str, BlueStyle()))
Dict(
"options" => join(imap(Iterators.filter(keep_option, connection_options)) do (k, v)
"-c k=(show_option(v))"
end, " "),
) Since in the changed version the
Instead, the |
So in an attempt to generalize this is it implied that if an argument spans multiple lines (the |
i'mma tag in @iamed2 on this one ^ |
Yep that's right |
woops apparently GH doesn't distinguish between issues and comments in issues |
746c456 disallows chained ternary |
94cb799 implements |
What about in all cases except for modules? |
Sounds perfect 🎊 |
This doesn't see quite right: given this code that goes over the line limit df[:, :some_column] = [some_big_function_name(blahhh) for (fooooo, blahhh) in my_long_list_of_vars] Ideally it'd be changed to df[:, :some_column] = [
some_big_function_name(blahhh) for (fooooo, blahhh) in my_long_list_of_vars
] But the (using JuliaFormatter v0.10) julia> str = """
df[:, :some_column] = [some_big_function_name(blahhh) for (fooooo, blahhh) in my_long_list_of_vars]
""";
julia> println(format_text(str, BlueStyle()))
df[
:, :some_column
] = [some_big_function_name(blahhh) for (fooooo, blahhh) in my_long_list_of_vars]
julia> println(format_text(str)) # DefaultStyle, for comparison
df[:, :some_column] =
[some_big_function_name(blahhh) for (fooooo, blahhh) in my_long_list_of_vars] I think we just never want to break-up the left-hand side of the assignment operator |
makes sense, that's what YASStyle does we can just dispatch that 1 fixed |
I'm trying out the formatter (v0.10.1) on a few repos, and it's pretty amazing. Great work! Here's one case where i can't help but feel it's made the code less clean/clear... not sure what we can do about it julia> str = """
function f()
for i in 1:n
@inbounds mul!(
reshape(view(C, :, i), eye_n, k),
reshape(view(B, :, i), eye_n, l),
transpose(A),
)
end
end
""";
julia> println(format_text(str, BlueStyle()))
function f()
for i in 1:n
@inbounds mul!(
reshape(view(C, :, i), eye_n, k), reshape(view(B, :, i), eye_n, l), transpose(
A
)
)
end
end I wonder if the rule "if the function call doesn't fit on one line, then try to fit all the args on the next line, otherwise put them on a line each" that we use for function definitions should only apply to definitions, not also to callsites? function long_name_of_function_because_i_am_writing_an_example(
arg1, arg2, arg3, arg4, arg5, arg6
)
# code
end is allowed/good should not be applied to the call, i.e. we'd still want return_var = long_name_of_function_because_i_am_writing_an_example(
arg1,
arg2,
arg3,
arg4,
arg5,
arg6,
) Or at least, if it's written in this arg-on-multiple-lines way, the formatter shouldn't format it into the args-on-a-single-line (unless the whole function call fits on a single line i.e. What does @iamed2 think? |
That actually looks like a bug tbh UPDATE: resolved as of v0.10.2 |
This issue is to organise and track efforts to implement BlueStyle as a Custom Style. Similar to how YASGuide is now available in JuliaFormatter via YASStyle. (see issue #198 and PRs #214, #217).
I think the first step is to come up with a list of cases where BlueStyle recommends a style change that is currently not available via DefaultStyle or YASStyle...
TO DOs (see comment below for details/examples):
using
andimport statements
to the top #284)using
overimport
Global Variables (not possible?)Function Naming (not possible)do
blocks) (second highest priority) (BlueStyle - implement top 3 issues #295)return nothing
rather than barereturn
(94cb799);
semicolon (third highest priority) (BlueStyle - implement top 3 issues #295):
in ranges, add brackets to clarify^
//
(Give//
the same binaryop whitespace exception as^
#288)end
)Blank lines before return (pending Simplify some current guidance to "have a blank line between multi-line blocks"? JuliaDiff/BlueStyle#61)=
character in NamedTuples should be spaced as in keyword arguments (i.e. likex=1
, no spaces)NamedTuple()
not(;)
(also see related issue JuliaFormatter changes empty NamedTuple(;)
to empty Tuple()
#285)No leading;
(but pending Relax opinion on use of;
in NamedTuples? JuliaDiff/BlueStyle#62)Float64
numbers should always include a leading and/or trailing zeroFloat32
numbers should always include a leading and/or trailing zero (see Float32 edge cases of leading/trailing0
in floating points #286)in
, never=
or∈
.Type annotation (not possible for BlueStyle advice)Package version specifications (not possible)#
.Still to look into:
Could maybe also look into, but leaving these for now:
Going through this i realise that BlueStyle has a lot of things that cannot be automated, and that is fine. I think the intended use case for the formatter is an environment where collaborators know what style is intended and attempt to follow it, but don't have to sweat the details and don't have to rely on code review to fix whitespace style issues. I think this also means that its much more important that the formatter doesn't make "good" style into "bad" style, than it is to make all "bad" cases "good" (and all cases wouldn't be realistic anyway).
The text was updated successfully, but these errors were encountered: