Skip to content

Commit

Permalink
improvement: add option to set a prefix to generate absolute image paths
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanLovato committed Jan 25, 2023
1 parent 854dd60 commit 73f3f03
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 79 deletions.
22 changes: 11 additions & 11 deletions src/buildcourse.nim
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,13 @@ proc getTempDir(workingDir: string): string =

proc resolveWorkingDir(appSettings: AppSettingsBuildCourse): AppSettingsBuildCourse =
## Tries to find the root directory of the course project by checking
## either `CFG_FILE` or `appSettings.courseDir` exist.
## either `CFG_FILE` or `appSettings.contentDir` exist.
##
## If a valid course project was found it returns the updated
## `AppSettingsBuildCourse.workingDir`.
result = appSettings
for dir in appSettings.inputDir.parentDirs:
if (dir / CFG_FILE).fileExists or (dir / result.courseDir).dirExists:
if (dir / CFG_FILE).fileExists or (dir / result.contentDir).dirExists:
result.workingDir = dir
break

Expand All @@ -176,15 +176,15 @@ proc resolveAppSettings(appSettings: AppSettingsBuildCourse): AppSettingsBuildCo

if (result.workingDir / CFG_FILE).fileExists:
let cfg = loadConfig(result.workingDir / CFG_FILE)
if result.courseDir == "": result.courseDir = cfg.getSectionValue("", "courseDir", COURSE_DIR)
if result.contentDir == "": result.contentDir = cfg.getSectionValue("", "contentDir", COURSE_DIR)
if result.distDir == "": result.distDir = cfg.getSectionValue("", "distDir", DIST_DIR)
if result.godotProjectDirs.len == 0: result.godotProjectDirs = cfg.getSectionValue("", "godotProjectDirs").split(",").mapIt(it.strip)
if result.ignoreDirs.len == 0: result.ignoreDirs = cfg.getSectionValue("", "ignoreDirs").split(",").mapIt(it.strip)
if result.pandocExe == "": result.pandocExe = cfg.getSectionValue("", "pandocExe", PANDOC_EXE)
if result.pandocAssetsDir == "": result.pandocAssetsDir = cfg.getSectionValue("", "pandocAssetsDir")
if result.exec.len == 0: result.exec = cfg.getSectionValue("", "exec").split(",").mapIt(it.strip)

if result.courseDir == "": result.courseDir = COURSE_DIR
if result.contentDir == "": result.contentDir = COURSE_DIR
if result.godotProjectDirs.len == 0: result.godotProjectDirs = GODOT_PROJECT_DIRS
if result.distDir == "": result.distDir = DIST_DIR
if result.pandocExe == "": result.pandocExe = PANDOC_EXE
Expand All @@ -193,8 +193,8 @@ proc resolveAppSettings(appSettings: AppSettingsBuildCourse): AppSettingsBuildCo
if findExe(result.pandocExe) == "":
fmt"Can't find `{result.pandocExe}` on your system. Exiting.".quit

if not dirExists(result.workingDir / result.courseDir):
fmt"Can't find course directory `{result.workingDir / result.courseDir}`. Exiting.".quit
if not dirExists(result.workingDir / result.contentDir):
fmt"Can't find course directory `{result.workingDir / result.contentDir}`. Exiting.".quit

let ignoreDirsErrors = result.ignoreDirs
.filterIt(not dirExists(result.workingDir / it))
Expand Down Expand Up @@ -227,7 +227,7 @@ proc getAppSettings(): AppSettingsBuildCourse =
case key
of "help", "h": HELP_MESSAGE.fmt.quit(QuitSuccess)
of "clean", "": result.isCleaning = true
of "course-dir", "c": result.courseDir = value
of "course-dir", "c": result.contentDir = value
of "dist-dir", "d": result.distDir = value
of "exec", "e": result.exec.add value
of "force", "f": result.isForced = true
Expand All @@ -248,7 +248,7 @@ proc getAppSettings(): AppSettingsBuildCourse =

proc process(appSettings: AppSettingsBuildCourse) =
## Main function that processes the Markdown files from
## `appSettings.courseDir` with Pandoc and the internal preprocessor.
## `appSettings.contentDir` with Pandoc and the internal preprocessor.
var
report = Report()
runner = PandocRunner.init(appSettings)
Expand Down Expand Up @@ -288,15 +288,15 @@ proc process(appSettings: AppSettingsBuildCourse) =
removeDir(tmpDir)

# Prepare the global `cache` for `appSettings.workingDir`.
cache = prepareCache(appSettings.workingDir, appSettings.courseDir, appSettings.ignoreDirs)
cache = prepareCache(appSettings.workingDir, appSettings.contentDir, appSettings.ignoreDirs)
for fileIn in cache.files.filterIt(
it.toLower.endsWith(MD_EXT) and
(appSettings.courseDir & DirSep) in it
(appSettings.contentDir & DirSep) in it
):
let
fileIn = appSettings.workingDir / fileIn
fileOut = fileIn.multiReplace(
(appSettings.courseDir & DirSep, appSettings.distDir & DirSep),
(appSettings.contentDir & DirSep, appSettings.distDir & DirSep),
(MD_EXT, HTML_EXT)
)

Expand Down
31 changes: 16 additions & 15 deletions src/md/preprocessgdschool.nim
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ proc preprocessCodeLine(cl: CodeLine, mdBlocks: seq[Block]; fileName: string): s
of clkRegular: cl.line


proc computeImageSlugs(fileName: string, rootSectionFolder: string): seq[string] =
proc computeImageSlugs(fileName: string, imagePathPrefix: string): seq[string] =
if fileName in cacheSlug:
return cacheSlug[fileName]

for dir in fileName.parentDirs(inclusive = false):
if dir.endsWith(rootSectionFolder):
if dir.endsWith(imagePathPrefix):
break

let meta_mdpath = dir / META_MDFILE
Expand All @@ -92,14 +92,14 @@ proc computeImageSlugs(fileName: string, rootSectionFolder: string): seq[string]
cacheSlug[fileName] = result


proc preprocessImage(img: Block, mdBlocks: seq[Block], fileName: string, rootSectionFolder: string): string =
proc preprocessImage(img: Block, mdBlocks: seq[Block], fileName: string, imagePathPrefix: string): string =
var img = img
img.path.removePrefix("./")
img.path = (@["", rootSectionFolder] & computeImageSlugs(fileName, rootSectionFolder) & img.path.split(AltSep)).join($AltSep)
img.path = (@["", imagePathPrefix] & computeImageSlugs(fileName, imagePathPrefix) & img.path.split(AltSep)).join($AltSep)
img.render


proc preprocessBlock(mdBlock: Block, mdBlocks: seq[Block]; fileName: string, rootSectionFolder: string): string =
proc preprocessBlock(mdBlock: Block, mdBlocks: seq[Block]; fileName: string, imagePathPrefix: string): string =
case mdBlock.kind
of bkShortcode:
SHORTCODESv2.getOrDefault(mdBlock.name, noOpShortcode)(mdBlock, mdBlocks, fileName)
Expand All @@ -117,21 +117,22 @@ proc preprocessBlock(mdBlock: Block, mdBlocks: seq[Block]; fileName: string, roo
].join(NL)

of bkImage:
mdBlock.preprocessImage(mdBlocks, fileName, rootSectionFolder)
mdBlock.preprocessImage(mdBlocks, fileName, imagePathPrefix)

else:
mdBlock.render


proc preprocess*(fileName, contents: string, courseName: string): string =
proc preprocess*(fileName, contents: string, imagePathPrefix: string): string =
let mdBlocks = contents.parse

var rootSectionFolder = courseName
for folderName in ROOT_SECTION_FOLDERS:
if folderName & AltSep in fileName:
rootSectionFolder = folderName
break
var prefix = imagePathPrefix
if prefix == "":
for folderName in ROOT_SECTION_FOLDERS:
if folderName & AltSep in fileName:
prefix = folderName
break
if prefix.isEmptyOrWhitespace():
error fmt"The file {fileName} should be in one of the following folders: {ROOT_SECTION_FOLDERS}"

if rootSectionFolder.isEmptyOrWhitespace():
error fmt"The file {fileName} should be in one of the following folders: {ROOT_SECTION_FOLDERS}"
mdBlocks.mapIt(preprocessBlock(it, mdBlocks, fileName, rootSectionFolder)).join(NL)
mdBlocks.mapIt(preprocessBlock(it, mdBlocks, fileName, prefix)).join(NL)
6 changes: 3 additions & 3 deletions src/md/utils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var cache*: Cache ## |
proc `$`*(r: Report): string = fmt"Summary: {r.built} built, {r.errors} errors, {r.skipped} skipped."


proc prepareCache*(workingDir, courseDir: string; ignoreDirs: openArray[string]): Cache =
proc prepareCache*(workingDir, contentDir: string; ignoreDirs: openArray[string]): Cache =
## Retruns a `Cache` object with:
## - `return.files`: `seq[string]` stores all Markdown, GDScript and Shader
## paths.
Expand Down Expand Up @@ -67,9 +67,9 @@ proc prepareCache*(workingDir, courseDir: string; ignoreDirs: openArray[string])
if (path.toLower.endsWith(GD_EXT) or path.toLower.endsWith(SHADER_EXT)):
blockResult.add searchDir / path

for path in walkDirRec(workingDir / courseDir, relative = true):
for path in walkDirRec(workingDir / contentDir, relative = true):
if path.toLower.endsWith(MD_EXT):
blockResult.add courseDir / path
blockResult.add contentDir / path

blockResult

Expand Down
81 changes: 35 additions & 46 deletions src/preprocessgdschool.nim
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ Options:
Default: {DIST_DIR}.
*Note* that DIR is relative to the course project
root directory.
-f, --force run even if course files are older than
the existing output files.
Default: false.
-g, --godot-project-dir:DIR
add DIR to a list for copying to {DIST_DIR}. All
anchor comments will be removed from the GDScript
Expand All @@ -65,6 +62,7 @@ Options:
- The outut directory is automatically
added to the ignored list.
-v, --verbose print extra information when building the course.
-q, --quiet suppress output
Shortcodes:
{{{{ include fileName(.gd|.shader) [anchorName] }}}}
Expand Down Expand Up @@ -112,13 +110,13 @@ proc getDepends(contents: string): seq[string] =

proc resolveWorkingDir(appSettings: AppSettingsBuildGDSchool): AppSettingsBuildGDSchool =
## Tries to find the root directory of the course project by checking
## either `CFG_FILE` or `appSettings.courseDir` exist.
## either `CFG_FILE` or `appSettings.contentDir` exist.
##
## If a valid course project was found it returns the updated
## `AppSettingsBuildGDSchool.workingDir`.
result = appSettings
for dir in appSettings.inputDir.parentDirs:
if (dir / CFG_FILE).fileExists or (dir / result.courseDir).dirExists:
if (dir / CFG_FILE).fileExists or (dir / result.contentDir).dirExists:
result.workingDir = dir
break

Expand All @@ -135,19 +133,20 @@ proc resolveAppSettings(appSettings: AppSettingsBuildGDSchool): AppSettingsBuild
result = resolveWorkingDir(result)

if (result.workingDir / CFG_FILE).fileExists:
let cfg = loadConfig(result.workingDir / CFG_FILE)
if result.courseDir == "": result.courseDir = cfg.getSectionValue("", "courseDir", COURSE_DIR)
if result.distDir == "": result.distDir = cfg.getSectionValue("", "distDir", DIST_DIR)
if result.godotProjectDirs.len == 0: result.godotProjectDirs = cfg.getSectionValue("", "godotProjectDirs").split(",").mapIt(it.strip)
if result.ignoreDirs.len == 0: result.ignoreDirs = cfg.getSectionValue("", "ignoreDirs").split(",").mapIt(it.strip)

if result.courseDir == "": result.courseDir = COURSE_DIR
if result.godotProjectDirs.len == 0: result.godotProjectDirs = GODOT_PROJECT_DIRS
let config = loadConfig(result.workingDir / CFG_FILE)
if result.contentDir == "": result.contentDir = config.getSectionValue("", "contentDir", COURSE_DIR)
if result.distDir == "": result.distDir = config.getSectionValue("", "distDir", DIST_DIR)
if result.ignoreDirs.len == 0: result.ignoreDirs = config.getSectionValue("", "ignoreDirs").split(",").mapIt(it.strip)
result.imagePathPrefix = config.getSectionValue("", "imagePathPrefix")
if result.imagePathPrefix == "":
warn(fmt"Missing imagePathPrefix key in {CFG_FILE}. The program will calculate a prefix to generate absolute file paths in markdown files, but this prefix will not work for courses. Exiting...")

if result.contentDir == "": result.contentDir = COURSE_DIR
if result.distDir == "": result.distDir = DIST_DIR

if not result.isCleaning:
if not dirExists(result.workingDir / result.courseDir):
fmt"Can't find course directory `{result.workingDir / result.courseDir}`. Exiting.".quit
if not dirExists(result.workingDir / result.contentDir):
fmt"Can't find course directory `{result.workingDir / result.contentDir}`. Exiting.".quit

let ignoreDirsErrors = result.ignoreDirs
.filterIt(not dirExists(result.workingDir / it))
Expand All @@ -166,8 +165,8 @@ proc getAppSettings(): AppSettingsBuildGDSchool =
result.workingDir = result.inputDir

for kind, key, value in getopt(
shortNoVal = {'f', 'h', 'v'},
longNoVal = @["clean", "force", "help", "verbose"]
shortNoVal = {'h', 'v', 'q'},
longNoVal = @["clean", "help", "verbose", "quiet"]
):
case kind
of cmdEnd: break
Expand All @@ -180,12 +179,11 @@ proc getAppSettings(): AppSettingsBuildGDSchool =
case key
of "help", "h": HELP_MESSAGE.fmt.quit(QuitSuccess)
of "clean", "": result.isCleaning = true
of "course-dir", "c": result.courseDir = value
of "course-dir", "c": result.contentDir = value
of "dist-dir", "d": result.distDir = value
of "force", "f": result.isForced = true
of "godot-project-dir", "g": result.godotProjectDirs.add value
of "ignore-dir", "i": result.ignoreDirs.add value
of "verbose", "v": logger.levelThreshold = lvlAll
of "quiet", "q": result.isQuiet = true
else: [ fmt"Unrecognized command line option: `{key}`."
, ""
, "Help:"
Expand All @@ -199,41 +197,32 @@ proc getAppSettings(): AppSettingsBuildGDSchool =
proc process(appSettings: AppSettingsBuildGDSchool) =
# Copy asset/all subfolders
createDir(appSettings.distDir)
for dirIn in walkDirs(appSettings.workingDir / appSettings.courseDir / "**" / "*"):
let dirOut = dirIn.replace(appSettings.courseDir & DirSep, appSettings.distDir & DirSep)
for dirIn in walkDirs(appSettings.workingDir / appSettings.contentDir / "**" / "*"):
let dirOut = dirIn.replace(appSettings.contentDir & DirSep, appSettings.distDir & DirSep)
copyDir(dirIn, dirOut)

cache = prepareCache(appSettings.workingDir, appSettings.courseDir, appSettings.ignoreDirs)
cache = prepareCache(appSettings.workingDir, appSettings.contentDir, appSettings.ignoreDirs)
for fileIn in cache.files.filterIt(
it.toLower.endsWith(MD_EXT) and
(appSettings.courseDir & DirSep) in it
(appSettings.contentDir & DirSep) in it
):
let
fileIn = appSettings.workingDir / fileIn
fileOut = fileIn.replace(appSettings.courseDir & DirSep, appSettings.distDir & DirSep)

var processingMsg = fmt"Processing `{fileIn.relativePath(appSettings.workingDir)}` -> `{fileOut.relativePath(appSettings.workingDir)}`..."
if logger.levelThreshold == lvlAll:
info processingMsg
else:
echo processingMsg

let
fileInContents = readFile(fileIn)
doProcess = (
appSettings.isForced or
not fileExists(fileOut) or
fileIn.fileNewer(fileOut) or
fileInContents.getDepends.anyIt(it.fileNewer(fileOut))
)

if doProcess:
createDir(fileOut.parentDir)
fileOut = fileIn.replace(appSettings.contentDir & DirSep, appSettings.distDir & DirSep)

if not appSettings.isQuiet:
let processingMsg = fmt"Processing `{fileIn.relativePath(appSettings.workingDir)}` -> `{fileOut.relativePath(appSettings.workingDir)}`..."
if logger.levelThreshold == lvlAll:
info processingMsg
else:
echo processingMsg

let fileInContents = readFile(fileIn)
createDir(fileOut.parentDir)
if not appSettings.isQuiet:
info fmt"Creating output `{fileOut.parentDir}` directory..."
writeFile(fileOut, preprocess(fileIn, fileInContents, appSettings.courseDir))

else:
info processingMsg
writeFile(fileOut, preprocess(fileIn, fileInContents, appSettings.imagePathPrefix))


when isMainModule:
Expand Down
11 changes: 7 additions & 4 deletions src/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type
AppSettingsBuildCourse* = object
inputDir*: string
workingDir*: string
courseDir*: string
contentDir*: string
distDir*: string
godotProjectDirs*: seq[string]
ignoreDirs*: seq[string]
Expand All @@ -29,12 +29,15 @@ type
AppSettingsBuildGDSchool* = object
inputDir*: string
workingDir*: string
courseDir*: string
contentDir*: string
distDir*: string
godotProjectDirs*: seq[string]
ignoreDirs*: seq[string]
isCleaning*: bool
isForced*: bool
isQuiet*: bool
# Prefix to preprend to markdown image urls when making them absolute for GDSchool.
imagePathPrefix*: string


func `$`*(appSettings: AppSettingsFormat): string =
Expand All @@ -49,7 +52,7 @@ func `$`*(appSettings: AppSettingsBuildCourse): string =
[ "AppSettings:"
, "\tinputDir: {appSettings.inputDir}".fmt
, "\tworkingDir: {appSettings.workingDir}".fmt
, "\tcourseDir: {appSettings.courseDir}".fmt
, "\tcontentDir: {appSettings.contentDir}".fmt
, "\tdistDir: {appSettings.distDir}".fmt
, "\tgodotProjectDirs: {appSettings.godotProjectDirs}".fmt
, "\tignoreDirs: {appSettings.ignoreDirs.join(\", \")}".fmt
Expand All @@ -65,7 +68,7 @@ func `$`*(appSettings: AppSettingsBuildGDSchool): string =
[ "AppSettings:"
, "\tinputDir: {appSettings.inputDir}".fmt
, "\tworkingDir: {appSettings.workingDir}".fmt
, "\tcourseDir: {appSettings.courseDir}".fmt
, "\tcontentDir: {appSettings.contentDir}".fmt
, "\tdistDir: {appSettings.distDir}".fmt
, "\tgodotProjectDirs: {appSettings.godotProjectDirs}".fmt
, "\tignoreDirs: {appSettings.ignoreDirs.join(\", \")}".fmt
Expand Down

0 comments on commit 73f3f03

Please sign in to comment.