Skip to content
Open
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
16 changes: 16 additions & 0 deletions src/public.jl
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,20 @@ function remove_file!(jw::JuliaWorkspace, uri::URI)

process_from_dynamic(jw)
_remove_file!(jw, uri)
_evict_derived_for_uris!(jw, (uri,))
_reconcile!(jw)
end

# Evict all memoized derived values keyed by any of the given (removed) URIs.
# Without this they would stay in the Salsa storage for the lifetime of the
# workspace, since nothing queries a removed URI again.
function _evict_derived_for_uris!(jw::JuliaWorkspace, uris)
uri_set = Set{URI}(uris)
Salsa.evict_derived!(jw.runtime) do key
any(arg -> arg isa URI && arg in uri_set, key.args)
end
end

# Input-only removal. Does not drain results or reconcile.
function _remove_file!(jw::JuliaWorkspace, uri::URI)
files = input_files(jw.runtime)
Expand Down Expand Up @@ -293,14 +304,18 @@ function remove_all_children!(jw::JuliaWorkspace, uri::URI)

uri_as_string = string(uri)

removed = URI[]
for file in files
file_as_string = string(file)

if startswith(file_as_string, uri_as_string)
_remove_file!(jw, file)
push!(removed, file)
end
end

isempty(removed) || _evict_derived_for_uris!(jw, removed)

_reconcile!(jw)
end

Expand Down Expand Up @@ -346,6 +361,7 @@ function clear_indirect_file!(jw::JuliaWorkspace, uri::URI)
process_from_dynamic(jw)

_clear_indirect_tracking!(jw, uri)
_evict_derived_for_uris!(jw, (uri,))

_reconcile!(jw)
end
Expand Down
55 changes: 55 additions & 0 deletions test/test_eviction.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
@testitem "remove_file! evicts derived values for the removed URI" begin
using JuliaWorkspaces.URIs2: URI
using JuliaWorkspaces: Salsa

n_entries_for(jw, uri) =
Salsa.evict_derived!(key -> any(arg -> isequal(arg, uri), key.args), jw.runtime)

uri1 = URI("file:///evicttest/src/a.jl")
uri2 = URI("file:///evicttest/src/b.jl")

jw = JuliaWorkspace()
add_file!(jw, TextFile(uri1, SourceText("f(x) = x + 1", "julia")))
add_file!(jw, TextFile(uri2, SourceText("g(x) = x - 1", "julia")))

# Populate the derived caches.
get_diagnostic(jw, uri1)
get_diagnostic(jw, uri2)

remove_file!(jw, uri1)

# No derived entry may still be keyed by the removed URI. (The probe uses
# evict_derived! itself: it returns the number of matching entries.)
@test n_entries_for(jw, uri1) == 0

# The surviving file is untouched and still queryable.
@test get_diagnostic(jw, uri2) isa Vector
@test has_file(jw, uri2)
end

@testitem "remove_all_children! evicts derived values for all removed URIs" begin
using JuliaWorkspaces.URIs2: URI
using JuliaWorkspaces: Salsa

n_entries_for(jw, uri) =
Salsa.evict_derived!(key -> any(arg -> isequal(arg, uri), key.args), jw.runtime)

uris = [URI("file:///evictfolder/src/f$i.jl") for i in 1:5]
outside = URI("file:///other/src/g.jl")

jw = JuliaWorkspace()
for uri in uris
add_file!(jw, TextFile(uri, SourceText("h(x) = x", "julia")))
end
add_file!(jw, TextFile(outside, SourceText("h(x) = x", "julia")))

get_diagnostics(jw)

remove_all_children!(jw, URI("file:///evictfolder"))

for uri in uris
@test n_entries_for(jw, uri) == 0
end
@test has_file(jw, outside)
@test get_diagnostic(jw, outside) isa Vector
end
Loading