11#!/usr/bin/env python3
22
3+ """
4+ Program Name: Maid
5+ Usage: Aggregates Markdown files from directories and files.
6+ Author: Fabio Rotondo
7+ License: MIT License
8+ """
9+
10+
311import argparse
412import os
513import mimetypes
816from pathlib import Path
917import fnmatch
1018
19+ VERSION = "0.2.0"
20+
1121# default blacklist
1222BLACKLIST = [
1323 ".git" ,
2030]
2131
2232
33+ def _ext2markdown (fname ):
34+ ext = os .path .splitext (fname )[1 ]
35+ ext = ext .lower ()
36+ return {
37+ ".py" : "python" ,
38+ ".sh" : "bash" ,
39+ ".js" : "javascript" ,
40+ ".json" : "json" ,
41+ ".html" : "html" ,
42+ ".css" : "css" ,
43+ ".md" : "markdown" ,
44+ ".txt" : "text" ,
45+ ".yml" : "yaml" ,
46+ ".yaml" : "yaml" ,
47+ ".xml" : "xml" ,
48+ ".csv" : "csv" ,
49+ ".ts" : "typescript" ,
50+ ".sql" : "sql" ,
51+ ".java" : "java" ,
52+ ".c" : "c" ,
53+ ".cpp" : "cpp" ,
54+ ".h" : "c" ,
55+ ".hpp" : "cpp" ,
56+ ".cs" : "csharp" ,
57+ ".php" : "php" ,
58+ ".rb" : "ruby" ,
59+ ".go" : "go" ,
60+ ".rs" : "rust" ,
61+ ".kt" : "kotlin" ,
62+ ".swift" : "swift" ,
63+ ".m" : "objectivec" ,
64+ ".pl" : "perl" ,
65+ ".r" : "r" ,
66+ ".lua" : "lua" ,
67+ ".ps1" : "powershell" ,
68+ ".bat" : "batch" ,
69+ ".cmd" : "batch" ,
70+ ".psm1" : "powershell" ,
71+ ".psd1" : "powershell" ,
72+ ".ps1xml" : "powershell" ,
73+ ".pssc" : "powershell" ,
74+ ".psrc" : "powershell" ,
75+ ".gd" : "gdscript" ,
76+ ".tscn" : "gdscript" ,
77+ }.get (ext , "text" )
78+
79+
2380def is_binary (file_path ):
2481 """
2582 Check if a file is binary.
@@ -40,28 +97,25 @@ def process_file(file_path, markdown_content):
4097 if is_binary (file_path ):
4198 file_type = mimetypes .guess_type (file_path )[0 ] or "Unknown"
4299 file_size = file_path .stat ().st_size
43- markdown_content .append (
44- "---------------------------------------------------------------------\n "
45- )
46- markdown_content .append (f"## { file_path } \n " )
47- markdown_content .append (
48- "---------------------------------------------------------------------\n "
49- )
100+ markdown_content .append (("-" * 40 ) + "\n " )
101+ markdown_content .append (f"## FILE: { file_path } \n " )
50102 markdown_content .append (f"- Type: { file_type } \n " )
51103 markdown_content .append (f"- Size: { file_size } bytes\n \n " )
52104 else :
105+ logging .info (f"Processing file: { file_path } " )
106+
53107 with open (file_path , "r" , encoding = "utf-8" , errors = "ignore" ) as f :
54108 content = f .read ()
55- markdown_content .append (
56- "---------------------------------------------------------------------\n "
57- )
58- markdown_content .append (f"## { file_path } \n " )
59- markdown_content .append ("```\n " )
109+
110+ ext = _ext2markdown (file_path )
111+ markdown_content .append (("-" * 40 ) + "\n \n " )
112+ markdown_content .append (f"## FILE: { file_path } \n \n " )
113+ markdown_content .append ("```%s\n " % ext )
114+ if ext == "markdown" :
115+ content = content .replace ("```" , "'''" )
116+
60117 markdown_content .append (content )
61- markdown_content .append ("\n ```\n " )
62- markdown_content .append (
63- "---------------------------------------------------------------------\n \n \n "
64- )
118+ markdown_content .append ("```\n \n " )
65119
66120
67121def is_blacklisted (file_path , blacklist ):
@@ -78,22 +132,22 @@ def load_maidignore(directory):
78132 """
79133 maidignore_path = os .path .join (directory , ".maidignore" )
80134 if os .path .exists (maidignore_path ):
135+ logging .info (f"Found .maidignore in { directory } " )
81136 with open (maidignore_path , "r" ) as f :
82137 return [
83138 line .strip () for line in f if line .strip () and not line .startswith ("#" )
84139 ]
85140 return []
86141
87142
88- def scan_directory (directory , markdown_content , global_blacklist , use_local_maidignore ):
143+ def scan_directory (directory , markdown_content , global_blacklist ):
89144 """
90145 Recursively scan a directory and process all files.
91146 """
92147 for root , dirs , files in os .walk (directory ):
93148 local_blacklist = global_blacklist .copy ()
94149
95- if use_local_maidignore :
96- local_blacklist .extend (load_maidignore (root ))
150+ local_blacklist .extend (load_maidignore (root ))
97151
98152 # Filter out blacklisted directories
99153 dirs [:] = [d for d in dirs if not is_blacklisted (d , local_blacklist )]
@@ -114,12 +168,38 @@ def load_blacklist(blacklist_file):
114168 return [line .strip () for line in f if line .strip () and not line .startswith ("#" )]
115169
116170
171+ def _blacklist_init (args ):
172+ blacklist = args .blacklist
173+
174+ if args .blacklist_file :
175+ blacklist .extend (load_blacklist (args .blacklist_file ))
176+
177+ # load global blacklist from home directory '.maidignore' if exists
178+ home = str (Path .home ())
179+ dirs = [
180+ home ,
181+ os .path .join (home , ".maid" ),
182+ os .path .join (home , ".local" , "share" , "maid" ),
183+ os .path .join (home , ".config" , "maid" ),
184+ ]
185+ for d in dirs :
186+ global_blacklist = load_maidignore (d )
187+ blacklist .extend (global_blacklist )
188+
189+ return blacklist
190+
191+
117192def main ():
118193 parser = argparse .ArgumentParser (
119194 description = "Create an aggregated Markdown file from directories and files."
120195 )
121196 parser .add_argument ("paths" , nargs = "+" , help = "Directories or files to process" )
122- parser .add_argument ("-o" , "--output" , required = True , help = "Output Markdown file" )
197+ parser .add_argument (
198+ "-o" ,
199+ "--output" ,
200+ help = "Output Markdown file full path (default: _content.md)" ,
201+ default = "_content.md" ,
202+ )
123203 parser .add_argument ("--log" , action = "store_true" , help = "Enable logging" )
124204 parser .add_argument (
125205 "--blacklist" ,
@@ -130,11 +210,9 @@ def main():
130210 parser .add_argument (
131211 "--blacklist-file" , help = "File containing blacklist glob patterns"
132212 )
133- parser .add_argument (
134- "--local-maidignore" ,
135- action = "store_true" ,
136- help = "Enable reading of .maidignore files in directories" ,
137- )
213+
214+ parser .add_argument ("--version" , action = "version" , version = f"%(prog)s { VERSION } " )
215+
138216 args = parser .parse_args ()
139217
140218 if args .log :
@@ -144,19 +222,19 @@ def main():
144222 format = "%(asctime)s - %(levelname)s - %(message)s" ,
145223 )
146224
147- blacklist = args .blacklist
148-
149- if args .blacklist_file :
150- blacklist .extend (load_blacklist (args .blacklist_file ))
225+ blacklist = _blacklist_init (args )
151226
152- logging .info (f"Using global blacklist patterns: { blacklist } " )
227+ logging .info (f"Blacklist patterns: { blacklist } " )
153228
154- markdown_content = []
229+ markdown_content = [
230+ "# Content\n \n This file was generated by [Maid](https://github.com/fsoft72/maid) v%s - by [Fabio Rotondo](https://github.com/fsoft72)\n \n "
231+ % VERSION
232+ ]
155233
156234 for path in args .paths :
157235 if os .path .isdir (path ):
158236 logging .info (f"Scanning directory: { path } " )
159- scan_directory (path , markdown_content , blacklist , args . local_maidignore )
237+ scan_directory (path , markdown_content , blacklist )
160238 elif os .path .isfile (path ):
161239 if not is_blacklisted (path , blacklist ):
162240 logging .info (f"Processing file: { path } " )
0 commit comments