lib/yard/code_objects/macro_object.rb in yard-0.9.18 vs lib/yard/code_objects/macro_object.rb in yard-0.9.19

- old
+ new

@@ -1,172 +1,172 @@ -# frozen_string_literal: true -require 'ostruct' - -module YARD - module CodeObjects - # A MacroObject represents a docstring defined through +@!macro NAME+ and can be - # reused by specifying the tag +@!macro NAME+. You can also provide the - # +attached+ type flag to the macro definition to have it attached to the - # specific DSL method so it will be implicitly reused. - # - # Macros are fully described in the {file:docs/Tags.md#macro Tags Overview} - # document. - # - # @example Creating a basic named macro - # # @!macro prop - # # @!method $1(${3-}) - # # @return [$2] the value of the $0 - # property :foo, String, :a, :b - # - # # @!macro prop - # property :bar, Numeric, :value - # - # @example Creating a macro that is attached to the method call - # # @!macro [attach] prop2 - # # @!method $1(value) - # property :foo - # - # # Extra data added to docstring - # property :bar - class MacroObject < Base - MACRO_MATCH = /(\\)?\$(?:\{(-?\d+|\*)(-)?(-?\d+)?\}|(-?\d+|\*))/ - - class << self - # Creates a new macro and fills in the relevant properties. - # @param [String] macro_name the name of the macro, must be unique. - # @param [String] data the data the macro should expand when re-used - # @param [CodeObjects::Base] method_object an object to attach this - # macro to. If supplied, {#attached?} will be true - # @return [MacroObject] the newly created object - def create(macro_name, data, method_object = nil) - obj = new(:root, macro_name) - obj.macro_data = data - obj.method_object = method_object - obj - end - - # Finds a macro using +macro_name+ - # @param [#to_s] macro_name the name of the macro - # @return [MacroObject] if a macro is found - # @return [nil] if there is no registered macro by that name - def find(macro_name) - Registry.at('.macro.' + macro_name.to_s) - end - - # Parses a given docstring and determines if the macro is "new" or - # not. If the macro has $variable names or if it has a @!macro tag - # with the [new] or [attached] flag, it is considered new. - # - # If a new macro is found, the macro is created and registered. Otherwise - # the macro name is searched and returned. If a macro is not found, - # nil is returned. - # - # @param [#to_s] macro_name the name of the macro - # @param [CodeObjects::Base] method_object an optional method to attach - # the macro to. Only used if the macro is being created, otherwise - # this argument is ignored. - # @return [MacroObject] the newly created or existing macro, depending - # on whether the @!macro tag was a new tag or not. - # @return [nil] if the +data+ has no macro tag or if the macro is - # not new and no macro by the macro name is found. - def find_or_create(macro_name, data, method_object = nil) - find(name) || create(macro_name, data, method_object) - end - alias create_docstring find_or_create - - # Expands +macro_data+ using the interpolation parameters. - # - # Interpolation rules: - # * $0, $1, $2, ... = the Nth parameter in +call_params+ - # * $* = the full statement source (excluding block) - # * Also supports $!{N-M} ranges, as well as negative indexes on N or M - # * Use \$ to escape the variable name in a macro. - # - # @!macro [new] macro.expand - # @param [Array<String>] call_params the method name and parameters - # to the method call. These arguments will fill \$0-N - # @param [String] full_source the full source line (excluding block) - # interpolated as \$* - # @param [String] block_source Currently unused. Will support - # interpolating the block data as a variable. - # @return [String] the expanded macro data - # @param [String] macro_data the macro data to expand (taken from {#macro_data}) - def expand(macro_data, call_params = [], full_source = '', block_source = '') # rubocop:disable Lint/UnusedMethodArgument - macro_data = macro_data.all if macro_data.is_a?(Docstring) - macro_data.gsub(MACRO_MATCH) do - escape = $1 - first = $2 || $5 - last = $4 - rng = $3 ? true : false - next $&[1..-1] if escape - if first == '*' - last ? $& : full_source - else - first_i = first.to_i - last_i = (last ? last.to_i : call_params.size) - last_i = first_i unless rng - params = call_params[first_i..last_i] - params ? params.join(", ") : '' - end - end - end - - # Applies a macro on a docstring by creating any macro data inside of - # the docstring first. Equivalent to calling {find_or_create} and {apply_macro} - # on the new macro object. - # - # @param [Docstring] docstring the docstring to create a macro out of - # @!macro macro.expand - # @see find_or_create - def apply(docstring, call_params = [], full_source = '', block_source = '', _method_object = nil) # rubocop:disable Lint/UnusedMethodArgument - docstring = docstring.all if Docstring === docstring - parser = Docstring.parser - handler = OpenStruct.new - handler.call_params = call_params[1..-1] - handler.caller_method = call_params.first - handler.statement = OpenStruct.new(:source => full_source) - parser.parse(docstring, nil, handler).to_docstring.to_raw - end - - # Applies a macro to a docstring, interpolating the macro's data on the - # docstring and appending any extra local docstring data that was in - # the original +docstring+ object. - # - # @param [MacroObject] macro the macro object - # @!macro macro.expand - def apply_macro(macro, docstring, call_params = [], full_source = '', block_source = '') # rubocop:disable Lint/UnusedMethodArgument - apply(docstring, call_params, full_source, block_source) - end - end - - # @return [String] the macro data stored on the object - attr_accessor :macro_data - - # @return [CodeObjects::Base] the method object that this macro is - # attached to. - attr_accessor :method_object - - # @return [Boolean] whether this macro is attached to a method - def attached?; method_object ? true : false end - - # Overrides {Base#path} so the macro path is ".macro.MACRONAME" - def path; '.macro.' + name.to_s end - - # Overrides the separator to be '.' - def sep; '.' end - - # Expands the macro using - # @param [Array<String>] call_params a list of tokens that are passed - # to the method call - # @param [String] full_source the full method call (not including the block) - # @param [String] block_source the source passed in the block of the method - # call, if there is a block. - # @example Expanding a Macro - # macro.expand(%w(property foo bar), 'property :foo, :bar', '') #=> - # "...macro data interpolating this line of code..." - # @see expand - def expand(call_params = [], full_source = '', block_source = '') - self.class.expand(macro_data, call_params, full_source, block_source) - end - end - end -end +# frozen_string_literal: true +require 'ostruct' + +module YARD + module CodeObjects + # A MacroObject represents a docstring defined through +@!macro NAME+ and can be + # reused by specifying the tag +@!macro NAME+. You can also provide the + # +attached+ type flag to the macro definition to have it attached to the + # specific DSL method so it will be implicitly reused. + # + # Macros are fully described in the {file:docs/Tags.md#macro Tags Overview} + # document. + # + # @example Creating a basic named macro + # # @!macro prop + # # @!method $1(${3-}) + # # @return [$2] the value of the $0 + # property :foo, String, :a, :b + # + # # @!macro prop + # property :bar, Numeric, :value + # + # @example Creating a macro that is attached to the method call + # # @!macro [attach] prop2 + # # @!method $1(value) + # property :foo + # + # # Extra data added to docstring + # property :bar + class MacroObject < Base + MACRO_MATCH = /(\\)?\$(?:\{(-?\d+|\*)(-)?(-?\d+)?\}|(-?\d+|\*))/ + + class << self + # Creates a new macro and fills in the relevant properties. + # @param [String] macro_name the name of the macro, must be unique. + # @param [String] data the data the macro should expand when re-used + # @param [CodeObjects::Base] method_object an object to attach this + # macro to. If supplied, {#attached?} will be true + # @return [MacroObject] the newly created object + def create(macro_name, data, method_object = nil) + obj = new(:root, macro_name) + obj.macro_data = data + obj.method_object = method_object + obj + end + + # Finds a macro using +macro_name+ + # @param [#to_s] macro_name the name of the macro + # @return [MacroObject] if a macro is found + # @return [nil] if there is no registered macro by that name + def find(macro_name) + Registry.at('.macro.' + macro_name.to_s) + end + + # Parses a given docstring and determines if the macro is "new" or + # not. If the macro has $variable names or if it has a @!macro tag + # with the [new] or [attached] flag, it is considered new. + # + # If a new macro is found, the macro is created and registered. Otherwise + # the macro name is searched and returned. If a macro is not found, + # nil is returned. + # + # @param [#to_s] macro_name the name of the macro + # @param [CodeObjects::Base] method_object an optional method to attach + # the macro to. Only used if the macro is being created, otherwise + # this argument is ignored. + # @return [MacroObject] the newly created or existing macro, depending + # on whether the @!macro tag was a new tag or not. + # @return [nil] if the +data+ has no macro tag or if the macro is + # not new and no macro by the macro name is found. + def find_or_create(macro_name, data, method_object = nil) + find(name) || create(macro_name, data, method_object) + end + alias create_docstring find_or_create + + # Expands +macro_data+ using the interpolation parameters. + # + # Interpolation rules: + # * $0, $1, $2, ... = the Nth parameter in +call_params+ + # * $* = the full statement source (excluding block) + # * Also supports $!{N-M} ranges, as well as negative indexes on N or M + # * Use \$ to escape the variable name in a macro. + # + # @!macro [new] macro.expand + # @param [Array<String>] call_params the method name and parameters + # to the method call. These arguments will fill \$0-N + # @param [String] full_source the full source line (excluding block) + # interpolated as \$* + # @param [String] block_source Currently unused. Will support + # interpolating the block data as a variable. + # @return [String] the expanded macro data + # @param [String] macro_data the macro data to expand (taken from {#macro_data}) + def expand(macro_data, call_params = [], full_source = '', block_source = '') # rubocop:disable Lint/UnusedMethodArgument + macro_data = macro_data.all if macro_data.is_a?(Docstring) + macro_data.gsub(MACRO_MATCH) do + escape = $1 + first = $2 || $5 + last = $4 + rng = $3 ? true : false + next $&[1..-1] if escape + if first == '*' + last ? $& : full_source + else + first_i = first.to_i + last_i = (last ? last.to_i : call_params.size) + last_i = first_i unless rng + params = call_params[first_i..last_i] + params ? params.join(", ") : '' + end + end + end + + # Applies a macro on a docstring by creating any macro data inside of + # the docstring first. Equivalent to calling {find_or_create} and {apply_macro} + # on the new macro object. + # + # @param [Docstring] docstring the docstring to create a macro out of + # @!macro macro.expand + # @see find_or_create + def apply(docstring, call_params = [], full_source = '', block_source = '', _method_object = nil) # rubocop:disable Lint/UnusedMethodArgument + docstring = docstring.all if Docstring === docstring + parser = Docstring.parser + handler = OpenStruct.new + handler.call_params = call_params[1..-1] + handler.caller_method = call_params.first + handler.statement = OpenStruct.new(:source => full_source) + parser.parse(docstring, nil, handler).to_docstring.to_raw + end + + # Applies a macro to a docstring, interpolating the macro's data on the + # docstring and appending any extra local docstring data that was in + # the original +docstring+ object. + # + # @param [MacroObject] macro the macro object + # @!macro macro.expand + def apply_macro(macro, docstring, call_params = [], full_source = '', block_source = '') # rubocop:disable Lint/UnusedMethodArgument + apply(docstring, call_params, full_source, block_source) + end + end + + # @return [String] the macro data stored on the object + attr_accessor :macro_data + + # @return [CodeObjects::Base] the method object that this macro is + # attached to. + attr_accessor :method_object + + # @return [Boolean] whether this macro is attached to a method + def attached?; method_object ? true : false end + + # Overrides {Base#path} so the macro path is ".macro.MACRONAME" + def path; '.macro.' + name.to_s end + + # Overrides the separator to be '.' + def sep; '.' end + + # Expands the macro using + # @param [Array<String>] call_params a list of tokens that are passed + # to the method call + # @param [String] full_source the full method call (not including the block) + # @param [String] block_source the source passed in the block of the method + # call, if there is a block. + # @example Expanding a Macro + # macro.expand(%w(property foo bar), 'property :foo, :bar', '') #=> + # "...macro data interpolating this line of code..." + # @see expand + def expand(call_params = [], full_source = '', block_source = '') + self.class.expand(macro_data, call_params, full_source, block_source) + end + end + end +end