Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions src/config.jl
Original file line number Diff line number Diff line change
Expand Up @@ -133,24 +133,23 @@ function collect_unmatched_keys!(
end

"""
get_config(manager::ConfigManager, key_path...)
get_config(manager::ConfigManager, key_path...) -> config

Retrieves the current configuration value.
Among the registered configuration files, fetches the value in order of priority.
If the key path does not exist in any of the configurations, returns `nothing`.

Even when the specified configuration is not explicitly set, a default value is returned,
so `config` is guaranteed to not be `nothing`.
"""
Base.@constprop :aggressive function get_config(manager::ConfigManager, key_path::Symbol...)
try
data = load(manager)
if is_static_setting(key_path...)
return getobjpath(data.static_settings, key_path...)
else
settings = get_settings(data)
return getobjpath(settings, key_path...)
end
catch e
e isa FieldError ? nothing : rethrow()
data = load(manager)
config = if is_static_setting(key_path...)
getobjpath(merge_setting(DEFAULT_CONFIG, data.static_settings), key_path...)
else
getobjpath(merge_setting(DEFAULT_CONFIG, get_settings(data)), key_path...)
end
@assert !isnothing(config) "Invalid default configuration values"
return config
end

function fix_static_settings!(manager::ConfigManager)
Expand Down
51 changes: 28 additions & 23 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -294,26 +294,29 @@ struct LSPostProcessor
end
LSPostProcessor() = LSPostProcessor(JET.PostProcessor())

# To extend configuration options, define new `@option struct`s here:
#
# @option struct NewConfig <: ConfigSection
# field1::Maybe{Type1} # Maybe{T} from Configurations.jl allows optional fields
# field2::Maybe{Type2}
# end
#
# All fields must be wrapped in `Maybe{}` to support partial configuration.
#
# Then, update the following methods properly to make the
# `is_static_setting(::Type{JETLSConfig}, field::Symbol)` and
# `default_config(::Type{JETLSConfig}) -> NewConfig` work correctly:
#
# - is_static_setting(::Type{NewConfig}, field::Symbol) -> Bool
# Returns whether a setting requires server restart.
# - default_config(::Type{NewConfig}) -> NewConfig
# Returns the default configuration values.
#
# Finally, add the new config section to `JETLSConfig` struct below.
"""
To extend configuration options, define new `@option struct`s here:

@option struct NewConfig <: ConfigSection
field1::Maybe{Type1} # Maybe{T} from Configurations.jl allows optional fields
field2::Maybe{Type2}
end

All fields must be wrapped in `Maybe{}` to distinguish between cases of
"no configuration set" and those of "user has set some configuration",
which are important for our configuration notification system to work.

Note that `TypeX` should not be defined to include the possibility of being `nothing` like `Union{Nothing,TypeX}`.
In such cases, a further inner configuration level should be used.

Then, overload the following methods properly:
- `is_static_setting(::Type{NewConfig}, field::Symbol) -> Bool`
Returns whether a setting requires server restart.
- `default_config(::Type{NewConfig}) -> NewConfig`
Returns the default configuration values.

Finally, add the new config section to `JETLSConfig` struct below.
"""
abstract type ConfigSection end

_unwrap_maybe(::Type{Maybe{S}}) where {S} = S
Expand Down Expand Up @@ -342,9 +345,7 @@ const FormatterConfig = Union{String,CustomFormatterConfig}

is_static_setting(::Type{FormatterConfig}) = false
is_static_setting(::Type{FormatterConfig}, ::Symbol) = false
function default_config(::Type{FormatterConfig})
return "Runic"
end
default_config(::Type{FormatterConfig}) = "Runic"

function default_executable(formatter::String)
if formatter == "Runic"
Expand Down Expand Up @@ -441,7 +442,11 @@ const DEFAULT_CONFIG = JETLSConfig(
internal = default_config(InternalConfig)
)

get_default_config(path::Symbol...) = getobjpath(DEFAULT_CONFIG, path...)
function get_default_config(path::Symbol...)
config = getobjpath(DEFAULT_CONFIG, path...)
@assert !isnothing(config) "Invalid default configuration values"
return config
end

const EMPTY_CONFIG = JETLSConfig()

Expand Down
15 changes: 5 additions & 10 deletions test/test_config.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ using JETLS
@test JETLS.get_default_config(:testrunner, :executable) ==
(@static Sys.iswindows() ? "testrunner.bat" : "testrunner")
@test JETLS.get_default_config(:formatter) == "Runic"

@test_throws FieldError JETLS.get_default_config(:nonexistent)
@test_throws FieldError JETLS.get_default_config(:full_analysis, :nonexistent)
end
Expand All @@ -25,13 +24,10 @@ using JETLS
testrunner=JETLS.TestRunnerConfig("base_runner"),
internal=JETLS.InternalConfig(10, 20)
)

overlay_config = JETLS.JETLSConfig(;
full_analysis=JETLS.FullAnalysisConfig(2.0),
testrunner=nothing,
internal=JETLS.InternalConfig(30, nothing)
)

merged = JETLS.merge_setting(base_config, overlay_config)

@test JETLS.getobjpath(merged, :full_analysis, :debounce) == 2.0
Expand All @@ -48,8 +44,7 @@ using JETLS
)
config2 = JETLS.JETLSConfig(;
full_analysis=JETLS.FullAnalysisConfig(2.0),
testrunner=JETLS.TestRunnerConfig("runner2"),
internal=nothing
testrunner=JETLS.TestRunnerConfig("runner2")
)
paths_called = []
JETLS.on_difference(config1, config2) do _, new_val, path
Expand Down Expand Up @@ -144,15 +139,15 @@ end

@test JETLS.get_config(manager, :full_analysis, :debounce) === 2.0
@test JETLS.get_config(manager, :testrunner, :executable) === "test_runner"
@test JETLS.get_config(manager, :non_existent_key) === nothing
@test_throws FieldError JETLS.get_config(manager, :nonexistent)

# Type stability check (N.B: Nothing is not allowed)
@test Base.infer_return_type((typeof(manager),)) do manager
JETLS.get_config(manager, :internal, :dynamic_setting)
end == Union{Nothing, Int}

end == Int
@test Base.infer_return_type((typeof(manager),)) do manager
JETLS.get_config(manager, :internal, :static_setting)
end == Union{Nothing, Int}
end == Int

# Test priority: file config has higher priority than LSP config
lsp_config = JETLS.JETLSConfig(;
Expand Down
Loading