This is an updated version of https://github.com/supercollider/supercollider/pul…l/6622, and the status of the latest commit with the corresponding latest sc-code can be found at https://github.com/supercollider/supercollider/pull/6626#issuecomment-2668315364.
This PR will be updated according to the consensus discussed in #6622, so please leave your opinions there. This includes
- Reordering the header: changing `class::` to `title::`, reordering the tags in the header.
- Arranging (adding or removing) whitespace after `::`.
- Blank line alignment: only one blank line between line codes.
- Change 3rd singular present form without grammatical subject after section tags like `description::`, `sections::`, `subsection::`, `method::methodName\n` and `argument::argumentName\n` to instructive (declarative) tone.
- One sentence per line.
- Some fragmented sentences across lines are concatenated.
- Some British spellings are converted to North American spellings.
- Some values are enclosed with teletype tags.
## Purpose and Motivation
This makes all SCDocs consistent, and is a sort of revival of https://github.com/supercollider/supercollider/pull/6534.
(The changes in https://github.com/supercollider/supercollider/pull/6621 are included in this PR to avoid an error),
but there are a lot of differences, because https://github.com/supercollider/supercollider/pull/6534 was done by hand, and the first commit in this PR is done using the following code snippet:
<details><summary>Details</summary>
<pre><code>
(
var pathHome_origin_relative = "HelpSource_original";
var pathHome_origin_absolute = ("~/Downloads" +/+ pathHome_origin_relative).standardizePath;
var pathHome_working_relative = "HelpSource";
var pathHome_working_absolute = pathHome_origin_absolute.replace(pathHome_origin_relative, pathHome_working_relative);
var headerTag = "class|title|summary|categories|related|redirect";
// no lines before/after; no white space of the right side of "tag::" and the left side of ending "::"
var inline = "strong|soft|emphasis|code|teletype|math";
var hypertext = "anchor|keyword|link";
// 1 empty line before; 0 empty line after
var supplementary = "table|(?:|definition|numbered)list|tree|(?:|class)tree|image|code|teletype|math";
var cautionary = "note|warning";
// 2 empty lines before; 1 empty line aftter
var level_C_Block = "returns|discussion";
var level_C_Title = "subsubsection";
var level_C_Argument = "argument";
// 4 empty lines before; 2 empty lines after
var level_B_Method = "method|copymethod|private";
// 6 empty lines before; 2 empty lines after
var level_B_Title = "subsection";
// 10 empty lines before; 4 empty lines after
var level_A_Block = "description|examples|(?:class|instance)methods";
var level_A_Title = "section";
var tagsAll = headerTag ++ "|" ++ inline ++ "|" ++ hypertext ++ "|"
++ supplementary ++ "|" ++ cautionary ++ "|"
++ level_C_Block ++ "|" ++ level_C_Title ++ "|" ++ level_C_Argument ++ "|"
++ level_B_Title ++ "|" ++ level_B_Method ++ "|"
++ level_A_Block ++ "|" ++ level_A_Title;
var header = { |array|
var detect = { |which|
array.collect { |item, indx|
/** 'class::' to 'title::' **/
item = item.replace("class::", "title::")
.replaceRegexp("^\\stitle::", "title::");
if(item.beginsWith(which)) {
if(which == "summary") {
item
/** Remove the period at the end of the sentence followed by the summary tag **/
.replaceRegexp(
"\\.$",
""
)
/** Capitalise the first letter of the following keywords with a white space **/
.replaceRegexp(
"summary::\\s*(\\w+)",
"summary:: \\u$1"
)
} {
/** Add a white space after 'tag delimiter::' **/
item.replaceRegexp("::\\s*", ":: ")
}
}
}.reject(_.isNil)[0]
};
/** Make the order of header consistently: title - categories - summary - related - redirect **/
var detected = [
detect.("title"),
detect.("categories"),
detect.("summary"),
detect.("related"),
detect.("redirect")
].reject(_.isNil);
while { detected.size < 5 } { detected = detected ++ ["\n"] };
detected.join($\n)
};
var body = { |array|
var firstLowercase_In_Block = "iota|allTuples|beatsPerBar|deepCollect|dumpBackTrace|flopDeep|"
++ "macOS|maxSizeAtDepth|mouseOverAction|onClose|plot[^s]|plugins|pluginSpec.plist|pop-global-mark|"
++ "pvcalc|pvcalc2|pvcollect|reset[^s]|reshapeLike|sclang|scsynth|serverConfig.plist|set-mark-command|"
++ "step[^s]|synth-definition-file|synthdefs|topEnvironment|valueEnvir|var[^iable]";
var wordsAfterInstructiveVerb = "a|an|any|another|the|this|that|those|these|its|their|if|true|false|e|nil|"
++ "(?:from|for) a(?:n|)|SuperCollider-barline sync|"
++ "midinote|cycle(?:s|)|function|two|noise|how|when|which|between|hsv|one|control (?:name|to|identified|rate)|up the|"
++ "task|random|both|methods|duration|input (?:signal|wave|into)|where|QtGUI|1.0|whether|item|pixel|infinities|"
++ "\\*correctly\\*|non-bandlimited|back|digital data|zero|buffer|node|synth|whitespace|parameter|integers|blocks|bins|"
++ "configuration|0 and 1|bar|beat|Carlson's|to (?:interpret|the|scope|immediately|sender|return|options)|be|garbage|prevVal|"
++ "in (?:to|the top)|sets of|linearly|quadratically|1 channel|on and off|1,|radian(?:s|)|degree(?:s|)|'git pull'|stream|Quant.default.|"
++ "waveshaping|supernova|you|recording|from what|bundle|same effect|(?:min|max)imum time|object|value(?:|s|\\.put\\(index, value\\))"
++ "(?|low|more) bins|magnitudes|only|several|current left border|polar|sound|subsequent|TempoClock\\.default|clock\\.nextTimeOnGrid\\.|over all|just|on the|next|"
++ inline ++ "|" ++ hypertext;
var consistencyConversionException = "(?<!exerc)(?<!surpr)(?<!compr)(?<!enterpr)(?<!adv)";
var item = array;
item = item.collect { |stringPerLine|
stringPerLine = if(stringPerLine.contains("\ ::")){
stringPerLine
}{
/** Place no white space inside inline and hypertext **/
stringPerLine.replaceRegexp(
"((?:"
++ inline ++ "|"
++ hypertext ++ "|"
++ ")::)[ \\t]*([\\s\\S]+?)[ \\t]*(::)",
"$1$2$3"
)
};
stringPerLine = stringPerLine
/** Add a white space after level_C_Title, level_C_Argument, level_B_Title, level_B_Method and level_A_Title **/
.replaceRegexp(
"(^|\\n)((?:"
++ level_C_Title ++ "|" ++ level_C_Argument ++ "|"
++ level_B_Title ++ "|" ++ level_B_Method ++ "|"
++ level_A_Title
++ ")::)(\\S)",
"$1$2 $3"
)
/** Add a line change between cautionary/level_C_Block/level_A_Block and the text in the same line **/
.replaceRegexp(
"^((?:"
++ cautionary ++ "|"
++ level_C_Block ++ "|"
++ level_A_Block
++ ")::(?!\\n)(\\s*)?(?=\\w+))",
"$1\n"
)
/** arrange '::' in cautionary **/
.replaceRegexp(
"^((?:"
++ cautionary
++ ")::.*?(?=::))(::)$",
"$1\n$2"
)
/** Capitalise the first letter after ':: ' followed by level_C_Title, level_B_Title and level_A_Title **/
.replaceRegexp(
"^((?:"
++ level_C_Title ++ "|"
++ level_B_Title ++ "|"
++ level_A_Title
++ ")::\\s)"
++ "(?:(?!"
++ firstLowercase_In_Block ++ "|"
++ tagsAll
++ "))([a-z])",
"$1\\U$2"
)
/** Orthogaphy consistency **/
.replaceRegexp("\\bugen\\b", "UGen")
.replaceRegexp("\\bugens\\b", "UGens")
.replaceRegexp("\\b(eg.|eg:)\\b", "e.g.:")
.replaceRegexp("(?<!// )(\\bie\\b(|.|:))", "i.e.:")
.replaceRegexp("\\b" ++ consistencyConversionException ++ "ise\\b", "$1ize")
.replaceRegexp("\\b" ++ consistencyConversionException ++ "isation\\b", "$1ization")
.replaceRegexp("\\b" ++ consistencyConversionException ++ "ising\\b", "$1izing")
.replaceRegexp("\\b" ++ consistencyConversionException ++ "isable\\b", "$1izable")
.replaceRegexp("\\b" ++ consistencyConversionException ++ "yse\\b", "$1yze")
.replaceRegexp("\\b" ++ consistencyConversionException ++ "ised\\b", "$1ized")
.replaceRegexp("\\b" ++ consistencyConversionException ++ "isers\\b", "$1izers")
.replaceRegexp("\\b" ++ consistencyConversionException ++ "iser\\b", "$1izer");
/** Enclose some values by teletype tag **/
stringPerLine = if(stringPerLine.contains("section::")){
stringPerLine
} {
stringPerLine
.replaceRegexp(
"([Ww]hen|[Ii]f|[Ff]rom|[Tt]o|[Oo]f|[Rr]eturn(?:s|)|[Aa]nswer(?:s|))"
++ "\\s\\b(true|false|0.0|0|1.0|-1.0|\\+1.0|±1.0|\\+1|zero|negative|positive|nil|e)\\b",
"$1 teletype::$2::"
)
.replaceRegexp(
"\\b(true|false|0.0|0|1.0|-1.0|\\+1.0|±1.0|\\+1|zero|negative|positive|nil|e)\\b"
++ "(?=\\s(when|if|from|to|of))",
"teletype::$1::"
)
.replaceRegexp(
"([Ww]hen|[Ii]f|[Ff]rom|[Tt]o|[Oo]f|[Rr]eturn(?:s|)|[Aa]nswer(?:s|))\\s"
++ "\\b1.0\\b",
"$1 teletype::$2::"
)
.replaceRegexp(
"\\b(1.0)\\b"
++ "(?=\\s(when|if|from|to|of))",
"teletype::$1::"
)
.replaceRegexp(
"([Ww]hen|[Ii]f|[Ff]rom|[Tt]o|[Oo]f|[Rr]eturn(?:s|)|[Aa]nswer(?:s|))\\s"
++ "-1",
"$1 teletype::-1::"
)
.replaceRegexp(
" -1 "
++ "(?=(when|if|from|to|of))",
" teletype::-1:: "
)
.replaceRegexp(
"([Ww]hen|[Ii]f|[Ff]rom|[Tt]o|[Oo]f|[Rr]eturn(?:s|)|[Aa]nswer(?:s|))"
++ " 1 ",
"$1 teletype::1::"
)
.replaceRegexp(
" 1 "
++ "(?=(when|if|from|to|of))",
" teletype::1:: "
)
};
/** Add newline (not any blank, just line change) after periods followed by space and non-whitespace **/
stringPerLine.replaceRegexp(
"([^\"]*?)"
++ "(?<!\\bie)(?<!\\bi\\se)(?<!\\bi\\.e)(?<!\\bi\\s\\.e)((?<!:)(?<!\\s)("
++ ("\\s(?!//\\s*)\\w+" ! 5).join
++ "[.!?:])"
++ "(?!:))(\\s)(?![^\"]*\")",
"$1$2\n"
)
.replaceRegexp(
"(::\\s\\w+)"
++ "([.!?:])"
++ "\\s(\\w+\\s\\w+\\s\\w+\\s)",
"$1$2\n$3"
)
.replaceRegexp(
"(::|\\))"
++ "([.!?:])"
++ "\\s(\\w+\\s\\w+\\s\\w+\\s)",
"$1$2\n$3"
)
};
item = item.join($\n);
item = item
/** Line alignment **/
/* Remove unnecessary empty line at the end of supplementary */
.replaceRegexp("\\n\\n+::", "\n::")
/* Ensure exactly 1 empty line before and no empty lines after supplementary and cautionary */
.replaceRegexp(
"(?:\\n{1, 7})^((?:"
++ supplementary ++ "|"
++ cautionary
++ ")::)(\\n{1, 7})",
"\n\n$1\n"
)
.replaceRegexp("^(classtree::\\n)", "classtree:: ")
/* add 1 blank line between the line including only '::' and the next line contaning non-code text */
.replaceRegexp(
"^(::)\n*([^\n])",
"$1\n\n$2"
)
/* Ensure exactly 2 empty lines before and 1 empty line after level_C_, level_C_Title and level_C_Argument */
.replaceRegexp(
"(?:\\n{1, 7})((?:"
++ level_C_Block
++ ")::)(\\n{0, 7})",
"\n\n\n$1\n"
)
.replaceRegexp(
"(?:\\n{1, 7})((?:"
++ level_C_Title ++ "|"
++ level_C_Argument
++ ")::\\s.*?$)(\\n{0, 7})",
"\n\n\n$1\n\n"
)
/* Ensure exactly 4 empty lines before and 2 empty lines after level_B_Method */
.replaceRegexp(
"(?:\\n{1, 7})(\\b(?:"
++ level_B_Method
++ ")\\b::\\s.*?$)(?:\\n{0, 7})",
"\n\n\n\n\n$1\n\n\n"
)
/* Ensure exactly 6 empty lines before and 2 empty lines after level_B_Title */
.replaceRegexp(
"(?:\\n{1, 7})(\\b(?:"
++ level_B_Title
++ ")\\b::\\s.*?$)(\\n{0, 7})",
"\n\n\n\n\n\n\n$1\n\n\n"
);
/* Concatenate a sentence across lines to a single line */
if(item.contains("section::")) {
item
}{
item.replaceRegexp(
"(\\w+\\s\\w+)\\n(?!" ++ tagsAll ++ "|" ++ firstLowercase_In_Block ++ ")(?=(\\w+\\s\\w+(\\s|\\.|\\!|\\?|,)))",
"$1 "
)
}
/** Capitalisation **/
/* Capitalise the first letter in the next line of the line including cautionary, and level_C_Blockand level_A_Block */
.replaceRegexp(
"^((?:"
++ level_A_Block ++ "|"
++ level_C_Block++ "|"
++ cautionary ++ ")::\\n*)"
++ "(?:(?!(?:"
++ tagsAll
++ ")::))"
++ "(?:(?!" ++ tagsAll
++ "))([a-z])",
"$1\\U$2"
)
/* Capitalise the first letter in the first non-empty line after the specified tags,
level_C_Title,
level_C_Argument,
level_B_Title,
level_B_Method and
level_A_Title
allowing up to two empty lines between the tag line and the target line */
.replaceRegexp(
"^((?:"
++ level_C_Title ++ "|" ++ level_C_Argument ++ "|"
++ level_B_Title ++ "|" ++ level_B_Method ++ "|"
++ level_A_Title ++ ")::\\s[^\n]+)"
++ "(\\n+)"
++ "(?:(?!("
++ tagsAll
++ ")))"
++ "(?:(?!" ++ firstLowercase_In_Block ++ "))"
++ "([a-z])",
"$1$2\\U$4"
)
/* Capitalise the first letter in the next non-empty line of :: */
.replaceRegexp(
"^(::)\n+(?!\\b("
++ tagsAll ++ "|"
++ firstLowercase_In_Block
++ ")\\b)([a-z])",
"$1\n\n\\u$3"
)
/** Apply 'instructive (declarative) tone' to the first sentence following specific tags **/
/* after ':: ' followed by level_C_Title, level_B_Title and level_A_Title **/
.replaceRegexp(
x = "^((?:"
++ level_C_Title ++ "|"
++ level_B_Title ++ "|"
++ level_A_Title
++ ")::\\s"
++ "(?:(?!" ++ firstLowercase_In_Block ++ "|" ++ tagsAll ++ "))";
x ++ "\\b\\w+)ies\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1y$2"
)
.replaceRegexp(
x ++ "\\b\\w+e)s\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2"
)
.replaceRegexp(
x ++ "\\b\\w+)s\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2"
)
/** in the next line of the line including cautionary, and level_C_Block and level_A_Block **/
.replaceRegexp(
x = "^((?:"
++ level_A_Block ++ "|"
++ level_C_Block++ "|"
++ cautionary
++ ")::\\n*";
x ++ "\\b\\w+)ies\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b\\s)",
"$1y$2"
)
.replaceRegexp(
x ++ "\\b\\w+e)s\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b\\s)",
"$1$2"
)
.replaceRegexp(
x ++ ")\\b(Reads\\s*" ++ "(/|or|and)\\s*writes)\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1Read/write"
)
.replaceRegexp(
x ++ ")\\b(Attenuates\\s*" ++ "(/|or|and)\\s*boosts)\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1Attenuate/boost"
)
.replaceRegexp(
x ++ ")\\b(\\w+)s\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b\\s)",
"$1$2"
)
/** in the first non-empty line after the specified tags,
level_C_Title,
level_C_Argument,
level_B_Title,
level_B_Method and
level_A_Title
allowing up to two empty lines between the tag line and the target line **/
.replaceRegexp(
x = "^((?:"
++ level_C_Title ++ "|"
++ level_C_Argument ++ "|"
++ level_B_Title ++ "|"
++ level_B_Method ++ "|"
++ level_A_Title ++ ")::\\s[^\n]+)"
++ "(\\n+)"
++ "(?:(?!(" ++ tagsAll ++ ")))"
++ "(?:(?!" ++ firstLowercase_In_Block ++ "))";
x ++ "\\b(\\w+)ies\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2y$4"
)
.replaceRegexp(
x ++ "\\b(Gets\\s*" ++ "(/|or|and)\\s*sets)\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2Get/set"
)
.replaceRegexp(
x ++ "\\b(Sets\\s*" ++ "(/|or|and)\\s*gets)\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2Set/get"
)
.replaceRegexp(
x ++ "\\b(Reads\\s*" ++ "(/|or|and)\\s*writes)\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2Read/write"
)
.replaceRegexp(
x ++ "\\b(Sets\\s*" ++ "(/|or|and)\\s*returns)\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2Set/return"
)
.replaceRegexp(
x ++ "\\b(Starts\\s*" ++ "(/|or|and)\\s*resumes)\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2Start/resume"
)
.replaceRegexp(
x ++ "\\b(\\w+e)s\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2$4"
)
.replaceRegexp(
x ++ "\\b(\\w+)s\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2$4"
)
/* in the next line of :: */
.replaceRegexp(
x = "^(::\\n\\n)(?!" ++ tagsAll ++ ")";
x ++ "\\b(\\w+)ies\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1y$2")
.replaceRegexp(
x ++ "\\b(\\w+e)s\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2"
)
.replaceRegexp(
x ++ "\\b(\\w+)s\\b" ++ "(?=\\s\\b(" ++ wordsAfterInstructiveVerb ++ ")\\b)",
"$1$2"
)
};
/** Make backup copy **/
PathName(pathHome_origin_absolute).filesDo { |aPathName|
var pathOld = aPathName.fullPath;
var pathNew = pathOld.replace(pathHome_origin_relative, pathHome_working_relative);
if(File.exists(pathNew.dirname).not) {
File.mkdir(pathNew.dirname)
};
File.copy(pathOld, pathNew);
};
/** Reformat schelp files **/
PathName(pathHome_working_absolute).filesDo { |aPathName|
if(aPathName.extension == "schelp") {
var thisPath = aPathName.fullPath;
var stringsFromFile = File.readAllString(thisPath);
var stringsTemp = stringsFromFile.split($\n);
/* Change tag keywords to lowercase */
stringsTemp.do { |textPerLine, index|
textPerLine = textPerLine.replaceRegexp("(?<![/:#\])(\\b(?i:" ++ tagsAll ++ ")\\b::)", "\\L$1");
stringsTemp[index] = textPerLine;
};
stringsTemp =
/* Reformat header */
header.(stringsTemp[0..3])
++
/* Reformat body */
body.(stringsTemp[4..]);
/* Ensure exactly 10 empty lines before and 4 empty lines after level_A_Block and level_A_Title */
stringsTemp = stringsTemp
.replaceRegexp(
"(?:\\n{1, 7})(\\b(?:"
++ level_A_Block
++ ")\\b::)(\\n{0, 7})",
"\n\n\n\n\n\n\n\n\n\n\n$1\n\n\n\n\n"
)
.replaceRegexp(
"(?:\\n{1, 7})(\\b(?:"
++ level_A_Title
++ ")\\b::\\s.*?$)(?:\\n{0, 7})",
"\n\n\n\n\n\n\n\n\n\n\n$1\n\n\n\n\n"
);
if(stringsTemp != stringsFromFile) {
var fileToOverwrite = File(thisPath, "w");
fileToOverwrite.write(stringsTemp);
fileToOverwrite.close;
}
}
}
)
</code></pre>
</details>
## Types of changes
- Documentation
## To-do list
- [x] Code is tested
- [x] All tests are passing
- [x] Updated documentation
- [x] This PR is ready for review