Skip to content

Commit 9511fd0

Browse files
committed
Simplify server caching: remove redundant search index cache
Also update clear_file_contributions for the Hash-based comment_location: - Add keep_position: keyword for server re-parse (preserves key position) - Add rebuild_comment_from_location method - Server passes keep_position: true when re-parsing changed files
1 parent 34fb19b commit 9511fd0

File tree

4 files changed

+80
-46
lines changed

4 files changed

+80
-46
lines changed

lib/rdoc/code_object/class_module.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,18 @@ def search_snippet
747747
snippet(first_comment)
748748
end
749749

750+
##
751+
# Rebuilds +@comment+ from the current +@comment_location+ entries,
752+
# skipping any empty placeholders.
753+
754+
def rebuild_comment_from_location
755+
texts = @comment_location.each_value.flat_map { |comments|
756+
comments.filter_map { |c| c.to_s unless c.empty? }
757+
}
758+
merged = texts.join("\n---\n")
759+
@comment = merged.empty? ? '' : RDoc::Comment.new(merged)
760+
end
761+
750762
##
751763
# Sets the store for this class or module and its contained code objects.
752764

lib/rdoc/server.rb

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ def initialize(rdoc, port)
6868
@port = port
6969

7070
@generator = create_generator
71+
@template_dir = File.expand_path(@generator.template_dir)
7172
@page_cache = {}
72-
@search_index_cache = nil
7373
@last_change_time = Time.now.to_f
7474
@mutex = Mutex.new
7575
@running = false
@@ -199,9 +199,8 @@ def serve_asset(path)
199199
rel_path = path.delete_prefix("/")
200200
asset_path = File.join(@generator.template_dir, rel_path)
201201
real_asset = File.expand_path(asset_path)
202-
real_template = File.expand_path(@generator.template_dir)
203202

204-
unless real_asset.start_with?("#{real_template}/") && File.file?(real_asset)
203+
unless real_asset.start_with?("#{@template_dir}/") && File.file?(real_asset)
205204
return [404, 'text/plain', "Asset not found: #{rel_path}"]
206205
end
207206

@@ -258,7 +257,7 @@ def generate_page(name)
258257
when 'table_of_contents.html'
259258
@generator.generate_table_of_contents
260259
when 'js/search_data.js'
261-
build_search_index
260+
"var search_data = #{JSON.generate(index: @generator.build_search_index)};"
262261
else
263262
text_name = name.chomp('.html')
264263
class_name = text_name.gsub('/', '::')
@@ -271,29 +270,13 @@ def generate_page(name)
271270
end
272271
end
273272

274-
##
275-
# Builds the search index JavaScript.
276-
277-
def build_search_index
278-
@search_index_cache ||=
279-
"var search_data = #{JSON.generate(index: @generator.build_search_index)};"
280-
end
281-
282273
##
283274
# Injects the live-reload polling script before +</body>+.
284275

285276
def inject_live_reload(html, last_change_time)
286277
html.sub('</body>', "#{self.class.live_reload_script(last_change_time)}</body>")
287278
end
288279

289-
##
290-
# Clears all cached HTML pages and the search index.
291-
292-
def invalidate_all_caches
293-
@page_cache.clear
294-
@search_index_cache = nil
295-
end
296-
297280
##
298281
# Starts a background thread that polls source file mtimes and triggers
299282
# re-parsing when changes are detected.
@@ -373,7 +356,7 @@ def reparse_and_refresh(changed_files, removed_files)
373356
changed_files.each do |f|
374357
begin
375358
relative = @rdoc.relative_path_for(f)
376-
@store.clear_file_contributions(relative)
359+
@store.clear_file_contributions(relative, keep_position: true)
377360
@rdoc.parse_file(f)
378361
@file_mtimes[f] = File.mtime(f) rescue nil
379362
rescue => e
@@ -385,7 +368,7 @@ def reparse_and_refresh(changed_files, removed_files)
385368
@store.complete(@options.visibility)
386369

387370
@generator.refresh_store_data
388-
invalidate_all_caches
371+
@page_cache.clear
389372
@last_change_time = Time.now.to_f
390373
end
391374
end

lib/rdoc/store.rb

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ def remove_file(relative_name)
220220
# prevents duplication when the file is re-parsed while preserving
221221
# shared namespaces like +RDoc+ that span many files.
222222

223-
def clear_file_contributions(relative_name)
223+
def clear_file_contributions(relative_name, keep_position: false)
224224
top_level = @files_hash[relative_name]
225225
return unless top_level
226226

@@ -245,30 +245,33 @@ def clear_file_contributions(relative_name)
245245
cm.aliases.reject! { |a| a.file == top_level }
246246
cm.external_aliases.reject! { |a| a.file == top_level }
247247

248-
# Remove comment entries from this file and rebuild the comment
248+
# Clear or remove comment entries from this file
249249
if cm.is_a?(RDoc::ClassModule)
250-
cm.comment_location.reject! { |(_, loc)| loc == top_level }
251-
texts = cm.comment_location.map { |(c, _)| c.to_s }
252-
merged = texts.join("\n---\n")
253-
cm.instance_variable_set(:@comment,
254-
merged.empty? ? '' : RDoc::Comment.new(merged))
250+
if keep_position
251+
cm.comment_location[top_level] = [] if cm.comment_location.key?(top_level)
252+
else
253+
cm.comment_location.delete(top_level)
254+
end
255+
cm.rebuild_comment_from_location
255256
end
256257

257-
# Remove this file from the class/module's file list
258-
cm.in_files.delete(top_level)
259-
260-
# If no files contribute to this class/module anymore, remove it
261-
# from the store entirely. This handles file deletion correctly
262-
# for classes that are only defined in the deleted file, while
263-
# preserving classes that span multiple files.
264-
if cm.in_files.empty?
265-
if cm.is_a?(RDoc::NormalModule)
266-
@modules_hash.delete(cm.full_name)
267-
else
268-
@classes_hash.delete(cm.full_name)
258+
unless keep_position
259+
# Remove this file from the class/module's file list
260+
cm.in_files.delete(top_level)
261+
262+
# If no files contribute to this class/module anymore, remove it
263+
# from the store entirely. This handles file deletion correctly
264+
# for classes that are only defined in the deleted file, while
265+
# preserving classes that span multiple files.
266+
if cm.in_files.empty?
267+
if cm.is_a?(RDoc::NormalModule)
268+
@modules_hash.delete(cm.full_name)
269+
else
270+
@classes_hash.delete(cm.full_name)
271+
end
272+
cm.parent&.classes_hash&.delete(cm.name)
273+
cm.parent&.modules_hash&.delete(cm.name)
269274
end
270-
cm.parent&.classes_hash&.delete(cm.name)
271-
cm.parent&.modules_hash&.delete(cm.name)
272275
end
273276
end
274277

test/rdoc/rdoc_store_test.rb

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,9 +1128,8 @@ def test_clear_file_contributions_multi_file_class
11281128
assert_includes method_names, 'from_b'
11291129

11301130
# Comment from a.rb is removed, comment from b.rb remains
1131-
comment_files = klass.comment_location.map { |(_, loc)| loc }
1132-
assert_not_include comment_files, file_a
1133-
assert_includes comment_files, file_b
1131+
assert_not_include klass.comment_location.keys, file_a
1132+
assert_includes klass.comment_location.keys, file_b
11341133
end
11351134

11361135
def test_clear_file_contributions_cleans_methods_and_constants
@@ -1219,4 +1218,41 @@ def test_clear_file_contributions_nonexistent_file
12191218
@s.clear_file_contributions 'nonexistent.rb'
12201219
end
12211220

1221+
def test_clear_file_contributions_keep_position
1222+
file_a = @s.add_file 'a.rb'
1223+
file_b = @s.add_file 'b.rb'
1224+
1225+
klass = file_a.add_class RDoc::NormalClass, 'KeepPosClass'
1226+
klass.record_location file_a
1227+
klass.record_location file_b
1228+
file_a.add_to_classes_or_modules klass
1229+
file_b.add_to_classes_or_modules klass
1230+
1231+
klass.add_comment 'comment from a', file_a
1232+
klass.add_comment 'comment from b', file_b
1233+
1234+
@s.clear_file_contributions 'a.rb', keep_position: true
1235+
1236+
# Class is preserved
1237+
assert_includes @s.classes_hash, 'KeepPosClass'
1238+
1239+
# comment_location still has two entries (empty placeholder for a.rb)
1240+
assert_equal 2, klass.comment_location.size
1241+
assert_equal [file_a, file_b], klass.comment_location.keys
1242+
1243+
# The placeholder is an empty array
1244+
assert_equal [], klass.comment_location[file_a]
1245+
1246+
# in_files is not modified
1247+
assert_includes klass.in_files, file_a
1248+
assert_includes klass.in_files, file_b
1249+
1250+
# Simulate re-parse: add_comment appends to array at existing key position
1251+
klass.add_comment 'updated comment from a', file_a
1252+
1253+
# Order is preserved: a.rb first, b.rb second
1254+
assert_equal [file_a, file_b], klass.comment_location.keys
1255+
assert_equal 'updated comment from a', klass.comment_location[file_a].first.to_s
1256+
end
1257+
12221258
end

0 commit comments

Comments
 (0)