Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions R/help/rdToHtml.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

#' Converts an .Rd file to HTML (output is printed to stdout)
#'
#' Execute this with the following trailing commandline args:
#' 1. path of an .Rd file
#' 2. name of the package
#' 3. version string of the package
#' 4. root dir of the package

args <- base::commandArgs(TRUE)

e <- tools::loadPkgRdMacros(args[4])
e <- tools::loadRdMacros(file.path(R.home("share"), "Rd", "macros", "system.Rd"), macros = e)

tools::Rd2HTML(
args[1],
package = args[2:3],
dynamic = TRUE,
encoding = "utf-8",
macros = e,
stages = c("build", "install", "render")
)
38 changes: 27 additions & 11 deletions src/helpViewer/helpPreviewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export interface RHelpPreviewerOptions {
previewListener?: (previewer: RLocalHelpPreviewer) => void;
// path to .ejs file to be used as 00Index.html in previewed packages
indexTemplatePath: string;
// path of the script used to convert .Rd to html
rdToHtmlScriptFile: string
}

export function makePreviewerList(options: RHelpPreviewerOptions): RLocalHelpPreviewer[] {
Expand Down Expand Up @@ -90,6 +92,9 @@ export class RLocalHelpPreviewer {
private readonly indexTemplate: string;
private callPreviewListener: () => void;

// path of the script used to convert .Rd to html
private readonly rdToHtmlScriptFile: string;

public isPackageDir: boolean = false;
public isDisposed: boolean = false;

Expand All @@ -108,6 +113,7 @@ export class RLocalHelpPreviewer {
this.isPackageDir = this.watchFiles();
this.dummyPackageName = `UnnamedPackage${unnamedId}`;
this.indexTemplate = fs.readFileSync(options.indexTemplatePath, 'utf-8');
this.rdToHtmlScriptFile = options.rdToHtmlScriptFile;
}

public refresh(): void {
Expand Down Expand Up @@ -196,14 +202,11 @@ export class RLocalHelpPreviewer {
}

public getPackageName(safe?: boolean): string {
let ret = this.getPackageInfo()?.name || this.dummyPackageName;
if(safe){
ret = ret.replaceAll(/[^a-zA-Z0-9.]/g, '');
if(ret.match(/^[0-9.]/) || ret.length < 2 || ret.match(/\.$/)){
ret = this.dummyPackageName;
}
const packageName = this.getPackageInfo()?.name;
if(!packageName || (safe && !isValidPackageName(packageName))){
return this.dummyPackageName;
}
return ret;
return packageName;
}

// Methods that imitate the HelpProvider
Expand Down Expand Up @@ -237,16 +240,20 @@ export class RLocalHelpPreviewer {
'--slave',
'--no-save',
'--no-restore',
'-e',
'tools::Rd2HTML(base::commandArgs(TRUE)[1],package=base::commandArgs(TRUE)[2:3],dynamic=TRUE,encoding="utf-8")',
'-f',
this.rdToHtmlScriptFile,
'--args',
rdFileName,
this.getPackageName(true),
this.getPackageInfo()?.version || DUMMY_TOPIC_VERSION
this.getPackageInfo()?.version || DUMMY_TOPIC_VERSION,
this.packageDir
];
const spawnRet = cp.spawnSync(this.rPath, args, {encoding: 'utf-8'});
if(spawnRet.status){
console.log(`Failed to convert .Rd file ${rdFileName} (status: ${spawnRet.status})`);
// The user expects this to work, so we show a warning if it doesn't:
const msg = `Failed to convert .Rd file ${rdFileName} (status: ${spawnRet.status}): ${spawnRet.stderr}`;
void vscode.window.showWarningMessage(msg);
console.log(msg);
console.log(spawnRet.stderr);
console.log(spawnRet.error);
return undefined;
Expand Down Expand Up @@ -458,6 +465,15 @@ function rdAliasToTreeViewTopic(rdAlias: RdAlias, pkgName: string): Topic {
}


// Check if a package name is valid
function isValidPackageName(pkgName: string): boolean {
// regex to chekc pkgName (length >=2 implied):
// /^[beging with letter][letters,numbers,dot][not end with .]$/
const re = /^[a-zA-Z][a-zA-Z0-9.]*[a-zA-Z0-9]$/;
return !!re.exec(pkgName);
}


// Helper functions to read/convert rdAliases and aliases

function rdAliasToAliases(rdAlias: RdAlias, pkgName: string): AliasExtra[] {
Expand Down
4 changes: 4 additions & 0 deletions src/helpViewer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export async function initializeHelp(
webviewStylePath: context.asAbsolutePath('./html/help/theme.css'),
rScriptFile: context.asAbsolutePath('./R/help/getAliases.R'),
indexTemplatePath: context.asAbsolutePath('./html/help/00Index.ejs'),
rdToHtmlScriptFile: context.asAbsolutePath('./R/help/rdToHtml.R'),
rPath: rPath,
cwd: cwd,
persistentState: persistentState,
Expand Down Expand Up @@ -196,6 +197,8 @@ export interface HelpOptions {
cwd?: string
// path of getAliases.R
rScriptFile: string
// path of the script used to convert .Rd to html
rdToHtmlScriptFile: string
// persistent state, either global or workspace specific
persistentState: vscode.Memento
// used by some helper classes:
Expand Down Expand Up @@ -266,6 +269,7 @@ export class RHelp implements api.HelpPanel, vscode.WebviewPanelSerializer<strin
};
this.helpPreviewerOptions = {
indexTemplatePath: options.indexTemplatePath,
rdToHtmlScriptFile: options.rdToHtmlScriptFile,
rPath: this.rPath,
previewListener: previewListener
};
Expand Down