diff --git a/README.md b/README.md
index b9d355a..43968e7 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
## Install
```sh
-go install github.com/croaky/mdembed
+go install github.com/croaky/mdembed@latest
```
For that to work, Go must be installed and
@@ -26,27 +26,38 @@ In `input1.md`:
Embed a whole file:
```embed
-f1.rb
+dir1/f1.rb
```
Embed multiple whole files:
```embed
-f1.rb
-f2.go
+dir1/f1.rb
+dir1/f2.go
```
Embed specific lines in a file:
```embed
-f3.js log
+dir1/subdir1/f3.js log
```
-Embed multiple blocks within the same file:
+Embed multiple whole files and multiple blocks within files:
```embed
-f4.html h1
-f4.html ul
+dir1/f1.rb
+dir1/f2.go log
+dir2/f4.css h1
+dir2/subdir2/f5.html h1
+dir2/subdir2/f5.html ul
+f6.sql users
+```
+
+Embed files using globs and blocks:
+
+```embed
+**/*.rb
+**/*.js log
```
Embed Markdown files and their embeds recursively:
@@ -56,13 +67,13 @@ input2.md
```
````
-In `f1.rb`:
+In `dir1/f1.rb`:
```rb
puts "hi"
```
-In `f2.go`:
+In `dir1/f2.go`:
```go
package main
@@ -74,7 +85,7 @@ func main() {
}
```
-In `f3.js`:
+In `dir1/subdir1/f3.js`:
```js
console.log("Not embedded");
@@ -86,7 +97,7 @@ console.log("hi");
console.log("Not embedded");
```
-In `f4.html`:
+In `dir2/subdir2/f5.html`:
```html
@@ -117,7 +128,7 @@ In `input2.md`:
Embed from within an embedded Markdown file:
```embed
-f1.rb
+dir1/f1.rb
```
````
@@ -135,50 +146,89 @@ The output will be:
Embed a whole file:
```rb
-# f1.rb
+# dir1/f1.rb
puts "hi"
```
Embed multiple whole files:
```rb
-# f1.rb
+# dir1/f1.rb
puts "hi"
```
```go
-// f2.go
+// dir1/f2.go
package main
import "fmt"
func main() {
- fmt.Println("hi")
+ // emdo log
+ fmt.Println("hi")
+ // emdone log
}
```
Embed specific lines in a file:
```js
-// f3.js
+// dir1/subdir1/f3.js
console.log("hi");
```
-Embed multiple blocks within the same file:
+Embed multiple whole files and multiple blocks within files:
+
+```rb
+# dir1/f1.rb
+puts "hi"
+```
+
+```go
+// dir1/f2.go
+fmt.Println("hi")
+```
+
+```css
+/* dir2/f4.css */
+h1 {
+ color: blue;
+}
+```
```html
-
+
h1
```
```html
-
+
```
+```sql
+-- f6.sql
+SELECT
+ *
+FROM
+ users;
+```
+
+Embed files using globs and blocks:
+
+```rb
+# dir1/f1.rb
+puts "hi"
+```
+
+```js
+// dir1/subdir1/f3.js
+console.log("hi");
+```
+
Embed Markdown files and their embeds recursively:
## Input2
@@ -186,7 +236,7 @@ Embed Markdown files and their embeds recursively:
Embed from within an embedded Markdown file:
```rb
-# f1.rb
+# dir1/f1.rb
puts "hi"
```
````
@@ -197,6 +247,8 @@ puts "hi"
removing surrounding whitespace.
The file extension is used as the code fence attribute.
+It parses exact file paths or file glob patterns.
+
If `emdo` and `emdone` magic comments were used, it will only embed the code
block wrapped by the magic comments.
diff --git a/examples/f1.rb b/examples/dir1/f1.rb
similarity index 100%
rename from examples/f1.rb
rename to examples/dir1/f1.rb
diff --git a/examples/f2.go b/examples/dir1/f2.go
similarity index 100%
rename from examples/f2.go
rename to examples/dir1/f2.go
diff --git a/examples/f3.js b/examples/dir1/subdir1/f3.js
similarity index 100%
rename from examples/f3.js
rename to examples/dir1/subdir1/f3.js
diff --git a/examples/f4.css b/examples/dir2/f4.css
similarity index 100%
rename from examples/f4.css
rename to examples/dir2/f4.css
diff --git a/examples/f5.html b/examples/dir2/subdir2/f5.html
similarity index 100%
rename from examples/f5.html
rename to examples/dir2/subdir2/f5.html
diff --git a/examples/input1.md b/examples/input1.md
index b12ded6..d74eb7a 100644
--- a/examples/input1.md
+++ b/examples/input1.md
@@ -3,33 +3,40 @@
Embed a whole file:
```embed
-f1.rb
+dir1/f1.rb
```
Embed multiple whole files:
```embed
-f1.rb
-f2.go
+dir1/f1.rb
+dir1/f2.go
```
Embed specific lines in a file:
```embed
-f3.js log
+dir1/subdir1/f3.js log
```
Embed multiple whole files and multiple blocks within files:
```embed
-f1.rb
-f2.go log
-f4.css h1
-f5.html h1
-f5.html ul
+dir1/f1.rb
+dir1/f2.go log
+dir2/f4.css h1
+dir2/subdir2/f5.html h1
+dir2/subdir2/f5.html ul
f6.sql users
```
+Embed files using globs and blocks:
+
+```embed
+**/*.rb
+**/*.js log
+```
+
Embed Markdown files and their embeds recursively:
```embed
diff --git a/examples/input2.md b/examples/input2.md
index f476e08..1d7772c 100644
--- a/examples/input2.md
+++ b/examples/input2.md
@@ -3,5 +3,5 @@
Embed from within an embedded Markdown file:
```embed
-f1.rb
+dir1/f1.rb
```
diff --git a/examples/outwant.md b/examples/outwant.md
index 639c87e..0f17ffc 100644
--- a/examples/outwant.md
+++ b/examples/outwant.md
@@ -3,19 +3,19 @@
Embed a whole file:
```rb
-# f1.rb
+# dir1/f1.rb
puts "hi"
```
Embed multiple whole files:
```rb
-# f1.rb
+# dir1/f1.rb
puts "hi"
```
```go
-// f2.go
+// dir1/f2.go
package main
import "fmt"
@@ -30,36 +30,36 @@ func main() {
Embed specific lines in a file:
```js
-// f3.js
+// dir1/subdir1/f3.js
console.log("hi");
```
Embed multiple whole files and multiple blocks within files:
```rb
-# f1.rb
+# dir1/f1.rb
puts "hi"
```
```go
-// f2.go
+// dir1/f2.go
fmt.Println("hi")
```
```css
-/* f4.css */
+/* dir2/f4.css */
h1 {
color: blue;
}
```
```html
-
+
h1
```
```html
-
+
- 1
- 2
@@ -74,6 +74,18 @@ FROM
users;
```
+Embed files using globs and blocks:
+
+```rb
+# dir1/f1.rb
+puts "hi"
+```
+
+```js
+// dir1/subdir1/f3.js
+console.log("hi");
+```
+
Embed Markdown files and their embeds recursively:
## Input2
@@ -81,6 +93,6 @@ Embed Markdown files and their embeds recursively:
Embed from within an embedded Markdown file:
```rb
-# f1.rb
+# dir1/f1.rb
puts "hi"
```
diff --git a/go.mod b/go.mod
index 3e38849..75c3605 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,5 @@
module github.com/croaky/mdembed
go 1.23.5
+
+require github.com/bmatcuk/doublestar/v4 v4.8.1
diff --git a/go.sum b/go.sum
index e69de29..7faa1ee 100644
--- a/go.sum
+++ b/go.sum
@@ -0,0 +1,2 @@
+github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
+github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
diff --git a/main.go b/main.go
index 0b30046..f10680d 100644
--- a/main.go
+++ b/main.go
@@ -4,9 +4,13 @@ import (
"bufio"
"fmt"
"io"
+ "io/fs"
"os"
+ "path"
"path/filepath"
"strings"
+
+ "github.com/bmatcuk/doublestar/v4"
)
var styles = map[string]Style{
@@ -146,8 +150,8 @@ func processEmbed(lines []string, output io.Writer, state *ProcessState) error {
continue
}
- filename := parts[0] // Required filename
- blockName := "" // Optional block name
+ pattern := parts[0] // Can be a filename or a glob pattern
+ blockName := "" // Optional block name
if len(parts) == 2 {
blockName = parts[1]
@@ -155,17 +159,48 @@ func processEmbed(lines []string, output io.Writer, state *ProcessState) error {
return fmt.Errorf("invalid format in embed code block: %s", line)
}
- content, err := os.ReadFile(filename)
+ // ensure pattern uses forward slashes
+ pattern = filepath.ToSlash(pattern)
+ // clean up the pattern to remove any ./ or ../
+ pattern = path.Clean(pattern)
+
+ // use doublestar.Glob with fs.FS
+ fsys := os.DirFS(".")
+
+ // support recursive glob patterns using the new doublestar v4 API
+ matches, err := doublestar.Glob(fsys, pattern)
if err != nil {
- return fmt.Errorf("failed to read file %s: %v", filename, err)
+ return fmt.Errorf("failed to glob pattern %s: %v", pattern, err)
}
- fileContent := string(content)
- if err := processFile(filename, blockName, fileContent, output, state); err != nil {
- return err
+ if len(matches) == 0 {
+ return fmt.Errorf("no files match pattern %s", pattern)
+ }
+
+ for j, match := range matches {
+ filename := match // relative to fs.FS root
+
+ // Read the file content from fsys
+ content, err := fs.ReadFile(fsys, filename)
+ if err != nil {
+ return fmt.Errorf("failed to read file %s: %v", filename, err)
+ }
+ fileContent := string(content)
+
+ // Adjust filename to include OS-specific path separators for display
+ displayFilename := filepath.FromSlash(filename)
+
+ if err := processFile(displayFilename, blockName, fileContent, output, state); err != nil {
+ return err
+ }
+
+ // Add newline between multiple code blocks
+ if j < len(matches)-1 {
+ fmt.Fprintln(output)
+ }
}
- // Add newline between multiple code blocks
+ // Add newline between multiple patterns
if i < len(lines)-1 {
fmt.Fprintln(output)
}
diff --git a/main_test.go b/main_test.go
index d87295c..a245327 100644
--- a/main_test.go
+++ b/main_test.go
@@ -77,7 +77,7 @@ func TestProcessMD_ErrorCases(t *testing.T) {
{
name: "File not found",
input: "Some text before.\n\n```embed\nnonexistent.go\n```\n",
- wantErrMsg: "failed to read file nonexistent.go",
+ wantErrMsg: "no files match pattern nonexistent.go",
},
{
name: "Unsupported file type",