1212from urllib .parse import quote
1313
1414from antsibull_core .logging import log
15+ from jinja2 .runtime import Context
16+ from jinja2 .utils import pass_context
1517
18+ from ..semantic_helper import parse_option , parse_return_value
19+ from .filters import extract_plugin_data
1620from .parser import Command , CommandSet , convert_text
1721
1822mlog = log .fields (mod = __name__ )
@@ -33,9 +37,13 @@ def _create_error(text: str, error: str) -> str:
3337
3438
3539class _Context :
40+ j2_context : Context
3641 counts : t .Dict [str , int ]
42+ plugin_fqcn : t .Optional [str ]
43+ plugin_type : t .Optional [str ]
3744
38- def __init__ (self ):
45+ def __init__ (self , j2_context : Context ):
46+ self .j2_context = j2_context
3947 self .counts = {
4048 'italic' : 0 ,
4149 'bold' : 0 ,
@@ -51,6 +59,7 @@ def __init__(self):
5159 'return-value' : 0 ,
5260 'ruler' : 0 ,
5361 }
62+ self .plugin_fqcn , self .plugin_type = extract_plugin_data (j2_context )
5463
5564
5665# In the following, we make heavy use of escaped whitespace ("\ ") being removed from the output.
@@ -157,6 +166,107 @@ def handle(self, parameters: t.List[str], context: t.Any) -> str:
157166 return f"<code class='docutils literal notranslate'>{ html_escape (parameters [0 ])} </code>"
158167
159168
169+ class _OptionName (Command ):
170+ command = 'O'
171+ parameter_count = 1
172+ escaped_content = True
173+
174+ def handle (self , parameters : t .List [str ], context : t .Any ) -> str :
175+ context .counts ['option-name' ] += 1
176+ if context .plugin_fqcn is None or context .plugin_type is None :
177+ raise Exception ('The markup O(...) cannot be used outside a plugin or role' )
178+ text = parameters [0 ]
179+ try :
180+ plugin_fqcn , plugin_type , option_link , option , value = parse_option (
181+ text , context .plugin_fqcn , context .plugin_type , require_plugin = False )
182+ except ValueError as exc :
183+ return _create_error (f'O({ text } )' , str (exc ))
184+ if value is None :
185+ cls = 'ansible-option'
186+ text = f'{ option } '
187+ strong_start = '<strong>'
188+ strong_end = '</strong>'
189+ else :
190+ cls = 'ansible-option-value'
191+ text = f'{ option } ={ value } '
192+ strong_start = ''
193+ strong_end = ''
194+ if plugin_fqcn and plugin_type and plugin_fqcn .count ('.' ) >= 2 :
195+ # TODO: handle role arguments (entrypoint!)
196+ namespace , name , plugin = plugin_fqcn .split ('.' , 2 )
197+ url = f'../../{ namespace } /{ name } /{ plugin } _{ plugin_type } .html'
198+ fragment = f'parameter-{ quote (option_link .replace ("." , "/" ))} '
199+ link_start = (
200+ f'<a class="reference internal" href="{ url } #{ fragment } ">'
201+ '<span class="std std-ref"><span class="pre">'
202+ )
203+ link_end = '</span></span></a>'
204+ else :
205+ link_start = ''
206+ link_end = ''
207+ return (
208+ f'<code class="{ cls } literal notranslate">'
209+ f'{ strong_start } { link_start } { text } { link_end } { strong_end } </code>'
210+ )
211+
212+
213+ class _OptionValue (Command ):
214+ command = 'V'
215+ parameter_count = 1
216+ escaped_content = True
217+
218+ def handle (self , parameters : t .List [str ], context : t .Any ) -> str :
219+ context .counts ['option-value' ] += 1
220+ text = parameters [0 ]
221+ return f'<code class="ansible-value literal notranslate">{ html_escape (text )} </code>'
222+
223+
224+ class _EnvVariable (Command ):
225+ command = 'E'
226+ parameter_count = 1
227+ escaped_content = True
228+
229+ def handle (self , parameters : t .List [str ], context : t .Any ) -> str :
230+ context .counts ['environment-var' ] += 1
231+ text = parameters [0 ]
232+ return f'<code class="xref std std-envvar literal notranslate">{ html_escape (text )} </code>'
233+
234+
235+ class _RetValue (Command ):
236+ command = 'RV'
237+ parameter_count = 1
238+ escaped_content = True
239+
240+ def handle (self , parameters : t .List [str ], context : t .Any ) -> str :
241+ context .counts ['return-value' ] += 1
242+ if context .plugin_fqcn is None or context .plugin_type is None :
243+ raise Exception ('The markup RV(...) cannot be used outside a plugin or role' )
244+ text = parameters [0 ]
245+ try :
246+ plugin_fqcn , plugin_type , rv_link , rv , value = parse_return_value (
247+ text , context .plugin_fqcn , context .plugin_type , require_plugin = False )
248+ except ValueError as exc :
249+ return _create_error (f'RV({ text } )' , str (exc ))
250+ cls = 'ansible-return-value'
251+ if value is None :
252+ text = f'{ rv } '
253+ else :
254+ text = f'{ rv } ={ value } '
255+ if plugin_fqcn and plugin_type and plugin_fqcn .count ('.' ) >= 2 :
256+ namespace , name , plugin = plugin_fqcn .split ('.' , 2 )
257+ url = f'../../{ namespace } /{ name } /{ plugin } _{ plugin_type } .html'
258+ fragment = f'return-{ quote (rv_link .replace ("." , "/" ))} '
259+ link_start = (
260+ f'<a class="reference internal" href="{ url } #{ fragment } ">'
261+ '<span class="std std-ref"><span class="pre">'
262+ )
263+ link_end = '</span></span></a>'
264+ else :
265+ link_start = ''
266+ link_end = ''
267+ return f'<code class="{ cls } literal notranslate">{ link_start } { text } { link_end } </code>'
268+
269+
160270class _HorizontalLine (Command ):
161271 command = 'HORIZONTALLINE'
162272 parameter_count = 0
@@ -176,16 +286,21 @@ def handle(self, parameters: t.List[str], context: t.Any) -> str:
176286 _Link (),
177287 _Ref (),
178288 _Const (),
289+ _OptionName (),
290+ _OptionValue (),
291+ _EnvVariable (),
292+ _RetValue (),
179293 _HorizontalLine (),
180294])
181295
182296
183- def html_ify (text : str ) -> str :
297+ @pass_context
298+ def html_ify (context : Context , text : str ) -> str :
184299 ''' convert symbols like I(this is in italics) to valid HTML '''
185300 flog = mlog .fields (func = 'html_ify' )
186301 flog .fields (text = text ).debug ('Enter' )
187302
188- our_context = _Context ()
303+ our_context = _Context (context )
189304
190305 try :
191306 text = convert_text (text , _COMMAND_SET , html_escape , our_context )
0 commit comments