diff --git a/gems/lib/asciidoctor/foodogsquared-extensions.rb b/gems/lib/asciidoctor/foodogsquared-extensions.rb index 3d57215..26df17f 100644 --- a/gems/lib/asciidoctor/foodogsquared-extensions.rb +++ b/gems/lib/asciidoctor/foodogsquared-extensions.rb @@ -13,6 +13,7 @@ require_relative 'github-raw-content-include-processor/extension' require_relative 'gitlab-link-inline-macro/extension' require_relative 'gitlab-raw-content-include-processor/extension' require_relative 'chat-block-processor/extension' +require_relative 'git-blob-include-processor/extension' require_relative 'wikipedia-inline-macro/extension' Asciidoctor::Extensions.register do @@ -28,5 +29,8 @@ Asciidoctor::Extensions.register do inline_macro GitLabLinkInlineMacro include_processor GitLabRawIncludeProcessor + include_processor GitBlobIncludeProcessor + preprocessor GitContentBranchAttributePreprocessor + inline_macro WikipediaInlineMacro end diff --git a/gems/lib/asciidoctor/git-blob-include-processor/README.adoc b/gems/lib/asciidoctor/git-blob-include-processor/README.adoc new file mode 100644 index 0000000..101150e --- /dev/null +++ b/gems/lib/asciidoctor/git-blob-include-processor/README.adoc @@ -0,0 +1,58 @@ += Git blob include processor +:toc: + + +An include processor that includes content from the current Git repo. +The use case for this is specifically for creating dedicated branches for certain content. + + +== Synopsis + +[source, asciidoc] +---- +include::git:$REVSPEC[] +---- + +Where `$REVSPEC` is a revision as specified in link:https://manpages.debian.org/gitrevisions.7[gitrevisions.7]. +Take note this include processor only accepts a blob object's content to be extracted. +This is meant to be used with other attributes. +See <> for more details. + +If the resulting operation ends in an error (i.e., non-existing revision, file, something went wrong), it should log a warning and transclude an error. + + +== Attributes + +- `path` is the filepath to be retrieved from the revision. + +- `context-lines` is the number of surrounding lines for the diff. +This is only effective if `diff` option is given. + +There's also a couple of link:https://docs.asciidoctor.org/asciidoc/latest/attributes/options/[options] for this component. +You can give the following options through `opts` attribute (i.e., `opts="diff,reverse"`). + +- `diff` will show the difference in link:https://en.wikipedia.org/wiki/Diff[diff] format. + +- `reverse` reverses the sides to be compared. + + +== Extra notes + +The extension is composed of two parts: an include processor and a preprocessor. + +The preprocessor add a document attribute, `doccontentref`, which it gives the Git reference for that content. +`doccontentref` is the difference between the process working directory (`Dir.pwd`) and the base directory of the content to be converted. +In case the process working directory is not entirely consistent for several reasons, you could give the attribute `rootdir` as the basis for the comparison. + +The include processor also accepts an empty target with the prefix (i.e., `include::git:[]`) as a shorthand for `include::git:{doccontentref}`. + + +== Example usage + +The following examples assume that `doccontentref` points to `content/posts/sample`. + +- `include::git:HEAD[path=shell.nix]` should transclude the current iteration of `shell.nix`. + +- `include::git:{doccontentref}[opts="diff", path=Gemfile]` should include a diff of the Gemfile from `content/posts/sample` branch (assuming that Gemfile has changed). + +- `include::git:non-existing-rev[opts="diff"]` should result in a warning with the non-existing revision. diff --git a/gems/lib/asciidoctor/git-blob-include-processor/extension.rb b/gems/lib/asciidoctor/git-blob-include-processor/extension.rb new file mode 100644 index 0000000..2b51c4e --- /dev/null +++ b/gems/lib/asciidoctor/git-blob-include-processor/extension.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +require 'rugged' + +class GitBlobIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor + def handles?(target) + target.start_with? 'git:' + end + + def process(doc, reader, target, attrs) + repo = Rugged::Repository.discover(__dir__) + + git_object_ref = target.delete_prefix 'git:' + git_object_ref = doc.attributes['doccontentref'] if git_object_ref.empty? + + begin + git_object = repo.rev_parse git_object_ref + + if attrs.key? 'diff-option' + options = {} + + options[:paths] = [attrs['path']] if attrs.key? 'path' + options[:context_lines] = attrs['context-lines'] if attrs.key? 'context-lines' + options[:reverse] = true if attrs.key? 'reverse-option' + + reader.push_include git_object.diff(**options).patch + else + inner_entry = case git_object.type + when :blob + git_object + when :commit + git_object.tree.path attrs['path'] + when :tree + git_object.path attrs['path'] + when :tag + git_object.target.tree.path attrs['path'] + end + + reader.push_include repo.lookup(inner_entry[:oid]).content + end + rescue StandardError => e + reader.push_include "Unresolved directive for '#{target}' with the following error:\n#{e}" + warn e + end + + reader + end +end + +class GitContentBranchAttributePreprocessor < Asciidoctor::Extensions::Preprocessor + def process(document, reader) + base_dir = Pathname.new(document.base_dir) + rootdir = if document.attributes['rootdir'].nil? + Dir.pwd + else + document.attributes['rootdir'] + end + + document.attributes['doccontentref'] = base_dir.relative_path_from(rootdir).to_s + reader + end +end