@schmolmo
Could you test the following code? I gave ChatGPT, Claude, Gemini, and Grok only the task description and the reported errors, but none of them could complete the task. After I shared my initial code—the one shown above—Grok started producing something usable, although not the final version I am posting here. Before reaching that final version, I also reviewed the code myself and pointed out the incorrect parts. The whole process took about four hours. I am not sure whether that was productive, unproductive, or somewhere in between. Still, I think this could become a productive workflow in the near future.
(
var path, source, output, lines, level1Headings, processInline, convertCode;
// Set path to the file
path = "/Users/prko/Dropbox/prko/Downloads/untitled folder 2/test";
source = File(path ++ ".md", "r");
output = File(path ++ ".schelp", "w+");
// 1. Read all lines and preprocess (remove comments, convert images, remove <br>)
lines = [];
block {
var line, trimmed, url, start, end; // Declare all vars at the top of the block
while {
line = source.getLine;
line.notNil
} {
// Remove <br> tags
line = line.replace("<br>", "");
// Strip whitespace only for checking (keep original for output, but trim for matching)
trimmed = line.stripWhiteSpace;
// Skip HTML comments (single/multi-line, <!-- ... -->)
if(trimmed.beginsWith("<!--").not and: { trimmed.endsWith("-->").not }) {
// Convert images without regex: check if starts with '![', extract url between ( and )
if(trimmed.beginsWith("![")) {
start = trimmed.find("(");
if(start.notNil) {
end = trimmed.find(")", start + 1);
if(end.notNil) {
url = trimmed[(start + 1) .. (end - 1)];
// Replace entire line with image tag
line = "image::" ++ url ++ "::";
} {
("Warning: No closing ')' found in image line: " ++ trimmed).postln;
};
} {
("Warning: No '(' found in image line: " ++ trimmed).postln;
};
};
lines = lines.add(line);
};
};
};
source.close;
// 2. Process Level 1 Headings and insert header tags if missing
level1Headings = lines.selectIndices({ |line| line.beginsWith("# ") });
if(level1Headings.size >= 1) {
var titleText = lines[level1Headings[0]][2..].stripWhiteSpace;
output << "title::" << titleText << "\n";
output << "summary::fill this field\n"; // Fill with appropriate summary
// output << "categories::fill this field\n"; // Fill with categories
// output << "related::fill this field\n\n"; // Fill with related topics
output << "description::\n";
lines.removeAt(level1Headings[0]); // Remove the first h1 (used as title)
} {
// Insert placeholders if no Level 1 heading
output << "title::fill this field\n"; // Fill with document title
output << "summary::fill this field\n"; // Fill with appropriate summary
// output << "categories::fill this field\n"; // Fill with categories
// output << "related::fill this field\n\n"; // Fill with related topics
output << "description::\n";
};
// 3. Inline processing function (URLs, bold, italic, etc.) with nil/type safety and safe regex (no infinite loop)
processInline = { |str|
var out;
str = str ? ""; // Nil safety: default to empty string
out = str;
// Convert bold: __text__ -> strong::text:: (safe regex, single pass)
if(out.notNil and: { out.size > 0 }) {
var matches = out.findRegexp("__([^_]+)__"); // [^_]+ to avoid issues
var j = 0;
while { j < matches.size } {
var full = matches[j][1] ? ""; // Full match
var text = matches[j+1][1] ? ""; // Group1 (text)
out = out.replace(full, "strong::" ++ text ++ "::");
j = j + 2; // Step by full/group
};
};
// Convert italic: _text_ -> emphasis::text:: (safe regex, single pass)
if(out.notNil and: { out.size > 0 }) {
var matches = out.findRegexp("_([^_]+)_"); // [^_]+ to avoid issues
var j = 0;
while { j < matches.size } {
var full = matches[j][1] ? ""; // Full match
var text = matches[j+1][1] ? ""; // Group1 (text)
out = out.replace(full, "emphasis::" ++ text ++ "::");
j = j + 2; // Step by full/group
};
};
// Convert standalone URLs to link::url:: (safe regex, single pass, but skip if already in image::)
if(out.notNil and: { out.size > 0 }) {
var matches = out.findRegexp("https?://[^ ]+"); // Simple [^ ]+
var j = 0;
while { j < matches.size } {
var full = matches[j][1] ? ""; // Full match (URL)
if(out.contains("link::" ++ full).not and: { out.contains("image::" ++ full).not }) { // Added check to skip image URLs
out = out.replace(full, "link::" ++ full ++ "::");
};
j = j + 1; // Step by match
};
};
out
};
// 4. Code conversion function (existing) with nil safety
convertCode = { |source|
source = source ? ""; // Nil safety: default to empty string
if(source.notNil and: { "^`.*`$".matchRegexp(source) }) {
"\ncode::\n" ++ source[1 .. source.size - 2] ++ "\n::\n"
} {
source
}
};
// 5. Parse body (using iterator for safety) with additional nil checks
{
var iter = lines.iter;
var previous = nil;
var current = iter.next;
while { current.notNil } {
current = current ? ""; // Nil safety for current line
output << (
case
// Code blocks: ```supercollider or general ```
{ current.stripWhiteSpace.beginsWith("```") } {
var isSupercollider = current.stripWhiteSpace == "```supercollider";
var codeLines = "";
var codeLine = iter.next;
while { codeLine.notNil and: { codeLine.stripWhiteSpace != "```" } } {
codeLines = codeLines ++ codeLine ++ "\n";
codeLine = iter.next;
};
"\ncode::\n" ++ codeLines ++ "::\n" // End with ::
}
// Reference links: [text]: url -> link::url##text::
{ current.beginsWith("[") and: { current.contains("]: ") } } {
var splitIndex = current.find("]: ");
var name = current[1 .. (splitIndex - 1)];
var url = current[(splitIndex + 3) ..].stripWhiteSpace;
"link::" ++ url ++ "##" ++ name ++ "::\n"
}
{ "^=.*=$".matchRegexp(current) } {
"title::" ++ previous ++ "\n" ++
"summary::" ++ previous ++ "\n" ++
"categories::" ++ previous ++ "\n" ++
"related::" ++ previous ++ "\n\n" ++
"description::\n"
}
{ "^-.*-$".matchRegexp(current) } {
"\nsection::Introduction\n"
}
{ "^## ".matchRegexp(current) } {
"\nsection::" ++ current[3..] ++ "\n"
}
{ "^### ".matchRegexp(current) } {
"\nsubsection::" ++ current[4..] ++ "\n"
}
{ "^#### ".matchRegexp(current) } {
"\nsubsubsection::" ++ current[5..] ++ "\n"
}
{ "^`.*`$".matchRegexp(current) } {
"\ncode::\n" ++ current[1 .. current.size - 2] ++ "\n::\n"
}
{ previous == "" && "^\\*".matchRegexp(current) } {
var subpartLineCurrent = iter.next;
var text = "\nlist::\n\n##" ++ current[2..];
while { subpartLineCurrent.notNil and: { "^[^#].*".matchRegexp(subpartLineCurrent) } } {
var subpartLineNext = iter.next;
subpartLineCurrent = if(subpartLineNext == "") {
subpartLineCurrent ++ "\n::"
} {
if("^#".matchRegexp(subpartLineNext)) {
subpartLineCurrent ++ "\n::"
} {
convertCode.(subpartLineCurrent)
}
};
text = text ++ "\n" ++ (
case
{ "^\\*".matchRegexp(subpartLineCurrent) } {
"\n##" ++ subpartLineCurrent[2..]
}
{
convertCode.(subpartLineCurrent)
}
);
current = subpartLineCurrent;
subpartLineCurrent = subpartLineNext;
};
text ++ "\n"
}
{ "^##### ".matchRegexp(current) } {
var subpartLineCurrent = iter.next;
var text = "\ntable::\n\n##" ++ current[6..];
while { subpartLineCurrent.notNil and: { subpartLineCurrent == "" || "^[^#].*".matchRegexp(subpartLineCurrent) } } {
var subpartLineNext = iter.next;
subpartLineCurrent = if(subpartLineNext == "" && "^#".matchRegexp(subpartLineNext)) {
subpartLineCurrent ++ "\n::"
} {
convertCode.(subpartLineCurrent)
};
text = text ++ "\n" ++ subpartLineCurrent;
current = subpartLineCurrent;
subpartLineCurrent = subpartLineNext;
};
text ++ "\n::\n"
}
// General line: apply processInline (URLs, bold, italic, etc.)
{
processInline.(current) ++ "\n"
}
);
previous = current;
current = iter.next;
};
output.close;
"Conversion finished.".postln;
}.value; // Execute the block
)