From 2b4c9c864e11b749386a19823a7e6d073f2d2d57 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 20 Aug 2026 20:20:52 +1200 Subject: [PATCH 1/2] Add language-prefixed inline code references --- context/documentation-guidelines.md | 6 ++-- guides/documentation-guidelines/readme.md | 6 ++-- lib/utopia/project/base.md | 2 +- lib/utopia/project/base.rb | 6 ++-- lib/utopia/project/document.rb | 33 ++++++++++++---------- lib/utopia/project/renderer.rb | 24 ++++++++++++++++ releases.md | 4 +++ test/utopia/project/document.rb | 34 +++++++++++++++++++++++ utopia-project.gemspec | 2 +- 9 files changed, 91 insertions(+), 26 deletions(-) diff --git a/context/documentation-guidelines.md b/context/documentation-guidelines.md index 71b93c5..1e6dd33 100644 --- a/context/documentation-guidelines.md +++ b/context/documentation-guidelines.md @@ -16,7 +16,7 @@ Source code documentation is included adjacent to the code it describes, using s However, in short: - Documentation is expected to be in markdown format. -- You may embed links to definitions using `{MyClass}` or `{my_method}`. +- You may embed links to definitions using ruby:`MyClass` or ruby:`my_method`. - You can use tags: - `@parameters name [Type] Description.` - `@yields {|argument| ...} If a block is given.` @@ -173,7 +173,7 @@ $ bundle add $project `$project` has several core concepts: -- A {ruby MyProject::MyClass} which represents the main entry point for using the project. +- A ruby:`MyProject::MyClass` which represents the main entry point for using the project. ## Usage @@ -284,7 +284,7 @@ Following `utopia-project` guidelines, each guide should: 2. **Provide user context**: Explain why users would need this feature 3. **Include practical examples**: Working code samples that demonstrate real scenarios 4. **Follow consistent structure**: Problem → Use Cases → Implementation → Best Practices -5. **Cross-reference appropriately**: Use `{ruby ClassName}` for internal references +5. **Cross-reference appropriately**: Use ruby:`ClassName` for internal references 6. **Include error handling**: Show how to handle common failure scenarios 7. **Provide troubleshooting**: Common issues and solutions 8. **Maintain currency**: Keep examples updated with latest best practices diff --git a/guides/documentation-guidelines/readme.md b/guides/documentation-guidelines/readme.md index 71b93c5..1e6dd33 100644 --- a/guides/documentation-guidelines/readme.md +++ b/guides/documentation-guidelines/readme.md @@ -16,7 +16,7 @@ Source code documentation is included adjacent to the code it describes, using s However, in short: - Documentation is expected to be in markdown format. -- You may embed links to definitions using `{MyClass}` or `{my_method}`. +- You may embed links to definitions using ruby:`MyClass` or ruby:`my_method`. - You can use tags: - `@parameters name [Type] Description.` - `@yields {|argument| ...} If a block is given.` @@ -173,7 +173,7 @@ $ bundle add $project `$project` has several core concepts: -- A {ruby MyProject::MyClass} which represents the main entry point for using the project. +- A ruby:`MyProject::MyClass` which represents the main entry point for using the project. ## Usage @@ -284,7 +284,7 @@ Following `utopia-project` guidelines, each guide should: 2. **Provide user context**: Explain why users would need this feature 3. **Include practical examples**: Working code samples that demonstrate real scenarios 4. **Follow consistent structure**: Problem → Use Cases → Implementation → Best Practices -5. **Cross-reference appropriately**: Use `{ruby ClassName}` for internal references +5. **Cross-reference appropriately**: Use ruby:`ClassName` for internal references 6. **Include error handling**: Show how to handle common failure scenarios 7. **Provide troubleshooting**: Common issues and solutions 8. **Maintain currency**: Keep examples updated with latest best practices diff --git a/lib/utopia/project/base.md b/lib/utopia/project/base.md index 297e8d9..36d1769 100644 --- a/lib/utopia/project/base.md +++ b/lib/utopia/project/base.md @@ -4,4 +4,4 @@ Provides structured access to a project directory which contains source code and ## Usage -To get an instance for the current project, use {Base#instance}. +To get an instance for the current project, use ruby:`Base#instance`. diff --git a/lib/utopia/project/base.rb b/lib/utopia/project/base.rb index e1e3a16..d7a9885 100644 --- a/lib/utopia/project/base.rb +++ b/lib/utopia/project/base.rb @@ -136,12 +136,12 @@ def linkify(text, definition, language: definition&.language) end # Format the given text in the context of the given definition and language. - # See {document} for details. + # See ruby:`document` for details. # @returns [XRB::MarkupString] # # @example Format text with code links # base = Utopia::Project::Base.new - # base.format("See {Utopia::Project::Base#guides}.") # => XRB::MarkupString + # base.format("See ruby:`Utopia::Project::Base#guides`.") # => XRB::MarkupString def format(text, definition = nil, language: definition&.language, **options) if document = self.document(text, definition, language: language) return XRB::Markup.raw( @@ -152,7 +152,7 @@ def format(text, definition = nil, language: definition&.language, **options) # Convert the given markdown text into HTML. # - # Updates source code references (`{language identifier}`) into links. + # Updates language-prefixed inline code (e.g. `ruby:` followed by inline code) into links. # # @returns [Document] # diff --git a/lib/utopia/project/document.rb b/lib/utopia/project/document.rb index d70c3e8..38f2bf8 100644 --- a/lib/utopia/project/document.rb +++ b/lib/utopia/project/document.rb @@ -29,7 +29,7 @@ def initialize(text, base = nil, definition: nil, default_language: nil) # Parse and resolve the document root. # @returns [Markly::Node] The root document node. def root - @root ||= resolve(Markly.parse(@text, extensions: [:table])) + @root ||= resolve(Markly.parse(@text, flags: Markly::INLINE_CODE_INFO, extensions: [:table])) end # Extract the leading heading as the document title. @@ -99,7 +99,12 @@ def to_markdown(**options) # @parameter node [Markly::Node] The node to render. # @returns [XRB::MarkupString] The rendered HTML markup. def to_html(node = self.root, **options) - renderer = Renderer.new(ids: true, flags: Markly::UNSAFE, **options) + renderer = Renderer.new( + inline_code_resolver: (@index ? method(:reference_node) : nil), + ids: true, + flags: Markly::UNSAFE, + **options + ) XRB::Markup.raw(renderer.render(node)) end @@ -160,15 +165,9 @@ def link_node(title, url, child) # @parameter language [String | Nil] The source language name. # @returns [Markly::Node] The code node. def code_node(content, language = nil) - if language - node = inline_html_node( - "#{XRB::Strings.to_html(content)}" - ) - else - node = Markly::Node.new(:code) - node.string_content = content - return node - end + node = Markly::Node.new(:code) + node.string_content = content + node.code_info = language if language return node end @@ -176,9 +175,13 @@ def code_node(content, language = nil) private # Replace source code references in the given text with HTML anchors. - # - def reference_node(content) - if reference = @index.languages.parse_reference(content, default_language: @default_language) + # @parameter content [String] The source code reference. + # @parameter language [String | Nil] The explicit source language. + # @returns [Markly::Node] The resolved link or code node. + def reference_node(content, language: nil) + reference_content = language ? "#{language} #{content}" : content + + if reference = @index.languages.parse_reference(reference_content, default_language: @default_language) definition = @index.lookup(reference, relative_to: @definition) end @@ -189,7 +192,7 @@ def reference_node(content) elsif reference code_node(reference.identifier, reference.language.name) else - code_node(content) + code_node(content, language) end end diff --git a/lib/utopia/project/renderer.rb b/lib/utopia/project/renderer.rb index 089640e..b82fc9c 100644 --- a/lib/utopia/project/renderer.rb +++ b/lib/utopia/project/renderer.rb @@ -10,6 +10,15 @@ module Utopia module Project # Renders project Markdown with support for Mermaid code blocks. class Renderer < Markly::Renderer::HTML + # Initialize the project renderer. + # @parameter inline_code_resolver [Proc | Nil] Resolves language-prefixed inline code into a replacement node. + def initialize(inline_code_resolver: nil, **options) + @inline_code_resolver = inline_code_resolver + @resolving_inline_code = false + + super(**options) + end + # Render a heading and expose its title to Pagefind for sub-results. # @parameter node [Markly::Node] The heading node. def header(node) @@ -45,6 +54,21 @@ def code_block(node) super end end + + # Render inline code, resolving language-prefixed references when possible. + # @parameter node [Markly::Node] The inline code node. + def code(node) + if @inline_code_resolver && !@resolving_inline_code && (language = node.code_language) + begin + @resolving_inline_code = true + out(@inline_code_resolver.call(node.string_content, language: language)) + ensure + @resolving_inline_code = false + end + else + super + end + end end end end diff --git a/releases.md b/releases.md index aa1cc29..600968b 100644 --- a/releases.md +++ b/releases.md @@ -1,5 +1,9 @@ # Changes +## Unreleased + + - Add support for language-prefixed inline code references such as ruby:`Object.new`. + ## v0.41.0 - Don't render empty signature block when there are only examples. diff --git a/test/utopia/project/document.rb b/test/utopia/project/document.rb index 73ae45a..b527bf9 100644 --- a/test/utopia/project/document.rb +++ b/test/utopia/project/document.rb @@ -22,6 +22,40 @@ expect(html).to be(:include?, '
') end + it "renders language-prefixed inline code" do + root = File.expand_path("../../..", __dir__) + base = Utopia::Project::Base.new(root) + document = subject.new("Use ruby:`Object.new` to create an object.", base) + html = document.to_html.to_s + + expect(html).to be(:include?, 'Object.new') + expect(html).not.to be(:include?, "Utopia::Project::Document#root') + end + + it "continues to resolve legacy brace references" do + root = File.expand_path("../../..", __dir__) + base = Utopia::Project::Base.new(root) + base.update([File.join(root, "lib/utopia/project/document.rb")]) + + document = subject.new("See {ruby Utopia::Project::Document#root}.", base) + html = document.to_html.to_s + + expect(html).to be(:include?, '")) diff --git a/utopia-project.gemspec b/utopia-project.gemspec index dc96086..76846c0 100644 --- a/utopia-project.gemspec +++ b/utopia-project.gemspec @@ -29,7 +29,7 @@ Gem::Specification.new do |spec| spec.add_dependency "decode", "~> 0.30" spec.add_dependency "falcon" - spec.add_dependency "markly", "~> 0.15" + spec.add_dependency "markly", "~> 0.17" spec.add_dependency "thread-local" spec.add_dependency "utopia", "~> 3.0.4" end From bebe8f0f11cf400946977bee18695d694217b1ab Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 20 Aug 2026 20:36:36 +1200 Subject: [PATCH 2/2] Use structured Decode references --- lib/utopia/project/document.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/utopia/project/document.rb b/lib/utopia/project/document.rb index 38f2bf8..3283cda 100644 --- a/lib/utopia/project/document.rb +++ b/lib/utopia/project/document.rb @@ -179,9 +179,13 @@ def code_node(content, language = nil) # @parameter language [String | Nil] The explicit source language. # @returns [Markly::Node] The resolved link or code node. def reference_node(content, language: nil) - reference_content = language ? "#{language} #{content}" : content + reference = if language + @index.languages.reference_for(language, content) + else + @index.languages.parse_reference(content, default_language: @default_language) + end - if reference = @index.languages.parse_reference(reference_content, default_language: @default_language) + if reference definition = @index.lookup(reference, relative_to: @definition) end