diff --git a/.editorconfig b/.editorconfig index 473207c5..477a603e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,12 +3,22 @@ root = true # All files [*] indent_style = space -dotnet_diagnostic.CA1047.severity = error # Xml files [*.xml] indent_size = 2 +# Xml project files +[*.{csproj,fsproj,vbproj,proj,slnx}] +indent_size = 2 + +# Xml config files +[*.{props,targets,config,nuspec}] +indent_size = 2 + +[*.json] +indent_size = 2 + # C# files [*.cs] @@ -19,7 +29,6 @@ indent_size = 4 tab_width = 4 # New line preferences -end_of_line = crlf insert_final_newline = false #### .NET Coding Conventions #### @@ -53,13 +62,16 @@ dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent dotnet_style_coalesce_expression = true:suggestion dotnet_style_collection_initializer = true:suggestion dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion dotnet_style_null_propagation = true:suggestion dotnet_style_object_initializer = true:suggestion dotnet_style_operator_placement_when_wrapping = beginning_of_line dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion dotnet_style_prefer_compound_assignment = true:suggestion dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion -dotnet_style_prefer_conditional_expression_over_return = false:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed:suggestion dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion dotnet_style_prefer_inferred_tuple_names = true:suggestion dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion @@ -79,15 +91,15 @@ dotnet_remove_unnecessary_suppression_exclusions = none [*.cs] # var preferences -csharp_style_var_elsewhere = false:silent +csharp_style_var_elsewhere = false:suggestion csharp_style_var_for_built_in_types = false:silent -csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_when_type_is_apparent = true:suggestion # Expression-bodied members csharp_style_expression_bodied_accessors = true:silent csharp_style_expression_bodied_constructors = false:silent csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_lambdas = when_on_single_line:error +csharp_style_expression_bodied_lambdas = true:suggestion csharp_style_expression_bodied_local_functions = false:silent csharp_style_expression_bodied_methods = false:silent csharp_style_expression_bodied_operators = false:silent @@ -96,6 +108,7 @@ csharp_style_expression_bodied_properties = true:silent # Pattern matching preferences csharp_style_pattern_matching_over_as_with_null_check = true:suggestion csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion csharp_style_prefer_not_pattern = true:suggestion csharp_style_prefer_pattern_matching = true:silent csharp_style_prefer_switch_expression = true:suggestion @@ -104,20 +117,31 @@ csharp_style_prefer_switch_expression = true:suggestion csharp_style_conditional_delegate_call = true:suggestion # Modifier preferences +csharp_prefer_static_anonymous_function = true:suggestion csharp_prefer_static_local_function = true:warning -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent +csharp_preferred_modifier_order = public,private,protected,internal,file,const,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion # Code-block preferences csharp_prefer_braces = true:silent csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = true:silent # Expression-level preferences csharp_prefer_simple_default_expression = true:suggestion csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_pattern_local_over_anonymous_function = true:suggestion csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion csharp_style_throw_expression = true:suggestion csharp_style_unused_value_assignment_preference = discard_variable:suggestion csharp_style_unused_value_expression_statement_preference = discard_variable:silent @@ -171,32 +195,21 @@ csharp_space_between_square_brackets = false # Wrapping preferences csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true -csharp_style_namespace_declarations = file_scoped:error -csharp_style_prefer_method_group_conversion = true:silent -csharp_style_prefer_top_level_statements = true:silent -csharp_style_prefer_primary_constructors = false:suggestion -csharp_style_prefer_null_check_over_type_check = true:suggestion -csharp_style_prefer_local_over_anonymous_function = true:suggestion -csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion -csharp_style_prefer_tuple_swap = true:suggestion -csharp_style_prefer_utf8_string_literals = true:suggestion -csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent -csharp_style_prefer_readonly_struct = true:suggestion -csharp_style_prefer_readonly_struct_member = true:suggestion -dotnet_diagnostic.CA1070.severity = error -dotnet_diagnostic.CA1032.severity = error -dotnet_diagnostic.CA1865.severity = error -dotnet_diagnostic.CA1866.severity = error -dotnet_diagnostic.CA1867.severity = error -dotnet_diagnostic.CA2014.severity = error csharp_prefer_system_threading_lock = true:suggestion -csharp_style_prefer_unbound_generic_type_in_nameof = true:suggestion -csharp_style_prefer_implicitly_typed_lambda_expression = true:suggestion csharp_style_prefer_simple_property_accessors = true:suggestion +csharp_style_prefer_implicitly_typed_lambda_expression = true:suggestion +csharp_style_prefer_unbound_generic_type_in_nameof = true:suggestion csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent +dotnet_diagnostic.CA1003.severity = error +dotnet_diagnostic.CA1008.severity = error +dotnet_diagnostic.CA1032.severity = error +dotnet_diagnostic.CA1034.severity = silent +dotnet_diagnostic.CA1036.severity = error +dotnet_diagnostic.CA1044.severity = error #### Naming styles #### [*.{cs,vb}] @@ -388,32 +401,17 @@ dotnet_naming_style.s_camelcase.required_prefix = s_ dotnet_naming_style.s_camelcase.required_suffix = dotnet_naming_style.s_camelcase.word_separator = dotnet_naming_style.s_camelcase.capitalization = camel_case -dotnet_style_prefer_collection_expression = when_types_exactly_match:suggestion -dotnet_style_namespace_match_folder = true:error tab_width = 4 indent_size = 4 end_of_line = crlf -dotnet_diagnostic.CA1000.severity = silent -dotnet_diagnostic.CA1001.severity = warning -dotnet_diagnostic.CA1002.severity = suggestion -dotnet_diagnostic.CA1008.severity = error -dotnet_diagnostic.CA1061.severity = error -dotnet_diagnostic.CA1063.severity = error -dotnet_diagnostic.CA2000.severity = error -dotnet_diagnostic.CA1816.severity = error -dotnet_diagnostic.CA2213.severity = error -dotnet_diagnostic.CA2215.severity = error -dotnet_diagnostic.CA1064.severity = error -dotnet_diagnostic.CA1069.severity = error -dotnet_diagnostic.CA1401.severity = error -dotnet_diagnostic.CA1501.severity = error -dotnet_diagnostic.CA1700.severity = error -dotnet_diagnostic.CA1821.severity = error -dotnet_diagnostic.CA1836.severity = error -dotnet_diagnostic.CA1843.severity = error -dotnet_diagnostic.CA1842.severity = error -dotnet_diagnostic.CA2011.severity = error dotnet_style_allow_multiple_blank_lines_experimental = true:silent dotnet_style_allow_statement_immediately_after_block_experimental = true:silent insert_final_newline = true +dotnet_diagnostic.CA1001.severity = error +dotnet_diagnostic.CA1002.severity = silent +dotnet_diagnostic.CA1005.severity = error +dotnet_diagnostic.CA1010.severity = error +dotnet_diagnostic.CA1041.severity = error +dotnet_diagnostic.CA2025.severity = error +dotnet_diagnostic.CA2213.severity = error diff --git a/BookGen.slnx b/BookGen.slnx index f5b834a6..e45768c8 100644 --- a/BookGen.slnx +++ b/BookGen.slnx @@ -13,7 +13,6 @@ - diff --git a/Commands.md b/Commands.md index 72748c9f..03855b09 100644 --- a/Commands.md +++ b/Commands.md @@ -2,8 +2,7 @@ BookGen - Markdown to Book tool. -For the tool to work in the work folder there must be a bookgen.json config file. This config file -can be created with the following command: +For the tool to work in the work folder there must be a bookgen.json config file. This config file can be created with the following command: `BookGen Newbook` @@ -17,500 +16,604 @@ To list available subcommands type: General arguments: -`-wd` -`--wait-debugger` - Waits for a debugger to be attached. Usefull for error reporting & error finding. +* `-wd` or `--wait-debugger` -`-ad` -`--attach-debugger` - Attaches a debugger. Usefull for error reporting & error finding. + Waits for a debugger to be attached. Usefull for error reporting & error finding. -`-js` -`--json-log` - Outputs log in JSON format. Usefull for interop purposes. +* `-ad` or `--attach-debugger` + + Attaches a debugger. Usefull for error reporting & error finding. + +* `-js` or `--json-log` + + Outputs log in JSON format. Usefull for interop purposes. # Addfrontmatter Add a basic YAML frontmatter information to all markdown files located in the current folder and it's subfolders. -`BookGen Addfrontmatter [-v] [-d [directory]]` -`BookGen Addfrontmatter [--verbose] [--dir [directory]]` +``` +BookGen Addfrontmatter [-v] [-d [directory]] +BookGen Addfrontmatter [--verbose] [--dir [directory]] +``` Arguments: --d, --dir: - Optional argument. Specifies work directory. If not specified, then - the current directory will be used as working directory. +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. + +* `-v`, `--verbose`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + Optional argument, turns on detailed logging. Usefull for locating issues # Assembly-document Generates a markdown file(s) from a given .NET assembly and it's XML documentation file. -`BookGen Assembly-document -i -o [-d] [-n]` -`BookGen Assembly-document --input --output [--dry] [--namespace-pages]` +``` +BookGen Assembly-document -i -o [-d] [-n] +BookGen Assembly-document --input --output [--dry] [--namespace-pages] +``` Arguments: --i, --input: - Required argument. Specifies the input assembly file path. The file must be a .NET assembly. +* `-i`, `--input`: + + Required argument. Specifies the input assembly file path. The file must be a .NET assembly. + +* `-o`, `--output`: + + Required argument. Specifies the output files path. + +* `-d`, `--dry`: --o, --output: - Required argument. Specifies the output files path. + Optional argument. If specified, the command will not write any files, but will only print the output to console. --d, --dry: - Optional argument. If specified, the command will not write any files, - but will only print the output to console. +* `-n`, `--namespace-pages`: --n, --namespace-pages: - Optional argument. If specified, the command will create a separate markdown file for each - namespace in the assembly. + Optional argument. If specified, the command will create a separate markdown file for each namespace in the assembly. # BuildEpub Build an epub3 file from the book. -`BookGen BuildEpub -o [-v] [-d [directory]]` -`BookGen BuildEpub --output [--verbose] [--dir [directory]]` +``` +BookGen BuildEpub -o [-v] [-d [directory]] +BookGen BuildEpub --output [--verbose] [--dir [directory]] +``` Arguments: --o, --output: - Required argument. Specifies the output directory name. +* `-o`, `--output`: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. + Required argument. Specifies the output directory name. + +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. + +* `-v`, `--verbose`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + Optional argument, turns on detailed logging. Usefull for locating issues # BuildExport Build a JSON file with schema for post processing of the book. -`BookGen BuildExport -o [-v] [-d [directory]]` -`BookGen BuildExport --output [--verbose] [--dir [directory]]` +``` +BookGen BuildExport -o [-v] [-d [directory]] +BookGen BuildExport --output [--verbose] [--dir [directory]] +``` Arguments: --o, --output: - Required argument. Specifies the output directory name. +* `-o`, `--output`: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. + Required argument. Specifies the output directory name. + +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues +* `-v`, `--verbose`: --h, --host: - Optional argument. If specified, the host name set in the config file will be ignored and the - host name will be set to the specified value. + Optional argument, turns on detailed logging. Usefull for locating issues + +* `-h`, `--host`: + + Optional argument. If specified, the host name set in the config file will be ignored and the host name will be set to the specified value. # BuildFeed Build an RSS 2.0 and an Atom 1.0 feed from the book. -`BookGen BuildFeed -o [-v] [-d [directory]]` -`BookGen BuildFeed --output [--verbose] [--dir [directory]]` +``` +BookGen BuildFeed -o [-v] [-d [directory]] +BookGen BuildFeed --output [--verbose] [--dir [directory]] +``` Arguments: --o, --output: - Required argument. Specifies the output directory name. +* `-o`, `--output`: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. + Required argument. Specifies the output directory name. + +* `-d`, `--dir`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. + +* `-v`, `--verbose`: + + Optional argument, turns on detailed logging. Usefull for locating issues # BuildPrint Build a printable html & xhtml file from the book -`BookGen BuildPrint -o [-v] [-d [directory]]` -`BookGen BuildPrint --output [--verbose] [--dir [directory]]` +``` +BookGen BuildPrint -o [-v] [-d [directory]] +BookGen BuildPrint --output [--verbose] [--dir [directory]] +``` Arguments: --o, --output: - Required argument. Specifies the output directory name. +* `-o`, `--output`: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. + Required argument. Specifies the output directory name. + +* `-d`, `--dir`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. --h, --host: - Optional argument. If specified, the host name set in the config file will be ignored and the - host name will be set to the specified value. +* `-v`, `--verbose`: + + Optional argument, turns on detailed logging. Usefull for locating issues + +* `-h`, `--host`: + + Optional argument. If specified, the host name set in the config file will be ignored and the host name will be set to the specified value. # BuildWeb Build a static website from the book -`BookGen BuildWeb -o [-v] [-d [directory]]` -`BookGen BuildWeb --output [--verbose] [--dir [directory]]` +``` +BookGen BuildWeb -o [-v] [-d [directory]] +BookGen BuildWeb --output [--verbose] [--dir [directory]] +``` Arguments: --o, --output: - Required argument. Specifies the output directory name. +* `-o`, `--output`: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. + Required argument. Specifies the output directory name. + +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. + +* `-v`, `--verbose`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + Optional argument, turns on detailed logging. Usefull for locating issues --h, --host: - Optional argument. If specified, the host name set in the config file will be ignored and the - host name will be set to the specified value. +* `-h`, `--host`: + + Optional argument. If specified, the host name set in the config file will be ignored and the host name will be set to the specified value. # BuildWp Build a wordpress export file from the book. -`BookGen BuildWp -o [-v] [-d [directory]]` -`BookGen BuildWp --output [--verbose] [--dir [directory]]` +``` +BookGen BuildWp -o [-v] [-d [directory]] +BookGen BuildWp --output [--verbose] [--dir [directory]] +``` Arguments: --o, --output: - Required argument. Specifies the output directory name. +* `-o`, `--output`: + + Required argument. Specifies the output directory name. --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues +* `-v`, `--verbose`: + + Optional argument, turns on detailed logging. Usefull for locating issues --h, --host: - Optional argument. If specified, the host name set in the config file will be ignored and the - host name will be set to the specified value. +* `-h`, `--host`: + + + Optional argument. If specified, the host name set in the config file will be ignored and the host name will be set to the specified value. # Config Get or set bookgen application specific settings -`BookGen Config` - List all currently supported application wide settings +* `BookGen Config` + + List all currently supported application wide settings + +* `BookGen Config ` -`BookGen Config ` - Gets a setting value, prints it to output and exits. + Gets a setting value, prints it to output and exits. -`BookGen Config ` - Sets a setting value and exits +* `BookGen Config ` + + Sets a setting value and exits # Edit Open a file for editing with configured editor. +`BookGen edit [filename]` + # Gui Starts the program with a command line gui interface - -`BookGen Gui [-v] [-d [directory]]` -`BookGen Gui [--verbose] [--dir [directory]]` +``` +BookGen Gui [-v] [-d [directory]] +BookGen Gui [--verbose] [--dir [directory]] +``` Arguments: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. +* `-d`, `--dir`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. -`BookGen edit [filename]` +* `-v`, `--verbose`: + + Optional argument, turns on detailed logging. Usefull for locating issues. # Html2Pdf Converts a HTML file to a png using edges or chromes headless mode. The tool will use chrome, if it's installed, otherwise it will use edge. This command is only supported on Windows OS. -`BookGen Html2Pdf -i -o ` -`BookGen Html2Pdf --input --output ` +``` +BookGen Html2Pdf -i -o +BookGen Html2Pdf --input --output +``` --i, --input: - Input html file with extension of .htm or .html +* `-i`, `--input`: --o, --output: - Output PDF file. + Input html file with extension of .htm or .html + +* `-o`, `--output`: + + Output PDF file. # Html2Png Converts a HTML file to a png using edges or chromes headless mode. The tool will use chrome, if it's installed, otherwise it will use edge. This command is only supported on Windows OS. -`BookGen Html2Png -i -o [-w [width]] [-h [height]]` -`BookGen Html2Png --input --output [--width [width]] [--height [height]]` +``` +BookGen Html2Png -i -o [-w [width]] [-h [height]] +BookGen Html2Png --input --output [--width [width]] [--height [height]] +``` + +* `-i`, `--input`: + + Input html file with extension of .htm or .html --i, --input: - Input html file with extension of .htm or .html +* `-o`, `--output`: --o, --output: - Output PNG file. + Output PNG file. --w, --width: - Optional argument. Specifies the width of the output image in pixels. +* `-w`, `--width`: --h, --height: - Optional argument. Specifies the height of the output image in pixels. + Optional argument. Specifies the width of the output image in pixels. + +* `-h`, `--height`: + + Optional argument. Specifies the height of the output image in pixels. # ImgConvert Converts an image file to a different format. The tool supports png, jpeg, webp and svg formats. -`BookGen ImgConvert -i -o -f [-q [quality]] [-r [resolution]]` +``` +BookGen ImgConvert -i -o -f [-q [quality]] [-r [resolution]] +``` Arguments: --i, --input: - Required argument. Specifies the input image file path. The file must be a valid image file or - a directory containing image files. +* `-i`, `--input`: + + Required argument. Specifies the input image file path. The file must be a valid image file or a directory containing image files. --o, --output: - Required argument. Specifies the output file path. The file must have a valid image file - extension, like .png, .jpg, .jpeg, .webp. Can also be a directory, in which case the output - files will be saved with the same name as the input files +* `-o`, `--output`: + + Required argument. Specifies the output file path. The file must have a valid image file extension, like .png, .jpg, .jpeg, .webp. Can also be a directory, in which case the output files will be saved with the same name as the input files + +* `-f`, `--format`: --f, --format: Required argument. Specifies the output image format. Supported formats are png, jpeg, webp. --q, --quality: - Optional argument. Specifies the quality of the output image. The value must be between - 0 and 100. Default is 90. If not specified, then the default value will be used. +* `-q`, `--quality`: + + Optional argument. Specifies the quality of the output image. The value must be between 0 and 100. Default is 90. If not specified, then the default value will be used. + +* `-r`, `--resolution`: --r, --resolution: - Optional argument. Specifies the resolution of the output image. The value must be a valid - resolution string, like 1920x1080 or 1280x720. If not specified, then the resolution will be - the same as the input image. + Optional argument. Specifies the resolution of the output image. The value must be a valid resolution string, like 1920x1080 or 1280x720. If not specified, then the resolution will be the same as the input image. # Install Windows only command that installs BookGen to the system PATH & optionally to the windows terminal. -`BookGen Install` +``` +BookGen Install +``` # JsonArgs Creates an empty json arguments template file for a given bookgen command. -`BookGen JsonArgs -c [-d [directory]]` -`BookGen JsonArgs --command [--dir [directory]]` +``` +BookGen JsonArgs -c [-d [directory]] +BookGen JsonArgs --command [--dir [directory]] +``` -A Json arguments template can be used to store command line arguments, so the bookgen command can -be invoked with the same arguments without having to type them in again. +A Json arguments template can be used to store command line arguments, so the bookgen command can be invoked with the same arguments without having to type them in again. Arguments: --c, --command: - Required argument. Specifies the command for which the json template will be created. +* `-c`, `--command`: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. + Required argument. Specifies the command for which the json template will be created. + +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. # Links Scans all markdown files in the current book and writes the lins to a markdown file, named links.md -`BookGen Links [-v] [-d [directory]]` -`BookGen Links [--verbose] [--dir [directory]]` +``` +BookGen Links [-v] [-d [directory]] +BookGen Links [--verbose] [--dir [directory]] +``` Arguments: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues +* `-v`, `--verbose`: + + Optional argument, turns on detailed logging. Usefull for locating issues # Math2Svg Renders a single markdown file containing Tex formulas to svg files -`BookGen Math2Svg -f -o [-s [scale]]` -`BookGen Math2Svg --formula --output [--scale [scale]]` +``` +BookGen Math2Svg -f -o [-s [scale]] +BookGen Math2Svg --formula --output [--scale [scale]] +``` Arguments: --f, --formula: - Formula to render to svg. The formula must be a valid Tex formula. +* `-f`, `--formula`: + + Formula to render to svg. The formula must be a valid Tex formula. + +* `-o`, `--output`: --o, --output: - Output svg file. + Output svg file. --s, --scale: - Optional argument. Specifies the scale of the output svg file. Default is 1.0. - If not specified, then the default value will be used. +* `-s`, `--scale`: + + Optional argument. Specifies the scale of the output svg file. Default is 1.0. If not specified, then the default value will be used. # Md2HTML Renders a single markdown file to an HTML file -`BookGen Md2HTML -i -o ` -`BookGen Md2HTML --input --output ` +``` +BookGen Md2HTML -i -o +BookGen Md2HTML --input --output +``` Arguments: --i, --input: - Input markdown file path. Multiple files can be set with multiple - -i arguments +* `-i`, `--input`: + + Input markdown file path. Multiple files can be set with multiple `-i` arguments + +* `-o`, `--output`: + + Output html file path. If file name is "-", outputs to console. + +* `-tf`, `--template` + + Optional argument. If not specified, default template is used. If custom file provided, then the file must contain the folloing tags: + + * `` - For document title + * `` - For document content + +* `-ns`, `--no-syntax` + + Optional argument. Disables syntax highlighting. + +* `-ne`, `--no-embed` + + Optional argument. Disables embedding of media site links, like youtube. --o, --output: - Output html file path. If file name is "con", outputs to console. +* `-r`, `--raw` --tf, --template - Optional argument. If not specified, default template is used. If custom file provided, then - the file must contain the folloing tags: - `` - For document title - `` - For document content + Optional argument. Disables full html generation, only outputs the html produced by the markdown formatting. --ns, --no-syntax - Optional argument. Disables syntax highlighting. +* `-s`, `--svg` --ne, --no-embed - Optional argument. Disables embedding of media site links, - like youtube. + Enables SVG Passthrough. When enabled SVG files will be embedded in resulting html, instead of being rendered to webp. --r, --raw - Optional argument. Disables full html generation, only outputs the html produced by - the markdown formatting. +* `-t`, `--title` --s, --svg - Enables SVG Passthrough. When enabled SVG files will be embedded in resulting html, instead - of being rendered to webp. + Optional argument. Specifies the rendered HTML page title. Only has affect, when `-r` or `--raw` is not specified. --t, --title - Optional argument. Specifies the rendered HTML page title. Only has affect, when -r or --raw - is not specified. +# Md2terminal + +Converts a markdown file to terminal formatted text. + +``` +BookGen Md2terminal -i -o +BookGen Md2terminal --input --output +``` + +* `-i`, `--input`: + + Input markdown file path. Multiple files can be set with multiple `-i` arguments + +* `-o`, `--output`: + + Output html file path. If file name is "-", outputs to console. # Migrate Migrate an old Bookgen book to the new format. -`BookGen Migrate [-v] [-d [directory]]` -`BookGen Migrate [--verbose] [--dir [directory]]` +``` +BookGen Migrate [-v] [-d [directory]] +BookGen Migrate [--verbose] [--dir [directory]] +``` Arguments: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. +* `-d`, `--dir`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. + +* `-v`, `--verbose`: + + Optional argument, turns on detailed logging. Usefull for locating issues # NewBook Creates a new book structure in the given folder -`BookGen NewBook [-v] [-d [directory]]` -`BookGen NewBook [--verbose] [-dir [directory]]` +``` +BookGen NewBook [-v] [-d [directory]] +BookGen NewBook [--verbose] [-dir [directory]] +``` Arguments: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues +* `-v`, `--verbose`: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. + Optional argument, turns on detailed logging. Usefull for locating issues + +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. # NewPage Creates a new markdown page. -`BookGen NewPage -n [-v] [-d [directory]]` -`BookGen NewPage --name [--verbose] [-dir [directory]]` +``` +BookGen NewPage -n [-v] [-d [directory]] +BookGen NewPage --name [--verbose] [-dir [directory]] +``` Arguments: --n, --name: - File name. Specifies new file name +* `-n`, `--name`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + File name. Specifies new file name --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. +* `-v`, `--verbose`: + + Optional argument, turns on detailed logging. Usefull for locating issues + +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. # QrCode Renders an url into a QRCode image -`BookGen QrCode -d -o [-c [color]]` -`BookGen QrCode --data --output [--color [color]]` +``` +BookGen QrCode -d -o [-c [color]] +BookGen QrCode --data --output [--color [color]] +``` Arguments: --d, --data: - Url data to encode. Minimum 1 byte, Maximum 900 bytes +* `-d`, `--data`: + + Url data to encode. Minimum 1 byte, Maximum 900 bytes + +* `-c`, `--color`: --c, --color: - Optional argument. Specifies the color of the QRCode. The color must be a valid hex color code, - like #FF0000 or #F00. + Optional argument. Specifies the color of the QRCode. The color must be a valid hex color code, like #FF0000 or #F00. --o, --output - Output file. Must have .png or .svg extension +* `-o`, `--output` + + Output file. Must have .png or .svg extension # Schemas Creates a schemas.md documentation file, describing the various config schemas used by bookgen. -`BookGen Schemas [-v] [-d [directory]]` -`BookGen Schemas [--verbose] [--dir [directory]]` +``` +BookGen Schemas [-v] [-d [directory]] +BookGen Schemas [--verbose] [--dir [directory]] +``` Arguments: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. + +* `-v`, `--verbose`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + Optional argument, turns on detailed logging. Usefull for locating issues # Serve Starts a local only http server that serves file from the given directory -`BookGen Serve [-d [directory]]` -`BookGen Serve [--dir [directory]]` +``` +BookGen Serve [-d [directory]] +BookGen Serve [--dir [directory]] +``` Arguments: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. # Shell Autocompleter command, that is used by Powershell -`BookGen Shell` +``` +BookGen Shell +``` # Shortcut -Create a cmd file in the current directory that can be used to start the bookgen Shell in the -current directory. +Create a cmd file in the current directory that can be used to start the bookgen Shell in the current directory. -`BookGen Shortcut` +``` +BookGen Shortcut +``` This command is only supported on Windows OS. @@ -518,64 +621,74 @@ This command is only supported on Windows OS. Displays various statistics about the bookgen project. -`BookGen Stats [-v] [-d [directory]]` -`BookGen Stats [--verbose] [--dir [directory]]` +``` +BookGen Stats [-v] [-d [directory]] +BookGen Stats [--verbose] [--dir [directory]] +``` Arguments: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues +* `-v`, `--verbose`: + + Optional argument, turns on detailed logging. Usefull for locating issues # Subcommands Listst all available subcommands -`BookGen SubCommands` +``` +BookGen SubCommands +``` # Templates -`Bookgen Templates [-n [template name]]` -`Bookgen Templates [--name [template name]]` - Lists all available templates, or extracts a single template to the current directory. +``` +Bookgen Templates [-n [template name]] +Bookgen Templates [--name [template name]] +``` + Arguments: --n, --name: - Optional argument. If specified, only the template with the given name will be extracted. - If not specified, all available templates will be printed. +* `-n`, `--name`: + + Optional argument. If specified, only the template with the given name will be extracted. If not specified, all available templates will be printed. # Terminalinstall Installs a bookgen profile to the Windows Termninal. -This command is only supported on Windows OS. +This command is only supported on Windows OS. Without arguments, performs terminal profile install. -`BookGen Terminalinstall [-c] [-t]` -`BookGen Terminalinstall [--checkinstall] [--checkterminalinstall]` +``` +BookGen Terminalinstall [-c] [-t] +BookGen Terminalinstall [--checkinstall] [--checkterminalinstall] +``` Arguments: --c, --checkinstall: - Optional argument. When specified checks, if terminal profile installed or not. If exit code - is 0, profile is installed. +* `-c`, `--checkinstall`: + + Optional argument. When specified checks, if terminal profile installed or not. If exit code is 0, profile is installed. --t, --checkterminalinstall: - Optional argument. When specified checks, if windows terminal is installed or not. If exit code - is 0, terminal is installed. +* `-t`, `--checkterminalinstall`: -Without arguments, performs terminal profile install. + Optional argument. When specified checks, if windows terminal is installed or not. If exit code is 0, terminal is installed. # Tools -Display a list of downloadable tools that can be installed and used with BookGen shell. This -command is only supported on Windows OS. +Display a list of downloadable tools that can be installed and used with BookGen shell. -`BookGen Tools` +This command is only supported on Windows OS. + +``` +BookGen Tools +``` # Upgrade @@ -583,34 +696,40 @@ Upgrades the bookgen project to the latest version. This command will upgrade th config file to the latest version, and will also upgrade the bookgen.toc.json file to the latest version. -`BookGen Upgrade [-v] [-d [directory]]` -`BookGen Upgrade [--verbose] [--dir [directory]]` +``` +BookGen Upgrade [-v] [-d [directory]] +BookGen Upgrade [--verbose] [--dir [directory]] +``` Arguments: --d, --dir: - Optional argument. Specifies work directory. If not specified, then - the current directory will be used as working directory. +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues +* `-v`, `--verbose`: + + Optional argument, turns on detailed logging. Usefull for locating issues # Validate Validate the configuration files used by bookgen in the specified folder. -`BookGen Validate [-v] [-d [directory]]` -`BookGen Validate [--verbose] [--dir [directory]]` +``` +BookGen Validate [-v] [-d [directory]] +BookGen Validate [--verbose] [--dir [directory]] +``` Arguments: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory will - be used as working directory. +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. + +* `-v`, `--verbose`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + Optional argument, turns on detailed logging. Usefull for locating issues # Version @@ -618,19 +737,21 @@ Print the current program and config API version `BookGen Version` - # Vstasks Generates a Visual Studio Code tasks.json file for the bookgen project. -`BookGen Vstasks [-v] [-d [directory]]` -`BookGen Vstasks [--verbose] [--dir [directory]]` +``` +BookGen Vstasks [-v] [-d [directory]] +BookGen Vstasks [--verbose] [--dir [directory]] +``` Arguments: --d, --dir: - Optional argument. Specifies work directory. If not specified, then the current directory - will be used as working directory. +* `-d`, `--dir`: + + Optional argument. Specifies work directory. If not specified, then the current directory will be used as working directory. + +* `-v`, `--verbose`: --v, --verbose: - Optional argument, turns on detailed logging. Usefull for locating issues + Optional argument, turns on detailed logging. Usefull for locating issues diff --git a/Directory.Packages.props b/Directory.Packages.props index 0aff0ee3..30d8fc8e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,23 +9,23 @@ - - - - + + + + - + - - + + diff --git a/Installers/debian-info.txt b/Installers/debian-info.txt deleted file mode 100644 index 95c8a9ab..00000000 --- a/Installers/debian-info.txt +++ /dev/null @@ -1,2 +0,0 @@ -Debian user: user -Debian password: pass \ No newline at end of file diff --git a/Installers/install.sh b/Installers/install.sh deleted file mode 100644 index 3100d2ee..00000000 --- a/Installers/install.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -#check for admin rights -if [ "$EUID" -ne 0 ] - then echo "Please run as root" - exit -fi - -mkdir -p /opt/bookgen -cp bin/* /opt/bookgen -ln -s /opt/bookgen/BookGen /usr/bin/bookgen - -echo "BookGen installed successfully!" \ No newline at end of file diff --git a/Installers/prepare-debian-base.sh b/Installers/prepare-debian-base.sh deleted file mode 100644 index 8506a236..00000000 --- a/Installers/prepare-debian-base.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -#check for admin rights -if [ "$EUID" -ne 0 ] - then echo "Please run as root" - exit -fi - -apt update -apt dist-upgrade -y -apt install -y curl wget mc - -wget https://packages.microsoft.com/config/debian/13/packages-microsoft-prod.deb -O packages-microsoft-prod.deb -sudo dpkg -i packages-microsoft-prod.deb -rm packages-microsoft-prod.deb - -apt update -apt install -y dotnet-sdk-10.0 -apt autoremove --purge -apt clean \ No newline at end of file diff --git a/Source/BookGen.Cli/packages.lock.json b/Source/BookGen.Cli/packages.lock.json index e1bba11c..d09f29db 100644 --- a/Source/BookGen.Cli/packages.lock.json +++ b/Source/BookGen.Cli/packages.lock.json @@ -4,17 +4,17 @@ "net10.0": { "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "bookgen.vfs": { diff --git a/Source/BookGen.Contents/BookGenShell.ps1 b/Source/BookGen.Contents/BookGenShell.ps1 index 66978089..a845e519 100644 --- a/Source/BookGen.Contents/BookGenShell.ps1 +++ b/Source/BookGen.Contents/BookGenShell.ps1 @@ -1,7 +1,7 @@ # ----------------------------------------------------------------------------- # BookGen PowerShell Registration script -# Version 3.7.2 -# Last modified: 2025-10-05 +# Version 3.8.0 +# Last modified: 2025-11-16 # ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- @@ -173,6 +173,15 @@ function gh { GetTool "gh" "github-cli" @Args } +function glow { + param ( + [string[]] + [Parameter(ValueFromRemainingArguments = $true)] + $Args + ) + GetTool "glow" "glow" @Args +} + function copyparty { param ( [string[]] @@ -252,14 +261,14 @@ function weather { [string]$Location ) - $url = "https://wttr.in/" + $url = "https://wttr.in/?n" if ($PSBoundParameters.ContainsKey('Location')) { $url += $Location } Clear-Host - curl $url + (Invoke-WebRequest $url).Content } # intro message diff --git a/Source/BookGen.Shell.Shared/packages.lock.json b/Source/BookGen.Shell.Shared/packages.lock.json index 012f2179..999b8ee4 100644 --- a/Source/BookGen.Shell.Shared/packages.lock.json +++ b/Source/BookGen.Shell.Shared/packages.lock.json @@ -4,11 +4,11 @@ "net10.0": { "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Webmaster442.WindowsTerminal": { @@ -19,9 +19,9 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" } } } diff --git a/Source/BookGen.Shellprog/packages.lock.json b/Source/BookGen.Shellprog/packages.lock.json index 5cdb23de..f9ce9d67 100644 --- a/Source/BookGen.Shellprog/packages.lock.json +++ b/Source/BookGen.Shellprog/packages.lock.json @@ -4,26 +4,26 @@ "net10.0": { "Microsoft.Extensions.Logging": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Microsoft.Extensions.Logging.Console": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "38Q8sEHwQ/+wVO/mwQBa0fcdHbezFpusHE+vBw/dSr6Fq/kzZm3H/NQX511Jki/R3FHd64IY559gdlHZQtYeEA==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "Z6gBfpHqJsz2hGH+eUQUQI+DSHsDNhTKt8toHAtDhYFRlxUN1FKPKzNmTgSrAz1gtDTOBjDU5lQZ50463Ehw7A==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Configuration": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Configuration": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Spectre.Console": { @@ -40,85 +40,85 @@ }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "resolved": "10.0.2", + "contentHash": "Lws+o4DFw6p5NquRoYA3d5QVvi49ugNw7TxbW4QGLsL8F1LCCyJqWFy0+RMQ/hzUuS9aKV5NJ/XGAF5N9/RQcQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", + "resolved": "10.0.2", + "contentHash": "KC5PslaTDnTuTvyke0KYAVBYdZ7IVTsU3JhHe69BpEbHLcj1YThP3bIGtZNOkZfast2AuLnul5lk4rZKxAdUGQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "resolved": "10.0.2", + "contentHash": "/SdW50prUuenglSy7MXU3eVQkOk4/J4fjc+GIhv4NkTmaZOQyTqpVAYi8nRjNtOKHzCy7g5cSlOSgkbT7clLwQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "resolved": "10.0.2", + "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Zg8LLnfZs5o2RCHD/+9NfDtJ40swauemwCa7sI8gQoAye/UJHRZNpCtC7a5XE7l9Z7mdI8iMWnLZ6m7Q6S3jLg==", + "resolved": "10.0.2", + "contentHash": "XVtNJfLZVTDmQS5RCUjIr7QEAgGhJ3yQ0L3PduN7rE4aijmqYl0pIF09ZSU8jgnxml91Mw59ze220g8S7anaOg==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Configuration.Binder": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.2" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "resolved": "10.0.2", + "contentHash": "1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", + "resolved": "10.0.2", + "contentHash": "8njGDg0OdDBM4Zox0ybuUOJZkQ8HcH49F+POZBlG+nsfzEyqOCHyHEkWeRVI62qsssiugUVEKqUttT1ZbV0aJQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Configuration.Binder": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + "resolved": "10.0.2", + "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" }, "bookgen.cli": { "type": "Project", "dependencies": { "BookGen.Vfs": "[1.0.0, )", - "Microsoft.Extensions.DependencyInjection.Abstractions": "[10.0.1, )", - "Microsoft.Extensions.Logging.Abstractions": "[10.0.1, )" + "Microsoft.Extensions.DependencyInjection.Abstractions": "[10.0.2, )", + "Microsoft.Extensions.Logging.Abstractions": "[10.0.2, )" } }, "bookgen.contents": { @@ -127,7 +127,7 @@ "bookgen.shell.shared": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Logging.Abstractions": "[10.0.2, )", "Webmaster442.WindowsTerminal": "[4.1.1, )" } }, @@ -136,17 +136,17 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" }, "Microsoft.Extensions.Logging.Abstractions": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Webmaster442.WindowsTerminal": { diff --git a/Source/BookGen.Vfs/Extensions.cs b/Source/BookGen.Vfs/Extensions.cs index f9ff6546..f5b767af 100644 --- a/Source/BookGen.Vfs/Extensions.cs +++ b/Source/BookGen.Vfs/Extensions.cs @@ -3,6 +3,7 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- +using System.Text; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Schema; @@ -12,60 +13,86 @@ namespace BookGen.Vfs; public static class Extensions { - public static XmlWriter CreateXmlWriter(this IWritableFileSystem fs, string path) + extension(IReadOnlyFileSystem fs) { - return XmlWriter.Create(fs.CreateTextWriter(path)); - } - - public static async Task DeserializeAsync(this IReadOnlyFileSystem fs, string path) - { - await using var stream = fs.OpenReadStream(path); - T? result = await JsonSerializer.DeserializeAsync(stream, JsonOptions.SerializerOptions); - return result; - } - - public static async Task SerializeAsync(this IWritableFileSystem fs, string path, T value, bool writeSchema) - { - await using var stream = fs.CreateWriteStream(path); - await JsonSerializer.SerializeAsync(stream, value, JsonOptions.SerializerOptions); - if (writeSchema) + public async Task DeserializeAsync(string path) { - var newName = Path.ChangeExtension(path, ".schema.json"); - await fs.WriteSchema(newName); + await using var stream = fs.OpenReadStream(path); + T? result = await JsonSerializer.DeserializeAsync(stream, JsonOptions.SerializerOptions); + return result; } - } - public static async Task WriteJsonAsync(this IWritableFileSystem fs, string path, JsonObject json) - { - await fs.WriteAllTextAsync(path, json.ToJsonString(JsonOptions.SerializerOptions)); - } + public async Task ReadJsonAsync(string path) + { + string content = await fs.ReadAllTextAsync(path); + JsonNode? parsed = JsonNode.Parse(content); + if (parsed is not JsonObject jsonObject) + { + throw new InvalidOperationException($"Failed to parse JSON from {path}"); + } + return jsonObject; + } - public static async Task ReadJsonAsync(this IReadOnlyFileSystem fs, string path) - { - string content = await fs.ReadAllTextAsync(path); - var parsed = JsonObject.Parse(content); - if (parsed is not JsonObject jsonObject) + public string GetFileNameInTargetFolder(IReadOnlyFileSystem targetFolder, string file, string newExtension) { - throw new InvalidOperationException($"Failed to parse JSON from {path}"); + ArgumentException.ThrowIfNullOrWhiteSpace(fs.Scope); + ArgumentException.ThrowIfNullOrWhiteSpace(targetFolder.Scope); + + var fullPath = Path.GetFullPath(file, fs.Scope); + + var relativePart = Path.GetRelativePath(fs.Scope, fullPath); + + return Path.ChangeExtension(Path.GetFullPath(relativePart, targetFolder.Scope), newExtension); } - return jsonObject; - } - public static async Task WriteSchema(this IWritableFileSystem fs, string path) - { - var node = JsonOptions.SerializerOptions.GetJsonSchemaAsNode(typeof(T), JsonOptions.ExporterOptions); - await fs.WriteAllTextAsync(path, node.ToJsonString(JsonOptions.SerializerOptions)); + public (string content, DateTime lastmodified) ReadInputFiles(string[] inputFiles) + { + StringBuilder md = new(inputFiles.Length * 1024); + DateTime lastmodified = DateTime.MinValue; + foreach (var inputFile in inputFiles) + { + string content = fs.ReadAllText(inputFile); + DateTime date = fs.GetLastModifiedUtc(inputFile); + + if (date > lastmodified) + lastmodified = date; + + md.Append(content); + + if (!content.EndsWith('\n')) + md.Append(System.Environment.NewLine); + } + return (md.ToString(), lastmodified); + } } - public static string GetFileNameInTargetFolder(this IReadOnlyFileSystem sourceFolder, IReadOnlyFileSystem targetFolder, string file, string newExtension) + extension(IWritableFileSystem fs) { - ArgumentException.ThrowIfNullOrWhiteSpace(sourceFolder.Scope); - ArgumentException.ThrowIfNullOrWhiteSpace(targetFolder.Scope); + public XmlWriter CreateXmlWriter(string path) + { + return XmlWriter.Create(fs.CreateTextWriter(path)); + } - var fullPath = Path.GetFullPath(file, sourceFolder.Scope); + public async Task WriteJsonAsync(string path, JsonObject json) + { + await fs.WriteAllTextAsync(path, json.ToJsonString(JsonOptions.SerializerOptions)); + } - var relativePart = Path.GetRelativePath(sourceFolder.Scope, fullPath); + public async Task WriteSchema(string path) + { + var node = JsonOptions.SerializerOptions.GetJsonSchemaAsNode(typeof(T), JsonOptions.ExporterOptions); + await fs.WriteAllTextAsync(path, node.ToJsonString(JsonOptions.SerializerOptions)); + } - return Path.ChangeExtension(Path.GetFullPath(relativePart, targetFolder.Scope), newExtension); + public async Task SerializeAsync(string path, T value, bool writeSchema) + { + await using var stream = fs.CreateWriteStream(path); + await JsonSerializer.SerializeAsync(stream, value, JsonOptions.SerializerOptions); + if (writeSchema) + { + var newName = Path.ChangeExtension(path, ".schema.json"); + await fs.WriteSchema(newName); + } + } } } diff --git a/Source/BookGen.Vfs/JsonOptions.cs b/Source/BookGen.Vfs/JsonOptions.cs index 9c0618d9..d6ae0798 100644 --- a/Source/BookGen.Vfs/JsonOptions.cs +++ b/Source/BookGen.Vfs/JsonOptions.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -13,7 +13,7 @@ namespace BookGen.Vfs; -internal static class JsonOptions +public static class JsonOptions { public readonly static JsonSerializerOptions SerializerOptions = new(JsonSerializerOptions.Default) { diff --git a/Source/BookGen/BookGenArgumentBase.cs b/Source/BookGen/BookGenArgumentBase.cs index 83126586..0c902746 100644 --- a/Source/BookGen/BookGenArgumentBase.cs +++ b/Source/BookGen/BookGenArgumentBase.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -17,8 +17,27 @@ public class BookGenArgumentBase : ArgumentsBase, IVerbosablityToggle [Switch("d", "dir")] public string Directory { get; set; } + [Switch("co", "configoverlay")] + public string ConfigOverlay { get; set; } = string.Empty; + public BookGenArgumentBase() { Directory = Environment.CurrentDirectory; } + + override public ValidationResult Validate(IValidationContext context) + { + if (!context.FileSystem.DirectoryExists(Directory)) + { + return ValidationResult.Error($"Directory '{Directory}' does not exist."); + } + + if (!string.IsNullOrEmpty(ConfigOverlay) + && !context.FileSystem.FileExists(ConfigOverlay)) + { + return ValidationResult.Error($"Config overlay file '{ConfigOverlay}' does not exist."); + } + + return ValidationResult.Ok(); + } } diff --git a/Source/BookGen/BuildCommandBase.cs b/Source/BookGen/BuildCommandBase.cs index b4e798f2..c0f4e1d8 100644 --- a/Source/BookGen/BuildCommandBase.cs +++ b/Source/BookGen/BuildCommandBase.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -47,7 +47,7 @@ public override async Task ExecuteAsync(BuildArguments arguments, IReadOnly _target.Scope = arguments.OutputDirectory; using var env = new BookEnvironment(_soruce, _target, _assetSource); - EnvironmentStatus status = await env.Initialize(); + EnvironmentStatus status = await env.Initialize(arguments.ConfigOverlay); if (!status.IsOk) { diff --git a/Source/BookGen/Commands/HelpCommand.cs b/Source/BookGen/Commands/HelpCommand.cs index 4f28c9fe..bc52e1c0 100644 --- a/Source/BookGen/Commands/HelpCommand.cs +++ b/Source/BookGen/Commands/HelpCommand.cs @@ -16,6 +16,7 @@ internal sealed class HelpCommand : Command { private readonly IHelpProvider _helpProvider; private readonly HashSet _commandNames; + private readonly HelpRenderer _renderer = new(); public HelpCommand(IHelpProvider helpProvider, ICommandRunnerProxy runnerProxy) { @@ -27,7 +28,7 @@ public override int Execute(IReadOnlyList context) { if (context.Count == 0) { - HelpRenderer.RenderHelp(_helpProvider.GetCommandHelp("help")); + _renderer.RenderHelp(_helpProvider.GetCommandHelp("help")); return ExitCodes.Success; } @@ -38,7 +39,7 @@ public override int Execute(IReadOnlyList context) return ExitCodes.GeneralError; } - HelpRenderer.RenderHelp(_helpProvider.GetCommandHelp(command)); + _renderer.RenderHelp(_helpProvider.GetCommandHelp(command)); return ExitCodes.Success; } diff --git a/Source/BookGen/Commands/LinksCommand.cs b/Source/BookGen/Commands/LinksCommand.cs index d29212b2..f377359c 100644 --- a/Source/BookGen/Commands/LinksCommand.cs +++ b/Source/BookGen/Commands/LinksCommand.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -33,7 +33,7 @@ public override async Task ExecuteAsync(BookGenArgumentBase arguments, IRea _soruce.Scope = arguments.Directory; using var env = new BookEnvironment(_soruce, _soruce); - EnvironmentStatus status = await env.Initialize(); + EnvironmentStatus status = await env.Initialize(arguments.ConfigOverlay); if (!status.IsOk) { diff --git a/Source/BookGen/Commands/Md2HtmlCommand.cs b/Source/BookGen/Commands/Md2HtmlCommand.cs index 4415f93a..f5e2355f 100644 --- a/Source/BookGen/Commands/Md2HtmlCommand.cs +++ b/Source/BookGen/Commands/Md2HtmlCommand.cs @@ -71,7 +71,7 @@ public override ValidationResult Validate(IValidationContext context) if (string.IsNullOrEmpty(OutputFile)) result.AddIssue("Output file must be specified"); - if (!InputFiles.Any()) + if (InputFiles.Length == 0) result.AddIssue("An Input file must be specified"); foreach (var inputfile in InputFiles) @@ -105,7 +105,7 @@ public Md2HtmlCommand(ILogger log, IWritableFileSystem fileSystem, IAssetSource public override int Execute(Md2HtmlArguments arguments, IReadOnlyList context) { - (string md, DateTime lastmodified) = ReadInputFiles(arguments.InputFiles); + (string md, DateTime lastmodified) = _fileSystem.ReadInputFiles(arguments.InputFiles); string? pageTemplate = string.Empty; @@ -133,9 +133,9 @@ public override int Execute(Md2HtmlArguments arguments, IReadOnlyList co PrismJsInterop = new PrismJsInterop(_assetSource) }; - using var mdToHtml = new MarkdownConverter(settings); + using var markdonwConverter = new MarkdownConverter(settings); - string? mdcontent = mdToHtml.RenderMarkdownToHtml(md); + string? mdcontent = markdonwConverter.RenderMarkdownToHtml(md); string rendered; if (arguments.RawHtml) @@ -163,26 +163,6 @@ public override int Execute(Md2HtmlArguments arguments, IReadOnlyList co return ExitCodes.Success; } - private (string content, DateTime lastmodified) ReadInputFiles(string[] inputFiles) - { - StringBuilder md = new(inputFiles.Length * 1024); - DateTime lastmodified = DateTime.MinValue; - foreach (var inputFile in inputFiles) - { - string content = _fileSystem.ReadAllText(inputFile); - DateTime date = _fileSystem.GetLastModifiedUtc(inputFile); - - if (date > lastmodified) - lastmodified = date; - - md.Append(content); - - if (!content.EndsWith('\n')) - md.Append(System.Environment.NewLine); - } - return (md.ToString(), lastmodified); - } - private bool ValidateTemplate(string pageTemplate) { bool returnValue = true; diff --git a/Source/BookGen/Commands/Md2TerminalCommand.cs b/Source/BookGen/Commands/Md2TerminalCommand.cs new file mode 100644 index 00000000..4c20eabd --- /dev/null +++ b/Source/BookGen/Commands/Md2TerminalCommand.cs @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2025 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Text; + +using Bookgen.Lib.Domain.IO.Configuration; +using Bookgen.Lib.Markdown; + +using BookGen.Cli; +using BookGen.Cli.Annotations; +using BookGen.Vfs; + +using Microsoft.Extensions.Logging; + +namespace BookGen.Commands; + +[CommandName("md2terminal")] +internal sealed class Md2TerminalCommand : Command +{ + private readonly ILogger _log; + private readonly IWritableFileSystem _fileSystem; + + internal sealed class Arguments : ArgumentsBase + { + [Switch("i", "input")] + public string[] InputFiles { get; set; } + + [Switch("o", "output")] + public string OutputFile { get; set; } + + public Arguments() + { + InputFiles = []; + OutputFile = string.Empty; + } + + public override ValidationResult Validate(IValidationContext context) + { + ValidationResult result = new(); + + if (string.IsNullOrEmpty(OutputFile)) + result.AddIssue("Output file must be specified"); + + if (InputFiles.Length == 0) + result.AddIssue("An Input file must be specified"); + + foreach (var inputfile in InputFiles) + { + if (!context.FileSystem.FileExists(inputfile)) + result.AddIssue($"Input file: {inputfile} doesn't exist"); + } + + return base.Validate(context); + } + } + + public Md2TerminalCommand(ILogger log, IWritableFileSystem fileSystem) + { + _log = log; + _fileSystem = fileSystem; + } + + private static void WriteToStdout(string rendered) + { + Console.OutputEncoding = Encoding.UTF8; + Spectre.Console.AnsiConsole.WriteLine(rendered); + } + + public override int Execute(Arguments arguments, IReadOnlyList context) + { + (string md, _) = _fileSystem.ReadInputFiles(arguments.InputFiles); + + using var settings = new RenderSettings(null!) + { + DeleteFirstH1 = false, + AutoEmbedSupportedLinks = false, + CssClasses = new CssClasses(), + HostUrl = string.Empty, + PrismJsInterop = null, + }; + + using var markdonwConverter = new MarkdownConverter(settings); + + var rendered = markdonwConverter.RenderMarkdownToTerminal(md); + + if (arguments.OutputFile == "-") + WriteToStdout(rendered); + else + _fileSystem.WriteAllText(arguments.OutputFile, rendered); + + return ExitCodes.Success; + } +} diff --git a/Source/BookGen/Commands/StatsCommand.cs b/Source/BookGen/Commands/StatsCommand.cs index 9ed62e59..6060acc3 100644 --- a/Source/BookGen/Commands/StatsCommand.cs +++ b/Source/BookGen/Commands/StatsCommand.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -35,7 +35,7 @@ public override async Task ExecuteAsync(BookGenArgumentBase arguments, IRea _soruce.Scope = arguments.Directory; using var env = new BookEnvironment(_soruce, _soruce); - var status = await env.Initialize(); + var status = await env.Initialize(arguments.ConfigOverlay); if (!status.IsOk) { diff --git a/Source/BookGen/Commands/ToolsCommand.cs b/Source/BookGen/Commands/ToolsCommand.cs index c4760aa5..5c64cdf7 100644 --- a/Source/BookGen/Commands/ToolsCommand.cs +++ b/Source/BookGen/Commands/ToolsCommand.cs @@ -36,6 +36,7 @@ public ToolsCommand(IApiClient apiClient, ILogger logger) new ChromaDownloader(apiClient, _memoryStreamManager, _logger), new CopyPartyDownloader(apiClient, _memoryStreamManager, _logger), new GithubDownloader(apiClient, _memoryStreamManager, _logger), + new GlowDownloader(apiClient, _memoryStreamManager, _logger), new MicrosoftEditToolDownloader(apiClient, _memoryStreamManager, _logger), new PandocTooldownloader(apiClient, _memoryStreamManager, _logger), ]; diff --git a/Source/BookGen/Commands/ValidateCommand.cs b/Source/BookGen/Commands/ValidateCommand.cs index 568a9918..5fd031c3 100644 --- a/Source/BookGen/Commands/ValidateCommand.cs +++ b/Source/BookGen/Commands/ValidateCommand.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -31,7 +31,7 @@ public override async Task ExecuteAsync(BookGenArgumentBase arguments, IRea using var environment = new BookEnvironment(_writableFileSystem, _writableFileSystem); - EnvironmentStatus status = await environment.Initialize(); + EnvironmentStatus status = await environment.Initialize(arguments.ConfigOverlay); if (!status.IsOk) { diff --git a/Source/BookGen/Infrastructure/HelpRenderer.cs b/Source/BookGen/Infrastructure/HelpRenderer.cs index e64b9e0c..0ee95062 100644 --- a/Source/BookGen/Infrastructure/HelpRenderer.cs +++ b/Source/BookGen/Infrastructure/HelpRenderer.cs @@ -3,113 +3,37 @@ // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- -using Spectre.Console; +using Bookgen.Lib.Markdown.Renderers.Terminal; + +using Markdig; +using Markdig.Parsers; namespace BookGen.Infrastructure; -public static class HelpRenderer +internal sealed class HelpRenderer { - public static string[][] GetPages(IEnumerable article) - { - int pageSize = Console.WindowHeight - 3; - IReadOnlyList reWraped = DoReWrap(article, pageSize, Console.WindowWidth); - return reWraped.Chunk(pageSize).ToArray(); - } + private readonly MarkdownPipeline _terminalPipeLine; - public static void RenderPage(string[] pageContent) + public HelpRenderer() { - foreach (var line in pageContent) - { - if (line.StartsWith("# ")) - AnsiConsole.MarkupInterpolated($"[green bold]{line}[/]{Environment.NewLine}"); - else if (line.StartsWith('`') || line.EndsWith('`')) - AnsiConsole.MarkupInterpolated($"[aqua]{line}[/]{Environment.NewLine}"); - else - AnsiConsole.MarkupInterpolated($"[italic]{line}[/]{Environment.NewLine}"); - } - } - - public static void RenderHelp(IEnumerable article) - { - var pages = GetPages(article); - Console.Clear(); - - int currentPage = -1; - int nextPage = 0; - bool run = pages.Length > 1; - do - { - if (currentPage != nextPage) - { - currentPage = nextPage; - Console.Clear(); - RenderPage(pages[currentPage]); - RenderUsage(currentPage, pages.Length); - } - - if (!run) continue; - - var key = Console.ReadKey(); - switch (key.Key) - { - case ConsoleKey.LeftArrow: - case ConsoleKey.UpArrow: - nextPage = CalculatePage(currentPage, pages.Length, -1); - break; - case ConsoleKey.DownArrow: - case ConsoleKey.RightArrow: - nextPage = CalculatePage(currentPage, pages.Length, +1); - break; - case ConsoleKey.Escape: - case ConsoleKey.Q: - run = false; - Console.Clear(); - break; - } - } - while (run); + _terminalPipeLine = new MarkdownPipelineBuilder().Build(); } - private static void RenderUsage(int currentPage, int pages) + public void RenderHelp(IEnumerable article) { - if (pages < 2) - return; + string md = string.Join(Environment.NewLine, article); + var document = MarkdownParser.Parse(md, _terminalPipeLine); - AnsiConsole.WriteLine(); - AnsiConsole.MarkupInterpolated($"[teal]{currentPage + 1} of {pages}[/]"); - AnsiConsole.MarkupInterpolated($" [silver]ESC or Q: Exit, <- Prev, Next ->[/]{Environment.NewLine}"); - } + using var writer = new StringWriter(); + var renderer = new TerminalRenderer(writer, new RenderOptions()); - private static int CalculatePage(int currentPage, int pages, int offset) - { - int newIndex = currentPage + offset; + renderer.Render(document); + renderer.Writer.Flush(); - if (newIndex < 0) - newIndex = 0; + using var reader = new StringReader(writer.ToString()); - if (newIndex > pages - 1) - newIndex = pages - 1; + Webmaster442.WindowsTerminal.Wigets.Pager pager = new(reader); - return newIndex; - } - - private static List DoReWrap(IEnumerable article, int pageSize, int windowWidth) - { - List result = new(pageSize); - foreach (string line in article) - { - if (line.Length > windowWidth) - { - var newLines = line - .Chunk(windowWidth) - .Select(chrs => new string(chrs)); - result.AddRange(newLines); - } - else - { - result.Add(line); - } - } - return result; + pager.Show(false); } } diff --git a/Source/BookGen/Program.cs b/Source/BookGen/Program.cs index 28fcbb5e..f274c009 100644 --- a/Source/BookGen/Program.cs +++ b/Source/BookGen/Program.cs @@ -62,10 +62,11 @@ ExcptionExitCode = -1, PlatformNotSupportedExitCode = 4, EnableUtf8Output = true, -}); - -runner.ExceptionHandlerDelegate = OnException; -runner.BeforeRunHook = OnBeforeRun; +}) +{ + ExceptionHandlerDelegate = OnException, + BeforeRunHook = OnBeforeRun +}; runner .AddDefaultCommand() diff --git a/Source/BookGen/Properties/launchSettings.json b/Source/BookGen/Properties/launchSettings.json index d3340b37..39bbda93 100644 --- a/Source/BookGen/Properties/launchSettings.json +++ b/Source/BookGen/Properties/launchSettings.json @@ -44,6 +44,10 @@ "Templates": { "commandName": "Project", "commandLineArgs": "templates" + }, + "Help": { + "commandName": "Project", + "commandLineArgs": "help" } } } \ No newline at end of file diff --git a/Source/BookGen/Tooldownloaders/GlowDownloader.cs b/Source/BookGen/Tooldownloaders/GlowDownloader.cs new file mode 100644 index 00000000..985b11f1 --- /dev/null +++ b/Source/BookGen/Tooldownloaders/GlowDownloader.cs @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2025 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using Bookgen.Lib.Domain.Github; + +using BookGen.Infrastructure.Tools; +using BookGen.Vfs; + +using Microsoft.Extensions.Logging; +using Microsoft.IO; + +namespace BookGen.Tooldownloaders; + +internal sealed class GlowDownloader : TooldownloaderBase +{ + public GlowDownloader(IApiClient apiClient, + RecyclableMemoryStreamManager memoryStreamManager, + ILogger log) + : base(apiClient, memoryStreamManager, log) + { + } + + protected override ToolInfo CreateToolInfo() + { + return new ToolInfo + { + Name = "Glow", + ApproximateSize = "18 MiB", + RepoOwner = "charmbracelet", + RepoName = "glow", + FolderName = "glow", + }; + } + + protected override ReleaseAsset? GetReleaseAsset(IEnumerable releaseAssets) + { + return releaseAssets + .Where(r => r.Name.EndsWith("Windows_x86_64.zip")) + .OrderByDescending(r => r.CreatedAt) + .FirstOrDefault(); + } +} diff --git a/Source/BookGen/Tooldownloaders/ToolDownloadUi.cs b/Source/BookGen/Tooldownloaders/ToolDownloadUi.cs index b5f6908f..0bd06bd2 100644 --- a/Source/BookGen/Tooldownloaders/ToolDownloadUi.cs +++ b/Source/BookGen/Tooldownloaders/ToolDownloadUi.cs @@ -11,7 +11,7 @@ namespace BookGen.Tooldownloaders; -internal class ToolDownloadUi : IDownloadUi +internal sealed class ToolDownloadUi : IDownloadUi { private class ExtendedProgresBar : Progressbar { diff --git a/Source/BookGen/packages.lock.json b/Source/BookGen/packages.lock.json index 1329cd17..c4e391d9 100644 --- a/Source/BookGen/packages.lock.json +++ b/Source/BookGen/packages.lock.json @@ -10,35 +10,35 @@ }, "Microsoft.Extensions.Logging": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "a0EWuBs6D3d7XMGroDXm+WsAi5CVVfjOJvyxurzWnuhBN9CO+1qHKcrKV1JK7H/T4ZtHIoVCOX/YyWM8K87qtw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Microsoft.Extensions.Logging.Abstractions": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "RZkez/JjpnO+MZ6efKkSynN6ZztLpw3WbxNzjLCPBd97wWj1S9ZYPWi0nmT4kWBRa6atHsdM1ydGkUr8GudyDQ==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Logging.Console": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "38Q8sEHwQ/+wVO/mwQBa0fcdHbezFpusHE+vBw/dSr6Fq/kzZm3H/NQX511Jki/R3FHd64IY559gdlHZQtYeEA==", + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "Z6gBfpHqJsz2hGH+eUQUQI+DSHsDNhTKt8toHAtDhYFRlxUN1FKPKzNmTgSrAz1gtDTOBjDU5lQZ50463Ehw7A==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging.Configuration": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging.Configuration": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2" } }, "Microsoft.IO.RecyclableMemoryStream": { @@ -113,78 +113,78 @@ }, "Microsoft.Extensions.Configuration": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "resolved": "10.0.2", + "contentHash": "Lws+o4DFw6p5NquRoYA3d5QVvi49ugNw7TxbW4QGLsL8F1LCCyJqWFy0+RMQ/hzUuS9aKV5NJ/XGAF5N9/RQcQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Configuration.Abstractions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", + "resolved": "10.0.2", + "contentHash": "KC5PslaTDnTuTvyke0KYAVBYdZ7IVTsU3JhHe69BpEbHLcj1YThP3bIGtZNOkZfast2AuLnul5lk4rZKxAdUGQ==", "dependencies": { - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Configuration.Binder": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "resolved": "10.0.2", + "contentHash": "/SdW50prUuenglSy7MXU3eVQkOk4/J4fjc+GIhv4NkTmaZOQyTqpVAYi8nRjNtOKHzCy7g5cSlOSgkbT7clLwQ==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2" } }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "resolved": "10.0.2", + "contentHash": "J/Zmp6fY93JbaiZ11ckWvcyxMPjD6XVwIHQXBjryTBgn7O6O20HYg9uVLFcZlNfgH78MnreE/7EH+hjfzn7VyA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2" } }, "Microsoft.Extensions.Logging.Configuration": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "Zg8LLnfZs5o2RCHD/+9NfDtJ40swauemwCa7sI8gQoAye/UJHRZNpCtC7a5XE7l9Z7mdI8iMWnLZ6m7Q6S3jLg==", + "resolved": "10.0.2", + "contentHash": "XVtNJfLZVTDmQS5RCUjIr7QEAgGhJ3yQ0L3PduN7rE4aijmqYl0pIF09ZSU8jgnxml91Mw59ze220g8S7anaOg==", "dependencies": { - "Microsoft.Extensions.Configuration": "10.0.1", - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Logging": "10.0.1", - "Microsoft.Extensions.Logging.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + "Microsoft.Extensions.Configuration": "10.0.2", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Configuration.Binder": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Logging": "10.0.2", + "Microsoft.Extensions.Logging.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.2" } }, "Microsoft.Extensions.Options": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "resolved": "10.0.2", + "contentHash": "1De2LJjmxdqopI5AYC5dIhoZQ79AR5ayywxNF1rXrXFtKQfbQOV9+n/IsZBa7qWlr0MqoGpW8+OY2v/57udZOA==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Options.ConfigurationExtensions": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", + "resolved": "10.0.2", + "contentHash": "8njGDg0OdDBM4Zox0ybuUOJZkQ8HcH49F+POZBlG+nsfzEyqOCHyHEkWeRVI62qsssiugUVEKqUttT1ZbV0aJQ==", "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", - "Microsoft.Extensions.Configuration.Binder": "10.0.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", - "Microsoft.Extensions.Options": "10.0.1", - "Microsoft.Extensions.Primitives": "10.0.1" + "Microsoft.Extensions.Configuration.Abstractions": "10.0.2", + "Microsoft.Extensions.Configuration.Binder": "10.0.2", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.2", + "Microsoft.Extensions.Options": "10.0.2", + "Microsoft.Extensions.Primitives": "10.0.2" } }, "Microsoft.Extensions.Primitives": { "type": "Transitive", - "resolved": "10.0.1", - "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + "resolved": "10.0.2", + "contentHash": "QmSiO+oLBEooGgB3i0GRXyeYRDHjllqt3k365jwfZlYWhvSHA3UL2NEVV5m8aZa041eIlblo6KMI5txvTMpTwA==" }, "Newtonsoft.Json": { "type": "Transitive", @@ -193,8 +193,8 @@ }, "ShimSkiaSharp": { "type": "Transitive", - "resolved": "3.2.1", - "contentHash": "WyMf+0aj5IJcHkG/t89WApGFg756oXB30ehu9kYu9hjdfS0snkXHoAiTJZqVlqyLOvzC6eA+nOQ2hI84g//5Pg==" + "resolved": "3.4.1", + "contentHash": "ab3J5OGdwYLyXnom90OghS9NyRPH9dtsRtW5B3mL+F43YTlxido3nUtiR9NjB/sI3jvG/J744C29bsSBQ28AfQ==" }, "SkiaSharp.NativeAssets.macOS": { "type": "Transitive", @@ -203,27 +203,27 @@ }, "Svg.Custom": { "type": "Transitive", - "resolved": "3.2.1", - "contentHash": "Wu0+Il+SZfY5+AT25b2PMlSbBrzGIo/6vMMMdOK8jd25I9tdJJbxOFJNG87gFgo9UY9OmpIFzVobGOHzFdJYeQ==", + "resolved": "3.4.1", + "contentHash": "ksHa7Zv8X1InxSWAeM0sSSvZM2W6FWgneXjU0bdYrQ20Q6q0nl3g0uaSWe53i/CC1OmR+/JAxsp8E3/wigtcng==", "dependencies": { "ExCSS": "4.3.1" } }, "Svg.Model": { "type": "Transitive", - "resolved": "3.2.1", - "contentHash": "N4yWlvqv1XWZ+VvI29lT4HkEgtiP3rS/poMfWiVzz6Ao/3IjQCfmo2qrKphq77IqTdMYyh7wGDknMId7YrQY5Q==", + "resolved": "3.4.1", + "contentHash": "Zn04CWWIwV+qlJ4x4vg8FdCYNew5dRcsvkcCpzZggFKHGExIn4WTBtJMuLH8htuiLnbbgF6woIYpUll+seHfjw==", "dependencies": { - "ShimSkiaSharp": "3.2.1", - "Svg.Custom": "3.2.1" + "ShimSkiaSharp": "3.4.1", + "Svg.Custom": "3.4.1" } }, "bookgen.cli": { "type": "Project", "dependencies": { "BookGen.Vfs": "[1.0.0, )", - "Microsoft.Extensions.DependencyInjection.Abstractions": "[10.0.1, )", - "Microsoft.Extensions.Logging.Abstractions": "[10.0.1, )" + "Microsoft.Extensions.DependencyInjection.Abstractions": "[10.0.2, )", + "Microsoft.Extensions.Logging.Abstractions": "[10.0.2, )" } }, "bookgen.contents": { @@ -241,15 +241,15 @@ "SkiaSharp": "[3.119.1, )", "SkiaSharp.NativeAssets.Linux": "[3.119.1, )", "SkiaSharp.NativeAssets.Win32": "[3.119.1, )", - "Svg.Skia": "[3.2.1, )", - "System.ServiceModel.Syndication": "[10.0.1, )", + "Svg.Skia": "[3.4.1, )", + "System.ServiceModel.Syndication": "[10.0.2, )", "YamlDotNet": "[16.3.0, )" } }, "bookgen.shell.shared": { "type": "Project", "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "[10.0.1, )", + "Microsoft.Extensions.Logging.Abstractions": "[10.0.2, )", "Webmaster442.WindowsTerminal": "[4.1.1, )" } }, @@ -283,9 +283,9 @@ }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "zOIurr59+kUf9vNcsUkCvKWZv+fPosUZXURZesYkJCvl0EzTc9F7maAO4Cd2WEV7ZJJ0AZrFQvuH6Npph9wdBw==" }, "SkiaSharp": { "type": "CentralTransitive", @@ -311,20 +311,20 @@ }, "Svg.Skia": { "type": "CentralTransitive", - "requested": "[3.2.1, )", - "resolved": "3.2.1", - "contentHash": "pRJtMOc2hTcnxSu7cx6lf806S+2VrQ0eqUpGMBUQQ/pIIvzrtn0+lUjdABTvjr5rsdQF9OxE+r9npq2+3c6T4A==", + "requested": "[3.4.1, )", + "resolved": "3.4.1", + "contentHash": "fNGHIeIUtEDo41P4MYfVqjNL902t8EP5/tBY9vYDg8VbKSOD84ZlBfCL4KSxjf//HcJi1Uz4uZCUXuEAKJirCw==", "dependencies": { "SkiaSharp": "2.88.9", - "Svg.Custom": "3.2.1", - "Svg.Model": "3.2.1" + "Svg.Custom": "3.4.1", + "Svg.Model": "3.4.1" } }, "System.ServiceModel.Syndication": { "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "Z3+s66hOp7JVDOhkVk0gH/sjsshn99NUMiILcXLaA+3qe2OkLr3nB+3bRzXdfF6arlJBxGfkkki9GP6R9kvdFg==" + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "t28WU9anHmmRD5GHz5qBv/tokqDmU/Ksexz2DI6jAcoSX+p8VQ7rzHCxubDqK6baOtxEyza/oHECzBGi3rHyHQ==" }, "Webmaster442.WindowsTerminal": { "type": "CentralTransitive", diff --git a/Source/Bookgen.Lib/BookEnvironment.cs b/Source/Bookgen.Lib/BookEnvironment.cs index 96f99460..0973e370 100644 --- a/Source/Bookgen.Lib/BookEnvironment.cs +++ b/Source/Bookgen.Lib/BookEnvironment.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// (c) 2019-2025 Ruzsinszki Gábor +// (c) 2019-2026 Ruzsinszki Gábor // This code is licensed under MIT license (see LICENSE for details) //----------------------------------------------------------------------------- @@ -57,7 +57,7 @@ public void Dispose() } } - public async Task Initialize() + public async Task Initialize(string configOverlay) { if (_isInitialized) { @@ -87,7 +87,17 @@ public async Task Initialize() return status; } - Config? config = await _source.DeserializeAsync(FileNameConstants.ConfigFile); + var baseConfig = await _source.ReadJsonAsync(FileNameConstants.ConfigFile); + + JsonMerger configMerger = new JsonMerger(baseConfig); + + if (!string.IsNullOrEmpty(configOverlay)) + { + var overlayConfig = await _source.ReadJsonAsync(configOverlay); + configMerger.Merge(overlayConfig); + } + + Config? config = configMerger.Deserialize(); if (config == null) { diff --git a/Source/Bookgen.Lib/Bookgen.Lib.csproj b/Source/Bookgen.Lib/Bookgen.Lib.csproj index e8c9dd53..5b8ae154 100644 --- a/Source/Bookgen.Lib/Bookgen.Lib.csproj +++ b/Source/Bookgen.Lib/Bookgen.Lib.csproj @@ -25,7 +25,7 @@ - + diff --git a/Source/Bookgen.Lib/Confighandling/JsonMerger.cs b/Source/Bookgen.Lib/Confighandling/JsonMerger.cs new file mode 100644 index 00000000..e8827c8d --- /dev/null +++ b/Source/Bookgen.Lib/Confighandling/JsonMerger.cs @@ -0,0 +1,84 @@ +using System.Text.Json; +using System.Text.Json.Nodes; + +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using BookGen.Vfs; + +namespace Bookgen.Lib.Confighandling; + +internal sealed class JsonMerger +{ + private JsonObject _baseObject; + + public JsonMerger(JsonObject baseObject) + { + _baseObject = baseObject; + } + + private static JsonNode? Merge(JsonNode jsonBase, + JsonNode jsonMerge, + bool mergeIfAlreadyExists = true) + { + if (jsonBase == null || jsonMerge == null) + return jsonBase; + + switch (jsonBase) + { + case JsonObject jsonBaseObj when jsonMerge is JsonObject jsonMergeObj: + { + //NOTE: We must materialize the set (e.g. to an Array), and then clear the merge array so the node can then be + // re-assigned to the target/base Json; clearing the Object seems to be the most efficient approach... + var mergeNodesArray = jsonMergeObj.ToArray(); + jsonMergeObj.Clear(); + + foreach (var prop in mergeNodesArray) + { + if (mergeIfAlreadyExists || !jsonBaseObj.ContainsKey(prop.Key)) + jsonBaseObj[prop.Key] = jsonBaseObj[prop.Key] switch + { + JsonObject jsonBaseChildObj when prop.Value is JsonObject jsonMergeChildObj + => Merge(jsonBaseChildObj, jsonMergeChildObj), + JsonArray jsonBaseChildArray when prop.Value is JsonArray jsonMergeChildArray + => Merge(jsonBaseChildArray, jsonMergeChildArray), + _ => prop.Value + }; + } + break; + } + case JsonArray jsonBaseArray when jsonMerge is JsonArray jsonMergeArray: + { + //NOTE: We must materialize the set (e.g. to an Array), and then clear the merge array, + // so they can then be re-assigned to the target/base Json... + var mergeNodesArray = jsonMergeArray.ToArray(); + jsonMergeArray.Clear(); + foreach (var mergeNode in mergeNodesArray) jsonBaseArray.Add(mergeNode); + break; + } + default: + throw new ArgumentException($"The JsonNode type [{jsonBase.GetType().Name}] is incompatible for merging with the target/base " + + $"type [{jsonMerge.GetType().Name}]; merge requires the types to be the same."); + + } + + return jsonBase; + } + + + public void Merge(JsonObject overlay) + { + var result = Merge(_baseObject, overlay); + if (result is JsonObject mergedObj) + { + _baseObject = mergedObj; + return; + } + throw new InvalidOperationException("Merging resulted in invalid object"); + } + + public T? Deserialize() + => JsonSerializer.Deserialize(_baseObject, JsonOptions.SerializerOptions); +} diff --git a/Source/Bookgen.Lib/Markdown/MarkdownConverter.cs b/Source/Bookgen.Lib/Markdown/MarkdownConverter.cs index c979d9a2..7e42a716 100644 --- a/Source/Bookgen.Lib/Markdown/MarkdownConverter.cs +++ b/Source/Bookgen.Lib/Markdown/MarkdownConverter.cs @@ -5,11 +5,9 @@ using Bookgen.Lib.Markdown.Renderers.Terminal; using Bookgen.Lib.Markdown.TableOfContents; -using Bookgen.Lib.Pipeline; using Markdig; - -using Microsoft.AspNetCore.Components; +using Markdig.Parsers; namespace Bookgen.Lib.Markdown; @@ -39,6 +37,7 @@ public MarkdownConverter(RenderSettings settings) _terminalPipeLine = new MarkdownPipelineBuilder() .UseYamlFrontMatter() + .UseAutoLinks() .Build(); } @@ -56,13 +55,19 @@ public void Dispose() public string RenderMarkdownToHtml(string markdown) => Markdig.Markdown.ToHtml(markdown, _htmlPipeLine); - public string RenderMarkdownToTerminal(string markdown) + public string RenderMarkdownToTerminal(string markdown, RenderOptions? renderOptions = null) { - PSMarkdownOptionInfo optionInfo = new(); + var document = MarkdownParser.Parse(markdown, _terminalPipeLine); using var writer = new StringWriter(); - var renderer = new VT100Renderer(writer, optionInfo); - return Markdig.Markdown.Convert(markdown, renderer, _terminalPipeLine).ToString() ?? ""; + renderOptions ??= new RenderOptions(); + + TerminalRenderer renderer = new TerminalRenderer(writer, renderOptions); + + renderer.Render(document); + renderer.Writer.Flush(); + + return renderer.Writer.ToString() ?? string.Empty; } } diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/AutolinkInlineRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/AutolinkInlineRenderer.cs new file mode 100644 index 00000000..cae7de68 --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/AutolinkInlineRenderer.cs @@ -0,0 +1,25 @@ +using System.Web; + +using Markdig.Syntax.Inlines; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +internal sealed class AutolinkInlineRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, AutolinkInline obj) + { + string url = obj.IsEmail + ? HttpUtility.UrlEncode($"mailto:{obj.Url}") + : HttpUtility.UrlEncode(obj.Url); + + var text = renderer + .Builder + .New() + .WithForegroundColor(renderer.RenderOptions.LinkColor) + .AppendLink(url, obj.Url) + .ResetFormat() + .ToString(); + + renderer.Write(text); + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/CodeBlockRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/CodeBlockRenderer.cs new file mode 100644 index 00000000..87559381 --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/CodeBlockRenderer.cs @@ -0,0 +1,40 @@ +using Markdig.Helpers; +using Markdig.Syntax; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +internal sealed class CodeBlockRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, CodeBlock obj) + { + if (obj?.Lines.Lines != null) + { + string begin = renderer.Builder + .New() + .WithBackgroundColor(renderer.RenderOptions.CodeBlockBackground) + .WithForegroundColor(renderer.RenderOptions.CodeBlockColor) + .ToString(); + + renderer.Write(begin); + + for (int i = 0; i < obj.Lines.Count; i++) + { + StringLine codeLine = obj.Lines.Lines[i]; + if (!string.IsNullOrWhiteSpace(codeLine.ToString())) + { + if (i == obj.Lines.Count - 1) + { + renderer.Write(codeLine.ToString()).WriteReset().WriteLine(); + } + else + { + renderer.WriteLine(codeLine.ToString()); + } + + } + } + + renderer.WriteLine(); + } + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/CodeInlineRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/CodeInlineRenderer.cs index 168158ce..aa9658bd 100644 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/CodeInlineRenderer.cs +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/CodeInlineRenderer.cs @@ -1,17 +1,21 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Syntax.Inlines; +using Markdig.Syntax.Inlines; namespace Bookgen.Lib.Markdown.Renderers.Terminal; -/// -/// Renderer for adding VT100 escape sequences for inline code elements. -/// -internal class CodeInlineRenderer : VT100ObjectRenderer +internal sealed class CodeInlineRenderer : TerminalObjectRenderer { - protected override void Write(VT100Renderer renderer, CodeInline obj) + protected override void Write(TerminalRenderer renderer, CodeInline obj) { - renderer.Write(renderer.EscapeSequences.FormatCode(obj.Content, isInline: true)); + var begin = renderer.Builder + .New() + .WithForegroundColor(renderer.RenderOptions.CodeInlineColor) + .WithItalic() + .ToString(); + + renderer.Write(begin); + + renderer.Write(obj.ContentSpan); + + renderer.WriteReset(); } } diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/DelimiterInlineRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/DelimiterInlineRenderer.cs new file mode 100644 index 00000000..713c6776 --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/DelimiterInlineRenderer.cs @@ -0,0 +1,12 @@ +using Markdig.Syntax.Inlines; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +internal sealed class DelimiterInlineRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, DelimiterInline obj) + { + renderer.Write(obj.ToLiteral()); + renderer.WriteChildren(obj); + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/EmphasisInlineRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/EmphasisInlineRenderer.cs index 8e585e71..842fe39e 100644 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/EmphasisInlineRenderer.cs +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/EmphasisInlineRenderer.cs @@ -1,17 +1,45 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. +using System.Diagnostics; using Markdig.Syntax.Inlines; namespace Bookgen.Lib.Markdown.Renderers.Terminal; -/// -/// Renderer for adding VT100 escape sequences for bold and italics elements. -/// -internal class EmphasisInlineRenderer : VT100ObjectRenderer +internal sealed class EmphasisInlineRenderer : TerminalObjectRenderer { - protected override void Write(VT100Renderer renderer, EmphasisInline obj) + private enum RenderAs { - renderer.Write(renderer.EscapeSequences.FormatEmphasis(obj.FirstChild?.ToString() ?? "", isBold: obj.DelimiterCount == 2)); + Regular = 0, + Bold, + Italic, + } + + private static RenderAs GetRenderOption(EmphasisInline obj) + { + if (obj.DelimiterChar is '*' or '_') + { + Debug.Assert(obj.DelimiterCount <= 2); + return obj.DelimiterCount == 2 ? RenderAs.Bold : RenderAs.Italic; + } + return RenderAs.Regular; + } + + protected override void Write(TerminalRenderer renderer, EmphasisInline obj) + { + RenderAs option = GetRenderOption(obj); + if (option == RenderAs.Regular) + { + renderer.WriteChildren(obj); + return; + } + + var preformat = renderer.Builder.New(); + + if (option == RenderAs.Bold) + preformat.WithBold(); + else + preformat.WithItalic(); + + renderer.Write(preformat.ToString()).WriteChildren(obj); + renderer.WriteReset(); } } diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/FencedCodeBlockRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/FencedCodeBlockRenderer.cs deleted file mode 100644 index b41df71a..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/FencedCodeBlockRenderer.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Helpers; -using Markdig.Syntax; - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Renderer for adding VT100 escape sequences for code blocks with language type. -/// -internal class FencedCodeBlockRenderer : VT100ObjectRenderer -{ - protected override void Write(VT100Renderer renderer, FencedCodeBlock obj) - { - if (obj?.Lines.Lines != null) - { - foreach (StringLine codeLine in obj.Lines.Lines) - { - if (!string.IsNullOrWhiteSpace(codeLine.ToString())) - { - // If the code block is of type YAML, then tab to right to improve readability. - // This specifically helps for parameters help content. - if (string.Equals(obj.Info, "yaml", StringComparison.OrdinalIgnoreCase)) - { - renderer.Write("\t").WriteLine(codeLine.ToString()); - } - else - { - renderer.WriteLine(renderer.EscapeSequences.FormatCode(codeLine.ToString(), isInline: false)); - } - } - } - - // Add a blank line after the code block for better readability. - renderer.WriteLine(); - } - } -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/HeaderBlockRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/HeaderBlockRenderer.cs deleted file mode 100644 index 342f2837..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/HeaderBlockRenderer.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Syntax; - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Renderer for adding VT100 escape sequences for headings. -/// -internal class HeaderBlockRenderer : VT100ObjectRenderer -{ - protected override void Write(VT100Renderer renderer, HeadingBlock obj) - { - string? headerText = obj.Inline?.FirstChild?.ToString(); - - if (!string.IsNullOrEmpty(headerText)) - { - // Format header and then add blank line to improve readability. - switch (obj.Level) - { - case 1: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader1(headerText)); - renderer.WriteLine(); - break; - - case 2: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader2(headerText)); - renderer.WriteLine(); - break; - - case 3: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader3(headerText)); - renderer.WriteLine(); - break; - - case 4: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader4(headerText)); - renderer.WriteLine(); - break; - - case 5: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader5(headerText)); - renderer.WriteLine(); - break; - - case 6: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader6(headerText)); - renderer.WriteLine(); - break; - } - } - } -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/HeadingRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/HeadingRenderer.cs new file mode 100644 index 00000000..dd275d5b --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/HeadingRenderer.cs @@ -0,0 +1,27 @@ +using Markdig.Syntax; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +internal sealed class HeadingRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, HeadingBlock obj) + { + string prefix = new string('#', obj.Level); + + var beginText = renderer + .Builder + .New() + .WithForegroundColor(renderer.RenderOptions.HeadingColor) + .WithBold() + .Append(prefix) + .Append(' ') + .ToString(); + + renderer + .Write(beginText) + .WriteLeafInline(obj) + .WriteReset() + .EnsureLine() + .WriteLine(); + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LeafInlineRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LeafInlineRenderer.cs deleted file mode 100644 index 44a3333d..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LeafInlineRenderer.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Syntax.Inlines; - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Renderer for adding VT100 escape sequences for leaf elements like plain text in paragraphs. -/// -internal class LeafInlineRenderer : VT100ObjectRenderer -{ - protected override void Write(VT100Renderer renderer, LeafInline obj) - { - // If the next sibling is null, then this is the last line in the paragraph. - // Add new line character at the end. - // Else just write without newline at the end. - if (obj.NextSibling == null) - { - renderer.WriteLine(obj.ToString() ?? ""); - } - else - { - renderer.Write(obj.ToString()); - } - } -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LineBreakInlineRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LineBreakInlineRenderer.cs new file mode 100644 index 00000000..928779d1 --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LineBreakInlineRenderer.cs @@ -0,0 +1,15 @@ +using Markdig.Syntax.Inlines; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +internal sealed class LineBreakInlineRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, LineBreakInline obj) + { + if (obj.IsHard) + { + renderer.WriteLine(); + } + renderer.EnsureLine(); + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LineBreakRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LineBreakRenderer.cs deleted file mode 100644 index 2d7054b2..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LineBreakRenderer.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Syntax.Inlines; - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Renderer for adding VT100 escape sequences for line breaks. -/// -internal class LineBreakRenderer : VT100ObjectRenderer -{ - protected override void Write(VT100Renderer renderer, LineBreakInline obj) - { - // If it is a hard line break add new line at the end. - // Else, add a space for after the last character to improve readability. - if (obj.IsHard) - { - renderer.WriteLine(); - } - else - { - renderer.Write(" "); - } - } -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LinkInlineRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LinkInlineRenderer.cs index a27491eb..9fc5dde2 100644 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LinkInlineRenderer.cs +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LinkInlineRenderer.cs @@ -1,27 +1,34 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Syntax.Inlines; +using Markdig.Syntax.Inlines; namespace Bookgen.Lib.Markdown.Renderers.Terminal; -/// -/// Renderer for adding VT100 escape sequences for links. -/// -internal class LinkInlineRenderer : VT100ObjectRenderer +internal sealed class LinkInlineRenderer : TerminalObjectRenderer { - protected override void Write(VT100Renderer renderer, LinkInline obj) + protected override void Write(TerminalRenderer renderer, LinkInline obj) { - string? text = obj.FirstChild?.ToString(); - - // Format link as image or link. if (obj.IsImage) { - renderer.Write(renderer.EscapeSequences.FormatImage(text)); + // TODO + return; } - else + + string? linkText = obj.FirstChild?.ToString(); + + if (obj.Url is null + || linkText is null) { - renderer.Write(renderer.EscapeSequences.FormatLink(text ?? "", obj.Url ?? "")); + return; } + + string text = renderer + .Builder + .New() + .WithForegroundColor(renderer.RenderOptions.LinkColor) + .AppendLink(obj.Url, linkText) + .ResetFormat() + .ToString(); + + renderer.Write(text); + } } diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ListBlockRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ListBlockRenderer.cs deleted file mode 100644 index b3a1f850..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ListBlockRenderer.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Syntax; - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Renderer for adding VT100 escape sequences for list blocks. -/// -internal class ListBlockRenderer : VT100ObjectRenderer -{ - protected override void Write(VT100Renderer renderer, ListBlock obj) - { - // start index of a numbered block. - int index = 1; - - foreach (var item in obj) - { - if (item is ListItemBlock listItem) - { - if (obj.IsOrdered) - { - RenderNumberedList(renderer, listItem, index++); - } - else - { - renderer.Write(listItem); - } - } - } - - renderer.WriteLine(); - } - - private static void RenderNumberedList(VT100Renderer renderer, ListItemBlock block, int index) - { - // For a numbered list, we need to make sure the index is incremented. - foreach (var line in block) - { - if (line is ParagraphBlock paragraphBlock && paragraphBlock.Inline != null) - { - renderer.Write(index.ToString()).Write(". ").Write(paragraphBlock.Inline); - } - } - } -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ListItemBlockRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ListItemBlockRenderer.cs deleted file mode 100644 index 7d4b60b0..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ListItemBlockRenderer.cs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Syntax; - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Renderer for adding VT100 escape sequences for items in a list block. -/// -internal class ListItemBlockRenderer : VT100ObjectRenderer -{ - protected override void Write(VT100Renderer renderer, ListItemBlock obj) - { - if (obj.Parent is ListBlock parent) - { - if (!parent.IsOrdered) - { - foreach (var line in obj) - { - RenderWithIndent(renderer, line, parent.BulletType, 0); - } - } - } - } - - private static void RenderWithIndent(VT100Renderer renderer, MarkdownObject block, char listBullet, int indentLevel) - { - // Indent left by 2 for each level on list. - string indent = Padding(indentLevel * 2); - - if (block is ParagraphBlock paragraphBlock && paragraphBlock.Inline != null) - { - renderer.Write(indent).Write(listBullet).Write(" ").Write(paragraphBlock.Inline); - } - else - { - // If there is a sublist, the block is a ListBlock instead of ParagraphBlock. - if (block is ListBlock subList) - { - foreach (var subListItem in subList) - { - if (subListItem is ListItemBlock subListItemBlock) - { - foreach (var line in subListItemBlock) - { - // Increment indent level for sub list. - RenderWithIndent(renderer, line, listBullet, indentLevel + 1); - } - } - } - } - } - } - - // Typical padding is at most a screen's width, any more than that and we won't bother caching. - private const int IndentCacheMax = 120; - - private static readonly string[] IndentCache = new string[IndentCacheMax]; - - internal static string Padding(int countOfSpaces) - { - if (countOfSpaces >= IndentCacheMax) - { - return new string(' ', countOfSpaces); - } - - var result = IndentCache[countOfSpaces]; - - if (result == null) - { - Interlocked.CompareExchange(ref IndentCache[countOfSpaces], new string(' ', countOfSpaces), comparand: null); - result = IndentCache[countOfSpaces]; - } - - return result; - } -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ListRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ListRenderer.cs new file mode 100644 index 00000000..0ae9bb82 --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ListRenderer.cs @@ -0,0 +1,66 @@ +using Markdig.Syntax; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +internal sealed class ListRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, ListBlock obj) + { + int indent = 0; + string sub = ""; + Write(renderer, obj, sub, ref indent); + } + + private static void Write(TerminalRenderer renderer, ListBlock obj, string sub, ref int indent) + { + const int indentSize = 2; + if (!obj.IsOrdered) + { + foreach (ListItemBlock item in obj.Cast()) + { + renderer.Write(new string(' ', indent * indentSize)); + renderer.Write("* "); + for (int i = 0; i < item.Count; i++) + { + Block subBlock = item[i]; + if (subBlock is ListBlock subListBlock) + { + indent++; + Write(renderer, subListBlock, "", ref indent); + } + else + { + renderer.Render(subBlock); + } + } + } + } + else + { + int number = 1; + foreach (ListItemBlock item in obj.Cast()) + { + renderer.Write(new string(' ', indent * indentSize)) + .Write(sub) + .Write(number.ToString()) + .Write(". "); + + for (int i = 0; i < item.Count; i++) + { + Block subBlock = item[i]; + if (subBlock is ListBlock subListBlock) + { + indent++; + var nsub = $"{sub} {number}."; + Write(renderer, subListBlock, nsub, ref indent); + } + else + { + renderer.Render(subBlock); + } + } + ++number; + } + } + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LiteralInlineRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LiteralInlineRenderer.cs new file mode 100644 index 00000000..56fadc82 --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/LiteralInlineRenderer.cs @@ -0,0 +1,12 @@ +using Markdig.Syntax.Inlines; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +internal sealed class LiteralInlineRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, LiteralInline obj) + { + string content = obj.Content.ToString(); + renderer.Write(content); + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/MarkdownOptionInfoProperty.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/MarkdownOptionInfoProperty.cs deleted file mode 100644 index 64406509..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/MarkdownOptionInfoProperty.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Enum to name all the properties of PSMarkdownOptionInfo. -/// -public enum MarkdownOptionInfoProperty -{ - /// - /// Property name Header1. - /// - Header1, - - /// - /// Property name Header2. - /// - Header2, - - /// - /// Property name Header3. - /// - Header3, - - /// - /// Property name Header4. - /// - Header4, - - /// - /// Property name Header5. - /// - Header5, - - /// - /// Property name Header6. - /// - Header6, - - /// - /// Property name Code. - /// - Code, - - /// - /// Property name Link. - /// - Link, - - /// - /// Property name Image. - /// - Image, - - /// - /// Property name EmphasisBold. - /// - EmphasisBold, - - /// - /// Property name EmphasisItalics. - /// - EmphasisItalics -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/PSMarkdownOptionInfo.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/PSMarkdownOptionInfo.cs deleted file mode 100644 index dbb230cc..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/PSMarkdownOptionInfo.cs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Class to represent color preference options for various Markdown elements. -/// -public sealed class PSMarkdownOptionInfo -{ - private const char Esc = (char)0x1b; - private const string EndSequence = "[0m"; - - /// - /// Gets or sets current VT100 escape sequence for header 1. - /// - public string Header1 { get; set; } - - /// - /// Gets or sets current VT100 escape sequence for header 2. - /// - public string Header2 { get; set; } - - /// - /// Gets or sets current VT100 escape sequence for header 3. - /// - public string Header3 { get; set; } - - /// - /// Gets or sets current VT100 escape sequence for header 4. - /// - public string Header4 { get; set; } - - /// - /// Gets or sets current VT100 escape sequence for header 5. - /// - public string Header5 { get; set; } - - /// - /// Gets or sets current VT100 escape sequence for header 6. - /// - public string Header6 { get; set; } - - /// - /// Gets or sets current VT100 escape sequence for code inline and code blocks. - /// - public string Code { get; set; } - - /// - /// Gets or sets current VT100 escape sequence for links. - /// - public string Link { get; set; } - - /// - /// Gets or sets current VT100 escape sequence for images. - /// - public string Image { get; set; } - - /// - /// Gets or sets current VT100 escape sequence for bold text. - /// - public string EmphasisBold { get; set; } - - /// - /// Gets or sets current VT100 escape sequence for italics text. - /// - public string EmphasisItalics { get; set; } - - /// - /// Gets or sets a value indicating whether VT100 escape sequences should be added. Default it true. - /// - public bool EnableVT100Encoding { get; set; } - - /// - /// Get the property as an rendered escape sequence. - /// This is used by formatting system for displaying. - /// - /// Name of the property to get as escape sequence. - /// Specified property name as escape sequence. - public string AsEscapeSequence(MarkdownOptionInfoProperty propertyName) - { - return propertyName switch - { - MarkdownOptionInfoProperty.Header1 => string.Concat(Esc, Header1, Header1, Esc, EndSequence), - MarkdownOptionInfoProperty.Header2 => string.Concat(Esc, Header2, Header2, Esc, EndSequence), - MarkdownOptionInfoProperty.Header3 => string.Concat(Esc, Header3, Header3, Esc, EndSequence), - MarkdownOptionInfoProperty.Header4 => string.Concat(Esc, Header4, Header4, Esc, EndSequence), - MarkdownOptionInfoProperty.Header5 => string.Concat(Esc, Header5, Header5, Esc, EndSequence), - MarkdownOptionInfoProperty.Header6 => string.Concat(Esc, Header6, Header6, Esc, EndSequence), - MarkdownOptionInfoProperty.Code => string.Concat(Esc, Code, Code, Esc, EndSequence), - MarkdownOptionInfoProperty.Link => string.Concat(Esc, Link, Link, Esc, EndSequence), - MarkdownOptionInfoProperty.Image => string.Concat(Esc, Image, Image, Esc, EndSequence), - MarkdownOptionInfoProperty.EmphasisBold => string.Concat(Esc, EmphasisBold, EmphasisBold, Esc, EndSequence), - MarkdownOptionInfoProperty.EmphasisItalics => string.Concat(Esc, EmphasisItalics, EmphasisItalics, Esc, EndSequence), - _ => throw new InvalidOperationException($"Unknown value: {propertyName}"), - }; - } - - /// - /// Initializes a new instance of the class and sets dark as the default theme. - /// - public PSMarkdownOptionInfo() - { - SetDarkTheme(); - EnableVT100Encoding = true; - } - - private const string Header1Dark = "[7m"; - private const string Header2Dark = "[4;93m"; - private const string Header3Dark = "[4;94m"; - private const string Header4Dark = "[4;95m"; - private const string Header5Dark = "[4;96m"; - private const string Header6Dark = "[4;97m"; - private const string CodeDark = "[48;2;155;155;155;38;2;30;30;30m"; - private const string CodeMacOS = "[107;95m"; - private const string LinkDark = "[4;38;5;117m"; - private const string ImageDark = "[33m"; - private const string EmphasisBoldDark = "[1m"; - private const string EmphasisItalicsDark = "[36m"; - - private const string Header1Light = "[7m"; - private const string Header2Light = "[4;33m"; - private const string Header3Light = "[4;34m"; - private const string Header4Light = "[4;35m"; - private const string Header5Light = "[4;36m"; - private const string Header6Light = "[4;30m"; - private const string CodeLight = "[48;2;155;155;155;38;2;30;30;30m"; - private const string LinkLight = "[4;38;5;117m"; - private const string ImageLight = "[33m"; - private const string EmphasisBoldLight = "[1m"; - private const string EmphasisItalicsLight = "[36m"; - - /// - /// Set all preference for dark theme. - /// - [MemberNotNull(nameof(Header1), - nameof(Header2), - nameof(Header3), - nameof(Header4), - nameof(Header5), - nameof(Header6), - nameof(Link), - nameof(Image), - nameof(EmphasisBold), - nameof(EmphasisItalics), - nameof(Code))] - public void SetDarkTheme() - { - Header1 = Header1Dark; - Header2 = Header2Dark; - Header3 = Header3Dark; - Header4 = Header4Dark; - Header5 = Header5Dark; - Header6 = Header6Dark; - Link = LinkDark; - Image = ImageDark; - EmphasisBold = EmphasisBoldDark; - EmphasisItalics = EmphasisItalicsDark; - SetCodeColor(isDarkTheme: true); - } - - /// - /// Set all preference for light theme. - /// - [MemberNotNull(nameof(Header1), - nameof(Header2), - nameof(Header3), - nameof(Header4), - nameof(Header5), - nameof(Header6), - nameof(Link), - nameof(Image), - nameof(EmphasisBold), - nameof(EmphasisItalics), - nameof(Code))] - public void SetLightTheme() - { - Header1 = Header1Light; - Header2 = Header2Light; - Header3 = Header3Light; - Header4 = Header4Light; - Header5 = Header5Light; - Header6 = Header6Light; - Link = LinkLight; - Image = ImageLight; - EmphasisBold = EmphasisBoldLight; - EmphasisItalics = EmphasisItalicsLight; - SetCodeColor(isDarkTheme: false); - } - - [MemberNotNull(nameof(Code))] - private void SetCodeColor(bool isDarkTheme) - { - // MacOS terminal app does not support extended colors for VT100, so we special case for it. - Code = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? CodeMacOS : isDarkTheme ? CodeDark : CodeLight; - } -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ParagraphBlockRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ParagraphBlockRenderer.cs deleted file mode 100644 index e063ccb0..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ParagraphBlockRenderer.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Syntax; - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Renderer for adding VT100 escape sequences for paragraphs. -/// -internal class ParagraphBlockRenderer : VT100ObjectRenderer -{ - protected override void Write(VT100Renderer renderer, ParagraphBlock obj) - { - if (obj.Inline == null) - return; - - // Call the renderer for children, leaf inline or line breaks. - renderer.WriteChildren(obj.Inline); - - // Add new line at the end of the paragraph. - renderer.WriteLine(); - } -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ParagraphRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ParagraphRenderer.cs new file mode 100644 index 00000000..b566102c --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ParagraphRenderer.cs @@ -0,0 +1,14 @@ +using Markdig.Syntax; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +internal sealed class ParagraphRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, ParagraphBlock obj) + { + renderer + .WriteLeafInline(obj) + .WriteLine() + .WriteLine(); + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/QuoteBlockRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/QuoteBlockRenderer.cs index ae2639d0..8debad31 100644 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/QuoteBlockRenderer.cs +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/QuoteBlockRenderer.cs @@ -1,24 +1,22 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Syntax; +using Markdig.Syntax; namespace Bookgen.Lib.Markdown.Renderers.Terminal; -/// -/// Renderer for adding VT100 escape sequences for quote blocks. -/// -internal class QuoteBlockRenderer : VT100ObjectRenderer +internal sealed class QuoteBlockRenderer : TerminalObjectRenderer { - protected override void Write(VT100Renderer renderer, QuoteBlock obj) + protected override void Write(TerminalRenderer renderer, QuoteBlock obj) { - // Iterate through each item and add the quote character before the content. - foreach (var item in obj) - { - renderer.Write(obj.QuoteChar).Write(" ").Write(item); - } + var begin = renderer.Builder.New() + .WithForegroundColor(renderer.RenderOptions.QuoteBlockColor) + .WithItalic() + .ToString(); + + renderer + .Write(begin) + .WriteChildren(obj); + + renderer + .WriteReset(); - // Add blank line after the quote block. - renderer.WriteLine(); } } diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/RenderOptions.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/RenderOptions.cs new file mode 100644 index 00000000..a0953288 --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/RenderOptions.cs @@ -0,0 +1,41 @@ +using Webmaster442.WindowsTerminal; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +public sealed class RenderOptions +{ + public TerminalColor HeadingColor { get; set; } + + public TerminalColor LinkColor { get; set; } + + public TerminalColor CodeInlineColor { get; set; } + + public TerminalColor QuoteBlockColor { get; set; } + + public TerminalColor CodeBlockBackground { get; set; } + + public TerminalColor CodeBlockColor { get; set; } + + public int Width + { + get => field; + set + { + if (value < 1) + throw new ArgumentOutOfRangeException(nameof(Width), "Width must be greater than 0."); + + field = value; + } + } + + public RenderOptions() + { + HeadingColor = TerminalColor.Green; + LinkColor = TerminalColor.BrightBlue; + CodeInlineColor = TerminalColor.BrightRed; + QuoteBlockColor = TerminalColor.BrightWhite; + CodeBlockColor = TerminalColor.Black; + CodeBlockBackground = TerminalColor.White; + Width = 120; + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/TerminalObjectRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/TerminalObjectRenderer.cs new file mode 100644 index 00000000..a97ebc24 --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/TerminalObjectRenderer.cs @@ -0,0 +1,8 @@ +using Markdig.Renderers; +using Markdig.Syntax; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +public abstract class TerminalObjectRenderer : MarkdownObjectRenderer where TObject : MarkdownObject +{ +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/TerminalRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/TerminalRenderer.cs new file mode 100644 index 00000000..a587a8ca --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/TerminalRenderer.cs @@ -0,0 +1,42 @@ +using Markdig.Renderers; + +using Webmaster442.WindowsTerminal; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +public sealed class TerminalRenderer : TextRendererBase +{ + + public TerminalRenderer(TextWriter writer, RenderOptions renderOptions) : base(writer) + { + RenderOptions = renderOptions; + Builder = new TerminalFormattedStringBuilder(); + + ObjectRenderers.Add(new CodeBlockRenderer()); + ObjectRenderers.Add(new ListRenderer()); + ObjectRenderers.Add(new HeadingRenderer()); + ObjectRenderers.Add(new ParagraphRenderer()); + ObjectRenderers.Add(new QuoteBlockRenderer()); + ObjectRenderers.Add(new ThematicBreakRenderer()); + + ObjectRenderers.Add(new AutolinkInlineRenderer()); + ObjectRenderers.Add(new CodeInlineRenderer()); + ObjectRenderers.Add(new DelimiterInlineRenderer()); + ObjectRenderers.Add(new EmphasisInlineRenderer()); + ObjectRenderers.Add(new LineBreakInlineRenderer()); + ObjectRenderers.Add(new LinkInlineRenderer()); + ObjectRenderers.Add(new LiteralInlineRenderer()); + } + + public RenderOptions RenderOptions { get; } + + internal TerminalFormattedStringBuilder Builder { get; } + + private const string ResetCode = "\u001b[0m"; + + public TerminalRenderer WriteReset() + { + Write(ResetCode); + return this; + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ThematicBreakRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ThematicBreakRenderer.cs new file mode 100644 index 00000000..18b01324 --- /dev/null +++ b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/ThematicBreakRenderer.cs @@ -0,0 +1,12 @@ +using Markdig.Syntax; + +namespace Bookgen.Lib.Markdown.Renderers.Terminal; + +internal sealed class ThematicBreakRenderer : TerminalObjectRenderer +{ + protected override void Write(TerminalRenderer renderer, ThematicBreakBlock obj) + { + renderer.WriteLine(new string('-', renderer.RenderOptions.Width)); + renderer.WriteLine(); + } +} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/VT100EscapeSequences.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/VT100EscapeSequences.cs deleted file mode 100644 index 6c174bd1..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/VT100EscapeSequences.cs +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Class to represent default VT100 escape sequences. -/// -public class VT100EscapeSequences -{ - private const char Esc = (char)0x1B; - - private readonly string _endSequence = Esc + "[0m"; - - // For code blocks, [500@ make sure that the whole line has background color. - private const string LongBackgroundCodeBlock = "[500@"; - - private readonly PSMarkdownOptionInfo _options; - - /// - /// Initializes a new instance of the class. - /// - /// PSMarkdownOptionInfo object to initialize with. - public VT100EscapeSequences(PSMarkdownOptionInfo optionInfo) - { - _options = optionInfo ?? throw new ArgumentNullException(nameof(optionInfo)); - } - - /// - /// Class to represent default VT100 escape sequences. - /// - /// Text of the header to format. - /// Formatted Header 1 string. - public string FormatHeader1(string headerText) - { - return FormatHeader(headerText, _options.Header1); - } - - /// - /// Class to represent default VT100 escape sequences. - /// - /// Text of the header to format. - /// Formatted Header 2 string. - public string FormatHeader2(string headerText) - { - return FormatHeader(headerText, _options.Header2); - } - - /// - /// Class to represent default VT100 escape sequences. - /// - /// Text of the header to format. - /// Formatted Header 3 string. - public string FormatHeader3(string headerText) - { - return FormatHeader(headerText, _options.Header3); - } - - /// - /// Class to represent default VT100 escape sequences. - /// - /// Text of the header to format. - /// Formatted Header 4 string. - public string FormatHeader4(string headerText) - { - return FormatHeader(headerText, _options.Header4); - } - - /// - /// Class to represent default VT100 escape sequences. - /// - /// Text of the header to format. - /// Formatted Header 5 string. - public string FormatHeader5(string headerText) - { - return FormatHeader(headerText, _options.Header5); - } - - /// - /// Class to represent default VT100 escape sequences. - /// - /// Text of the header to format. - /// Formatted Header 6 string. - public string FormatHeader6(string headerText) - { - return FormatHeader(headerText, _options.Header6); - } - - /// - /// Class to represent default VT100 escape sequences. - /// - /// Text of the code block to format. - /// True if it is a inline code block, false otherwise. - /// Formatted code block string. - public string FormatCode(string codeText, bool isInline) - { - bool isVT100Enabled = _options.EnableVT100Encoding; - - if (isInline) - { - if (isVT100Enabled) - { - return string.Concat(Esc, _options.Code, codeText, _endSequence); - } - else - { - return codeText; - } - } - else - { - if (isVT100Enabled) - { - return string.Concat(Esc, _options.Code, codeText, Esc, LongBackgroundCodeBlock, _endSequence); - } - else - { - return codeText; - } - } - } - - /// - /// Class to represent default VT100 escape sequences. - /// - /// Text of the link to format. - /// URL of the link. - /// True url should be hidden, false otherwise. Default is true. - /// Formatted link string. - public string FormatLink(string linkText, string url, bool hideUrl = true) - { - bool isVT100Enabled = _options.EnableVT100Encoding; - - if (hideUrl) - { - if (isVT100Enabled) - { - return string.Concat(Esc, _options.Link, "\"", linkText, "\"", _endSequence); - } - else - { - return string.Concat("\"", linkText, "\""); - } - } - else - { - if (isVT100Enabled) - { - return string.Concat("\"", linkText, "\" (", Esc, _options.Link, url, _endSequence, ")"); - } - else - { - return string.Concat("\"", linkText, "\" (", url, ")"); - } - } - } - - /// - /// Class to represent default VT100 escape sequences. - /// - /// Text to format as emphasis. - /// True if it is to be formatted as bold, false to format it as italics. - /// Formatted emphasis string. - public string FormatEmphasis(string emphasisText, bool isBold) - { - var sequence = isBold ? _options.EmphasisBold : _options.EmphasisItalics; - - if (_options.EnableVT100Encoding) - { - return string.Concat(Esc, sequence, emphasisText, _endSequence); - } - else - { - return emphasisText; - } - } - - /// - /// Class to represent default VT100 escape sequences. - /// - /// Text of the image to format. - /// Formatted image string. - public string FormatImage(string? altText) - { - var text = altText; - - if (string.IsNullOrEmpty(altText)) - { - text = "Image"; - } - - if (_options.EnableVT100Encoding) - { - return string.Concat(Esc, _options.Image, "[", text, "]", _endSequence); - } - else - { - return string.Concat("[", text, "]"); - } - } - - private string FormatHeader(string headerText, string headerEscapeSequence) - { - if (_options.EnableVT100Encoding) - { - return string.Concat(Esc, headerEscapeSequence, headerText, _endSequence); - } - else - { - return headerText; - } - } -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/VT100ObjectRenderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/VT100ObjectRenderer.cs deleted file mode 100644 index 04f144c1..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/VT100ObjectRenderer.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Renderers; -using Markdig.Syntax; - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Implement the MarkdownObjectRenderer with VT100Renderer. -/// -/// The element type of the renderer. -public abstract class VT100ObjectRenderer : MarkdownObjectRenderer where T : MarkdownObject -{ -} diff --git a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/VT100Renderer.cs b/Source/Bookgen.Lib/Markdown/Renderers/Terminal/VT100Renderer.cs deleted file mode 100644 index ff25494a..00000000 --- a/Source/Bookgen.Lib/Markdown/Renderers/Terminal/VT100Renderer.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Markdig.Renderers; - -namespace Bookgen.Lib.Markdown.Renderers.Terminal; - -/// -/// Initializes an instance of the VT100 renderer. -/// -public sealed class VT100Renderer : TextRendererBase -{ - /// - /// Initializes a new instance of the class. - /// - /// TextWriter to write to. - /// PSMarkdownOptionInfo object with options. - public VT100Renderer(TextWriter writer, PSMarkdownOptionInfo optionInfo) : base(writer) - { - EscapeSequences = new VT100EscapeSequences(optionInfo); - - // Add the various element renderers. - ObjectRenderers.Add(new HeaderBlockRenderer()); - ObjectRenderers.Add(new LineBreakRenderer()); - ObjectRenderers.Add(new CodeInlineRenderer()); - ObjectRenderers.Add(new FencedCodeBlockRenderer()); - ObjectRenderers.Add(new EmphasisInlineRenderer()); - ObjectRenderers.Add(new ParagraphBlockRenderer()); - ObjectRenderers.Add(new LeafInlineRenderer()); - ObjectRenderers.Add(new LinkInlineRenderer()); - ObjectRenderers.Add(new ListBlockRenderer()); - ObjectRenderers.Add(new ListItemBlockRenderer()); - ObjectRenderers.Add(new QuoteBlockRenderer()); - } - - /// - /// Gets the current escape sequences. - /// - public VT100EscapeSequences EscapeSequences { get; } -} diff --git a/Source/Bookgen.Lib/packages.lock.json b/Source/Bookgen.Lib/packages.lock.json index f155489c..b344021b 100644 --- a/Source/Bookgen.Lib/packages.lock.json +++ b/Source/Bookgen.Lib/packages.lock.json @@ -57,20 +57,20 @@ }, "Svg.Skia": { "type": "Direct", - "requested": "[3.2.1, )", - "resolved": "3.2.1", - "contentHash": "pRJtMOc2hTcnxSu7cx6lf806S+2VrQ0eqUpGMBUQQ/pIIvzrtn0+lUjdABTvjr5rsdQF9OxE+r9npq2+3c6T4A==", + "requested": "[3.4.1, )", + "resolved": "3.4.1", + "contentHash": "fNGHIeIUtEDo41P4MYfVqjNL902t8EP5/tBY9vYDg8VbKSOD84ZlBfCL4KSxjf//HcJi1Uz4uZCUXuEAKJirCw==", "dependencies": { "SkiaSharp": "2.88.9", - "Svg.Custom": "3.2.1", - "Svg.Model": "3.2.1" + "Svg.Custom": "3.4.1", + "Svg.Model": "3.4.1" } }, "System.ServiceModel.Syndication": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "Z3+s66hOp7JVDOhkVk0gH/sjsshn99NUMiILcXLaA+3qe2OkLr3nB+3bRzXdfF6arlJBxGfkkki9GP6R9kvdFg==" + "requested": "[10.0.2, )", + "resolved": "10.0.2", + "contentHash": "t28WU9anHmmRD5GHz5qBv/tokqDmU/Ksexz2DI6jAcoSX+p8VQ7rzHCxubDqK6baOtxEyza/oHECzBGi3rHyHQ==" }, "YamlDotNet": { "type": "Direct", @@ -131,8 +131,8 @@ }, "ShimSkiaSharp": { "type": "Transitive", - "resolved": "3.2.1", - "contentHash": "WyMf+0aj5IJcHkG/t89WApGFg756oXB30ehu9kYu9hjdfS0snkXHoAiTJZqVlqyLOvzC6eA+nOQ2hI84g//5Pg==" + "resolved": "3.4.1", + "contentHash": "ab3J5OGdwYLyXnom90OghS9NyRPH9dtsRtW5B3mL+F43YTlxido3nUtiR9NjB/sI3jvG/J744C29bsSBQ28AfQ==" }, "SkiaSharp.NativeAssets.macOS": { "type": "Transitive", @@ -141,19 +141,19 @@ }, "Svg.Custom": { "type": "Transitive", - "resolved": "3.2.1", - "contentHash": "Wu0+Il+SZfY5+AT25b2PMlSbBrzGIo/6vMMMdOK8jd25I9tdJJbxOFJNG87gFgo9UY9OmpIFzVobGOHzFdJYeQ==", + "resolved": "3.4.1", + "contentHash": "ksHa7Zv8X1InxSWAeM0sSSvZM2W6FWgneXjU0bdYrQ20Q6q0nl3g0uaSWe53i/CC1OmR+/JAxsp8E3/wigtcng==", "dependencies": { "ExCSS": "4.3.1" } }, "Svg.Model": { "type": "Transitive", - "resolved": "3.2.1", - "contentHash": "N4yWlvqv1XWZ+VvI29lT4HkEgtiP3rS/poMfWiVzz6Ao/3IjQCfmo2qrKphq77IqTdMYyh7wGDknMId7YrQY5Q==", + "resolved": "3.4.1", + "contentHash": "Zn04CWWIwV+qlJ4x4vg8FdCYNew5dRcsvkcCpzZggFKHGExIn4WTBtJMuLH8htuiLnbbgF6woIYpUll+seHfjw==", "dependencies": { - "ShimSkiaSharp": "3.2.1", - "Svg.Custom": "3.2.1" + "ShimSkiaSharp": "3.4.1", + "Svg.Custom": "3.4.1" } }, "bookgen.shell.shared": { diff --git a/Test/Bookgen.Tests/Lib/UT_JsonMerger.cs b/Test/Bookgen.Tests/Lib/UT_JsonMerger.cs new file mode 100644 index 00000000..6c7ad7ce --- /dev/null +++ b/Test/Bookgen.Tests/Lib/UT_JsonMerger.cs @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// (c) 2019-2026 Ruzsinszki Gábor +// This code is licensed under MIT license (see LICENSE for details) +//----------------------------------------------------------------------------- + +using System.Text.Json; +using System.Text.Json.Nodes; + +using Bookgen.Lib.Confighandling; +using Bookgen.Lib.Domain.IO.Configuration; + +namespace Bookgen.Tests.Lib; + +[TestFixture] +public class UT_JsonMerger +{ + [Test] + public void EnsureThat_Merge_Works() + { + var config1 = new Config + { + Book2LetterISO639Language = "en", + BookAuthor = "Author One", + PrintConfig = new PrintConfig + { + Images = new ImageConfig + { + ImageQualityOnResize = 70, + ResizeAndRecodeImages = ImgRecodeOption.AsPng, + SvgRecode = SvgRecodeOption.AsPng, + } + }, + WordpressConfig = new WordpressConfig + { + CssClasses = new CssClasses + { + H1 = "custom-h1", + H2 = "custom-h2", + } + }, + }; + + var overlay = """ + { + "BookAuthor": "Author Two", + "PrintConfig": { + "Images": { + "ImageQualityOnResize": 85 + } + }, + "WordpressConfig": { + "CssClasses": { + "H2": "overridden-h2", + "H3": "custom-h3" + } + } + } + """; + + var node1 = JsonSerializer.SerializeToNode(config1) as JsonObject; + var nodeOverlay = JsonNode.Parse(overlay) as JsonObject; + + JsonMerger sut = new JsonMerger(node1!); + sut.Merge(nodeOverlay!); + + var result = sut.Deserialize(); + + Assert.That(result, Is.Not.Null); + + using (Assert.EnterMultipleScope()) + { + Assert.That(result!.Book2LetterISO639Language, Is.EqualTo("en")); + Assert.That(result.BookAuthor, Is.EqualTo("Author Two")); + Assert.That(result.PrintConfig.Images.ImageQualityOnResize, Is.EqualTo(85)); + Assert.That(result.PrintConfig.Images.ResizeAndRecodeImages, Is.EqualTo(ImgRecodeOption.AsPng)); + Assert.That(result.PrintConfig.Images.SvgRecode, Is.EqualTo(SvgRecodeOption.AsPng)); + Assert.That(result.WordpressConfig.CssClasses.H1, Is.EqualTo("custom-h1")); + Assert.That(result.WordpressConfig.CssClasses.H2, Is.EqualTo("overridden-h2")); + Assert.That(result.WordpressConfig.CssClasses.H3, Is.EqualTo("custom-h3")); + } + } +} diff --git a/publish.ps1 b/publish.ps1 index cdfe4433..f67a2c75 100644 --- a/publish.ps1 +++ b/publish.ps1 @@ -1,31 +1,63 @@ Clear-Host -# publish windows & linux self-contained -dotnet publish -c release -o "bin\publish\windows\bin" --self-contained true -r win-x64 -p PublishReadyToRun BookGen.slnx -dotnet publish -c release -o "bin\publish\linux\bin" --self-contained true -r linux-x64 -p PublishReadyToRun BookGen.slnx - -# copy installer scripts -Copy-Item "Installers\install.cmd" "bin\publish\windows\install.cmd" -Copy-Item "Installers\install.sh" "bin\publish\linux\install.sh" - -# copy assets -Copy-Item "bin\Release\assets.zip" "bin\publish\windows\bin\assets.zip" -Copy-Item "bin\Release\assets.zip" "bin\publish\linux\bin\assets.zip" - -# write version.txt -.\bin\publish\windows\bin\BookGen.exe version > .\bin\publish\windows\version.txt -.\bin\publish\windows\bin\BookGen version > .\bin\publish\linux\version.txt - -# Generate docs -.\bin\publish\windows\bin\BookGen Schemas -.\bin\publish\windows\bin\BookGen md2html -i Schemas.md -o "bin\publish\windows\Schemas.html" -t "Configuration schemas" -.\bin\publish\windows\bin\BookGen md2html -i Schemas.md -o "bin\publish\linux\Schemas.html" -t "Configuration schemas" -.\bin\publish\windows\bin\BookGen md2html -i Changelog.md -o "bin\publish\windows\Changelog.html" -t "Change Log" -.\bin\publish\windows\bin\BookGen md2html -i Changelog.md -o "bin\publish\linux\Changelog.html" -t "Change Log" -.\bin\publish\windows\bin\BookGen md2html -i Commands.md -o "bin\publish\windows\Commands.html" -t "BookGen Commands" -.\bin\publish\windows\bin\BookGen md2html -i Commands.md -o "bin\publish\linux\Commands.html" -t "BookGen Commands" -Remove-Item Schemas.md - -# zip -Compress-Archive -Path "bin\publish\windows\*" -DestinationPath "bin\publish\BookGen-windows.zip" -Force -tar -czvf "bin\publish\BookGen-linux.tar.gz" -C "bin\publish\linux" . +function Invoke-Publish { + param( + [bool] $SelfContained, + [string] $WindowsArchiveName, + [string] $LinuxArchiveName + ) + + if (Test-Path "bin\publish\windows") { + Remove-Item "bin\publish\windows*" -Recurse -Force + } + + if (Test-Path "bin\publish\linux") { + Remove-Item "bin\publish\windows*" -Recurse -Force + } + + # publish windows & linux + if ($SelfContained) { + dotnet publish -c release -o "bin\publish\windows\bin" --self-contained true -r win-x64 -p PublishReadyToRun BookGen.slnx + dotnet publish -c release -o "bin\publish\linux\bin" --self-contained true -r linux-x64 -p PublishReadyToRun BookGen.slnx + } + else { + dotnet publish -c release -o "bin\publish\windows\bin" -r win-x64 -p PublishReadyToRun BookGen.slnx + dotnet publish -c release -o "bin\publish\linux\bin" -r linux-x64 -p PublishReadyToRun BookGen.slnx + } + + # copy installer scripts + Copy-Item "Installers\install.cmd" "bin\publish\windows\install.cmd" + + # copy assets + Copy-Item "bin\Release\assets.zip" "bin\publish\windows\bin\assets.zip" + Copy-Item "bin\Release\assets.zip" "bin\publish\linux\bin\assets.zip" + + # write version.txt + .\bin\publish\windows\bin\BookGen.exe version > .\bin\publish\windows\version.txt + .\bin\publish\windows\bin\BookGen version > .\bin\publish\linux\version.txt + + # Generate docs + .\bin\publish\windows\bin\BookGen Schemas + .\bin\publish\windows\bin\BookGen md2html -i Schemas.md -o "bin\publish\windows\Schemas.html" -t "Configuration schemas" + .\bin\publish\windows\bin\BookGen md2html -i Schemas.md -o "bin\publish\linux\Schemas.html" -t "Configuration schemas" + .\bin\publish\windows\bin\BookGen md2html -i Changelog.md -o "bin\publish\windows\Changelog.html" -t "Change Log" + .\bin\publish\windows\bin\BookGen md2html -i Changelog.md -o "bin\publish\linux\Changelog.html" -t "Change Log" + .\bin\publish\windows\bin\BookGen md2html -i Commands.md -o "bin\publish\windows\Commands.html" -t "BookGen Commands" + .\bin\publish\windows\bin\BookGen md2html -i Commands.md -o "bin\publish\linux\Commands.html" -t "BookGen Commands" + Remove-Item Schemas.md + + # zip + if ($SelfContained) { + Compress-Archive -Path "bin\publish\windows\*" -DestinationPath "bin\publish\$WindowsArchiveName" -Force + tar -czvf "bin\publish\$LinuxArchiveName" -C "bin\publish\linux" . + } + else { + Compress-Archive -Path "bin\publish\windows\*" -DestinationPath "bin\publish\$WindowsArchiveName" -Force + tar -czvf "bin\publish\$LinuxArchiveName" -C "bin\publish\linux" . + } +} + +# Self-contained build and archives +Invoke-Publish -SelfContained $true -WindowsArchiveName "BookGen-windows-selefcontained.zip" -LinuxArchiveName "BookGen-linux-selefcontained.tar.gz" +# Framework-dependent build and archives +Invoke-Publish -SelfContained $false -WindowsArchiveName "BookGen-windows.zip" -LinuxArchiveName "BookGen-linux.tar.gz" \ No newline at end of file