From baeb12133a0d446a8ae08e152073e581a01fd3af Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Fri, 9 Feb 2024 20:51:44 -0800 Subject: [PATCH 1/4] Add doc-comment for getFileExtension. Rename findStrings to multilineMatch and add doc-comment. Create makeFencedCodeBlock expression. Refactor createLessonMetadata to accept an attrset for high level arguments. Refactor createLessonMetadata to use more of the new functions. Refactor createLessonMetadata by moving keys that don't need to be in the output to the let/in block. Refactor filesToSubstitute to append the parent path to the file name. Refactor textToSubstitute to use the new makeFencedCodeBlock expression. --- flake.nix | 7 +- lib/lessons.nix | 168 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 126 insertions(+), 49 deletions(-) diff --git a/flake.nix b/flake.nix index 19566a6..3b5af3d 100644 --- a/flake.nix +++ b/flake.nix @@ -37,7 +37,12 @@ python = pkgs.python311; }; - lessonsDocumentation = lessonsLib.generateLessonsDocumentation {lessonsPath = ./lessons;}; + lessonsDocumentation = + lessonsLib.generateLessonsDocumentation + { + lessonsPath = ./lessons; + lessonFile = "lesson.md"; + }; site = pkgs.stdenvNoCC.mkDerivation { name = "modules-lessons-site"; diff --git a/lib/lessons.nix b/lib/lessons.nix index 2a0be3f..ae05be6 100644 --- a/lib/lessons.nix +++ b/lib/lessons.nix @@ -14,6 +14,37 @@ ) ); + /* + Return the extension of a file. + + # Example + + ```nix + getFileExtension ./directory/eval.nix + => "nix" + getFileExtension ./directory/run + => "" + getFileExtension ./directory/archive.tar.xz + => "tar.xz" + getFileExtension "./directory/eval.nix" + => "nix" + getFileExtension "./directory/run" + => "" + getFileExtension "./directory/archive.tar.xz" + => "tar.xz" + ``` + + # Type + + ``` + getFileExtension :: Path -> String + getFileExtension :: String -> String + ``` + + # Arguments + + - [path] A path or string that contains a path to a file. + */ getFileExtension = path: ( lib.concatStringsSep "." @@ -29,42 +60,102 @@ ) ); - createLessonMetadata = name: value: let + /* + Like `match` but works on multiline strings. + + Returns a list if the extended POSIX regular expression regex matches str precisely, otherwise returns null. + Each item in the list is a regex group. + + # Example + + ```nix + multilineMatch + ''(\[//]: # \(.*\..*\))'' + '' + In the `options.nix` file, we have declared boolean, enumeration, integer, and string options. + + [//]: # (./options.nix) + + In the `config.nix` file, we have declared values for all these options. + + [//]: # (./config.nix) + + In the `eval.nix` file, we evaluate our options and config and have it return the config values. + '' + => [ "[//]: # (./options.nix)" "[//]: # (./config.nix)" ] + ``` + + # Type + + ``` + multilineMatch :: String -> String -> [String] + ``` + + # Arguments + + - [regex] The regular expression. + - [input] The string to search. + */ + multilineMatch = regex: input: ( + lib.flatten + ( + builtins.filter + (elem: ! builtins.isNull elem) + ( + builtins.map + ( + lib.strings.match + regex + ) + ( + lib.splitString + "\n" + input + ) + ) + ) + ); + + makeFencedCodeBlock = file: '' + ``` ${getFileExtension file} title="${builtins.baseNameOf file}" + ${builtins.readFile file} + ``` + ''; + + createLessonMetadata = {lessonFile ? "lesson.md", ...}: name: value: let + lessonDir = name; lessonPath = value; - rawLesson = builtins.readFile (lib.path.append lessonPath "lesson.md"); - in rec { - outputParentDir = "lessons/" + name; - outputFilePath = outputParentDir + "/lesson.md"; - linesToReplace = findStrings ''(\[//]: # \(.*\..*\))'' rawLesson; + rawLesson = builtins.readFile (lib.path.append lessonPath lessonFile); + + commentLineMatch = ''(\[//]: # \(.*\..*\))''; + commentFileMatch = ''\[//]: # \(\./(.*)\)''; + linesToReplace = multilineMatch commentLineMatch rawLesson; filesToSubstitute = ( - lib.flatten + builtins.map ( - builtins.map + lib.path.append + lessonPath + ) + ( + lib.flatten ( - findStrings - ''\[//]: # \(\./(.*)\)'' + builtins.map + ( + multilineMatch + commentFileMatch + ) + linesToReplace ) - linesToReplace ) ); textToSubstitute = ( builtins.map - ( - file: let - fileExtension = getFileExtension file; - in '' - ``` ${fileExtension} title="${file}" - ${ - builtins.readFile - ( - lib.path.append lessonPath file - ) - } - ``` - '' - ) + makeFencedCodeBlock filesToSubstitute ); + in rec { + outputParentDir = "lessons/" + lessonDir; + outputFilePath = outputParentDir + "/" + lessonFile; subsLesson = ( builtins.replaceStrings [''[//]: # (evaluatedLesson)''] @@ -94,30 +185,10 @@ .config; }; - lessonsToMetadata = {lessonsPath ? ../lessons, ...}: ( + lessonsToMetadata = {lessonsPath ? ../lessons, ...} @ args: ( lib.mapAttrs - createLessonMetadata - (getLessons {inherit lessonsPath;}) - ); - - findStrings = regex: input: ( - lib.flatten - ( - builtins.filter - (elem: ! builtins.isNull elem) - ( - builtins.map - ( - lib.strings.match - regex - ) - ( - lib.splitString - "\n" - input - ) - ) - ) + (createLessonMetadata args) + (getLessons args) ); copyLessonsToNixStore = lessons: @@ -144,6 +215,7 @@ ) } ''; + generateLessonsDocumentation = args: ( copyLessonsToNixStore ( From 67793a4f3bd6079218e6d29d77b31ebf0d83ef0d Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Fri, 9 Feb 2024 20:59:02 -0800 Subject: [PATCH 2/4] Add doc-comment for makeFencedCodeBlock. --- lib/lessons.nix | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/lessons.nix b/lib/lessons.nix index ae05be6..8282b10 100644 --- a/lib/lessons.nix +++ b/lib/lessons.nix @@ -116,6 +116,33 @@ ) ); + /* + Create a fenced code block with language identifier and file name given a file. + + # Example + + ````nix + makeFencedCodeBlock ./eval.nix + => '' + ``` nix title="eval.nix" + let + a = 1; + in + a + ``` + '' + ```` + + # Type + + ``` + makeFencedCodeBlock :: Path -> String + ``` + + # Arguments + + - [path] The file. + */ makeFencedCodeBlock = file: '' ``` ${getFileExtension file} title="${builtins.baseNameOf file}" ${builtins.readFile file} From c0c3b702d6379add9247180d15ad6f8ba7d83f59 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Fri, 9 Feb 2024 21:12:35 -0800 Subject: [PATCH 3/4] Add comments for all the higher level functions that create metadata or building markdown documentation. --- lib/lessons.nix | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/lessons.nix b/lib/lessons.nix index 8282b10..29fefc8 100644 --- a/lib/lessons.nix +++ b/lib/lessons.nix @@ -149,6 +149,9 @@ ``` ''; + /* + Given a lesson, create the metadata necessary to create the markdown documentation. + */ createLessonMetadata = {lessonFile ? "lesson.md", ...}: name: value: let lessonDir = name; lessonPath = value; @@ -212,12 +215,18 @@ .config; }; - lessonsToMetadata = {lessonsPath ? ../lessons, ...} @ args: ( + /* + Maps over all the lessons and generates metadata. + */ + lessonsToMetadata = args: ( lib.mapAttrs (createLessonMetadata args) (getLessons args) ); + /* + Given a list of lesson metadata attrsets, copy the contents to the nix store. + */ copyLessonsToNixStore = lessons: pkgs.runCommand "copy-module-lessons" @@ -243,6 +252,11 @@ } ''; + /* + Primary function for building lesson documentation. + + Is used when building the site. + */ generateLessonsDocumentation = args: ( copyLessonsToNixStore ( @@ -251,6 +265,11 @@ ) ); + /* + Builds the lessons documentation and copies it to the needed location in the mkdocs directory. + + Primary use is for developing with `mkdocs serve`. + */ copyLessonsToSite = pkgs.writeShellScriptBin "copy-lessons-to-site" From eeb1975a35d5a3103fbf40fad484b3b682046974 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Fri, 9 Feb 2024 21:17:16 -0800 Subject: [PATCH 4/4] Add doc-comment for getLessons. --- lib/lessons.nix | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/lessons.nix b/lib/lessons.nix index 29fefc8..74596ce 100644 --- a/lib/lessons.nix +++ b/lib/lessons.nix @@ -4,6 +4,30 @@ lib, ... }: rec { + /* + Creates an attrset where the key is the lesson directory name and the value is the path to the directory. + + # Example + + ```nix + getLessons + {lessonsPath = ../lessons;} + => { + "001-a-module" = /lessons/001-a-module; + "010-basic-types" = /lessons/010-basic-types; + } + ``` + + # Type + + ``` + getLessons :: Attrset -> Attrset + ``` + + # Arguments + + - [lessonsPath] The path to the lessons directory. + */ getLessons = {lessonsPath ? ../lessons, ...}: ( lib.mapAttrs (name: _: lib.path.append lessonsPath name)