From 0c163304b0a0969993d9f112edc08ae15912b63f Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Fri, 31 Oct 2025 13:05:18 +0000 Subject: [PATCH 1/3] Add AGENTS.md and CLAUDE.md to the project (#1439) I've been using AI coding agents to assist my development in RDoc for a while. And having these instructions can save me (or anyone who wants to contribute) quite a bit of time. `AGENTS.md` is what most agentic tools use, such as OpenAI Codex or Cursor. `CLAUDE.md` is only for Claude Code, which is the main tool I use. --- .rdoc_options | 5 ++ AGENTS.md | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 12 +++ 3 files changed, 240 insertions(+) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.rdoc_options b/.rdoc_options index 2a7a20f0ff..1593d904d3 100644 --- a/.rdoc_options +++ b/.rdoc_options @@ -3,3 +3,8 @@ title: rdoc Documentation main_page: README.md autolink_excluded_words: - RDoc + +exclude: +- AGENTS.md +- CLAUDE.md +- lib/rdoc/markdown.kpeg diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..360a2cd65b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,223 @@ +# RDoc Project Guide for AI Agents + +## Project Overview + +**RDoc** is Ruby's default documentation generation tool that produces HTML and command-line documentation for Ruby projects. It parses Ruby source code, C extensions, and markup files to generate documentation. + +- **Repository:** https://github.com/ruby/rdoc +- **Homepage:** https://ruby.github.io/rdoc +- **Required Ruby:** See the version specified in gemspec +- **Main Executables:** `rdoc` and `ri` + +## Key Development Commands + +### Testing + +```bash +# Run all tests (default task) +bundle exec rake + +# Run unit tests only (excludes RubyGems integration) +bundle exec rake normal_test + +# Run RubyGems integration tests only +bundle exec rake rubygems_test + +# Verify generated parser files are current (CI check) +bundle exec rake verify_generated +``` + +**Test Framework:** Test::Unit with `test-unit` gem +**Test Location:** `test/` directory +**Test Helper:** `test/lib/helper.rb` + +### Linting + +#### RuboCop (Ruby Linting) + +```bash +# Check Ruby code style +bundle exec rubocop + +# Auto-fix style issues +bundle exec rubocop -A +``` + +**Configuration:** `.rubocop.yml` + +- Target Ruby: 3.0 +- Minimal cop set (opt-in approach) +- Excludes generated parser files + +#### Herb Linter (ERB/RHTML Files) + +```bash +# Lint ERB template files +npx @herb-tools/linter "**/*.rhtml" + +# Lint specific directory +npx @herb-tools/linter "lib/**/*.rhtml" +``` + +**Template Location:** `lib/rdoc/generator/template/**/*.rhtml` +**CI Workflow:** `.github/workflows/lint.yml` + +### Documentation Generation + +```bash +# Generate documentation (creates _site directory) +bundle exec rake rdoc + +# Force regenerate documentation +bundle exec rake rerdoc + +# Show documentation coverage +bundle exec rake rdoc:coverage +bundle exec rake coverage +``` + +**Output Directory:** `_site/` (GitHub Pages compatible) +**Configuration:** `.rdoc_options` + +### Parser Generation + +RDoc uses generated parsers for Markdown and RD formats: + +```bash +# Generate all parser files from sources +bundle exec rake generate + +# Remove generated parser files +bundle exec rake clean +``` + +**Generated Files:** + +- `lib/rdoc/rd/block_parser.rb` (from `.ry` via racc) +- `lib/rdoc/rd/inline_parser.rb` (from `.ry` via racc) +- `lib/rdoc/markdown.rb` (from `.kpeg` via kpeg) +- `lib/rdoc/markdown/literals.rb` (from `.kpeg` via kpeg) + +**Note:** These files are auto-generated and should not be edited manually. Always regenerate after modifying source `.ry` or `.kpeg` files. + +### Building and Releasing + +```bash +# Build gem package +bundle exec rake build + +# Install gem locally +bundle exec rake install + +# Create tag and push to rubygems.org +bundle exec rake release +``` + +## Project Structure + +```sh +lib/rdoc/ +├── rdoc.rb # Main entry point (RDoc::RDoc class) +├── version.rb # Version constant +├── task.rb # Rake task integration +├── parser/ # Source code parsers (Ruby, C, Markdown, RD) +│ ├── ruby.rb # Ruby code parser +│ ├── c.rb # C extension parser +│ ├── prism_ruby.rb # Prism-based Ruby parser +│ └── ... +├── generator/ # Documentation generators +│ ├── darkfish.rb # HTML generator (default theme) +│ ├── markup.rb # Markup format generator +│ ├── ri.rb # RI command generator +│ └── template/darkfish/ # ERB templates (.rhtml files) +├── markup/ # Markup parsing and formatting +├── code_object/ # AST objects for documented items +├── markdown/ # Markdown parsing +├── rd/ # RD format parsing +└── ri/ # RI (Ruby Info) tool + +test/ # 79 test files +├── lib/helper.rb # Test helpers +└── rdoc/ # Main test directory + +exe/ +├── rdoc # rdoc command executable +└── ri # ri command executable +``` + +## Important Files + +### Configuration + +- `.rubocop.yml` - RuboCop configuration (main) +- `.generated_files_rubocop.yml` - RuboCop config for generated files +- `.rdoc_options` - RDoc generation options +- `.document` - File list for documentation +- `Rakefile` - Task definitions +- `lib/rdoc/task.rb` - Task definitions provided by RDoc +- `rdoc.gemspec` - Gem specification +- `Gemfile` - Development dependencies + +### CI/CD + +- `.github/workflows/test.yml` - Test execution across Ruby versions/platforms +- `.github/workflows/lint.yml` - Linting (RuboCop + Herb) +- `.github/workflows/push_gem.yml` - Gem publishing + +### Documentation + +- `README.md` - Basic usage guide +- `ExampleRDoc.rdoc` - RDoc markup examples +- `doc/rdoc/markup_reference.rb` - RDoc markup references +- `ExampleMarkdown.md` - Markdown examples + +## Architecture Notes + +### Pluggable System + +- **Parsers:** Ruby, C, Markdown, RD, Prism-based Ruby (experimental) +- **Generators:** HTML/Darkfish, RI, POT (gettext), JSON, Markup + +## Common Workflows + +Do NOT commit anything. Ask the developer to review the changes after tasks are finished. + +NEVER pushes code to any repositories. + +### Making Code Changes + +Use Red, Green, Refactor approach: + +1. **Ensure Ruby version**: Verify you're using Ruby 3.3.0+ (prepend `chruby ` if needed) +2. **Red - Write failing tests**: Add tests that fail for the new behavior +3. **Verify failure**: Run `bundle exec rake` to confirm tests fail as expected +4. **Green - Make it work**: Implement the minimum code to make tests pass +5. **Refactor - Make it right**: Improve code quality while keeping tests green + - Run `bundle exec rake` after each refactor to ensure tests still pass + - Iterate on steps 4-5 as needed +6. **Lint your changes**: + - Ruby code: `bundle exec rubocop -A` (auto-fix when possible) + - ERB templates: `npx @herb-tools/linter "**/*.rhtml"` (if modified) + +### Modifying Parsers + +1. Edit source files (`.ry` or `.kpeg`) +2. Regenerate: `bundle exec rake generate` +3. Verify: `bundle exec rake verify_generated` +4. Run tests: `bundle exec rake` + +### Updating Documentation + +1. Modify documentation comments in source +2. Regenerate: `bundle exec rake rerdoc` +3. Check output in `_site/` directory +4. Check coverage: `bundle exec rake coverage` + +## Notes for AI Agents + +1. **Always run tests** after making changes: `bundle exec rake` +2. **Check both RuboCop and Herb** for linting +3. **Regenerate parsers** if you modify `.ry` or `.kpeg` files +4. **Use `rake rerdoc`** to regenerate documentation (not just `rdoc`) +5. **Verify generated files** with `rake verify_generated` +6. **Don't edit generated files** directly (in `lib/rdoc/markdown/` and `lib/rdoc/rd/`) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..164dc8bcfe --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,12 @@ +# Claude Code Instructions for RDoc + +Please refer to `AGENTS.md` for comprehensive project documentation, including: + +- Rake tasks and workflows +- Testing setup +- Linting configuration +- Project structure +- Development commands +- CI/CD information + +All project-specific instructions and guidelines are maintained in `AGENTS.md`. From 16ae9e83abdee2d066ffc16d58c901d43cbede8b Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Fri, 31 Oct 2025 14:16:22 +0000 Subject: [PATCH 2/3] Improve search result by prioritizing exact matches (#1433) I know adding 2 new phases for exact matches feels like a workaround, but without a more thorough review on the entire searching algorithm this is the best solution I have. I also added basic tests for `search.js` using `mini_racer` gem (dev dependency) to simulate JS evaluation. I chose this approach because it should be simpler than maintaining a whole set of JS dependencies and setups. Fixes #1194 --- Gemfile | 6 + .../template/json_index/js/searcher.js | 43 ++- ...rdoc_generator_json_index_searcher_test.rb | 343 ++++++++++++++++++ 3 files changed, 387 insertions(+), 5 deletions(-) create mode 100644 test/rdoc/rdoc_generator_json_index_searcher_test.rb diff --git a/Gemfile b/Gemfile index 2a54c09b14..957cbbd227 100644 --- a/Gemfile +++ b/Gemfile @@ -11,3 +11,9 @@ gem 'rubocop', '>= 1.31.0' gem 'gettext' gem 'prism', '>= 0.30.0' gem 'webrick' + +platforms :ruby do + if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.2') + gem 'mini_racer' # For testing the searcher.js file + end +end diff --git a/lib/rdoc/generator/template/json_index/js/searcher.js b/lib/rdoc/generator/template/json_index/js/searcher.js index f1ffe8cffe..8f03f67a93 100644 --- a/lib/rdoc/generator/template/json_index/js/searcher.js +++ b/lib/rdoc/generator/template/json_index/js/searcher.js @@ -29,7 +29,7 @@ Searcher.prototype = new function() { var results = performSearch(_this.data, regexps, queries, highlighters, state); - var hasMore = (state.limit > 0 && state.pass < 4); + var hasMore = (state.limit > 0 && state.pass < 6); triggerResults.call(_this, results, !hasMore); if (hasMore) { @@ -85,6 +85,30 @@ Searcher.prototype = new function() { /* ----- Mathchers ------ */ + /* + * This record matches if both the index and longIndex exactly equal queries[0] + * and the record matches all of the regexps. This ensures top-level exact matches + * like "String" are prioritized over nested classes like "Gem::Module::String". + */ + function matchPassExact(index, longIndex, queries) { + return index == queries[0] && longIndex == queries[0]; + } + + /* + * This record matches if the index without "()" exactly equals queries[0]. + * This prioritizes methods like "attribute()" when searching for "attribute". + */ + function matchPassExactMethod(index, longIndex, queries, regexps) { + var indexWithoutParens = index.replace(/\(\)$/, ''); + if (indexWithoutParens != queries[0]) return false; + if (index === indexWithoutParens) return false; // Not a method (no parens to remove) + for (var i=1, l = regexps.length; i < l; i++) { + if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) + return false; + }; + return true; + } + /* * This record matches if the index starts with queries[0] and the record * matches all of the regexps @@ -192,17 +216,26 @@ Searcher.prototype = new function() { var togo = CHUNK_SIZE; var matchFunc, hltFunc; - while (state.pass < 4 && state.limit > 0 && togo > 0) { + var isLowercaseQuery = queries[0] === queries[0].toLowerCase(); + + while (state.pass < 6 && state.limit > 0 && togo > 0) { + // When query is lowercase, prioritize methods over classes if (state.pass == 0) { - matchFunc = matchPassBeginning; + matchFunc = isLowercaseQuery ? matchPassExactMethod : matchPassExact; hltFunc = highlightQuery; } else if (state.pass == 1) { - matchFunc = matchPassLongIndex; + matchFunc = isLowercaseQuery ? matchPassExact : matchPassExactMethod; hltFunc = highlightQuery; } else if (state.pass == 2) { - matchFunc = matchPassContains; + matchFunc = matchPassBeginning; hltFunc = highlightQuery; } else if (state.pass == 3) { + matchFunc = matchPassLongIndex; + hltFunc = highlightQuery; + } else if (state.pass == 4) { + matchFunc = matchPassContains; + hltFunc = highlightQuery; + } else if (state.pass == 5) { matchFunc = matchPassRegexp; hltFunc = highlightRegexp; } diff --git a/test/rdoc/rdoc_generator_json_index_searcher_test.rb b/test/rdoc/rdoc_generator_json_index_searcher_test.rb new file mode 100644 index 0000000000..36613e57c4 --- /dev/null +++ b/test/rdoc/rdoc_generator_json_index_searcher_test.rb @@ -0,0 +1,343 @@ +# frozen_string_literal: true + +require_relative 'helper' + +return if RUBY_DESCRIPTION =~ /truffleruby/ || RUBY_DESCRIPTION =~ /jruby/ + +begin + require 'mini_racer' +rescue LoadError + return +end + +# This test is a simpler setup for testing the searcher.js file without pulling all the JS dependencies. +# If there are more JS functionalities to test in the future, we can move to use JS test frameworks. +class RDocGeneratorJsonIndexSearcherTest < Test::Unit::TestCase + def setup + @context = MiniRacer::Context.new + + # Add RegExp.escape polyfill to avoid `RegExp.escape is not a function` error + @context.eval(<<~JS) + RegExp.escape = function(string) { + return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'); + }; + JS + + searcher_js_path = File.expand_path( + '../../lib/rdoc/generator/template/json_index/js/searcher.js', + __dir__ + ) + searcher_js = File.read(searcher_js_path) + @context.eval(searcher_js) + end + + def teardown + @context.dispose + end + + def test_exact_match_prioritized + results = run_search( + query: 'string', + data: { + searchIndex: ['string', 'string', 'strings'], + longSearchIndex: ['gem::safemarshal::elements::string', 'string', 'strings'], + info: [ + ['String', 'Gem::SafeMarshal::Elements', 'Gem/SafeMarshal/Elements/String.html', '', 'Nested String class', '', 'class'], + ['String', '', 'String.html', '', 'Top-level String class', '', 'class'], + ['Strings', '', 'Strings.html', '', 'Strings class', '', 'class'] + ] + } + ) + + assert_equal 3, results.length + # Top-level String should come first despite being second in the array + assert_equal 'String', strip_highlights(results[0]['title']) + assert_equal '', results[0]['namespace'], 'Top-level String should be prioritized over nested String' + assert_equal 'String.html', results[0]['path'] + + # Nested String should come second + assert_equal 'String', strip_highlights(results[1]['title']) + assert_equal 'Gem::SafeMarshal::Elements', strip_highlights(results[1]['namespace']) + end + + def test_exact_method_match + results = run_search( + query: 'attribute', + data: { + searchIndex: ['attributemanager', 'attributes', 'attribute()'], + longSearchIndex: ['rdoc::markup::attributemanager', 'rdoc::markup::attributes', 'rdoc::markup::attributemanager#attribute()'], + info: [ + ['AttributeManager', 'RDoc::Markup', 'RDoc/Markup/AttributeManager.html', '', 'AttributeManager class', '', 'class'], + ['Attributes', 'RDoc::Markup', 'RDoc/Markup/Attributes.html', '', 'Attributes class', '', 'class'], + ['attribute', 'RDoc::Markup::AttributeManager', 'RDoc/Markup/AttributeManager.html#method-i-attribute', '()', 'Attribute method', '', 'method'] + ] + } + ) + + assert_equal 3, results.length + # attribute() method should come first despite being last in the array + assert_equal 'attribute', strip_highlights(results[0]['title']) + assert_equal 'RDoc::Markup::AttributeManager', strip_highlights(results[0]['namespace']) + end + + def test_capitalized_query_prioritizes_exact_class + results = run_search( + query: 'String', + data: { + searchIndex: ['string', 'string()'], + longSearchIndex: ['string', 'object#string()'], + info: [ + ['String', '', 'String.html', '', 'String class', '', 'class'], + ['string', 'Object', 'Object.html#method-i-string', '()', 'String method', '', 'method'] + ] + } + ) + + assert_equal 2, results.length + # Capitalized query: exact class (Pass 0) beats exact method (Pass 1) + assert_equal 'String', strip_highlights(results[0]['title']) + assert_equal '', results[0]['namespace'] + assert_equal 'String.html', results[0]['path'] + + # Method comes second + assert_equal 'string', strip_highlights(results[1]['title']) + assert_equal 'Object', strip_highlights(results[1]['namespace']) + end + + def test_lowercase_query_prioritizes_method + results = run_search( + query: 'options', + data: { + searchIndex: ['options', 'options()'], + longSearchIndex: ['rdoc::options', 'rdoc::codeobject#options()'], + info: [ + ['Options', 'RDoc', 'RDoc/Options.html', '', 'Options class', '', 'class'], + ['options', 'RDoc::CodeObject', 'RDoc/CodeObject.html#method-i-options', '()', 'Options method', '', 'method'] + ] + } + ) + + assert_equal 2, results.length + # Lowercase query should prioritize method over class + assert_equal 'options', strip_highlights(results[0]['title']) + assert_equal 'RDoc::CodeObject', strip_highlights(results[0]['namespace']) + assert_equal 'RDoc/CodeObject.html#method-i-options', results[0]['path'] + + # Class comes second + assert_equal 'Options', strip_highlights(results[1]['title']) + assert_equal 'RDoc', strip_highlights(results[1]['namespace']) + end + + def test_beginning_match + results = run_search( + query: 'attr', + data: { + searchIndex: ['attribute()', 'attributemanager', 'generator'], + longSearchIndex: ['rdoc::markup#attribute()', 'rdoc::markup::attributemanager', 'rdoc::generator'], + info: [ + ['attribute', 'RDoc::Markup', 'RDoc/Markup.html#method-i-attribute', '()', 'Attribute method', '', 'method'], + ['AttributeManager', 'RDoc::Markup', 'RDoc/Markup/AttributeManager.html', '', 'Manager class', '', 'class'], + ['Generator', 'RDoc', 'RDoc/Generator.html', '', 'Generator class', '', 'class'] + ] + } + ) + + assert_equal 2, results.length + assert_equal 'attribute', strip_highlights(results[0]['title']) + assert_equal 'AttributeManager', strip_highlights(results[1]['title']) + end + + def test_long_index_match + results = run_search( + query: 'rdoc::markup', + data: { + searchIndex: ['attributes', 'parser'], + longSearchIndex: ['rdoc::markup::attributes', 'rdoc::parser'], + info: [ + ['Attributes', 'RDoc::Markup', 'RDoc/Markup/Attributes.html', '', 'Attributes class', '', 'class'], + ['Parser', 'RDoc', 'RDoc/Parser.html', '', 'Parser class', '', 'class'] + ] + } + ) + + assert_equal 1, results.length + assert_equal 'Attributes', strip_highlights(results[0]['title']) + assert_equal 'RDoc::Markup', strip_highlights(results[0]['namespace']) + end + + def test_contains_match + results = run_search( + query: 'manager', + data: { + searchIndex: ['attributemanager', 'parser'], + longSearchIndex: ['rdoc::markup::attributemanager', 'rdoc::parser'], + info: [ + ['AttributeManager', 'RDoc::Markup', 'RDoc/Markup/AttributeManager.html', '', 'Manager class', '', 'class'], + ['Parser', 'RDoc', 'RDoc/Parser.html', '', 'Parser class', '', 'class'] + ] + } + ) + + assert_equal 1, results.length + assert_equal 'AttributeManager', strip_highlights(results[0]['title']) + end + + def test_regexp_match + results = run_search( + query: 'atrbt', + data: { + searchIndex: ['attribute()', 'generator'], + longSearchIndex: ['rdoc::markup#attribute()', 'rdoc::generator'], + info: [ + ['attribute', 'RDoc::Markup', 'RDoc/Markup.html#method-i-attribute', '()', 'Attribute method', '', 'method'], + ['Generator', 'RDoc', 'RDoc/Generator.html', '', 'Generator class', '', 'class'] + ] + } + ) + + assert_equal 1, results.length + assert_equal 'attribute', strip_highlights(results[0]['title']) + end + + def test_empty_query + results = run_search( + query: '', + data: { + searchIndex: ['string'], + longSearchIndex: ['string'], + info: [['String', '', 'String.html', '', 'String class', '', 'class']] + } + ) + + assert_equal 0, results.length + end + + def test_no_matches + results = run_search( + query: 'nonexistent', + data: { + searchIndex: ['string', 'attribute()'], + longSearchIndex: ['string', 'rdoc#attribute()'], + info: [ + ['String', '', 'String.html', '', 'String class', '', 'class'], + ['attribute', 'RDoc', 'RDoc.html#attribute', '()', 'Attribute method', '', 'method'] + ] + } + ) + + assert_equal 0, results.length + end + + def test_multiple_exact_matches + results = run_search( + query: 'test', + data: { + searchIndex: ['test', 'test', 'testing'], + longSearchIndex: ['test', 'rdoc::test', 'testing'], + info: [ + ['Test', '', 'Test.html', '', 'Top-level Test', '', 'class'], + ['Test', 'RDoc', 'RDoc/Test.html', '', 'RDoc Test', '', 'class'], + ['Testing', '', 'Testing.html', '', 'Testing class', '', 'class'] + ] + } + ) + + assert_equal 3, results.length + # First result should be the exact match with both indexes matching + assert_equal 'Test', strip_highlights(results[0]['title']) + assert_equal '', results[0]['namespace'] + end + + # Test case insensitive search + def test_case_insensitive + results = run_search( + query: 'STRING', + data: { + searchIndex: ['string'], + longSearchIndex: ['string'], + info: [['String', '', 'String.html', '', 'String class', '', 'class']] + } + ) + + assert_equal 1, results.length + assert_equal 'String', strip_highlights(results[0]['title']) + end + + def test_multi_word_query + results = run_search( + query: 'rdoc markup', + data: { + searchIndex: ['attributemanager'], + longSearchIndex: ['rdoc::markup::attributemanager'], + info: [['AttributeManager', 'RDoc::Markup', 'RDoc/Markup/AttributeManager.html', '', 'Manager', '', 'class']] + } + ) + + assert_equal 1, results.length + assert_equal 'AttributeManager', results[0]['title'] + end + + def test_highlighting + results = run_search( + query: 'string', + data: { + searchIndex: ['string'], + longSearchIndex: ['string'], + info: [['String', '', 'String.html', '', 'String class', '', 'class']] + } + ) + + assert_equal 1, results.length + # Check that highlighting markers (unicode \u0001 and \u0002) are present + assert_match(/[\u0001\u0002]/, results[0]['title']) + end + + def test_max_results_limit + # Create 150 entries (more than MAX_RESULTS = 100) + search_index = [] + long_search_index = [] + info = [] + + 150.times do |i| + search_index << "test#{i}" + long_search_index << "test#{i}" + info << ["Test#{i}", '', "Test#{i}.html", '', "Test class #{i}", '', 'class'] + end + + results = run_search( + query: 'test', + data: { + searchIndex: search_index, + longSearchIndex: long_search_index, + info: info + } + ) + + # Should return at most 100 results + assert_operator results.length, :<=, 100 + end + + private + + def run_search(query:, data:) + @context.eval("var testResults = [];") + @context.eval(<<~JS) + var data = #{data.to_json}; + var searcher = new Searcher(data); + searcher.ready(function(res, isLast) { + testResults = testResults.concat(res); + }); + searcher.find(#{query.to_json}); + JS + + # Give some time for async operations + sleep 0.01 + + @context.eval('testResults') + end + + # Helper to strip highlighting markers from a string + def strip_highlights(str) + str.gsub(/[\u0001\u0002]/, '') + end +end From 386baadd2d32a92980ea57a652f398c20a286b30 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Fri, 31 Oct 2025 17:53:15 +0000 Subject: [PATCH 3/3] Bump version to 6.15.1 (#1441) https://github.com/ruby/rdoc/compare/v6.15.0...master --- lib/rdoc/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rdoc/version.rb b/lib/rdoc/version.rb index a715633064..5286b953f3 100644 --- a/lib/rdoc/version.rb +++ b/lib/rdoc/version.rb @@ -5,6 +5,6 @@ module RDoc ## # RDoc version you are using - VERSION = '6.15.0' + VERSION = '6.15.1' end