Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User-defined macros needed to re-parse transformed Rd source #28

Open
aitap opened this issue Sep 5, 2024 · 3 comments
Open

User-defined macros needed to re-parse transformed Rd source #28

aitap opened this issue Sep 5, 2024 · 3 comments

Comments

@aitap
Copy link
Contributor

aitap commented Sep 5, 2024

Consider a package that uses user-defined macros in the Rd source. In order to parse the documentation successfully, you have to give the right macros argument to parse_Rd. It must contain just enough state, including the pre-defined R macros, package's own macros from man/macros/*.Rd, and any of the package dependencies declared in the RdMacros field of the DESCRIPTION file. tools::loadPkgRdMacros should do the trick for a package source directory or an installed package directory inside a library.

Without the macros, either make_text or to_text eventually fails:

download.packages('Rdpack', '.') -> x
untar(x[,2])
unlink('Rdpack.es', recursive=TRUE)
rhelpi18n::i18n_module_create('Rdpack.es', 'es', 'Rdpack.es', 'Rdpack')
# produces warnings and stops with an error
traceback()
# 9: FUN(X[[i]], ...)
# 8: vapply(x, to_text, FUN.VALUE = character(1))
# 7: FUN(X[[i]], ...)
# 6: lapply(Rd, make_text, untranslatable = untranslatable)
# 5: rd_flatten(rd_parsed)
# 4: (function (rd_file, template_file)
#    {
#        rd_parsed <- tools::parse_Rd(rd_file)
#        rd_flatten <- rd_flatten(rd_parsed)
#        rd_flat_write(rd_flatten, template_file)
#        template_file
#    })(dots[[1L]][[2L]], dots[[2L]][[2L]])
# 3: mapply(i18n_translation_template, rd_files, template_files)
# 2: i18n_translation_templates(rd_files, file.path(module_path, "translations"))
# 1: rhelpi18n::i18n_module_create("Rdpack.es", "es", "Rdpack.es",
#        "Rdpack")

Once you provide the macro definitions correctly, they will not show up in the deparsed Rd text:

r"{\newcommand{\foo}{}\foo}" |>
 textConnection(name = 'foo') |>
 tools::parse_Rd(fragment = TRUE) |>
 as.character(deparse = TRUE)
# [1] "\n"

This may or may not become a problem when translating. (Most interesting macros expand to \Sexpr[stage=build]{...}.)

Edit: I'm sorry, but I seem to be followed by virus-spreading spammers. Please block and report them on sight.

@eliocamp
Copy link
Owner

eliocamp commented Nov 5, 2024

Thanks for the report. Yes, macros are a problem. I'm not super familiar with how they work since I never used them.

If I understood you correctly. By passing macro definition, when parsing the Rd file they are evaluated, which means that the translated text won't have them?

Ideally, we would need to keep the macros as they are in the translated text so they can be translated (if necessary) and then translated.

@eliocamp
Copy link
Owner

eliocamp commented Nov 5, 2024

Added support for parsing custom macros. However, these are not evaluated after translation, so the result is a mess.

image

I think the most robust way to support this would be to preserve the macro in the translated text and the re-parse the Rd file after translation. The problem is that .translateHelpFile() gets the result from tools:::fetchRdDB in which the macros are already evaluated, so that would break the string matching step. 😖

@aitap
Copy link
Contributor Author

aitap commented Nov 5, 2024

Looks like you've successfully expanded the macros and are now being bitten by #25: too late to evaluate \Sexpr[stage=build].

Preserving unexpanded macros in the translatable source may be impossible with tools::parse_Rd. Consider a help file that uses a macro defined as \newcommand{\eqn3}{...}. Without the macro definitions, tools::parse_Rd sees a broken \eqn followed by a literal 3.

There might be a way to manually deparse unexpanded macros from the returned parse tree, because the body of the macro and its arguments are preserved there:

r"{\newcommand{\foo}{aaaaaaaaaaaa \eqn{#1} aaaaaaaaaaaaaaa}\foo{1}bbbbbbbbb}" |>
 textConnection(name = 'foo') |>
 tools::parse_Rd(fragment = TRUE) -> foo
foo[[2]] # the unevaluated macro is preserved together with the arguments
# [1] "aaaaaaaaaaaa \\eqn{#1} aaaaaaaaaaaaaaa" "1"
# attr(,"Rd_tag")
# [1] "USERMACRO"
# attr(,"macro")
[1] "\\foo"

...but then you'll have to separate them from the results of their expansion, which is hard:

foo[[5]] # macro expansion concatenated with the following text
# [1] " aaaaaaaaaaaaaaabbbbbbbbb\n"
# attr(,"Rd_tag")
# [1] "TEXT"

Even terrible stuff like \newcommand{\bar}{baz}\newcommand{\foo}{\eqn{#1}\ba}\foo{1}r} works, concatenates into a working macro \bar and gives you \eqn{1}baz.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants
@aitap @eliocamp and others