# frozen_string_literal: true### Handle common directives that can occur in a block of text:## \:include: filename## Directives can be escaped by preceding them with a backslash.## RDoc plugin authors can register additional directives to be handled by# using RDoc::Markup::PreProcess::register.## Any directive that is not built-in to RDoc (including those registered via# plugins) will be stored in the metadata hash on the CodeObject the comment# is attached to. See RDoc::Markup@Directives for the list of built-in# directives.classRDoc::Markup::PreProcess### An RDoc::Options instance that will be filled in with overrides from# directivesattr_accessor:options### Adds a post-process handler for directives. The handler will be called# with the result RDoc::Comment (or text String) and the code object for the# comment (if any).defself.post_process&block@post_processors<<blockend### Registered post-processorsdefself.post_processors@post_processorsend### Registers +directive+ as one handled by RDoc. If a block is given the# directive will be replaced by the result of the block, otherwise the# directive will be removed from the processed text.## The block will be called with the directive name and the directive# parameter:## RDoc::Markup::PreProcess.register 'my-directive' do |directive, param|# # replace text, etc.# enddefself.registerdirective,&block@registered[directive]=blockend### Registered directivesdefself.registered@registeredend### Clears all registered directives and post-processorsdefself.reset@post_processors=[]@registered={}endreset### Creates a new pre-processor for +input_file_name+ that will look for# included files in +include_path+definitialize(input_file_name,include_path)@input_file_name=input_file_name@include_path=include_path@options=nilend### Look for directives in the given +text+.## Options that we don't handle are yielded. If the block returns false the# directive is restored to the text. If the block returns nil or no block# was given the directive is handled according to the registered directives.# If a String was returned the directive is replaced with the string.## If no matching directive was registered the directive is restored to the# text.## If +code_object+ is given and the directive is unknown then the# directive's parameter is set as metadata on the +code_object+. See# RDoc::CodeObject#metadata for details.defhandletext,code_object=nil,&blockfirst_line=1ifRDoc::Comment===textthencomment=texttext=text.textfirst_line=comment.line||1end# regexp helper (square brackets for optional)# $1 $2 $3 $4 $5# [prefix][\]:directive:[spaces][param]newlinetext=text.lines.map.with_index(first_line)do|line,num|nextlineunlessline=~/\A([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):([\w-]+):([ \t]*)(.+)?(\r?\n|$)/# skip something like ':toto::'next$&if$4.empty?and$5and$5[0,1]==':'# skip if escapednext"#$1:#$3:#$4#$5\n"unless$2.empty?# This is not in handle_directive because I didn't want to pass another# argument into itifcommentand$3=='markup'thennext"#{$1.strip}\n"unless$5comment.format=$5.downcasenext"#{$1.strip}\n"endhandle_directive$1,$3,$5,code_object,text.encoding,num,&blockend.joinifcommentthencomment.text=textelsecomment=textendself.class.post_processors.eachdo|handler|handler.callcomment,code_objectendtextend### Performs the actions described by +directive+ and its parameter +param+.## +code_object+ is used for directives that operate on a class or module.# +prefix+ is used to ensure the replacement for handled directives is# correct. +encoding+ is used for the <tt>include</tt> directive.## For a list of directives in RDoc see RDoc::Markup.#--# When 1.8.7 support is ditched prefix can be defaulted to ''defhandle_directiveprefix,directive,param,code_object=nil,encoding=nil,line=nilblankline="#{prefix.strip}\n"directive=directive.downcasecasedirectivewhen'arg','args'thenreturn"#{prefix}:#{directive}: #{param}\n"unlesscode_object&&code_object.kind_of?(RDoc::AnyMethod)code_object.params=paramblanklinewhen'category'thenifRDoc::Context===code_objectthensection=code_object.add_sectionparamcode_object.temporary_section=sectionelsifRDoc::AnyMethod===code_objectthencode_object.section_title=paramendblankline# ignore category if we're not on an RDoc::Contextwhen'doc'thenreturnblanklineunlesscode_objectcode_object.document_self=truecode_object.force_documentation=trueblanklinewhen'enddoc'thenreturnblanklineunlesscode_objectcode_object.done_documenting=trueblanklinewhen'include'thenfilename=param.split(' ',2).firstinclude_filefilename,prefix,encodingwhen'main'then@options.main_page=paramif@options.respond_to?:main_pagewarn<<~MSG
The :main: directive is deprecated and will be removed in RDoc 7.
You can use these options to specify the initial page displayed instead:
- `--main=#{param}` via the command line
- `rdoc.main = "#{param}"` if you use `RDoc::Task`
- `main_page: #{param}` in your `.rdoc_options` file
MSGblanklinewhen'nodoc'thenreturnblanklineunlesscode_objectcode_object.document_self=nil# notify nodoccode_object.document_children=param!~/all/iblanklinewhen'notnew','not_new','not-new'thenreturnblanklineunlessRDoc::AnyMethod===code_objectcode_object.dont_rename_initialize=trueblanklinewhen'startdoc'thenreturnblanklineunlesscode_objectcode_object.start_doccode_object.force_documentation=trueblanklinewhen'stopdoc'thenreturnblanklineunlesscode_objectcode_object.stop_docblanklinewhen'title'then@options.default_title=paramif@options.respond_to?:default_title=warn<<~MSG
The :title: directive is deprecated and will be removed in RDoc 7.
You can use these options to specify the title displayed instead:
- `--title=#{param}` via the command line
- `rdoc.title = "#{param}"` if you use `RDoc::Task`
- `title: #{param}` in your `.rdoc_options` file
MSGblanklinewhen'yield','yields'thenreturnblanklineunlesscode_object# remove parameter &blockcode_object.params=code_object.params.sub(/,?\s*&\w+/,'')ifcode_object.paramscode_object.block_params=param||''blanklineelseresult=yielddirective,param,lineifblock_given?caseresultwhennilthencode_object.metadata[directive]=paramifcode_objectifRDoc::Markup::PreProcess.registered.include?directivethenhandler=RDoc::Markup::PreProcess.registered[directive]result=handler.calldirective,paramifhandlerelseresult="#{prefix}:#{directive}: #{param}\n"endwhenfalsethenresult="#{prefix}:#{directive}: #{param}\n"endresultendend### Handles the <tt>:include: _filename_</tt> directive.## If the first line of the included file starts with '#', and contains# an encoding information in the form 'coding:' or 'coding=', it is# removed.## If all lines in the included file start with a '#', this leading '#'# is removed before inclusion. The included content is indented like# the <tt>:include:</tt> directive.#--# so all content will be verbatim because of the likely space after '#'?# TODO shift left the whole file content in that case# TODO comment stop/start #-- and #++ in included file must be processed heredefinclude_filename,indent,encodingfull_name=find_include_filenameunlessfull_namethenwarn"Couldn't find file to include '#{name}' from #{@input_file_name}"return''endcontent=RDoc::Encoding.read_filefull_name,encoding,truecontent=RDoc::Encoding.remove_magic_commentcontent# strip magic commentcontent=content.sub(/\A# .*coding[=:].*$/,'').lstrip# strip leading '#'s, but only if all lines start with themifcontent=~/^[^#]/thencontent.gsub(/^/,indent)elsecontent.gsub(/^#?/,indent)endend### Look for the given file in the directory containing the current file,# and then in each of the directories specified in the RDOC_INCLUDE pathdeffind_include_file(name)to_search=[File.dirname(@input_file_name)].concat@include_pathto_search.eachdo|dir|full_name=File.join(dir,name)stat=File.stat(full_name)rescuenextreturnfull_nameifstat.readable?endnilendend