diff --git a/src/core/jupyter/jupyter.ts b/src/core/jupyter/jupyter.ts index 765774e4d23..8ec7f336c07 100644 --- a/src/core/jupyter/jupyter.ts +++ b/src/core/jupyter/jupyter.ts @@ -1249,6 +1249,7 @@ const kLangCommentChars: Record = { mermaid: "%%", apl: "⍝", ocaml: ["(*", "*)"], + q: "/", rust: "//", }; diff --git a/src/core/jupyter/percent.ts b/src/core/jupyter/percent.ts index b41416949cd..586f61ae610 100644 --- a/src/core/jupyter/percent.ts +++ b/src/core/jupyter/percent.ts @@ -12,19 +12,32 @@ import { kCellRawMimeType } from "../../config/constants.ts"; import { mdFormatOutput, mdRawOutput } from "./jupyter.ts"; import { lines, trimEmptyLines } from "../lib/text.ts"; import { asYamlText } from "./jupyter-fixups.ts"; +import { kLangCommentChars } from "../lib/partition-cell-options.ts"; export const kJupyterPercentScriptExtensions = [ ".py", ".jl", ".r", + ".q", ]; +export const kLanguageExtensions: Record = { + ".jl": "julia", + ".r": "r", + ".py": "python", + ".q": "q", +}; + + export function isJupyterPercentScript(file: string, extensions?: string[]) { const ext = extname(file).toLowerCase(); const availableExtensions = extensions ?? kJupyterPercentScriptExtensions; if (availableExtensions.includes(ext)) { const text = Deno.readTextFileSync(file); - return !!text.match(/^\s*#\s*%%+\s+\[markdown|raw\]/); + const cms = kLangCommentChars[kLanguageExtensions[ext]]; + const pat = new RegExp(`^\\s*${cms}\\s*%%+\\s+\\[markdown|raw\\]`); + return !!text.match(pat); + } else { return false; } @@ -33,13 +46,17 @@ export function isJupyterPercentScript(file: string, extensions?: string[]) { export function markdownFromJupyterPercentScript(file: string) { // determine language/kernel const ext = extname(file).toLowerCase(); - const language = ext === ".jl" ? "julia" : ext === ".r" ? "r" : "python"; + const language = kLanguageExtensions[ext]; + const cms = kLangCommentChars[language]; + if (!language) { + throw new Error(`Could not determine language from file extension ${ext}`); + } // break into cells const cells: PercentCell[] = []; const activeCell = () => cells[cells.length - 1]; for (const line of lines(Deno.readTextFileSync(file).trim())) { - const header = percentCellHeader(line); + const header = percentCellHeader(line, language); if (header) { cells.push({ header, lines: [] }); } else { @@ -50,7 +67,7 @@ export function markdownFromJupyterPercentScript(file: string) { // resolve markdown and raw cells const isTripleQuote = (line: string) => !!line.match(/^"{3,}\s*$/); const asCell = (lines: string[]) => lines.join("\n") + "\n\n"; - const stripPrefix = (line: string) => line.replace(/^#\s?/, ""); + const stripPrefix = (line: string) => line.replace(new RegExp(`^${cms}\\s?`), ""); const cellContent = (cellLines: string[]) => { if ( cellLines.length > 2 && isTripleQuote(cellLines[0]) && @@ -68,7 +85,7 @@ export function markdownFromJupyterPercentScript(file: string) { if (cell.header.type === "code") { if (cell.header.metadata) { const yamlText = asYamlText(cell.header.metadata); - cellLines.unshift(...lines(yamlText).map((line) => `#| ${line}`)); + cellLines.unshift(...lines(yamlText).map((line) => `${cms}| ${line}`)); } markdown += asCell(["```{" + language + "}", ...cellLines, "```"]); } else if (cell.header.type === "markdown") { @@ -99,9 +116,10 @@ interface PercentCellHeader { metadata?: Metadata; } -function percentCellHeader(line: string): PercentCellHeader | undefined { +function percentCellHeader(line: string, language: string): PercentCellHeader | undefined { + const cms = kLangCommentChars[language]; const match = line.match( - /^\s*#\s*%%+\s*(?:\[(markdown|raw)\])?\s*(.*)?$/, + new RegExp(`^\\s*${cms}\\s*%%+\\s*(?:\\[(markdown|raw)\\])?\\s*(.*)?$`), ); if (match) { const type = match[1] || "code"; diff --git a/src/core/lib/partition-cell-options.ts b/src/core/lib/partition-cell-options.ts index c066fe57a03..8d056f5a283 100644 --- a/src/core/lib/partition-cell-options.ts +++ b/src/core/lib/partition-cell-options.ts @@ -353,6 +353,7 @@ export const kLangCommentChars: Record = { ojs: "//", apl: "⍝", ocaml: ["(*", "*)"], + q: "/", rust: "//", }; diff --git a/src/resources/filters/common/log.lua b/src/resources/filters/common/log.lua index 67a736bbe93..1fad6971a2d 100644 --- a/src/resources/filters/common/log.lua +++ b/src/resources/filters/common/log.lua @@ -25,7 +25,7 @@ function warn(message, offset) end function error(message, offset) - io.stderr:write(lunacolors.red("ERROR (" .. caller_info(offset) .. ") " .. message .. "\n")) + io.stderr:write(lunacolors.red(("ERROR (%s) %s\n"):format(caller_info(offset), message))) end function fatal(message, offset) diff --git a/src/resources/filters/modules/constants.lua b/src/resources/filters/modules/constants.lua index 42a59d77b4c..e22a6774417 100644 --- a/src/resources/filters/modules/constants.lua +++ b/src/resources/filters/modules/constants.lua @@ -116,6 +116,7 @@ local kLangCommentChars = { powershell = {"#"}, psql = {"--"}, python = {"#"}, + q = {"/"}, r = {"#"}, ruby = {"#"}, rust = {"//"}, diff --git a/src/resources/jupyter/notebook.py b/src/resources/jupyter/notebook.py index 2d4ca301525..4ac5383e004 100644 --- a/src/resources/jupyter/notebook.py +++ b/src/resources/jupyter/notebook.py @@ -875,6 +875,7 @@ def nb_language_comment_chars(lang): haskell="--", dot="//", apl="⍝", + q = "/", ocaml=["(*", "*)"], ) if lang in langs: diff --git a/src/resources/rmd/hooks.R b/src/resources/rmd/hooks.R index 7e3ca19dbd9..11ab12e33a9 100644 --- a/src/resources/rmd/hooks.R +++ b/src/resources/rmd/hooks.R @@ -1046,6 +1046,7 @@ engine_comment_chars <- function(engine) { dot = "//", apl = "\u235D", ocaml = c("(*", "*)"), + q = "/", rust = "//" ) comment_chars[[engine]] %||% "#"