Última atividade 1755161877

glance.bash Bruto
1# glance - Provides a quick and intelligent overview of a directory's contents.
2#
3# Authors: anduin
4#
5# Features:
6# - Auto-detects Git repositories to intelligently list files.
7# - Dynamically calculates and adjusts the `tree` view depth to prevent clutter.
8# - File content display strictly adheres to the calculated tree depth.
9# - Limits the maximum lines printed per file to avoid long scrolls.
10# - Supports powerful filtering by file glob patterns.
11#
12# Usage:
13# glance [DIRECTORY] [OPTIONS...]
14#
15# Options:
16# --max-files-preview <count> : Max items (files+dirs) in tree view. (Default: 100)
17# --max-file-lines <count> : Max lines to print per file. (Default: 150)
18# --filter <patterns> : Comma-separated glob patterns to filter files (e.g., '*.md,*.cs')
19#
20glance() {
21 local target_dir="."
22 local max_items_preview=100
23 local max_file_lines=150
24 local filter_patterns=""
25 local positional_args=()
26
27 # --- Internal helper function to process and print a single file ---
28 _glance_process_file() {
29 local file="$1"
30 if [ ! -f "$file" ]; then return; fi
31
32 # Check if it's a text file
33 if file -b --mime-type "$file" | grep -q '^text/'; then
34 local total_lines
35 total_lines=$(wc -l < "$file") # More efficient line count
36
37 local extension="${file##*.}"; local lang=""
38 case "$extension" in
39 js) lang="javascript" ;; py) lang="python" ;; rb) lang="ruby" ;;
40 sh) lang="bash" ;; css) lang="css" ;; html) lang="html" ;;
41 json) lang="json" ;; md) lang="markdown" ;; yml|yaml) lang="yaml" ;;
42 java) lang="java" ;; c) lang="c" ;; cpp) lang="cpp" ;; go) lang="go" ;;
43 *) lang="" ;;
44 esac
45
46 echo
47 echo "Content of file \"$file\":"
48 echo "\`\`\`$lang"
49
50 if [ "$total_lines" -gt "$max_file_lines" ]; then
51 head -n "$max_file_lines" "$file"
52 echo
53 echo "... (Only shown top $max_file_lines lines of $total_lines) ..."
54 else
55 cat "$file"
56 fi
57
58 echo
59 echo "\`\`\`"
60 echo "----------------------------------------------------------"
61 else
62 echo
63 echo "--> Skipped binary file: \"$file\""
64 echo "----------------------------------------------------------"
65 fi
66 }
67
68 # --- Step 1: Parse arguments ---
69 while [[ $# -gt 0 ]]; do
70 case "$1" in
71 --max-files-preview)
72 if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then max_items_preview="$2"; shift 2;
73 else echo "Error: '--max-files-preview' requires a positive integer argument." >&2; return 1; fi ;;
74 --max-file-lines)
75 if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then max_file_lines="$2"; shift 2;
76 else echo "Error: '--max-file-lines' requires a positive integer argument." >&2; return 1; fi ;;
77 --filter)
78 if [[ -n "$2" && "$2" != -* ]]; then filter_patterns="$2"; shift 2;
79 else echo "Error: '--filter' requires a pattern string argument." >&2; return 1; fi ;;
80 -*) echo "Error: Unknown option '$1'" >&2; return 1 ;;
81 *) positional_args+=("$1"); shift ;;
82 esac
83 done
84 [[ ${#positional_args[@]} -gt 0 ]] && target_dir="${positional_args[0]}"
85
86 # --- Step 2: Check dependencies and directory validity ---
87 if ! command -v tree &>/dev/null; then echo "Error: 'tree' command not found. Please install it first." >&2; return 1; fi
88 if [ ! -d "$target_dir" ]; then echo "Error: Directory '$target_dir' does not exist." >&2; return 1; fi
89
90 echo "=========================================================="
91 echo "🔎 Overview for directory: $(realpath "$target_dir")"
92 echo "=========================================================="
93 echo
94
95 # --- Step 3: Dynamically calculate the optimal tree depth (based on the full, unfiltered directory) ---
96 echo "🌳 Directory Structure:"
97 local best_depth=1; local max_depth_to_check=6; local prev_item_count=-1
98 for depth in $(seq 1 $max_depth_to_check); do
99 local current_item_count; current_item_count=$(tree -L "$depth" -a --noreport "$target_dir" | wc -l)
100 if [ "$depth" -gt 1 ] && [ "$current_item_count" -gt "$max_items_preview" ]; then
101 best_depth=$((depth - 1)); break;
102 fi
103 # If item count stops increasing, we've reached the maximum effective depth
104 if [ "$depth" -gt 1 ] && [ "$current_item_count" -eq "$prev_item_count" ]; then
105 best_depth=$depth; break;
106 fi
107 best_depth=$depth; prev_item_count=$current_item_count
108 done
109
110 # --- Step 3.5: Apply filter and print the tree ---
111 local tree_args=(-L "$best_depth")
112 if [ -n "$filter_patterns" ]; then
113 local tree_pattern; tree_pattern=$(echo "$filter_patterns" | tr ',' '|')
114 tree_args+=(-P "$tree_pattern")
115 echo "(Filter active: ${filter_patterns})"
116 fi
117 echo "(Dynamic depth set to L${best_depth} to fit item limit: ${max_items_preview})"
118 tree "${tree_args[@]}" "$target_dir"
119 echo
120
121 # --- Step 4: Iterate and print file contents (applying filters and depth limits) ---
122 local header_suffix=""
123 if [ -n "$filter_patterns" ]; then header_suffix=" (filter: ${filter_patterns})"; fi
124 echo "📄 File Content Details (within depth L${best_depth}, max ${max_file_lines} lines per file${header_suffix}):"
125 (
126 cd "$target_dir" || exit 1
127 echo "----------------------------------------------------------"
128
129 local filter_array=()
130 [ -n "$filter_patterns" ] && IFS=',' read -ra filter_array <<< "$filter_patterns"
131
132 if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
133 # Git mode
134 local git_files
135 if [ ${#filter_array[@]} -gt 0 ]; then
136 git_files=$(git ls-files -- "${filter_array[@]}")
137 else
138 git_files=$(git ls-files)
139 fi
140 # Use awk to filter paths by slash count to simulate depth limit
141 echo "$git_files" | awk -F'/' -v depth="$best_depth" 'NF <= depth' | while read -r file; do
142 _glance_process_file "$file"
143 done
144 else
145 # Normal mode
146 local find_args=(-maxdepth "$best_depth" -type f)
147 if [ ${#filter_array[@]} -gt 0 ]; then
148 find_args+=("(")
149 for i in "${!filter_array[@]}"; do
150 [ "$i" -ne 0 ] && find_args+=("-o")
151 find_args+=(-name "${filter_array[$i]}")
152 done
153 find_args+=(")")
154 fi
155 # Use find's -maxdepth parameter and name patterns
156 find . "${find_args[@]}" | while read -r file; do
157 # Remove leading './' from file path for cleaner output
158 _glance_process_file "${file#./}"
159 done
160 fi
161 )
162}