github.com/unstoppablemango/fenced

Parse code fences from anywhere


Keywords
fence, go, markdown, md, parser, tool
License
MIT
Install
go get github.com/unstoppablemango/fenced

Documentation

fenced

CI codecov Go Report Card License Go Version

Parse code fences from text.

Usage

Install

go install github.com/unstoppablemango/fenced@latest

Or add as a tool in your go.mod:

go get -tool github.com/unstoppablemango/fenced

Or download a binary from GitHub Releases:

# Linux/macOS
curl -L https://github.com/UnstoppableMango/fenced/releases/latest/download/fenced_$(uname -s)_$(uname -m).tar.gz | tar xz
mkdir -p ~/.local/bin && mv fenced ~/.local/bin/

CLI

Parse fenced code blocks from a file:

$ fenced testdata/markdown.md
import "fmt"

func main() {
	fmt.Println("Hello, World!")
}

Parse from multiple files:

$ fenced file1.md file2.md file3.md
# Output from all files concatenated

Add a delimiter between code blocks:

$ fenced -d --- file1.md file2.md
# First block
---
# Second block
---
# Third block

An implicit newline is appended after the delimiter by default. Use -N/--no-implicit-newline to disable this:

$ fenced -d --- -N file1.md file2.md
# First block
---# Second block
---# Third block

Or pipe content to stdin:

$ cat testdata/markdown.md | fenced
import "fmt"

func main() {
	fmt.Println("Hello, World!")
}

Use - to explicitly read from stdin (useful in scripts or mixing with files):

$ echo '```bash
echo "Hello"
```' | fenced -
echo "Hello"

$ fenced file1.md - file2.md < input.md
# Reads file1.md, then stdin, then file2.md

Nix

Run directly without installing:

nix run github:UnstoppableMango/fenced -- testdata/markdown.md

Install into your profile:

nix profile install github:UnstoppableMango/fenced

As a flake (NixOS configuration):

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    fenced.url = "github:UnstoppableMango/fenced";
  };

  outputs = { nixpkgs, fenced, ... }: {
    nixosConfigurations.mySystem = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [{
        environment.systemPackages = [ fenced.packages.x86_64-linux.default ];
      }];
    };
  };
}

Or use in a devShell:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    fenced.url = "github:UnstoppableMango/fenced";
  };

  outputs = { nixpkgs, fenced, ... }:
    let
      system = "x86_64-linux";
      pkgs = nixpkgs.legacyPackages.${system};
    in {
      devShells.${system}.default = pkgs.mkShell {
        packages = [ fenced.packages.${system}.default ];
      };
    };
}

Docker

docker run -v $(pwd):/data ghcr.io/unstoppablemango/fenced:latest /data/testdata/markdown.md

Or with stdin:

cat testdata/markdown.md | docker run -i ghcr.io/unstoppablemango/fenced:latest

Library

GoDoc

import (
    "fmt"
    "io/fs"
    "os"

    fenced "github.com/unstoppablemango/fenced/pkg"
)

func main() {
    f, err := os.Open("testdata/markdown.md")
    if err != nil {
        panic(err)
    }
    defer f.Close()

    blocks, err := fenced.Parse(f)
    if err != nil {
        panic(err)
    }

    for _, b := range blocks {
        fmt.Println(b)
    }
}

Write a single block to an io.Writer:

import (
    "os"

    fenced "github.com/unstoppablemango/fenced/pkg"
)

func main() {
    block := fenced.Block{
        Lang:    "go",
        Content: "fmt.Println(\"Hello, World!\")\n",
    }

    if _, err := fenced.Write(os.Stdout, block); err != nil {
        panic(err)
    }
}

Write multiple blocks with a delimiter using WriteAll:

import (
    "os"

    fenced "github.com/unstoppablemango/fenced/pkg"
)

func main() {
    f, err := os.Open("testdata/markdown.md")
    if err != nil {
        panic(err)
    }
    defer f.Close()

    blocks, err := fenced.Parse(f)
    if err != nil {
        panic(err)
    }

    if _, err := fenced.WriteAll(os.Stdout, blocks, fenced.WithDelimiter("---")); err != nil {
        panic(err)
    }
}