├── .envrc
├── .github
└── workflows
│ └── rust.yml
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── default.nix
├── flake.lock
├── flake.nix
├── images
└── scrot.png
└── src
├── args.rs
├── display.rs
├── hist_file.rs
└── main.rs
/.envrc:
--------------------------------------------------------------------------------
1 | use flake
2 |
--------------------------------------------------------------------------------
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: Rust
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Run check
20 | run: cargo check --verbose
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | .direnv
3 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "aho-corasick"
7 | version = "0.7.20"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
10 | dependencies = [
11 | "memchr",
12 | ]
13 |
14 | [[package]]
15 | name = "autocfg"
16 | version = "1.1.0"
17 | source = "registry+https://github.com/rust-lang/crates.io-index"
18 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
19 |
20 | [[package]]
21 | name = "bitflags"
22 | version = "1.3.2"
23 | source = "registry+https://github.com/rust-lang/crates.io-index"
24 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
25 |
26 | [[package]]
27 | name = "cc"
28 | version = "1.0.78"
29 | source = "registry+https://github.com/rust-lang/crates.io-index"
30 | checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
31 |
32 | [[package]]
33 | name = "cfg-if"
34 | version = "1.0.0"
35 | source = "registry+https://github.com/rust-lang/crates.io-index"
36 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
37 |
38 | [[package]]
39 | name = "clap"
40 | version = "4.0.32"
41 | source = "registry+https://github.com/rust-lang/crates.io-index"
42 | checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
43 | dependencies = [
44 | "bitflags",
45 | "clap_derive",
46 | "clap_lex",
47 | "is-terminal",
48 | "once_cell",
49 | "strsim",
50 | "termcolor",
51 | ]
52 |
53 | [[package]]
54 | name = "clap_derive"
55 | version = "4.0.21"
56 | source = "registry+https://github.com/rust-lang/crates.io-index"
57 | checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
58 | dependencies = [
59 | "heck",
60 | "proc-macro-error",
61 | "proc-macro2",
62 | "quote",
63 | "syn",
64 | ]
65 |
66 | [[package]]
67 | name = "clap_lex"
68 | version = "0.3.0"
69 | source = "registry+https://github.com/rust-lang/crates.io-index"
70 | checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
71 | dependencies = [
72 | "os_str_bytes",
73 | ]
74 |
75 | [[package]]
76 | name = "crossterm"
77 | version = "0.26.1"
78 | source = "registry+https://github.com/rust-lang/crates.io-index"
79 | checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13"
80 | dependencies = [
81 | "bitflags",
82 | "crossterm_winapi",
83 | "libc",
84 | "mio",
85 | "parking_lot",
86 | "signal-hook",
87 | "signal-hook-mio",
88 | "winapi",
89 | ]
90 |
91 | [[package]]
92 | name = "crossterm_winapi"
93 | version = "0.9.0"
94 | source = "registry+https://github.com/rust-lang/crates.io-index"
95 | checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
96 | dependencies = [
97 | "winapi",
98 | ]
99 |
100 | [[package]]
101 | name = "errno"
102 | version = "0.2.8"
103 | source = "registry+https://github.com/rust-lang/crates.io-index"
104 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
105 | dependencies = [
106 | "errno-dragonfly",
107 | "libc",
108 | "winapi",
109 | ]
110 |
111 | [[package]]
112 | name = "errno-dragonfly"
113 | version = "0.1.2"
114 | source = "registry+https://github.com/rust-lang/crates.io-index"
115 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
116 | dependencies = [
117 | "cc",
118 | "libc",
119 | ]
120 |
121 | [[package]]
122 | name = "heck"
123 | version = "0.4.0"
124 | source = "registry+https://github.com/rust-lang/crates.io-index"
125 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
126 |
127 | [[package]]
128 | name = "hermit-abi"
129 | version = "0.2.6"
130 | source = "registry+https://github.com/rust-lang/crates.io-index"
131 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
132 | dependencies = [
133 | "libc",
134 | ]
135 |
136 | [[package]]
137 | name = "io-lifetimes"
138 | version = "1.0.3"
139 | source = "registry+https://github.com/rust-lang/crates.io-index"
140 | checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
141 | dependencies = [
142 | "libc",
143 | "windows-sys 0.42.0",
144 | ]
145 |
146 | [[package]]
147 | name = "is-terminal"
148 | version = "0.4.2"
149 | source = "registry+https://github.com/rust-lang/crates.io-index"
150 | checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
151 | dependencies = [
152 | "hermit-abi",
153 | "io-lifetimes",
154 | "rustix",
155 | "windows-sys 0.42.0",
156 | ]
157 |
158 | [[package]]
159 | name = "libc"
160 | version = "0.2.139"
161 | source = "registry+https://github.com/rust-lang/crates.io-index"
162 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
163 |
164 | [[package]]
165 | name = "linux-raw-sys"
166 | version = "0.1.4"
167 | source = "registry+https://github.com/rust-lang/crates.io-index"
168 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
169 |
170 | [[package]]
171 | name = "lock_api"
172 | version = "0.4.9"
173 | source = "registry+https://github.com/rust-lang/crates.io-index"
174 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
175 | dependencies = [
176 | "autocfg",
177 | "scopeguard",
178 | ]
179 |
180 | [[package]]
181 | name = "log"
182 | version = "0.4.17"
183 | source = "registry+https://github.com/rust-lang/crates.io-index"
184 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
185 | dependencies = [
186 | "cfg-if",
187 | ]
188 |
189 | [[package]]
190 | name = "memchr"
191 | version = "2.5.0"
192 | source = "registry+https://github.com/rust-lang/crates.io-index"
193 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
194 |
195 | [[package]]
196 | name = "mio"
197 | version = "0.8.6"
198 | source = "registry+https://github.com/rust-lang/crates.io-index"
199 | checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
200 | dependencies = [
201 | "libc",
202 | "log",
203 | "wasi",
204 | "windows-sys 0.45.0",
205 | ]
206 |
207 | [[package]]
208 | name = "muc"
209 | version = "0.1.0"
210 | dependencies = [
211 | "clap",
212 | "crossterm",
213 | "regex",
214 | "utf8_slice",
215 | ]
216 |
217 | [[package]]
218 | name = "once_cell"
219 | version = "1.16.0"
220 | source = "registry+https://github.com/rust-lang/crates.io-index"
221 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
222 |
223 | [[package]]
224 | name = "os_str_bytes"
225 | version = "6.4.1"
226 | source = "registry+https://github.com/rust-lang/crates.io-index"
227 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
228 |
229 | [[package]]
230 | name = "parking_lot"
231 | version = "0.12.1"
232 | source = "registry+https://github.com/rust-lang/crates.io-index"
233 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
234 | dependencies = [
235 | "lock_api",
236 | "parking_lot_core",
237 | ]
238 |
239 | [[package]]
240 | name = "parking_lot_core"
241 | version = "0.9.7"
242 | source = "registry+https://github.com/rust-lang/crates.io-index"
243 | checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
244 | dependencies = [
245 | "cfg-if",
246 | "libc",
247 | "redox_syscall",
248 | "smallvec",
249 | "windows-sys 0.45.0",
250 | ]
251 |
252 | [[package]]
253 | name = "proc-macro-error"
254 | version = "1.0.4"
255 | source = "registry+https://github.com/rust-lang/crates.io-index"
256 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
257 | dependencies = [
258 | "proc-macro-error-attr",
259 | "proc-macro2",
260 | "quote",
261 | "syn",
262 | "version_check",
263 | ]
264 |
265 | [[package]]
266 | name = "proc-macro-error-attr"
267 | version = "1.0.4"
268 | source = "registry+https://github.com/rust-lang/crates.io-index"
269 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
270 | dependencies = [
271 | "proc-macro2",
272 | "quote",
273 | "version_check",
274 | ]
275 |
276 | [[package]]
277 | name = "proc-macro2"
278 | version = "1.0.49"
279 | source = "registry+https://github.com/rust-lang/crates.io-index"
280 | checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
281 | dependencies = [
282 | "unicode-ident",
283 | ]
284 |
285 | [[package]]
286 | name = "quote"
287 | version = "1.0.23"
288 | source = "registry+https://github.com/rust-lang/crates.io-index"
289 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
290 | dependencies = [
291 | "proc-macro2",
292 | ]
293 |
294 | [[package]]
295 | name = "redox_syscall"
296 | version = "0.2.16"
297 | source = "registry+https://github.com/rust-lang/crates.io-index"
298 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
299 | dependencies = [
300 | "bitflags",
301 | ]
302 |
303 | [[package]]
304 | name = "regex"
305 | version = "1.7.0"
306 | source = "registry+https://github.com/rust-lang/crates.io-index"
307 | checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
308 | dependencies = [
309 | "aho-corasick",
310 | "memchr",
311 | "regex-syntax",
312 | ]
313 |
314 | [[package]]
315 | name = "regex-syntax"
316 | version = "0.6.28"
317 | source = "registry+https://github.com/rust-lang/crates.io-index"
318 | checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
319 |
320 | [[package]]
321 | name = "rustix"
322 | version = "0.36.5"
323 | source = "registry+https://github.com/rust-lang/crates.io-index"
324 | checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
325 | dependencies = [
326 | "bitflags",
327 | "errno",
328 | "io-lifetimes",
329 | "libc",
330 | "linux-raw-sys",
331 | "windows-sys 0.42.0",
332 | ]
333 |
334 | [[package]]
335 | name = "scopeguard"
336 | version = "1.1.0"
337 | source = "registry+https://github.com/rust-lang/crates.io-index"
338 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
339 |
340 | [[package]]
341 | name = "signal-hook"
342 | version = "0.3.15"
343 | source = "registry+https://github.com/rust-lang/crates.io-index"
344 | checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
345 | dependencies = [
346 | "libc",
347 | "signal-hook-registry",
348 | ]
349 |
350 | [[package]]
351 | name = "signal-hook-mio"
352 | version = "0.2.3"
353 | source = "registry+https://github.com/rust-lang/crates.io-index"
354 | checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
355 | dependencies = [
356 | "libc",
357 | "mio",
358 | "signal-hook",
359 | ]
360 |
361 | [[package]]
362 | name = "signal-hook-registry"
363 | version = "1.4.1"
364 | source = "registry+https://github.com/rust-lang/crates.io-index"
365 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
366 | dependencies = [
367 | "libc",
368 | ]
369 |
370 | [[package]]
371 | name = "smallvec"
372 | version = "1.10.0"
373 | source = "registry+https://github.com/rust-lang/crates.io-index"
374 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
375 |
376 | [[package]]
377 | name = "strsim"
378 | version = "0.10.0"
379 | source = "registry+https://github.com/rust-lang/crates.io-index"
380 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
381 |
382 | [[package]]
383 | name = "syn"
384 | version = "1.0.107"
385 | source = "registry+https://github.com/rust-lang/crates.io-index"
386 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
387 | dependencies = [
388 | "proc-macro2",
389 | "quote",
390 | "unicode-ident",
391 | ]
392 |
393 | [[package]]
394 | name = "termcolor"
395 | version = "1.1.3"
396 | source = "registry+https://github.com/rust-lang/crates.io-index"
397 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
398 | dependencies = [
399 | "winapi-util",
400 | ]
401 |
402 | [[package]]
403 | name = "unicode-ident"
404 | version = "1.0.6"
405 | source = "registry+https://github.com/rust-lang/crates.io-index"
406 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
407 |
408 | [[package]]
409 | name = "utf8_slice"
410 | version = "1.0.0"
411 | source = "registry+https://github.com/rust-lang/crates.io-index"
412 | checksum = "44109e280ecf0f5d8e6ee671c0ee650008275c51dc9e494badd11fb39d785408"
413 |
414 | [[package]]
415 | name = "version_check"
416 | version = "0.9.4"
417 | source = "registry+https://github.com/rust-lang/crates.io-index"
418 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
419 |
420 | [[package]]
421 | name = "wasi"
422 | version = "0.11.0+wasi-snapshot-preview1"
423 | source = "registry+https://github.com/rust-lang/crates.io-index"
424 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
425 |
426 | [[package]]
427 | name = "winapi"
428 | version = "0.3.9"
429 | source = "registry+https://github.com/rust-lang/crates.io-index"
430 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
431 | dependencies = [
432 | "winapi-i686-pc-windows-gnu",
433 | "winapi-x86_64-pc-windows-gnu",
434 | ]
435 |
436 | [[package]]
437 | name = "winapi-i686-pc-windows-gnu"
438 | version = "0.4.0"
439 | source = "registry+https://github.com/rust-lang/crates.io-index"
440 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
441 |
442 | [[package]]
443 | name = "winapi-util"
444 | version = "0.1.5"
445 | source = "registry+https://github.com/rust-lang/crates.io-index"
446 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
447 | dependencies = [
448 | "winapi",
449 | ]
450 |
451 | [[package]]
452 | name = "winapi-x86_64-pc-windows-gnu"
453 | version = "0.4.0"
454 | source = "registry+https://github.com/rust-lang/crates.io-index"
455 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
456 |
457 | [[package]]
458 | name = "windows-sys"
459 | version = "0.42.0"
460 | source = "registry+https://github.com/rust-lang/crates.io-index"
461 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
462 | dependencies = [
463 | "windows_aarch64_gnullvm",
464 | "windows_aarch64_msvc",
465 | "windows_i686_gnu",
466 | "windows_i686_msvc",
467 | "windows_x86_64_gnu",
468 | "windows_x86_64_gnullvm",
469 | "windows_x86_64_msvc",
470 | ]
471 |
472 | [[package]]
473 | name = "windows-sys"
474 | version = "0.45.0"
475 | source = "registry+https://github.com/rust-lang/crates.io-index"
476 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
477 | dependencies = [
478 | "windows-targets",
479 | ]
480 |
481 | [[package]]
482 | name = "windows-targets"
483 | version = "0.42.2"
484 | source = "registry+https://github.com/rust-lang/crates.io-index"
485 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
486 | dependencies = [
487 | "windows_aarch64_gnullvm",
488 | "windows_aarch64_msvc",
489 | "windows_i686_gnu",
490 | "windows_i686_msvc",
491 | "windows_x86_64_gnu",
492 | "windows_x86_64_gnullvm",
493 | "windows_x86_64_msvc",
494 | ]
495 |
496 | [[package]]
497 | name = "windows_aarch64_gnullvm"
498 | version = "0.42.2"
499 | source = "registry+https://github.com/rust-lang/crates.io-index"
500 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
501 |
502 | [[package]]
503 | name = "windows_aarch64_msvc"
504 | version = "0.42.2"
505 | source = "registry+https://github.com/rust-lang/crates.io-index"
506 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
507 |
508 | [[package]]
509 | name = "windows_i686_gnu"
510 | version = "0.42.2"
511 | source = "registry+https://github.com/rust-lang/crates.io-index"
512 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
513 |
514 | [[package]]
515 | name = "windows_i686_msvc"
516 | version = "0.42.2"
517 | source = "registry+https://github.com/rust-lang/crates.io-index"
518 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
519 |
520 | [[package]]
521 | name = "windows_x86_64_gnu"
522 | version = "0.42.2"
523 | source = "registry+https://github.com/rust-lang/crates.io-index"
524 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
525 |
526 | [[package]]
527 | name = "windows_x86_64_gnullvm"
528 | version = "0.42.2"
529 | source = "registry+https://github.com/rust-lang/crates.io-index"
530 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
531 |
532 | [[package]]
533 | name = "windows_x86_64_msvc"
534 | version = "0.42.2"
535 | source = "registry+https://github.com/rust-lang/crates.io-index"
536 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
537 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "muc"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [dependencies]
7 | clap = { version = "4.0.32", features = ["derive"] }
8 | utf8_slice = "1.0.0"
9 | regex = "1.7.0"
10 | crossterm = "0.26.1"
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The GPLv3 License (GPLv3)
2 |
3 | Copyright (c) 2022 Nathan Dawit
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MUC
2 | Visualize your most used commands
3 |
4 | 
5 |
6 | ## Usage
7 |
8 | ### Installing
9 |
10 | #### Arch users (AUR)
11 |
12 | Use your favorite AUR helper to install [muc-git](https://aur.archlinux.org/packages/muc-git) package (or build manually using `git` and `makepkg -si`), for example: `paru -S muc-git`
13 |
14 | #### Nix
15 |
16 | You can use the outputs provided by the `flake.nix` inside this repository to install `muc`. Either with the `overlays.default` output for your system configuration, or the package output to imperatively install it with `nix install github:nate-sys/muc` or create an ad-hoc shell with `nix shell github:nate-sys/muc`.
17 |
18 | To quicky run muc use following command.
19 | ```sh
20 | nix run github:nate-sys/muc
21 | ```
22 |
23 | #### Other distros
24 |
25 | ```sh
26 | cargo install --git=https://github.com/nate-sys/muc
27 | ```
28 |
29 | ### Running
30 |
31 | muc uses your $HISTFILE environment variable to get your history
32 | ```sh
33 | muc # Bash or Vanilla zsh
34 | muc --shell ohmyzsh # ohmyzsh
35 | muc --shell fish # Fish
36 | muc --regexp # parse the histfile yourself (this overrides shell)
37 |
38 | muc -c 5 # show top 5 instead of the default 10
39 |
40 | muc --bar "=,*,-,=" # change the appearance of the bar =*****-----=
41 | ```
42 |
43 |
44 | ### Roadmap
45 | - [X] Colors
46 | - [X] Customizable bar
47 | - [ ] Resolve aliases
48 | - [ ] Recognize leader commands (sudo, doas, git, etc)
49 |
--------------------------------------------------------------------------------
/default.nix:
--------------------------------------------------------------------------------
1 | (import (fetchTarball https://github.com/edolstra/flake-compat/archive/master.tar.gz) {
2 | src = builtins.fetchGit ./.;
3 | }).defaultNix
4 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-compat": {
4 | "flake": false,
5 | "locked": {
6 | "lastModified": 1668681692,
7 | "narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
8 | "owner": "edolstra",
9 | "repo": "flake-compat",
10 | "rev": "009399224d5e398d03b22badca40a37ac85412a1",
11 | "type": "github"
12 | },
13 | "original": {
14 | "owner": "edolstra",
15 | "repo": "flake-compat",
16 | "type": "github"
17 | }
18 | },
19 | "flake-utils": {
20 | "locked": {
21 | "lastModified": 1667395993,
22 | "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
23 | "owner": "numtide",
24 | "repo": "flake-utils",
25 | "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
26 | "type": "github"
27 | },
28 | "original": {
29 | "owner": "numtide",
30 | "repo": "flake-utils",
31 | "type": "github"
32 | }
33 | },
34 | "nixpkgs": {
35 | "locked": {
36 | "lastModified": 1671722432,
37 | "narHash": "sha256-ojcZUekIQeOZkHHzR81st7qxX99dB1Eaaq6PU5MNeKc=",
38 | "owner": "nixos",
39 | "repo": "nixpkgs",
40 | "rev": "652e92b8064949a11bc193b90b74cb727f2a1405",
41 | "type": "github"
42 | },
43 | "original": {
44 | "owner": "nixos",
45 | "ref": "nixos-unstable",
46 | "repo": "nixpkgs",
47 | "type": "github"
48 | }
49 | },
50 | "root": {
51 | "inputs": {
52 | "flake-compat": "flake-compat",
53 | "flake-utils": "flake-utils",
54 | "nixpkgs": "nixpkgs"
55 | }
56 | }
57 | },
58 | "root": "root",
59 | "version": 7
60 | }
61 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4 | flake-utils.url = "github:numtide/flake-utils";
5 | flake-compat = {
6 | url = "github:edolstra/flake-compat";
7 | flake = false;
8 | };
9 |
10 | };
11 |
12 | outputs = { self, nixpkgs, flake-utils, flake-compat }: {
13 | overlays.default = _: prev:
14 | let
15 | inherit (prev.rustPlatform) buildRustPackage;
16 | toml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
17 | in
18 | {
19 | muc = buildRustPackage {
20 | pname = "muc";
21 | src = self;
22 | inherit (toml.package) version;
23 | cargoHash = "sha256-w/b4qps4z1HrtSlSklflU6i1QfSFQw20+uALIpCIk8I=";
24 | };
25 | };
26 | } //
27 | (flake-utils.lib.eachDefaultSystem (system:
28 | let
29 | pkgs = import nixpkgs {
30 | inherit system;
31 | overlays = [ self.overlays.default ];
32 | };
33 | inherit (pkgs) muc;
34 | in
35 | {
36 | packages = {
37 | inherit muc;
38 | default = muc;
39 | };
40 | }));
41 | }
42 |
--------------------------------------------------------------------------------
/images/scrot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nthnd/muc/d8336bc2f387c79b33c87cf2aba28d3c93486af2/images/scrot.png
--------------------------------------------------------------------------------
/src/args.rs:
--------------------------------------------------------------------------------
1 | use std::{str::FromStr, path::PathBuf};
2 |
3 | use clap::Parser;
4 |
5 | #[derive(Parser, Debug)]
6 | #[command(author, version, about, long_about = None)]
7 | pub struct Args {
8 | /// The path to the file to be parsed
9 | #[arg(short, long)]
10 | pub file: Option,
11 |
12 | /// Display top n commands
13 | #[arg(short, long, default_value_t = 10)]
14 | pub count: usize,
15 |
16 | /// Show debug messages
17 | #[arg(long, default_value_t = false)]
18 | pub debug: bool,
19 |
20 | /// Change how the bar looks --bar [,▮, ,]
21 | #[arg(long, default_value_t = Default::default())]
22 | pub bar: Bar,
23 |
24 |
25 | /// Preset regular expressions for common shells: Bash, ZSH, Fish.
26 | #[arg(long, default_value_t = String::from(""))]
27 | pub shell: String,
28 |
29 | /// Regular expression to allow for the removal of prefixes in shells like zsh. Default value is for zsh. NOTE: overrides the shell arg
30 | #[arg(short, long, default_value_t = String::from(""))]
31 | pub regexp: String,
32 | }
33 |
34 | #[derive(Debug, Clone)]
35 | pub struct Bar {
36 | pub opening: String,
37 | pub closing: String,
38 | pub fill: String,
39 | pub empty: String,
40 | }
41 | impl Default for Bar {
42 | fn default() -> Self {
43 | Bar {
44 | opening: "[".to_owned(),
45 | fill: "▮".to_owned(),
46 | empty: " ".to_owned(),
47 | closing: "]".to_owned(),
48 | }
49 | }
50 | }
51 |
52 | impl FromStr for Bar {
53 | type Err = String;
54 |
55 | fn from_str(s: &str) -> Result {
56 | let chars = s.split(',').collect::>();
57 | match chars.len() {
58 | 4 => Ok(Bar {
59 | opening: chars[0].to_string(),
60 | fill: chars[1].to_string(),
61 | empty: chars[2].to_string(),
62 | closing: chars[3].to_string(),
63 | }),
64 | _ => Err("Invalid bar length".to_string()),
65 | }
66 | }
67 | }
68 |
69 | impl std::fmt::Display for Bar {
70 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 | write!(
72 | f,
73 | "{},{},{},{}",
74 | self.opening, self.fill, self.empty, self.closing
75 | )
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/display.rs:
--------------------------------------------------------------------------------
1 | use crossterm::{
2 | execute, queue,
3 | style::{Color, Print, PrintStyledContent, ResetColor, SetForegroundColor, Stylize},
4 | };
5 | use std::{
6 | collections::{BTreeMap, HashMap},
7 | io::{stdout, Stdout, Write},
8 | usize,
9 | };
10 | use utf8_slice::slice;
11 |
12 | use crate::{hist_file::CommandMap, Args};
13 |
14 | type VeryComplexType = (String, Option, HashMap);
15 | pub fn print(data: CommandMap, args: Args) {
16 | let tree: BTreeMap = data
17 | .into_iter()
18 | .map(|(s, (f, o, h))| (f, (s, o, h)))
19 | .collect();
20 |
21 | let total: usize = tree.keys().sum();
22 | if total == 0 {
23 | println!("No commands found");
24 | return;
25 | }
26 | let max = *tree.last_key_value().unwrap().0;
27 |
28 | let reversed_tree: Vec<(usize, VeryComplexType)> = tree.into_iter().rev().collect();
29 | let limited_tree = reversed_tree[..(usize::min(args.count, reversed_tree.len()))].to_vec();
30 |
31 | let mut stdout = stdout();
32 |
33 | for (freq, elem) in limited_tree.iter() {
34 | let (s, _o, h) = elem;
35 | let mut sub_commands = h.iter().collect::>();
36 | sub_commands.sort_by(|a, b| b.1.cmp(a.1));
37 |
38 | let sub_commands = if sub_commands.is_empty() {
39 | None
40 | } else {
41 | Some(
42 | sub_commands[..(usize::min(3, sub_commands.len()))]
43 | .iter()
44 | .map(|x| x.0.to_owned())
45 | .collect(),
46 | )
47 | };
48 |
49 | print_command(s, *freq, max, total, &args, sub_commands, &mut stdout);
50 | }
51 |
52 | stdout.flush().unwrap();
53 |
54 | let others = total - limited_tree.iter().fold(0, |acc, x| acc + x.0);
55 | let other_percentage = (others as f64 / total as f64) * 100.;
56 | execute! {
57 | stdout,
58 | SetForegroundColor(Color::Grey),
59 | Print(format!("...{} ({:.2}%) others\n", others, other_percentage)),
60 | ResetColor,
61 | }
62 | .unwrap();
63 | execute! {
64 | stdout,
65 | Print(format!("Total: {} commands\n", total))
66 | }
67 | .unwrap();
68 | }
69 |
70 | pub fn print_command(
71 | command: &str,
72 | invocations: usize,
73 | max: usize,
74 | total: usize,
75 | args: &Args,
76 | sub_commands: Option>,
77 | stdout: &mut Stdout,
78 | ) {
79 | let percentage = (invocations as f32 / total as f32) * 100.0;
80 | let num_of_bars = ((invocations as f32 / max as f32) * 10.) as usize;
81 | let bar: String = format!(
82 | "{}{}",
83 | args.bar.fill.repeat(num_of_bars),
84 | args.bar.empty.repeat(10 - num_of_bars)
85 | );
86 | let pretty_sub_commands = if let Some(sub_commands) = sub_commands {
87 | format!("{} ...", sub_commands[..(sub_commands.len().min(3))].join(", "))
88 | } else {
89 | "".to_string()
90 | };
91 |
92 | queue!(
93 | stdout,
94 | SetForegroundColor(Color::Reset),
95 | Print(&args.bar.opening),
96 | PrintStyledContent(format!("{: <2}", slice(&bar, 0, 2)).red()),
97 | PrintStyledContent(format!("{: <3}", slice(&bar, 2, 5)).yellow()),
98 | PrintStyledContent(format!("{: <5}", slice(&bar, 5, 10)).green()),
99 | PrintStyledContent(format!("{} ", args.bar.closing).reset()),
100 | Print(format!("{: >5.2}% ", percentage)),
101 | PrintStyledContent(format!("{:5}", invocations).grey()),
102 | PrintStyledContent(format!(" {} ", command).reset().bold()),
103 | PrintStyledContent(format!("{}\n", pretty_sub_commands).reset().grey()),
104 | SetForegroundColor(Color::Reset),
105 | )
106 | .unwrap();
107 | }
108 |
--------------------------------------------------------------------------------
/src/hist_file.rs:
--------------------------------------------------------------------------------
1 | use crate::Args;
2 | use crossterm::execute;
3 | use crossterm::style::{Attribute, PrintStyledContent, SetAttribute, Stylize};
4 | use regex::Regex;
5 | use std::collections::HashMap;
6 | use std::io::stdout;
7 | use std::io::{BufRead, BufReader};
8 |
9 | pub(crate) type CommandMap = HashMap, HashMap)>;
10 |
11 | pub fn get_contents(hist_file: std::fs::File, args: &Args) -> String {
12 | let reader = BufReader::new(hist_file);
13 | let mut contents = String::new();
14 |
15 | for (index, line) in reader.lines().enumerate() {
16 | if let Ok(line) = line {
17 | contents.push_str(&line);
18 | contents.push('\n');
19 | } else if args.debug {
20 | execute!{
21 | stdout(),
22 | PrintStyledContent(format!("[Error] Could not read line : {index} = {line:#?}\n").yellow().bold()),
23 | SetAttribute(Attribute::Reset),
24 | }.unwrap();
25 | }
26 | }
27 |
28 | contents
29 | }
30 |
31 | fn get_commands(line: String) -> Vec {
32 | line.split(&['&', '|', ';'])
33 | .filter(|x| !x.is_empty())
34 | .map(str::to_string)
35 | .collect()
36 | }
37 |
38 | /// Takes contents of a file and returns a vector of valid commands
39 | pub fn parse_contents(contents: String, args: &Args) -> Vec {
40 | let mut lines: Vec = contents
41 | .lines()
42 | .filter(|line| !line.is_empty())
43 | .map(str::trim)
44 | .map(str::to_string)
45 | .collect();
46 |
47 | let shell_strat = match args.shell.as_str() {
48 | "fish" => {
49 | |line: String| -> String { line.strip_prefix("- cmd: ").unwrap_or("").to_owned() }
50 | }
51 | "ohmyzsh" => |line: String| -> String { line[7..].to_owned() },
52 | _ => |line: String| -> String { line },
53 | };
54 |
55 | let regex_strat = |line: String, re: Regex| -> String {
56 | if let Some(cap) = re.captures(&line) {
57 | cap[0].to_owned()
58 | } else {
59 | String::new()
60 | }
61 | };
62 |
63 | if args.regexp.is_empty() {
64 | lines = lines.into_iter().map(shell_strat).collect();
65 | } else {
66 | let re = Regex::new(&args.regexp).unwrap();
67 | lines = lines
68 | .into_iter()
69 | .map(move |line| regex_strat(line, re.clone()))
70 | .collect();
71 | };
72 |
73 | let reg = Regex::new("('(?:.|[^'\n])*'|\"(?:.|[^\"\n])*\")").unwrap();
74 | let unquoted_lines = lines
75 | .into_iter()
76 | .map(|line| reg.replace_all(&line, "").to_string());
77 | unquoted_lines.flat_map(get_commands).collect()
78 | }
79 |
80 | pub fn process_lines(lines: Vec, _args: &Args) -> CommandMap {
81 | let leaders = ["sudo", "doas"];
82 | let super_commands = ["git", "entr", "time"];
83 |
84 | let mut output: CommandMap = HashMap::new();
85 |
86 | for line in lines.into_iter() {
87 | let words = line.split_whitespace().collect::>();
88 |
89 | let (first, second) = (words.first().unwrap().to_string(), words.get(1));
90 |
91 | output
92 | .entry(first.clone())
93 | .or_insert((0, None, HashMap::new()))
94 | .0 += 1;
95 |
96 | if let Some(second) = second {
97 | let mut parent_entry = output.get_mut(&first).unwrap();
98 |
99 | if parent_entry.1.is_some() {
100 | *parent_entry.2.entry(second.to_string()).or_insert(0) += 1;
101 | }
102 |
103 | if leaders.contains(&first.as_str()) {
104 | parent_entry.1 = Some(true);
105 | output
106 | .entry((*second).to_string())
107 | .or_insert((0, None, HashMap::new()))
108 | .0 += 1;
109 | } else if super_commands.contains(&first.as_str()) {
110 | parent_entry.1 = Some(false);
111 | }
112 | }
113 | }
114 | output
115 | }
116 |
117 | #[cfg(test)]
118 | mod parsing {
119 | #[test]
120 | #[ignore = "reformat pending ..."]
121 | fn tood() {
122 | todo!()
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | mod args;
2 | mod display;
3 | mod hist_file;
4 | use std::{env, path::PathBuf};
5 |
6 | use args::Args;
7 |
8 | use clap::Parser;
9 |
10 | fn main() -> Result<(), Box> {
11 | let args = Args::parse();
12 |
13 | let file_path = match args.file.as_ref() {
14 | Some(p) => p.to_owned(),
15 | None => match env::var("HISTFILE") {
16 | Ok(p) => PathBuf::from(p),
17 | Err(_e) => {
18 | return Err("Could not find HISTFILE environment variable. Supply the file path with the --file option".into());
19 | }
20 | },
21 | };
22 |
23 | let file = std::fs::File::open(file_path).expect("Failed to get histfile");
24 |
25 | let contents = hist_file::get_contents(file, &args);
26 | let command_lines = hist_file::parse_contents(contents, &args);
27 | let commands = hist_file::process_lines(command_lines, &args);
28 |
29 | display::print(commands, args);
30 |
31 | Ok(())
32 | }
33 |
--------------------------------------------------------------------------------