Repository: bwplotka/mdox
Pull request #99 (2022-12-12 20:30:29)

Upgrade markdownfmt, add opt-out for gofmt
markdownfmt v3 was recently released,
and among the changes is how Go source blocks are handled.
They're no longer automatically run through gofmt.
Instead, users may opt-into this functionality with the following option:

    markdown.WithCodeFormatters(markdown.GoCodeFormatter)

Since mdox prefers to have Go blocks auto-formatted,
this PR enables that by default,
and adds an opt-out flag (`--no-code-fmt`)
for users who don't want that functionality.

Supersedes #98


bwplotka: Nice! I remember we wanted or maybe it's there - the way to change gofmt formatting per code source basis too, do you remember @saswatamcode? 

Otherwise generally looks good, thanks!
abhinav: Hey, @bwplotka, thanks for looking.

> I remember we wanted or maybe it's there - the way to change gofmt formatting per code source basis too

Yeah, we discussed the possibility of that in #98. A good design for it in markdownfmt wasn't obvious so I deferred it at the time. After thinking about it more, I've got a couple points I'd like to make:

1. We should reconsider whether per-block opt-outs are actually a feature users will use. In my opinion, for the people who want this, it's an all-or-nothing situation: they either want all blocks reformatted or none of them.
    1. Just to add another use case, the reason the folks at [uber-go/fx](https://github.com/uber-go/fx) wanted full opt-out was this: code snippets in their [documentation](https://uber-go.github.io/fx/) are extracted from tested files and run through a [filter](https://github.com/uber-go/fx/blob/3a86277ab0e5d84cfee67a53b4a0f15c824a80a4/docs/bin/region) that transforms tabs to 2 spaces so as to reduce horizontal space lost to long lines. Having mdox reformat them means that some code samples (the ones that are completely valid code) end up reformatted back to tabs, and everything else remains using 2 spaces.
2. If we do want per-block opt-outs, it's my opinion that from the point of view of layer boundaries, markdownfmt should not try to interpret code block info strings. markdownfmt should try to be just a Markdown formatter/renderer. Selective formatting logic should be implemented inside mdox. One way to go about this is: mdox never enables markdownfmt's code formatting, and instead transforms the AST on its end, reformatting code blocks as needed, before calling markdownfmt to render the AST. That said, I don't think I have the time to implement this functionality in mdox right now.

Regardless of the above, I'd like to ask that you please consider this PR (with global opt-out support) separate from the discussion of per-block opt-outs. Global opt-outs do not obviate the possibility of per-block opt-in/opt-out as a feature. Even if we fully agreed on per-block opt-outs support, it would make sense for it to be a separate PR, and not part of this one.

Patch:
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index 798b7c6..7e94feb 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -15,14 +15,14 @@ jobs:
       GOBIN: /tmp/.bin
     steps:
       - name: Checkout code into the Go module directory.
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Install Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19.x
 
-      - uses: actions/cache@v1
+      - uses: actions/cache@v3
         with:
           path: ~/go/pkg/mod
           key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
diff --git a/.github/workflows/go.yaml b/.github/workflows/go.yaml
index 21666bc..890ec32 100644
--- a/.github/workflows/go.yaml
+++ b/.github/workflows/go.yaml
@@ -13,14 +13,14 @@ jobs:
     name: Linters (Static Analysis) for Go
     steps:
       - name: Checkout code into the Go module directory.
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Install Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: 1.19.x
 
-      - uses: actions/cache@v1
+      - uses: actions/cache@v3
         with:
           path: ~/go/pkg/mod
           key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
@@ -40,14 +40,14 @@ jobs:
     name: Unit tests on Go ${{ matrix.go }} ${{ matrix.platform }}
     steps:
       - name: Checkout code into the Go module directory.
-        uses: actions/checkout@v2
+        uses: actions/checkout@v3
 
       - name: Install Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: ${{ matrix.go }}
 
-      - uses: actions/cache@v1
+      - uses: actions/cache@v3
         with:
           path: ~/go/pkg/mod
           key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
diff --git a/README.md b/README.md
index bf0edc4..58f73f7 100644
--- a/README.md
+++ b/README.md
@@ -55,6 +55,7 @@ Flags:
                                  instead it will fail if files needs formatting
       --soft-wraps               If true, fmt will preserve soft line breaks for
                                  given files
+      --no-code-fmt              If true, don't reformat code snippets
       --code.disable-directives  If false, fmt will parse custom fenced
                                  code directives prefixed with 'mdox-gen' to
                                  autogenerate code snippets. For example:
@@ -70,7 +71,7 @@ Flags:
                                  If specified, all HTTP(s) links that target a
                                  domain and path matching given regexp will be
                                  transformed to relative to anchor dir path (if
-                                 exists).Absolute path links will be converted
+                                 exists). Absolute path links will be converted
                                  to relative links to anchor dir as well.
   -l, --links.validate           If true, all links will be validated
       --links.validate.config-file=<file-path>  
@@ -99,7 +100,7 @@ It's not uncommon that documentation is explaining code or configuration snippet
 For example this Readme contains `mdox --help` which is has to be auto generated on every PR:
 
 ```markdown
-``` bash mdox-exec="mdox fmt --help"
+```bash mdox-exec="mdox fmt --help"
 ...
 ```
 
@@ -113,7 +114,7 @@ This also enables auto updating snippets of code in code blocks using tools like
 Some commands might have non-zero exit codes. mdox will fail commands in such cases(otherwise errors might get formatted into markdown) but the expected exit code can also be passed as a code block directive! For example, below code block executes `go --help` which has 2 as its exit code,
 
 ```markdown
-```go mdox-exec="go --help" mdox-expect-exit-code=2
+```text mdox-exec="go --help" mdox-expect-exit-code=2
 ...
 ```
 
@@ -164,7 +165,7 @@ YAML can be passed in directly as well using `links.validate.config` flag! For m
 
 ### Link localization
 
-It is expected for documentation to contain remote links to the project website. However, in such cases, it creates problems for multi-version docs or multi-domain websites (links would need to be updated for each version which is cumbersome). Also, it would not be navigatable locally or through GitHub(would always redirect to the website) and requires additional link checking.
+It is expected for documentation to contain remote links to the project website. However, in such cases, it creates problems for multi-version docs or multi-domain websites (links would need to be updated for each version which is cumbersome). Also, it would not be navigatable locally or through GitHub (would always redirect to the website) and requires additional link checking.
 
 This is where the `links.localize.address-regex` flag comes in handy!
 
@@ -181,7 +182,7 @@ Just run `mdox transform --config-file=.mdox.yaml` and pass in YAML configuratio
 ```bash mdox-exec="mdox transform --help"
 usage: mdox transform [<flags>]
 
-Transform markdown files in various ways. For example pre process markdown files
+Transform markdown files in various ways. For example pre-process markdown files
 to allow it for use for popular static HTML websites based on markdown source
 code and front matter options.
 
@@ -275,11 +276,11 @@ YAML can be passed in directly as well using `--config` flag! For more details [
 
 Requirements to build this tool:
 
-* Go 1.15+
+* Go 1.19+
 * Linux or MacOS
 
 ```shell
-go get github.com/bwplotka/mdox && go mod tidy
+go install github.com/bwplotka/mdox@latest
 ```
 
 or via [bingo](https://github.com/bwplotka/bingo) if want to pin it:
diff --git a/go.mod b/go.mod
index 7e0514c..549bce9 100644
--- a/go.mod
+++ b/go.mod
@@ -3,10 +3,10 @@ module github.com/bwplotka/mdox
 go 1.19
 
 require (
-	github.com/Kunde21/markdownfmt/v2 v2.1.1-0.20210810103848-727f02f4c51c
+	github.com/Kunde21/markdownfmt/v3 v3.0.0
 	github.com/charmbracelet/glamour v0.5.0
-	github.com/efficientgo/tools/core v0.0.0-20210609125236-d73259166f20
-	github.com/efficientgo/tools/extkingpin v0.0.0-20210609125236-d73259166f20
+	github.com/efficientgo/core v1.0.0-rc.2
+	github.com/efficientgo/tools/extkingpin v0.0.0-20221005154214-b82254effbb1
 	github.com/fatih/structtag v1.2.0
 	github.com/felixge/fgprof v0.9.1
 	github.com/go-kit/kit v0.10.0
@@ -50,6 +50,7 @@ require (
 	github.com/go-logfmt/logfmt v0.5.0 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
 	github.com/gorilla/css v1.0.0 // indirect
 	github.com/kennygrant/sanitize v1.2.4 // indirect
@@ -64,7 +65,6 @@ require (
 	github.com/niklasfasching/go-org v1.6.5 // indirect
 	github.com/olekukonko/tablewriter v0.0.5 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.2 // indirect
-	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_model v0.2.0 // indirect
 	github.com/prometheus/procfs v0.7.3 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
@@ -75,11 +75,8 @@ require (
 	github.com/tdewolff/parse/v2 v2.6.0 // indirect
 	github.com/temoto/robotstxt v1.1.1 // indirect
 	github.com/yuin/goldmark-emoji v1.0.1 // indirect
-	go.uber.org/goleak v1.1.10 // indirect
-	golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
 	golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
 	golang.org/x/text v0.3.7 // indirect
-	golang.org/x/tools v0.1.11 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/protobuf v1.28.0 // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
diff --git a/go.sum b/go.sum
index 6c75674..1c1daa0 100644
--- a/go.sum
+++ b/go.sum
@@ -39,8 +39,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
-github.com/Kunde21/markdownfmt/v2 v2.1.1-0.20210810103848-727f02f4c51c h1:rnouiLtDKeaWKnxRViK454oCI8jkhWv5fItCvZ9nJOU=
-github.com/Kunde21/markdownfmt/v2 v2.1.1-0.20210810103848-727f02f4c51c/go.mod h1:LFJueuHZej/Z7Xhqh/XgClfkDjZiiEBOLVTt1Duq1r0=
+github.com/Kunde21/markdownfmt/v3 v3.0.0 h1:iRzqcuRSHTlZ4QPNAGgoJtfcwTDlPlupx+8OAFJpUx4=
+github.com/Kunde21/markdownfmt/v3 v3.0.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc=
 github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
 github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@@ -128,10 +128,10 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
 github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
-github.com/efficientgo/tools/core v0.0.0-20210609125236-d73259166f20 h1:3wGt9Bx/h2D/0yOlYIhWB7GmsFBPG9vWDg90UUGgn8c=
-github.com/efficientgo/tools/core v0.0.0-20210609125236-d73259166f20/go.mod h1:OmVcnJopJL8d3X3sSXTiypGoUSgFq1aDGmlrdi9dn/M=
-github.com/efficientgo/tools/extkingpin v0.0.0-20210609125236-d73259166f20 h1:kM/ALyvAnTrwSB+nlKqoKaDnZbInp1YImZvW+gtHwc8=
-github.com/efficientgo/tools/extkingpin v0.0.0-20210609125236-d73259166f20/go.mod h1:ZV0utlglOczUWv3ih2AbqPSoLoFzdplUYxwV62eZi6Q=
+github.com/efficientgo/core v1.0.0-rc.2 h1:7j62qHLnrZqO3V3UA0AqOGd5d5aXV3AX6m/NZBHp78I=
+github.com/efficientgo/core v1.0.0-rc.2/go.mod h1:FfGdkzWarkuzOlY04VY+bGfb1lWrjaL6x/GLcQ4vJps=
+github.com/efficientgo/tools/extkingpin v0.0.0-20221005154214-b82254effbb1 h1:TbuaQwgvyQ68dpiy95Dm6tiwJASUVD1RCIxfmRhiAeY=
+github.com/efficientgo/tools/extkingpin v0.0.0-20221005154214-b82254effbb1/go.mod h1:ZV0utlglOczUWv3ih2AbqPSoLoFzdplUYxwV62eZi6Q=
 github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -223,7 +223,8 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -478,8 +479,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
 github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 github.com/tdewolff/parse/v2 v2.6.0 h1:f2D7w32JtqjCv6SczWkfwK+m15et42qEtDnZXHoNY70=
 github.com/tdewolff/parse/v2 v2.6.0/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
 github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
@@ -496,7 +497,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
 github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0=
 github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -514,8 +514,6 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
-go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
@@ -554,8 +552,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
 golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -722,7 +718,6 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -756,8 +751,6 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
-golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/main.go b/main.go
index 0f00f91..f25a733 100644
--- a/main.go
+++ b/main.go
@@ -24,8 +24,8 @@ import (
 	"github.com/bwplotka/mdox/pkg/transform"
 	"github.com/bwplotka/mdox/pkg/version"
 	"github.com/charmbracelet/glamour"
-	"github.com/efficientgo/tools/core/pkg/errcapture"
-	"github.com/efficientgo/tools/core/pkg/logerrcapture"
+	"github.com/efficientgo/core/errcapture"
+	"github.com/efficientgo/core/logerrcapture"
 	extflag "github.com/efficientgo/tools/extkingpin"
 	"github.com/felixge/fgprof"
 	"github.com/go-kit/kit/log"
@@ -124,7 +124,6 @@ func main() {
 		level.Error(logger).Log("err", errors.Wrapf(err, "%s command failed", cmd))
 		os.Exit(1)
 	}
-
 }
 
 func snapshotProfiles(dir string) (func() error, error) {
@@ -203,12 +202,13 @@ func registerFmt(_ context.Context, app *extkingpin.App, metricsPath *string) {
 	files := cmd.Arg("files", "Markdown file(s) to process.").Required().ExistingFiles()
 	checkOnly := cmd.Flag("check", "If true, fmt will not modify the given files, instead it will fail if files needs formatting").Bool()
 	softWraps := cmd.Flag("soft-wraps", "If true, fmt will preserve soft line breaks for given files").Bool()
+	noCodeFmt := cmd.Flag("no-code-fmt", "If true, don't reformat code snippets").Bool()
 
 	disableGenCodeBlocksDirectives := cmd.Flag("code.disable-directives", `If false, fmt will parse custom fenced code directives prefixed with 'mdox-gen' to autogenerate code snippets. For example:
 	`+"```"+`<lang> mdox-exec="<executable + arguments>"
 This directive runs executable with arguments and put its stderr and stdout output inside code block content, replacing existing one.`).Bool()
 	anchorDir := cmd.Flag("anchor-dir", "Anchor directory for all transformers. PWD is used if flag is not specified.").ExistingDir()
-	linksLocalizeForAddress := cmd.Flag("links.localize.address-regex", "If specified, all HTTP(s) links that target a domain and path matching given regexp will be transformed to relative to anchor dir path (if exists)."+
+	linksLocalizeForAddress := cmd.Flag("links.localize.address-regex", "If specified, all HTTP(s) links that target a domain and path matching given regexp will be transformed to relative to anchor dir path (if exists). "+
 		"Absolute path links will be converted to relative links to anchor dir as well.").Regexp()
 	linksValidateEnabled := cmd.Flag("links.validate", "If true, all links will be validated").Short('l').Bool()
 	linksValidateConfig := extflag.RegisterPathOrContent(cmd, "links.validate.config", "YAML file for skipping link check, with spec defined in github.com/bwplotka/mdox/pkg/linktransformer.ValidatorConfig", extflag.WithEnvSubstitution())
@@ -228,6 +228,9 @@ This directive runs executable with arguments and put its stderr and stdout outp
 		if *softWraps {
 			opts = append(opts, mdformatter.WithSoftWraps())
 		}
+		if *noCodeFmt {
+			opts = append(opts, mdformatter.WithNoCodeFmt())
+		}
 		if len(*files) == 0 {
 			return errors.New("no files to format")
 		}
@@ -332,7 +335,7 @@ func validateAnchorDir(anchorDir string, files []string) (_ string, err error) {
 }
 
 func registerTransform(_ context.Context, app *extkingpin.App) {
-	cmd := app.Command("transform", "Transform markdown files in various ways. For example pre process markdown files to allow it for use for popular static HTML websites based on markdown source code and front matter options.")
+	cmd := app.Command("transform", "Transform markdown files in various ways. For example pre-process markdown files to allow it for use for popular static HTML websites based on markdown source code and front matter options.")
 	cfg := extflag.RegisterPathOrContent(cmd, "config", "Path to the YAML file with spec defined in github.com/bwplotka/mdox/pkg/transform.Config", extflag.WithEnvSubstitution())
 	cmd.Run(func(ctx context.Context, logger log.Logger) error {
 		validateConfig, err := cfg.Content()
diff --git a/pkg/clilog/clilog.go b/pkg/clilog/clilog.go
index a23fd62..5c7e943 100644
--- a/pkg/clilog/clilog.go
+++ b/pkg/clilog/clilog.go
@@ -21,7 +21,7 @@ import (
 	"reflect"
 	"sync"
 
-	"github.com/efficientgo/tools/core/pkg/merrors"
+	"github.com/efficientgo/core/merrors"
 	"github.com/go-kit/kit/log"
 )
 
diff --git a/pkg/mdformatter/linktransformer/link.go b/pkg/mdformatter/linktransformer/link.go
index 9c492c0..24040f8 100644
--- a/pkg/mdformatter/linktransformer/link.go
+++ b/pkg/mdformatter/linktransformer/link.go
@@ -22,7 +22,7 @@ import (
 
 	"github.com/bwplotka/mdox/pkg/cache"
 	"github.com/bwplotka/mdox/pkg/mdformatter"
-	"github.com/efficientgo/tools/core/pkg/merrors"
+	"github.com/efficientgo/core/merrors"
 	"github.com/go-kit/kit/log"
 	"github.com/go-kit/kit/log/level"
 	"github.com/gocolly/colly/v2"
diff --git a/pkg/mdformatter/mdformatter.go b/pkg/mdformatter/mdformatter.go
index 07b4ee4..fa89bca 100644
--- a/pkg/mdformatter/mdformatter.go
+++ b/pkg/mdformatter/mdformatter.go
@@ -11,10 +11,10 @@ import (
 	"sort"
 	"time"
 
-	"github.com/Kunde21/markdownfmt/v2/markdown"
+	"github.com/Kunde21/markdownfmt/v3/markdown"
 	"github.com/bwplotka/mdox/pkg/gitdiff"
-	"github.com/efficientgo/tools/core/pkg/logerrcapture"
-	"github.com/efficientgo/tools/core/pkg/merrors"
+	"github.com/efficientgo/core/logerrcapture"
+	"github.com/efficientgo/core/merrors"
 	"github.com/go-kit/kit/log"
 	"github.com/gohugoio/hugo/parser/pageparser"
 	"github.com/mattn/go-isatty"
@@ -83,9 +83,10 @@ type Formatter struct {
 	bm   BackMatterTransformer
 	link LinkTransformer
 	cb   CodeBlockTransformer
+	reg  *prometheus.Registry
 
-	reg       *prometheus.Registry
 	softWraps bool
+	noCodeFmt bool
 }
 
 // Option is a functional option type for Formatter objects.
@@ -133,6 +134,13 @@ func WithSoftWraps() Option {
 	}
 }
 
+// WithNoCodeFmt specifies that we shouldn't reformat code snippets.
+func WithNoCodeFmt() Option {
+	return func(m *Formatter) {
+		m.noCodeFmt = true
+	}
+}
+
 func New(ctx context.Context, opts ...Option) *Formatter {
 	f := &Formatter{
 		ctx: ctx,
@@ -395,6 +403,10 @@ func (f *Formatter) Format(file *os.File, out io.Writer) error {
 	if f.softWraps {
 		renderer.AddMarkdownOptions(markdown.WithSoftWraps())
 	}
+	// Enable Go code reformatting unless --no-code-fmt is set.
+	if !f.noCodeFmt {
+		renderer.AddMarkdownOptions(markdown.WithCodeFormatters(markdown.GoCodeFormatter))
+	}
 	tr := &transformer{
 		wrapped:   renderer,
 		sourceCtx: sourceCtx,
diff --git a/pkg/mdformatter/mdgen/mdgen.go b/pkg/mdformatter/mdgen/mdgen.go
index d00a8f8..d179108 100644
--- a/pkg/mdformatter/mdgen/mdgen.go
+++ b/pkg/mdformatter/mdgen/mdgen.go
@@ -83,10 +83,13 @@ func (t *genCodeBlockTransformer) TransformCodeBlock(ctx mdformatter.SourceConte
 		cmd.Stdout = &b
 		if err := cmd.Run(); err != nil {
 			expectedCode, _ := strconv.Atoi(infoStringAttr[infoStringKeyExitCode])
-			if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == expectedCode {
-				return b.Bytes(), nil
+			if exitErr, ok := err.(*exec.ExitError); ok {
+				if exitErr.ExitCode() != expectedCode {
+					return nil, errors.Wrapf(err, "run %v, expected exit code %v, got %v, out: %v", execCmd, expectedCode, exitErr.ExitCode(), b.String())
+				}
+			} else {
+				return nil, errors.Wrapf(err, "run %v, out: %v", execCmd, b.String())
 			}
-			return nil, errors.Wrapf(err, "run %v, out: %v", execCmd, b.String())
 		}
 		output := b.Bytes()
 		// Add newline to output if not present.
diff --git a/pkg/mdformatter/transformer.go b/pkg/mdformatter/transformer.go
index e804a22..43ada32 100644
--- a/pkg/mdformatter/transformer.go
+++ b/pkg/mdformatter/transformer.go
@@ -9,7 +9,7 @@ import (
 	"regexp"
 	"strconv"
 
-	"github.com/efficientgo/tools/core/pkg/merrors"
+	"github.com/efficientgo/core/merrors"
 	"github.com/yuin/goldmark/ast"
 	"github.com/yuin/goldmark/renderer"
 	"github.com/yuin/goldmark/text"
diff --git a/pkg/transform/transform.go b/pkg/transform/transform.go
index bd5e131..a4faf53 100644
--- a/pkg/transform/transform.go
+++ b/pkg/transform/transform.go
@@ -13,7 +13,7 @@ import (
 	"strings"
 
 	"github.com/bwplotka/mdox/pkg/mdformatter"
-	"github.com/efficientgo/tools/core/pkg/errcapture"
+	"github.com/efficientgo/core/errcapture"
 	"github.com/go-kit/kit/log"
 	"github.com/go-kit/kit/log/level"
 	"github.com/pkg/errors"


Test patch:
diff --git a/main_test.go b/main_test.go
index 97b4ba2..9816f19 100644
--- a/main_test.go
+++ b/main_test.go
@@ -7,7 +7,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/efficientgo/tools/core/pkg/testutil"
+	"github.com/efficientgo/core/testutil"
 )
 
 func TestValidateAnchorDir(t *testing.T) {
diff --git a/pkg/mdformatter/linktransformer/link_bench_test.go b/pkg/mdformatter/linktransformer/link_bench_test.go
index 084581b..4c7559e 100644
--- a/pkg/mdformatter/linktransformer/link_bench_test.go
+++ b/pkg/mdformatter/linktransformer/link_bench_test.go
@@ -11,7 +11,7 @@ import (
 	"testing"
 
 	"github.com/bwplotka/mdox/pkg/mdformatter"
-	"github.com/efficientgo/tools/core/pkg/testutil"
+	"github.com/efficientgo/core/testutil"
 	"github.com/go-kit/kit/log"
 )
 
diff --git a/pkg/mdformatter/linktransformer/link_test.go b/pkg/mdformatter/linktransformer/link_test.go
index c1d7042..3eabdba 100644
--- a/pkg/mdformatter/linktransformer/link_test.go
+++ b/pkg/mdformatter/linktransformer/link_test.go
@@ -14,7 +14,7 @@ import (
 
 	"github.com/bwplotka/mdox/pkg/cache"
 	"github.com/bwplotka/mdox/pkg/mdformatter"
-	"github.com/efficientgo/tools/core/pkg/testutil"
+	"github.com/efficientgo/core/testutil"
 	"github.com/go-kit/kit/log"
 	_ "github.com/mattn/go-sqlite3"
 )
diff --git a/pkg/mdformatter/mdformatter_bench_test.go b/pkg/mdformatter/mdformatter_bench_test.go
index 40a9204..bd99bb1 100644
--- a/pkg/mdformatter/mdformatter_bench_test.go
+++ b/pkg/mdformatter/mdformatter_bench_test.go
@@ -9,7 +9,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/efficientgo/tools/core/pkg/testutil"
+	"github.com/efficientgo/core/testutil"
 )
 
 var testBuf bytes.Buffer
diff --git a/pkg/mdformatter/mdformatter_test.go b/pkg/mdformatter/mdformatter_test.go
index 656726e..787c9c9 100644
--- a/pkg/mdformatter/mdformatter_test.go
+++ b/pkg/mdformatter/mdformatter_test.go
@@ -9,7 +9,7 @@ import (
 	"os"
 	"testing"
 
-	"github.com/efficientgo/tools/core/pkg/testutil"
+	"github.com/efficientgo/core/testutil"
 	"github.com/go-kit/kit/log"
 )
 
@@ -147,3 +147,35 @@ func TestCheck_SoftWraps(t *testing.T) {
 	testutil.Ok(t, err)
 	testutil.Equals(t, string(exp), diff.String())
 }
+
+func TestFormatBadlyFormattedGoCode(t *testing.T) {
+	t.Run("Format WithNoCodeFmt", func(t *testing.T) {
+		file, err := os.OpenFile("testdata/badly_formatted_go_code.md", os.O_RDONLY, 0)
+		testutil.Ok(t, err)
+		defer file.Close()
+
+		f := New(context.Background(), WithNoCodeFmt())
+
+		exp, err := os.ReadFile("testdata/badly_formatted_go_code.md")
+		testutil.Ok(t, err)
+
+		buf := bytes.Buffer{}
+		testutil.Ok(t, f.Format(file, &buf))
+		testutil.Equals(t, string(exp), buf.String())
+	})
+
+	t.Run("Format", func(t *testing.T) {
+		file, err := os.OpenFile("testdata/badly_formatted_go_code.md", os.O_RDONLY, 0)
+		testutil.Ok(t, err)
+		defer file.Close()
+
+		f := New(context.Background())
+
+		exp, err := os.ReadFile("testdata/badly_formatted_go_code.reformatted.md")
+		testutil.Ok(t, err)
+
+		buf := bytes.Buffer{}
+		testutil.Ok(t, f.Format(file, &buf))
+		testutil.Equals(t, string(exp), buf.String())
+	})
+}
diff --git a/pkg/mdformatter/mdgen/mdgen_bench_test.go b/pkg/mdformatter/mdgen/mdgen_bench_test.go
index 106fe4a..5668539 100644
--- a/pkg/mdformatter/mdgen/mdgen_bench_test.go
+++ b/pkg/mdformatter/mdgen/mdgen_bench_test.go
@@ -10,7 +10,7 @@ import (
 	"testing"
 
 	"github.com/bwplotka/mdox/pkg/mdformatter"
-	"github.com/efficientgo/tools/core/pkg/testutil"
+	"github.com/efficientgo/core/testutil"
 )
 
 var testBuf bytes.Buffer
diff --git a/pkg/mdformatter/mdgen/mdgen_test.go b/pkg/mdformatter/mdgen/mdgen_test.go
index dcc6c32..e7837a7 100644
--- a/pkg/mdformatter/mdgen/mdgen_test.go
+++ b/pkg/mdformatter/mdgen/mdgen_test.go
@@ -10,7 +10,7 @@ import (
 	"testing"
 
 	"github.com/bwplotka/mdox/pkg/mdformatter"
-	"github.com/efficientgo/tools/core/pkg/testutil"
+	"github.com/efficientgo/core/testutil"
 )
 
 func TestFormat_FormatSingle_CodeBlockTransformer(t *testing.T) {
diff --git a/pkg/mdformatter/mdgen/testdata/mdgen_formatted.md b/pkg/mdformatter/mdgen/testdata/mdgen_formatted.md
index 5e61df8..726adbd 100755
--- a/pkg/mdformatter/mdgen/testdata/mdgen_formatted.md
+++ b/pkg/mdformatter/mdgen/testdata/mdgen_formatted.md
@@ -34,7 +34,7 @@ test output3
 ```bash mdox-exec="sed -n '1,3p' ./testdata/out3.sh"
 #!/usr/bin/env bash
 
-echo "test output3"
+echo -n "test output3"
 ```
 
 ```yaml mdox-exec="bash ./testdata/out2.sh --name=queryfrontend.InMemoryResponseCacheConfig"
@@ -45,6 +45,6 @@ newline
 ```bash mdox-exec="cat ./testdata/out3.sh"
 #!/usr/bin/env bash
 
-echo "test output3"
+echo -n "test output3"
 exit 2
 ```
diff --git a/pkg/mdformatter/mdgen/testdata/out3.sh b/pkg/mdformatter/mdgen/testdata/out3.sh
index eafec28..953dfd0 100755
--- a/pkg/mdformatter/mdgen/testdata/out3.sh
+++ b/pkg/mdformatter/mdgen/testdata/out3.sh
@@ -1,4 +1,4 @@
 #!/usr/bin/env bash
 
-echo "test output3"
+echo -n "test output3"
 exit 2
\ No newline at end of file
diff --git a/pkg/mdformatter/testdata/badly_formatted_go_code.md b/pkg/mdformatter/testdata/badly_formatted_go_code.md
new file mode 100644
index 0000000..cfdaef9
--- /dev/null
+++ b/pkg/mdformatter/testdata/badly_formatted_go_code.md
@@ -0,0 +1,17 @@
+This file contains a poorly formatted Go source file.
+
+```go
+package main
+
+import "net/http"
+
+func main() {
+  // Using two spaces for indentation.
+  fmt.Println(
+    "intentionally",
+      "poorly",
+        "formatted",)
+}
+```
+
+Next paragraph.
diff --git a/pkg/mdformatter/testdata/badly_formatted_go_code.reformatted.md b/pkg/mdformatter/testdata/badly_formatted_go_code.reformatted.md
new file mode 100644
index 0000000..c6faa3d
--- /dev/null
+++ b/pkg/mdformatter/testdata/badly_formatted_go_code.reformatted.md
@@ -0,0 +1,17 @@
+This file contains a poorly formatted Go source file.
+
+```go
+package main
+
+import "net/http"
+
+func main() {
+	// Using two spaces for indentation.
+	fmt.Println(
+		"intentionally",
+		"poorly",
+		"formatted")
+}
+```
+
+Next paragraph.
diff --git a/pkg/transform/transform_e2e_test.go b/pkg/transform/transform_e2e_test.go
index 6a3c590..a53aabc 100644
--- a/pkg/transform/transform_e2e_test.go
+++ b/pkg/transform/transform_e2e_test.go
@@ -11,7 +11,7 @@ import (
 	"testing"
 
 	"github.com/bwplotka/mdox/pkg/transform"
-	"github.com/efficientgo/tools/core/pkg/testutil"
+	"github.com/efficientgo/core/testutil"
 	"github.com/go-kit/kit/log"
 	"github.com/pkg/errors"
 )
diff --git a/pkg/transform/transform_test.go b/pkg/transform/transform_test.go
index 0aea616..77bc162 100644
--- a/pkg/transform/transform_test.go
+++ b/pkg/transform/transform_test.go
@@ -7,7 +7,7 @@ import (
 	"fmt"
 	"testing"
 
-	"github.com/efficientgo/tools/core/pkg/testutil"
+	"github.com/efficientgo/core/testutil"
 	"github.com/pkg/errors"
 )