lib/solargraph/pin/documenting.rb
# frozen_string_literal: true require 'kramdown' require 'kramdown-parser-gfm' require 'yard' require 'reverse_markdown' require 'solargraph/converters/dl' require 'solargraph/converters/dt' require 'solargraph/converters/dd' require 'solargraph/converters/misc' module Solargraph module Pin # A module to add the Pin::Base#documentation method. # module Documenting # A documentation formatter that either performs Markdown conversion for # text, or applies backticks for code blocks. # class DocSection # @return [String] attr_reader :plaintext # @param code [Boolean] True if this section is a code block def initialize code @plaintext = String.new('') @code = code end def code? @code end # @param text [String] # @return [String] def concat text @plaintext.concat text end def to_s return to_code if code? to_markdown end private # @return [String] def to_code "\n```ruby\n#{Documenting.normalize_indentation(@plaintext)}#{@plaintext.end_with?("\n") ? '' : "\n"}```\n\n" end # @return [String] def to_markdown ReverseMarkdown.convert Kramdown::Document.new(@plaintext, input: 'GFM').to_html end end # @return [String] def documentation @documentation ||= begin # Using DocSections allows for code blocks that start with an empty # line and at least two spaces of indentation. This is a common # convention in Ruby core documentation, e.g., String#split. sections = [DocSection.new(false)] Documenting.normalize_indentation(Documenting.strip_html_comments(docstring.to_s.gsub("\t", ' '))).lines.each do |l| if l.start_with?(' ') # Code block sections.push DocSection.new(true) unless sections.last.code? elsif sections.last.code? # Regular documentation sections.push DocSection.new(false) end sections.last.concat l end sections.map(&:to_s).join.strip end end # @param text [String] # @return [String] def self.strip_html_comments text text.gsub(/<!--([\s\S]*?)-->/, '').strip end # @param text [String] # @return [String] def self.normalize_indentation text left = text.lines.map do |line| match = line.match(/^ +/) next 0 unless match match[0].length end.min return text if left.nil? || left.zero? text.lines.map { |line| line[left..] }.join end end end end