@fnndsc/brasa 0.56.1 → 0.56.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/dist/buildinfo.json +1 -1
  2. package/dist/builtins/berth/berth.d.ts +90 -3
  3. package/dist/builtins/berth/berth.js +129 -76
  4. package/dist/builtins/berth/berth.js.map +1 -1
  5. package/dist/builtins/berth/berthClose.d.ts +25 -6
  6. package/dist/builtins/berth/berthClose.js +8 -64
  7. package/dist/builtins/berth/berthClose.js.map +1 -1
  8. package/dist/builtins/berth/berthCube.d.ts +3 -2
  9. package/dist/builtins/berth/berthCube.js +9 -16
  10. package/dist/builtins/berth/berthCube.js.map +1 -1
  11. package/dist/builtins/berth/berthProcesses.js +33 -3
  12. package/dist/builtins/berth/berthProcesses.js.map +1 -1
  13. package/dist/builtins/debug.d.ts +24 -3
  14. package/dist/builtins/debug.js +66 -13
  15. package/dist/builtins/debug.js.map +1 -1
  16. package/dist/builtins/fs/acl.args.d.ts +5 -38
  17. package/dist/builtins/fs/acl.args.js +6 -72
  18. package/dist/builtins/fs/acl.args.js.map +1 -1
  19. package/dist/builtins/fs/acl.d.ts +100 -10
  20. package/dist/builtins/fs/acl.js +222 -68
  21. package/dist/builtins/fs/acl.js.map +1 -1
  22. package/dist/builtins/fs/cat.args.d.ts +14 -15
  23. package/dist/builtins/fs/cat.args.js +13 -40
  24. package/dist/builtins/fs/cat.args.js.map +1 -1
  25. package/dist/builtins/fs/cat.d.ts +40 -3
  26. package/dist/builtins/fs/cat.js +92 -13
  27. package/dist/builtins/fs/cat.js.map +1 -1
  28. package/dist/builtins/fs/cd.d.ts +24 -6
  29. package/dist/builtins/fs/cd.js +71 -18
  30. package/dist/builtins/fs/cd.js.map +1 -1
  31. package/dist/builtins/fs/configWrite.d.ts +24 -8
  32. package/dist/builtins/fs/configWrite.js +77 -36
  33. package/dist/builtins/fs/configWrite.js.map +1 -1
  34. package/dist/builtins/fs/cp.d.ts +39 -4
  35. package/dist/builtins/fs/cp.js +122 -56
  36. package/dist/builtins/fs/cp.js.map +1 -1
  37. package/dist/builtins/fs/download.d.ts +31 -4
  38. package/dist/builtins/fs/download.js +75 -38
  39. package/dist/builtins/fs/download.js.map +1 -1
  40. package/dist/builtins/fs/du.d.ts +59 -33
  41. package/dist/builtins/fs/du.js +111 -79
  42. package/dist/builtins/fs/du.js.map +1 -1
  43. package/dist/builtins/fs/edit.d.ts +19 -4
  44. package/dist/builtins/fs/edit.js +77 -16
  45. package/dist/builtins/fs/edit.js.map +1 -1
  46. package/dist/builtins/fs/find.d.ts +25 -0
  47. package/dist/builtins/fs/find.js +76 -5
  48. package/dist/builtins/fs/find.js.map +1 -1
  49. package/dist/builtins/fs/ls.d.ts +69 -4
  50. package/dist/builtins/fs/ls.js +84 -45
  51. package/dist/builtins/fs/ls.js.map +1 -1
  52. package/dist/builtins/fs/mkdir.d.ts +28 -18
  53. package/dist/builtins/fs/mkdir.js +95 -66
  54. package/dist/builtins/fs/mkdir.js.map +1 -1
  55. package/dist/builtins/fs/mv.d.ts +34 -4
  56. package/dist/builtins/fs/mv.js +129 -64
  57. package/dist/builtins/fs/mv.js.map +1 -1
  58. package/dist/builtins/fs/pull.args.d.ts +56 -17
  59. package/dist/builtins/fs/pull.args.js +16 -66
  60. package/dist/builtins/fs/pull.args.js.map +1 -1
  61. package/dist/builtins/fs/pull.d.ts +62 -0
  62. package/dist/builtins/fs/pull.js +123 -31
  63. package/dist/builtins/fs/pull.js.map +1 -1
  64. package/dist/builtins/fs/pwd.d.ts +19 -5
  65. package/dist/builtins/fs/pwd.js +62 -8
  66. package/dist/builtins/fs/pwd.js.map +1 -1
  67. package/dist/builtins/fs/rm.d.ts +58 -21
  68. package/dist/builtins/fs/rm.js +99 -82
  69. package/dist/builtins/fs/rm.js.map +1 -1
  70. package/dist/builtins/fs/rmdir.d.ts +29 -11
  71. package/dist/builtins/fs/rmdir.js +87 -36
  72. package/dist/builtins/fs/rmdir.js.map +1 -1
  73. package/dist/builtins/fs/touch.d.ts +44 -9
  74. package/dist/builtins/fs/touch.js +91 -27
  75. package/dist/builtins/fs/touch.js.map +1 -1
  76. package/dist/builtins/fs/tree.d.ts +36 -9
  77. package/dist/builtins/fs/tree.js +126 -36
  78. package/dist/builtins/fs/tree.js.map +1 -1
  79. package/dist/builtins/fs/upload.d.ts +22 -3
  80. package/dist/builtins/fs/upload.js +82 -14
  81. package/dist/builtins/fs/upload.js.map +1 -1
  82. package/dist/builtins/fs/xattr.args.d.ts +12 -33
  83. package/dist/builtins/fs/xattr.args.js +17 -95
  84. package/dist/builtins/fs/xattr.args.js.map +1 -1
  85. package/dist/builtins/fs/xattr.d.ts +98 -8
  86. package/dist/builtins/fs/xattr.js +163 -24
  87. package/dist/builtins/fs/xattr.js.map +1 -1
  88. package/dist/builtins/games/bytes.d.ts +201 -5
  89. package/dist/builtins/games/bytes.js +255 -54
  90. package/dist/builtins/games/bytes.js.map +1 -1
  91. package/dist/builtins/games/chimes.d.ts +180 -3
  92. package/dist/builtins/games/chimes.js +203 -26
  93. package/dist/builtins/games/chimes.js.map +1 -1
  94. package/dist/builtins/games/chrislab.d.ts +101 -3
  95. package/dist/builtins/games/chrislab.js +117 -17
  96. package/dist/builtins/games/chrislab.js.map +1 -1
  97. package/dist/builtins/games/cowsay.d.ts +93 -2
  98. package/dist/builtins/games/cowsay.js +90 -31
  99. package/dist/builtins/games/cowsay.js.map +1 -1
  100. package/dist/builtins/games/figlet.d.ts +70 -2
  101. package/dist/builtins/games/figlet.js +83 -6
  102. package/dist/builtins/games/figlet.js.map +1 -1
  103. package/dist/builtins/games/lab.d.ts +104 -5
  104. package/dist/builtins/games/lab.js +144 -6
  105. package/dist/builtins/games/lab.js.map +1 -1
  106. package/dist/builtins/games/lolcat.d.ts +61 -1
  107. package/dist/builtins/games/lolcat.js +59 -24
  108. package/dist/builtins/games/lolcat.js.map +1 -1
  109. package/dist/builtins/games/morse.d.ts +36 -1
  110. package/dist/builtins/games/morse.js +50 -2
  111. package/dist/builtins/games/morse.js.map +1 -1
  112. package/dist/builtins/games/numbers.d.ts +241 -5
  113. package/dist/builtins/games/numbers.js +298 -23
  114. package/dist/builtins/games/numbers.js.map +1 -1
  115. package/dist/builtins/games/play.d.ts +96 -6
  116. package/dist/builtins/games/play.js +137 -6
  117. package/dist/builtins/games/play.js.map +1 -1
  118. package/dist/builtins/games/qr.d.ts +40 -1
  119. package/dist/builtins/games/qr.js +51 -2
  120. package/dist/builtins/games/qr.js.map +1 -1
  121. package/dist/builtins/games/show.d.ts +131 -6
  122. package/dist/builtins/games/show.js +208 -20
  123. package/dist/builtins/games/show.js.map +1 -1
  124. package/dist/builtins/games/sky.d.ts +178 -4
  125. package/dist/builtins/games/sky.js +228 -12
  126. package/dist/builtins/games/sky.js.map +1 -1
  127. package/dist/builtins/games/text.d.ts +258 -7
  128. package/dist/builtins/games/text.js +310 -29
  129. package/dist/builtins/games/text.js.map +1 -1
  130. package/dist/builtins/games/trek.d.ts +67 -3
  131. package/dist/builtins/games/trek.js +63 -10
  132. package/dist/builtins/games/trek.js.map +1 -1
  133. package/dist/builtins/games/wtf.d.ts +58 -1
  134. package/dist/builtins/games/wtf.js +67 -6
  135. package/dist/builtins/games/wtf.js.map +1 -1
  136. package/dist/builtins/help.d.ts +41 -3
  137. package/dist/builtins/help.js +92 -796
  138. package/dist/builtins/help.js.map +1 -1
  139. package/dist/builtins/net/connect.d.ts +36 -3
  140. package/dist/builtins/net/connect.js +84 -26
  141. package/dist/builtins/net/connect.js.map +1 -1
  142. package/dist/builtins/net/cubepath.d.ts +49 -5
  143. package/dist/builtins/net/cubepath.js +69 -27
  144. package/dist/builtins/net/cubepath.js.map +1 -1
  145. package/dist/builtins/net/logout.d.ts +13 -4
  146. package/dist/builtins/net/logout.js +49 -3
  147. package/dist/builtins/net/logout.js.map +1 -1
  148. package/dist/builtins/net/netstat.d.ts +29 -4
  149. package/dist/builtins/net/netstat.js +64 -23
  150. package/dist/builtins/net/netstat.js.map +1 -1
  151. package/dist/builtins/net/pacs.d.ts +169 -15
  152. package/dist/builtins/net/pacs.js +92 -33
  153. package/dist/builtins/net/pacs.js.map +1 -1
  154. package/dist/builtins/net/query.args.d.ts +95 -0
  155. package/dist/builtins/net/query.args.js +26 -0
  156. package/dist/builtins/net/query.args.js.map +1 -0
  157. package/dist/builtins/net/query.d.ts +98 -5
  158. package/dist/builtins/net/query.js +89 -83
  159. package/dist/builtins/net/query.js.map +1 -1
  160. package/dist/builtins/net/status.d.ts +5 -3
  161. package/dist/builtins/net/status.js +5 -12
  162. package/dist/builtins/net/status.js.map +1 -1
  163. package/dist/builtins/parametersofplugin.d.ts +82 -3
  164. package/dist/builtins/parametersofplugin.js +79 -23
  165. package/dist/builtins/parametersofplugin.js.map +1 -1
  166. package/dist/builtins/proc.d.ts +210 -3
  167. package/dist/builtins/proc.js +210 -119
  168. package/dist/builtins/proc.js.map +1 -1
  169. package/dist/builtins/res/compute.d.ts +67 -3
  170. package/dist/builtins/res/compute.js +121 -35
  171. package/dist/builtins/res/compute.js.map +1 -1
  172. package/dist/builtins/res/dicom.d.ts +55 -7
  173. package/dist/builtins/res/dicom.js +86 -52
  174. package/dist/builtins/res/dicom.js.map +1 -1
  175. package/dist/builtins/res/expect.d.ts +52 -3
  176. package/dist/builtins/res/expect.js +92 -29
  177. package/dist/builtins/res/expect.js.map +1 -1
  178. package/dist/builtins/res/feed.d.ts +475 -3
  179. package/dist/builtins/res/feed.js +253 -158
  180. package/dist/builtins/res/feed.js.map +1 -1
  181. package/dist/builtins/res/files.d.ts +517 -3
  182. package/dist/builtins/res/files.js +124 -44
  183. package/dist/builtins/res/files.js.map +1 -1
  184. package/dist/builtins/res/gather.d.ts +113 -3
  185. package/dist/builtins/res/gather.js +153 -44
  186. package/dist/builtins/res/gather.js.map +1 -1
  187. package/dist/builtins/res/gather.process.d.ts +27 -19
  188. package/dist/builtins/res/gather.process.js +29 -59
  189. package/dist/builtins/res/gather.process.js.map +1 -1
  190. package/dist/builtins/res/group.d.ts +284 -3
  191. package/dist/builtins/res/group.js +147 -76
  192. package/dist/builtins/res/group.js.map +1 -1
  193. package/dist/builtins/res/image.d.ts +30 -2
  194. package/dist/builtins/res/image.js +62 -10
  195. package/dist/builtins/res/image.js.map +1 -1
  196. package/dist/builtins/res/play.d.ts +71 -9
  197. package/dist/builtins/res/play.js +124 -82
  198. package/dist/builtins/res/play.js.map +1 -1
  199. package/dist/builtins/res/plugininstance.d.ts +307 -2
  200. package/dist/builtins/res/plugininstance.js +122 -50
  201. package/dist/builtins/res/plugininstance.js.map +1 -1
  202. package/dist/builtins/res/pluginmeta.d.ts +244 -2
  203. package/dist/builtins/res/pluginmeta.js +98 -40
  204. package/dist/builtins/res/pluginmeta.js.map +1 -1
  205. package/dist/builtins/res/record.d.ts +37 -3
  206. package/dist/builtins/res/record.js +127 -57
  207. package/dist/builtins/res/record.js.map +1 -1
  208. package/dist/builtins/res/resourceList.d.ts +136 -0
  209. package/dist/builtins/res/resourceList.js +51 -0
  210. package/dist/builtins/res/resourceList.js.map +1 -0
  211. package/dist/builtins/res/tag.d.ts +244 -3
  212. package/dist/builtins/res/tag.js +97 -41
  213. package/dist/builtins/res/tag.js.map +1 -1
  214. package/dist/builtins/res/user.d.ts +122 -3
  215. package/dist/builtins/res/user.js +83 -21
  216. package/dist/builtins/res/user.js.map +1 -1
  217. package/dist/builtins/res/workflow.d.ts +244 -2
  218. package/dist/builtins/res/workflow.js +97 -40
  219. package/dist/builtins/res/workflow.js.map +1 -1
  220. package/dist/builtins/store.d.ts +85 -3
  221. package/dist/builtins/store.js +135 -68
  222. package/dist/builtins/store.js.map +1 -1
  223. package/dist/builtins/sys/apropos.d.ts +80 -5
  224. package/dist/builtins/sys/apropos.js +121 -15
  225. package/dist/builtins/sys/apropos.js.map +1 -1
  226. package/dist/builtins/sys/cal.d.ts +44 -5
  227. package/dist/builtins/sys/cal.js +57 -10
  228. package/dist/builtins/sys/cal.js.map +1 -1
  229. package/dist/builtins/sys/command.d.ts +20 -0
  230. package/dist/builtins/sys/command.js +31 -0
  231. package/dist/builtins/sys/command.js.map +1 -1
  232. package/dist/builtins/sys/context.d.ts +13 -3
  233. package/dist/builtins/sys/context.js +51 -4
  234. package/dist/builtins/sys/context.js.map +1 -1
  235. package/dist/builtins/sys/date.d.ts +56 -5
  236. package/dist/builtins/sys/date.js +63 -9
  237. package/dist/builtins/sys/date.js.map +1 -1
  238. package/dist/builtins/sys/echo.d.ts +63 -2
  239. package/dist/builtins/sys/echo.js +87 -8
  240. package/dist/builtins/sys/echo.js.map +1 -1
  241. package/dist/builtins/sys/fortune.d.ts +27 -2
  242. package/dist/builtins/sys/fortune.js +44 -2
  243. package/dist/builtins/sys/fortune.js.map +1 -1
  244. package/dist/builtins/sys/grep.d.ts +245 -11
  245. package/dist/builtins/sys/grep.js +418 -131
  246. package/dist/builtins/sys/grep.js.map +1 -1
  247. package/dist/builtins/sys/joblets.d.ts +66 -7
  248. package/dist/builtins/sys/joblets.js +101 -14
  249. package/dist/builtins/sys/joblets.js.map +1 -1
  250. package/dist/builtins/sys/motd.d.ts +37 -3
  251. package/dist/builtins/sys/motd.js +49 -8
  252. package/dist/builtins/sys/motd.js.map +1 -1
  253. package/dist/builtins/sys/notes.d.ts +75 -3
  254. package/dist/builtins/sys/notes.js +90 -29
  255. package/dist/builtins/sys/notes.js.map +1 -1
  256. package/dist/builtins/sys/physicalmode.d.ts +24 -3
  257. package/dist/builtins/sys/physicalmode.js +68 -14
  258. package/dist/builtins/sys/physicalmode.js.map +1 -1
  259. package/dist/builtins/sys/tasks.d.ts +74 -5
  260. package/dist/builtins/sys/tasks.js +102 -14
  261. package/dist/builtins/sys/tasks.js.map +1 -1
  262. package/dist/builtins/sys/textFilters.d.ts +673 -41
  263. package/dist/builtins/sys/textFilters.js +719 -264
  264. package/dist/builtins/sys/textFilters.js.map +1 -1
  265. package/dist/builtins/sys/timing.d.ts +24 -3
  266. package/dist/builtins/sys/timing.js +67 -14
  267. package/dist/builtins/sys/timing.js.map +1 -1
  268. package/dist/builtins/sys/variables.d.ts +70 -4
  269. package/dist/builtins/sys/variables.js +103 -31
  270. package/dist/builtins/sys/variables.js.map +1 -1
  271. package/dist/builtins/sys/version.d.ts +27 -2
  272. package/dist/builtins/sys/version.js +48 -3
  273. package/dist/builtins/sys/version.js.map +1 -1
  274. package/dist/builtins/sys/weather.d.ts +92 -15
  275. package/dist/builtins/sys/weather.js +71 -45
  276. package/dist/builtins/sys/weather.js.map +1 -1
  277. package/dist/builtins/sys/whoami.d.ts +38 -18
  278. package/dist/builtins/sys/whoami.js +112 -8
  279. package/dist/builtins/sys/whoami.js.map +1 -1
  280. package/dist/builtins/utils.d.ts +1 -38
  281. package/dist/builtins/utils.js +1 -87
  282. package/dist/builtins/utils.js.map +1 -1
  283. package/dist/chris/backend.js +2 -0
  284. package/dist/chris/backend.js.map +1 -1
  285. package/dist/chris/chrisCommands.js +62 -159
  286. package/dist/chris/chrisCommands.js.map +1 -1
  287. package/dist/chris/commandFallback.d.ts +12 -0
  288. package/dist/chris/commandFallback.js +47 -0
  289. package/dist/chris/commandFallback.js.map +1 -1
  290. package/dist/chris/files.js +24 -85
  291. package/dist/chris/files.js.map +1 -1
  292. package/dist/chris/filesystem.js +7 -5
  293. package/dist/chris/filesystem.js.map +1 -1
  294. package/dist/chris/help.js +0 -1082
  295. package/dist/chris/help.js.map +1 -1
  296. package/dist/chris/kinds.d.ts +30 -0
  297. package/dist/chris/kinds.js +154 -0
  298. package/dist/chris/kinds.js.map +1 -0
  299. package/dist/chris/mounts.d.ts +4 -4
  300. package/dist/chris/mounts.js +9 -16
  301. package/dist/chris/mounts.js.map +1 -1
  302. package/dist/chris/navigation.d.ts +2 -3
  303. package/dist/chris/navigation.js +6 -20
  304. package/dist/chris/navigation.js.map +1 -1
  305. package/dist/chris/pacsResources.d.ts +259 -0
  306. package/dist/chris/pacsResources.js +168 -0
  307. package/dist/chris/pacsResources.js.map +1 -0
  308. package/dist/chris/promptContext.d.ts +6 -0
  309. package/dist/chris/promptContext.js +3 -1
  310. package/dist/chris/promptContext.js.map +1 -1
  311. package/dist/core/argumentKinds.d.ts +107 -0
  312. package/dist/core/argumentKinds.js +223 -0
  313. package/dist/core/argumentKinds.js.map +1 -0
  314. package/dist/core/backend.d.ts +4 -1
  315. package/dist/core/backend.js.map +1 -1
  316. package/dist/core/commandDeclaration.d.ts +453 -0
  317. package/dist/core/commandDeclaration.js +52 -0
  318. package/dist/core/commandDeclaration.js.map +1 -0
  319. package/dist/core/commandParse.d.ts +64 -0
  320. package/dist/core/commandParse.js +468 -0
  321. package/dist/core/commandParse.js.map +1 -0
  322. package/dist/core/commandRegistry.d.ts +37 -0
  323. package/dist/core/commandRegistry.js +100 -3
  324. package/dist/core/commandRegistry.js.map +1 -1
  325. package/dist/core/commandSchema.d.ts +32 -0
  326. package/dist/core/commandSchema.js +117 -0
  327. package/dist/core/commandSchema.js.map +1 -0
  328. package/dist/core/commandUsage.d.ts +62 -0
  329. package/dist/core/commandUsage.js +247 -0
  330. package/dist/core/commandUsage.js.map +1 -0
  331. package/dist/core/coreCommands.js +66 -214
  332. package/dist/core/coreCommands.js.map +1 -1
  333. package/dist/core/dispatch.js +15 -8
  334. package/dist/core/dispatch.js.map +1 -1
  335. package/dist/core/paths.d.ts +51 -0
  336. package/dist/core/paths.js +86 -2
  337. package/dist/core/paths.js.map +1 -1
  338. package/dist/core/preprocess.d.ts +10 -9
  339. package/dist/core/preprocess.js +10 -20
  340. package/dist/core/preprocess.js.map +1 -1
  341. package/dist/core/warmupFailures.d.ts +34 -2
  342. package/dist/core/warmupFailures.js +43 -2
  343. package/dist/core/warmupFailures.js.map +1 -1
  344. package/dist/lib/completer/cfsPaths.d.ts +14 -0
  345. package/dist/lib/completer/cfsPaths.js +91 -0
  346. package/dist/lib/completer/cfsPaths.js.map +1 -0
  347. package/dist/lib/completer/declared.d.ts +15 -0
  348. package/dist/lib/completer/declared.js +189 -0
  349. package/dist/lib/completer/declared.js.map +1 -0
  350. package/dist/lib/completer/index.js +33 -79
  351. package/dist/lib/completer/index.js.map +1 -1
  352. package/dist/lib/parser.d.ts +5 -8
  353. package/dist/lib/parser.js +2 -11
  354. package/dist/lib/parser.js.map +1 -1
  355. package/dist/lib/vfs/vfs.d.ts +2 -1
  356. package/dist/lib/vfs/vfs.js +9 -6
  357. package/dist/lib/vfs/vfs.js.map +1 -1
  358. package/dist/lib/wildcard.js +2 -3
  359. package/dist/lib/wildcard.js.map +1 -1
  360. package/dist/notes.json +1 -1
  361. package/package.json +6 -5
@@ -9,111 +9,164 @@
9
9
  * wore — `@SER41` stays `@SER41`. The kept rows become the held listing, so
10
10
  * `gather add @SER41` and `${listing}` read what grep left.
11
11
  *
12
- * It takes grep's own flags only, with grep's meanings: -i -v -w -E -F -c
13
- * and -e. Anything else (or a file operand) is the system grep's business,
14
- * and the whole line goes there as text, said in one line. Plain text in is
15
- * always the system grep where the surface can run it; `command grep` skips
16
- * this builtin altogether.
12
+ * It takes grep's flags with grep's meanings: -i -v -w -E -F -c -e, and for
13
+ * text and files -n -l -o -h -r -A -B -C, which a listing (whole rows) has no
14
+ * use for and refuses by name. It is declared in one record, so any other
15
+ * flag is refused by the parser. A file named is a ChRIS path, read through
16
+ * the one store-path resolver (a folder walked under -r) and filtered here,
17
+ * named as typed. Plain text in is the system grep where the surface can run
18
+ * it, its line written back from the typed options, and filtered here where
19
+ * it cannot; `command grep` skips this builtin altogether.
20
+ *
21
+ * Key components:
22
+ * - grepCommand: the declaration.
23
+ * - builtin_grep: the declaration run over words already split.
24
+ * - grepExpression_build, grepMatcher_build: the expression, and the test a line passes.
25
+ * - textSources_grep: the lines a grep over text writes, as GNU grep writes them.
26
+ * - listingRows_grep: the rows a grep keeps of a listing.
27
+ *
28
+ * Typical usage:
29
+ * ```ts
30
+ * await builtin_grep(['-i', 't1']);
31
+ * await builtin_grep(['-c', 'todo', 'notes.txt']);
32
+ * ```
33
+ *
34
+ * Dependencies: the held and piped listings, the session's filesystem (via
35
+ * the text filters' reader), and the surface's system grep.
17
36
  *
18
37
  * @module
19
38
  */
20
39
  import chalk from 'chalk';
21
40
  import { envelope_ok, envelope_error } from '@fnndsc/menu';
22
41
  import { stdin_take } from '../games/stdin.js';
42
+ import { errorStack, errno_words } from '@fnndsc/fond';
43
+ import { filterFile_read, filterRead_answer, filterSources_read, FILES_OPERAND, lines_join, lines_split, word_quote, } from './textFilters.js';
44
+ import { storePath_resolve } from '../utils.js';
45
+ import { vfsDispatcher_get } from '../../core/filesystem.js';
46
+ import { itemType_isContainer } from '../../core/itemKinds.js';
47
+ import { command_declare } from '../../core/commandDeclaration.js';
48
+ import { declaredCommand_run } from '../../core/commandParse.js';
23
49
  import { listingIn_take } from '../../core/listing.js';
24
50
  import { answerKinds_get } from '../../core/answerKinds.js';
25
51
  import { surface_get } from '../../core/surface.js';
26
52
  import { answer_get, answerFilter_apply, answerNumbering_derive, answerOrdinals_attach, answerRow_key, answerRows_derive, handle_word, numberedFooter_render, } from '../../session/answer.js';
27
53
  import { exitCode_set } from '../../core/handlerRun.js';
28
- /** grep's long flags this grep takes, to their short form. */
29
- const LONG_FLAGS = {
30
- '--ignore-case': 'i', '--invert-match': 'v', '--word-regexp': 'w',
31
- '--extended-regexp': 'E', '--fixed-strings': 'F', '--count': 'c',
54
+ /**
55
+ * The layout of a grep line that asks none: lines as they are.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * const layout: GrepLayout = { ...GREP_LAYOUT_PLAIN, onlyMatching: true };
60
+ * ```
61
+ */
62
+ export const GREP_LAYOUT_PLAIN = {
63
+ lineNumber: false, filesWithMatches: false, onlyMatching: false, noFilename: false, withFilename: false, recursive: false,
64
+ after: undefined, before: undefined, context: undefined, color: undefined,
65
+ };
66
+ /** The switches of a grep line, as the line writes them back. */
67
+ const GREP_SWITCHES = [
68
+ { letter: 'i', on: (ask) => ask.insensitive },
69
+ { letter: 'v', on: (ask) => ask.invert },
70
+ { letter: 'w', on: (ask) => ask.word },
71
+ { letter: 'E', on: (ask) => ask.extended },
72
+ { letter: 'F', on: (ask) => ask.fixed },
73
+ { letter: 'c', on: (ask) => ask.count },
74
+ { letter: 'n', on: (_ask, layout) => layout.lineNumber },
75
+ { letter: 'l', on: (_ask, layout) => layout.filesWithMatches },
76
+ { letter: 'o', on: (_ask, layout) => layout.onlyMatching },
77
+ { letter: 'h', on: (_ask, layout) => layout.noFilename },
78
+ { letter: 'H', on: (_ask, layout) => layout.withFilename },
79
+ { letter: 'r', on: (_ask, layout) => layout.recursive },
80
+ ];
81
+ /** The layout flags a numbered listing has no use for, each with why, as grep says it. */
82
+ const LISTING_REFUSALS = [
83
+ { flag: '-n', given: (layout) => layout.lineNumber, why: 'a listing\'s rows wear their handles, not line numbers' },
84
+ { flag: '-l', given: (layout) => layout.filesWithMatches, why: 'a listing is not files to name' },
85
+ { flag: '-o', given: (layout) => layout.onlyMatching, why: 'a listing keeps whole rows' },
86
+ { flag: '-h', given: (layout) => layout.noFilename, why: 'a listing has no file names to drop' },
87
+ { flag: '-H', given: (layout) => layout.withFilename, why: 'a listing has no file names to show' },
88
+ { flag: '-r', given: (layout) => layout.recursive, why: 'a listing is not a folder to walk' },
89
+ { flag: '-A', given: (layout) => layout.after !== undefined, why: 'a listing keeps a row with its group, not lines after it' },
90
+ { flag: '-B', given: (layout) => layout.before !== undefined, why: 'a listing keeps a row with its group, not lines before it' },
91
+ { flag: '-C', given: (layout) => layout.context !== undefined, why: 'a listing keeps a row with its group, not lines around it' },
92
+ ];
93
+ /** What a `grep` line may hold. */
94
+ const GREP_GRAMMAR = {
95
+ operands: [
96
+ { name: 'pattern', kind: 'word', optional: true, unless: 'regexp', summary: 'The pattern, unless -e gives it' },
97
+ FILES_OPERAND,
98
+ ],
99
+ options: {
100
+ insensitive: { short: 'i', long: 'ignore-case', summary: 'Ignore case' },
101
+ invert: { short: 'v', long: 'invert-match', summary: 'Keep what does not match' },
102
+ word: { short: 'w', long: 'word-regexp', summary: 'Whole words' },
103
+ extended: { short: 'E', long: 'extended-regexp', summary: 'Extended regular expressions' },
104
+ fixed: { short: 'F', long: 'fixed-strings', summary: 'Fixed strings' },
105
+ count: { short: 'c', long: 'count', summary: 'Count only' },
106
+ regexp: { short: 'e', long: 'regexp', repeat: true, value: { name: 'pattern', kind: 'word' }, summary: 'A pattern (repeatable: any of them matches)' },
107
+ lineNumber: { short: 'n', long: 'line-number', summary: 'Each line\'s number (text and files)' },
108
+ filesWithMatches: { short: 'l', long: 'files-with-matches', summary: 'Only the names of files with a match' },
109
+ onlyMatching: { short: 'o', long: 'only-matching', summary: 'Only the matching parts, one per line' },
110
+ noFilename: { short: 'h', long: 'no-filename', summary: 'No file-name prefix' },
111
+ withFilename: { short: 'H', long: 'with-filename', summary: 'The file-name prefix, even for one file' },
112
+ color: { long: 'color', value: { name: 'when', kind: 'word', optional: true }, summary: 'Colour the matches (always, never, auto): the system grep colours; the session\'s own grep writes plain text' },
113
+ recursive: { short: 'r', long: 'recursive', summary: 'Walk each folder for files (. when none is named)' },
114
+ after: { short: 'A', long: 'after-context', value: { name: 'N', kind: 'int', min: 0 }, summary: 'N lines after each match' },
115
+ before: { short: 'B', long: 'before-context', value: { name: 'N', kind: 'int', min: 0 }, summary: 'N lines before each match' },
116
+ context: { short: 'C', long: 'context', value: { name: 'N', kind: 'int', min: 0 }, summary: 'N lines around each match' },
117
+ },
32
118
  };
33
119
  /**
34
- * Reads a grep line's words.
120
+ * `grep [-ivwEFcnlohr] [-A N] [-B N] [-C N] [-e PATTERN]... [PATTERN] [FILE]...`: the declaration.
35
121
  *
36
- * @param args - The words after `grep`.
37
- * @returns What they ask.
122
+ * @example
123
+ * ```ts
124
+ * commands_register({ declarations: [grepCommand] });
125
+ * ```
38
126
  */
39
- export function grepArgs_parse(args) {
40
- const ask = { insensitive: false, invert: false, word: false, extended: false, fixed: false, count: false, patterns: [], foreign: null };
41
- const operands = [];
42
- const flag = (letter) => {
43
- if (letter === 'i')
44
- ask.insensitive = true;
45
- else if (letter === 'v')
46
- ask.invert = true;
47
- else if (letter === 'w')
48
- ask.word = true;
49
- else if (letter === 'E')
50
- ask.extended = true;
51
- else if (letter === 'F')
52
- ask.fixed = true;
53
- else if (letter === 'c')
54
- ask.count = true;
55
- else
56
- return false;
57
- return true;
58
- };
59
- for (let at = 0; at < args.length; at++) {
60
- const word = args[at] ?? '';
61
- if (word === '--') {
62
- operands.push(...args.slice(at + 1));
63
- break;
64
- }
65
- if (word === '-e' || word === '--regexp') {
66
- const pattern = args[at + 1];
67
- if (pattern === undefined) {
68
- ask.foreign = word;
69
- break;
70
- }
71
- ask.patterns.push(pattern);
72
- at += 1;
73
- continue;
74
- }
75
- if (word.startsWith('--regexp=')) {
76
- ask.patterns.push(word.slice('--regexp='.length));
77
- continue;
78
- }
79
- if (word.startsWith('--')) {
80
- const short = LONG_FLAGS[word];
81
- if (short === undefined) {
82
- ask.foreign ??= word;
83
- continue;
84
- }
85
- flag(short);
86
- continue;
87
- }
88
- if (word.startsWith('-') && word.length > 1) {
89
- // Clustered short flags: -vi. An -e inside a cluster takes the rest.
90
- const letters = word.slice(1);
91
- for (let index = 0; index < letters.length; index++) {
92
- const letter = letters[index] ?? '';
93
- if (letter === 'e') {
94
- const rest = letters.slice(index + 1);
95
- const pattern = rest !== '' ? rest : args[++at];
96
- if (pattern === undefined)
97
- ask.foreign ??= word;
98
- else
99
- ask.patterns.push(pattern);
100
- break;
101
- }
102
- if (!flag(letter)) {
103
- ask.foreign ??= `-${letter}`;
104
- break;
105
- }
106
- }
107
- continue;
108
- }
109
- operands.push(word);
110
- }
111
- if (ask.patterns.length === 0 && operands.length > 0)
112
- ask.patterns.push(operands.shift());
113
- // A file to read is the system grep's: this one reads a listing or a pipe.
114
- if (operands.length > 0)
115
- ask.foreign ??= operands[0] ?? null;
116
- return ask;
127
+ export const grepCommand = command_declare({
128
+ name: 'grep',
129
+ grammar: GREP_GRAMMAR,
130
+ help: {
131
+ summary: 'Keep the lines, or the rows of a numbered listing, that match',
132
+ description: 'Filters text, or a numbered listing keeping its handles. Fed a numbered listing (piped in, or the held one when nothing is piped), grep keeps rows: a matching row stays with the rows above it, a matched group keeps all of its rows, and every kept row keeps its handle (@SER41 stays @SER41). What it kept becomes the listing @ and ${listing} name; -c counts and changes nothing. A pattern matches a study\'s and a patient\'s own text too: grep -i t1 meant for series descriptions keeps a WHOLE study whose description says t1, and grep -i "mri brain" picking whole studies is the same rule. A file named is a ChRIS path, read here (- is the pipe; a wildcard matches paths); with several, each line or count is under its file\'s name as typed. -r walks each folder named (. when none is) for its files; links met on the way are not followed. -n numbers lines, -l names the files with a match, -o keeps only the matching parts, -h drops the names and -H keeps them for one file, --color colours the matches of the system grep (this one writes plain text), and -A, -B and -C show lines after, before and around each match, groups apart by --; a listing keeps whole rows, so these are refused for one. Text piped in is the system grep where the surface runs one, else filtered here with the same flags. With -e every word after the options is a file. A flag it does not take is refused; command grep runs the system grep.',
133
+ category: 'Files',
134
+ posix: { group: 'Files', differs: 'also filters numbered listings, keeping their handles (help grep)' },
135
+ examples: [
136
+ 'query PatientID:1234567,Modality:MR | grep -i t1 | grep -vi gadavist',
137
+ 'grep -i -e t1 -e mprage',
138
+ 'gather list | grep -i sag | gather remove',
139
+ 'cat notes.txt | grep -c todo',
140
+ 'grep -c error a.log b.log',
141
+ 'grep -rn TODO notes',
142
+ 'grep -C 2 error run.log',
143
+ ],
144
+ },
145
+ holds: [],
146
+ handler: async (args) => grep_run(args),
147
+ });
148
+ /**
149
+ * A grep line written back from what it asks: its switches, its context
150
+ * counts, then its patterns (each under -e when there are several, or one
151
+ * starts with a dash).
152
+ *
153
+ * @param ask - What the grep line asks of a match.
154
+ * @param layout - How it writes what it kept.
155
+ * @returns The line, `grep -i -v -n -A 2 -e t1 -e t2`.
156
+ */
157
+ function grepLine_render(ask, layout) {
158
+ const switches = GREP_SWITCHES.filter(({ on }) => on(ask, layout)).map(({ letter }) => `-${letter}`);
159
+ const counts = [
160
+ ...(layout.after !== undefined ? ['-A', String(layout.after)] : []),
161
+ ...(layout.before !== undefined ? ['-B', String(layout.before)] : []),
162
+ ...(layout.context !== undefined ? ['-C', String(layout.context)] : []),
163
+ ...(layout.color === undefined ? [] : [layout.color === '' ? '--color' : `--color=${word_quote(layout.color)}`]),
164
+ ];
165
+ const lone = ask.patterns.length === 1 ? ask.patterns[0] : undefined;
166
+ const patterns = lone !== undefined && !lone.startsWith('-')
167
+ ? [word_quote(lone)]
168
+ : ask.patterns.flatMap((pattern) => ['-e', word_quote(pattern)]);
169
+ return ['grep', ...switches, ...counts, ...patterns].join(' ');
117
170
  }
118
171
  /**
119
172
  * A basic regular expression (grep's default) as a JavaScript one: in a BRE
@@ -139,24 +192,46 @@ function basic_convertToSource(pattern) {
139
192
  return out;
140
193
  }
141
194
  /**
142
- * Builds the test a line (or a row's text) passes.
195
+ * Builds the expression a line (or a row's text) is matched by: any of the
196
+ * patterns, read as grep reads them under -F, -E or neither, -w and -i.
143
197
  *
144
- * @param ask - What the grep line asks.
145
- * @returns The test, or a refusal when a pattern does not compile.
198
+ * @param ask - What the grep line asks of a match.
199
+ * @returns The expression (not global), or a refusal when a pattern does not compile.
200
+ *
201
+ * @example
202
+ * ```ts
203
+ * grepExpression_build({ ...ask, patterns: ['t1\\|t2'] }); // /(?:t1|t2)/
204
+ * ```
146
205
  */
147
- export function grepMatcher_build(ask) {
206
+ export function grepExpression_build(ask) {
148
207
  const sources = ask.patterns.map((pattern) => {
149
208
  const source = ask.fixed ? pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') : ask.extended ? pattern : basic_convertToSource(pattern);
150
209
  return ask.word ? `(?<![\\w])(?:${source})(?![\\w])` : `(?:${source})`;
151
210
  });
152
211
  try {
153
- const expression = new RegExp(sources.join('|'), ask.insensitive ? 'i' : '');
154
- return (text) => expression.test(text);
212
+ return new RegExp(sources.join('|'), ask.insensitive ? 'i' : '');
155
213
  }
156
214
  catch (error) {
157
215
  return { refused: `grep: ${error instanceof Error ? error.message : String(error)}` };
158
216
  }
159
217
  }
218
+ /**
219
+ * Builds the test a line (or a row's text) passes.
220
+ *
221
+ * @param ask - What the grep line asks of a match.
222
+ * @returns The test, or a refusal when a pattern does not compile.
223
+ *
224
+ * @example
225
+ * ```ts
226
+ * const matches = grepMatcher_build({ ...ask, patterns: ['T1'], word: true });
227
+ * ```
228
+ */
229
+ export function grepMatcher_build(ask) {
230
+ const built = grepExpression_build(ask);
231
+ if (!(built instanceof RegExp))
232
+ return built;
233
+ return (text) => built.test(text);
234
+ }
160
235
  /**
161
236
  * Which rows a grep keeps of a listing: a matching row with its parents, a
162
237
  * matched group with all its rows; under -v the matching rows (and a
@@ -266,63 +341,275 @@ function listing_grep(listing, ask, line) {
266
341
  return envelope_ok(rendered, { kind: listing.kind, data: filtered });
267
342
  }
268
343
  /**
269
- * Plain grep on text: the system's, where the surface runs host programs;
270
- * otherwise this grep's own line filter, with the same flags.
344
+ * The lines a grep over text writes for its sources, as GNU grep writes
345
+ * them: `name:` before each line when named, `N:` under -n (`-` for a
346
+ * context line), `--` between groups apart under -A, -B or -C; -l the
347
+ * names with a match, -c a count each, -o the matching parts.
348
+ *
349
+ * @param expression - The match, compiled.
350
+ * @param ask - What the line asks of a match.
351
+ * @param layout - How it writes what it kept.
352
+ * @param sources - The texts, under the names shown.
353
+ * @param named - Whether each line is under its file's name.
354
+ * @returns The output and how many lines were kept.
355
+ *
356
+ * @example
357
+ * ```ts
358
+ * textSources_grep(/b/, ask, GREP_LAYOUT_PLAIN, [{ name: null, text: 'a\nb\n' }], false).text; // 'b\n'
359
+ * ```
360
+ */
361
+ export function textSources_grep(expression, ask, layout, sources, named) {
362
+ const after = layout.after ?? layout.context ?? 0;
363
+ const before = layout.before ?? layout.context ?? 0;
364
+ const grouped = (after > 0 || before > 0) && !layout.onlyMatching;
365
+ const every = new RegExp(expression.source, `${expression.flags.replace('g', '')}g`);
366
+ const out = [];
367
+ let selected = 0;
368
+ let printedAny = false;
369
+ for (const source of sources) {
370
+ const name = source.name === '-' || source.name === null ? '(standard input)' : source.name;
371
+ const lines = lines_split(source.text);
372
+ const hits = lines.map((line) => expression.test(line) !== ask.invert);
373
+ const kept = hits.filter((hit) => hit).length;
374
+ selected += kept;
375
+ if (layout.filesWithMatches) {
376
+ if (kept > 0)
377
+ out.push(name);
378
+ continue;
379
+ }
380
+ if (ask.count) {
381
+ out.push(named ? `${name}:${kept}` : String(kept));
382
+ continue;
383
+ }
384
+ const prefix = (at, mark) => `${named ? `${name}${mark}` : ''}${layout.lineNumber ? `${at + 1}${mark}` : ''}`;
385
+ if (layout.onlyMatching) {
386
+ // Under -v the kept lines are those with no match: no part to show.
387
+ if (ask.invert)
388
+ continue;
389
+ lines.forEach((line, at) => {
390
+ for (const match of line.matchAll(every))
391
+ if (match[0] !== '')
392
+ out.push(`${prefix(at, ':')}${match[0]}`);
393
+ });
394
+ continue;
395
+ }
396
+ const shown = lines.map(() => false);
397
+ hits.forEach((hit, at) => {
398
+ if (!hit)
399
+ return;
400
+ for (let near = Math.max(0, at - before); near <= Math.min(lines.length - 1, at + after); near++)
401
+ shown[near] = true;
402
+ });
403
+ let last = -2;
404
+ lines.forEach((line, at) => {
405
+ if (!shown[at])
406
+ return;
407
+ if (grouped && printedAny && at !== last + 1)
408
+ out.push('--');
409
+ out.push(`${prefix(at, hits[at] === true ? ':' : '-')}${line}`);
410
+ printedAny = true;
411
+ last = at;
412
+ });
413
+ }
414
+ return { text: lines_join(out), selected };
415
+ }
416
+ /**
417
+ * Walks a folder for its files, depth first: each file read under its name
418
+ * as shown (the folder as typed, then the names below it). A link met on the
419
+ * way is not followed, as grep -r does not.
420
+ *
421
+ * @param store - The folder's store path.
422
+ * @param shown - The folder as the output names it; empty for the working directory unnamed.
423
+ * @param read - Where the files and the errors go.
424
+ */
425
+ async function folder_walk(store, shown, read) {
426
+ const mark = errorStack.checkpoint_mark();
427
+ const listed = await vfsDispatcher_get().list(store);
428
+ errorStack.checkpoint_drain(mark);
429
+ if (!listed.ok) {
430
+ read.errors += `grep: ${shown || '.'}: cannot list the folder\n`;
431
+ return;
432
+ }
433
+ for (const item of listed.value) {
434
+ const childStore = store === '/' ? `/${item.name}` : `${store}/${item.name}`;
435
+ const childShown = shown === '' ? item.name : `${shown}/${item.name}`;
436
+ if (itemType_isContainer(item.type)) {
437
+ await folder_walk(childStore, childShown, read);
438
+ continue;
439
+ }
440
+ if (item.type === 'link')
441
+ continue;
442
+ const file = await vfsDispatcher_get().read(childStore);
443
+ if (file.ok)
444
+ read.sources.push({ name: childShown, text: file.value });
445
+ else
446
+ read.errors += `grep: ${childShown}: ${file.reason ?? errno_words(file.errno)}\n`;
447
+ }
448
+ }
449
+ /**
450
+ * Reads what grep -r works on: each operand a file, or a folder walked for
451
+ * its files (`-` is the pipe); no operand walks the working directory, its
452
+ * files named below it.
453
+ *
454
+ * @param operands - The operands, as typed.
455
+ * @returns The files read, under the names shown, and whether a folder was walked.
456
+ */
457
+ async function recursiveSources_read(operands) {
458
+ const piped = stdin_take();
459
+ const read = { sources: [], errors: '', walked: false };
460
+ if (operands.length === 0) {
461
+ read.walked = true;
462
+ await folder_walk(await storePath_resolve('.'), '', read);
463
+ return read;
464
+ }
465
+ for (const operand of operands) {
466
+ if (operand === '-') {
467
+ read.sources.push({ name: '-', text: piped ?? '' });
468
+ continue;
469
+ }
470
+ const file = await filterFile_read('grep', operand);
471
+ if (!('error' in file)) {
472
+ read.sources.push(file);
473
+ continue;
474
+ }
475
+ const store = await storePath_resolve(operand);
476
+ const mark = errorStack.checkpoint_mark();
477
+ const listed = await vfsDispatcher_get().list(store);
478
+ errorStack.checkpoint_drain(mark);
479
+ if (!listed.ok) {
480
+ read.errors += file.error;
481
+ continue;
482
+ }
483
+ read.walked = true;
484
+ await folder_walk(store, operand.length > 1 ? operand.replace(/\/+$/, '') : operand, read);
485
+ }
486
+ return read;
487
+ }
488
+ /**
489
+ * grep over text it filters itself: ChRIS files (each read through the one
490
+ * store-path resolver, a folder walked under -r) or the piped text, named
491
+ * as typed.
492
+ *
493
+ * @param ask - What the grep line asks of a match.
494
+ * @param layout - How it writes what it kept.
495
+ * @param files - The file operands (`-` is the pipe); none, with text piped, for the pipe alone.
496
+ * @returns The envelope; exit 1 when no line was kept.
497
+ */
498
+ async function sources_grep(ask, layout, files) {
499
+ const built = grepExpression_build(ask);
500
+ if (!(built instanceof RegExp)) {
501
+ exitCode_set(1);
502
+ return envelope_error('', undefined, `${built.refused}\n`);
503
+ }
504
+ let read;
505
+ if (layout.recursive) {
506
+ read = await recursiveSources_read(files);
507
+ }
508
+ else {
509
+ const plain = await filterSources_read('grep', files);
510
+ if ('refusal' in plain)
511
+ return envelope_error('', undefined, `${plain.refusal}\n`);
512
+ read = { ...plain, walked: false };
513
+ }
514
+ let selected = 0;
515
+ const envelope = filterRead_answer(read, (sources) => {
516
+ const named = layout.withFilename || (!layout.noFilename && (sources.length > 1 || read.walked));
517
+ const answer = textSources_grep(built, ask, layout, sources, named);
518
+ selected = answer.selected;
519
+ return answer.text;
520
+ });
521
+ if (selected === 0)
522
+ exitCode_set(1);
523
+ return envelope;
524
+ }
525
+ /**
526
+ * Plain grep on piped text: the system's, its line written back from the
527
+ * typed options, where the surface runs host programs; otherwise this
528
+ * grep's own filter, with the same flags.
271
529
  *
272
530
  * @param text - The text piped in.
273
- * @param ask - What the grep line asks.
274
- * @param args - The words after `grep`, for the system grep.
531
+ * @param ask - What the grep line asks of a match.
532
+ * @param layout - How it writes what it kept.
275
533
  * @returns The envelope.
276
534
  */
277
- async function text_grep(text, ask, args) {
535
+ async function text_grep(text, ask, layout) {
278
536
  const surface = surface_get();
279
537
  if (surface.capabilities.pipeSegments) {
280
- const out = await surface.pipeSegment(`grep ${args.map(word_quote).join(' ')}`, Buffer.from(text, 'utf-8'));
538
+ const out = await surface.pipeSegment(grepLine_render(ask, layout), Buffer.from(text, 'utf-8'));
281
539
  return envelope_ok(out.toString('utf-8'));
282
540
  }
283
- if (ask.foreign !== null)
284
- return envelope_error('', undefined, `grep ${ask.foreign}: this surface has no system grep to pass it to\n`);
285
- const built = grepMatcher_build(ask);
286
- if (typeof built !== 'function')
541
+ const built = grepExpression_build(ask);
542
+ if (!(built instanceof RegExp))
287
543
  return envelope_error('', undefined, `${built.refused}\n`);
288
- const lines = text.replace(/\n$/, '').split('\n').filter((line) => built(line) !== ask.invert);
289
- if (lines.length === 0)
544
+ const answer = textSources_grep(built, ask, layout, [{ name: null, text }], false);
545
+ if (answer.selected === 0)
290
546
  exitCode_set(1);
291
- return envelope_ok(ask.count ? `${lines.length}\n` : lines.length > 0 ? `${lines.join('\n')}\n` : '');
547
+ return envelope_ok(answer.text);
292
548
  }
293
- /** A word as a POSIX shell reads it back. */
294
- function word_quote(word) {
295
- return /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replace(/'/g, `'\\''`)}'`;
549
+ /**
550
+ * The refusal of a layout flag a numbered listing has no use for, or null.
551
+ *
552
+ * @param layout - How the line asks to write what it kept.
553
+ * @returns The envelope that says so, or null when the line asks none.
554
+ */
555
+ function listingLayout_refuse(layout) {
556
+ const refused = LISTING_REFUSALS.find(({ given }) => given(layout));
557
+ if (refused === undefined)
558
+ return null;
559
+ exitCode_set(1);
560
+ return envelope_error('', undefined, `grep ${refused.flag}: ${refused.why}\n`);
296
561
  }
297
562
  /**
298
- * `grep [-ivwEFc] [-e PATTERN]... [PATTERN]`
563
+ * The typed grep line run: the files named (or walked under -r), else a
564
+ * piped listing, else piped text, else the held listing.
299
565
  *
300
- * @param args - The words after `grep`.
566
+ * @param args - The typed line.
301
567
  * @returns The envelope.
302
568
  */
303
- export async function builtin_grep(args) {
304
- const ask = grepArgs_parse(args);
305
- const line = `grep ${args.map(word_quote).join(' ')}`.trim();
306
- const piped = listingIn_take();
307
- const text = stdin_take();
308
- // A flag or a file this grep does not take: the system grep's, as text.
309
- if (ask.foreign !== null) {
310
- const answered = await text_grep(text ?? '', ask, args);
311
- if (answered.status === 'ok') {
312
- return { ...answered, renderedErr: `${chalk.gray(`grep ${ask.foreign}: passed to the system grep; the held listing is unchanged`)}\n` };
313
- }
314
- return answered;
315
- }
569
+ async function grep_run(args) {
570
+ const given = args.options.regexp;
571
+ const pattern = args.operands.pattern;
572
+ const files = args.operands.files;
573
+ const ask = {
574
+ insensitive: args.options.insensitive, invert: args.options.invert, word: args.options.word,
575
+ extended: args.options.extended, fixed: args.options.fixed, count: args.options.count,
576
+ patterns: given.length > 0 ? given : pattern === undefined ? [] : [pattern],
577
+ };
578
+ const layout = {
579
+ lineNumber: args.options.lineNumber, filesWithMatches: args.options.filesWithMatches, onlyMatching: args.options.onlyMatching,
580
+ noFilename: args.options.noFilename, withFilename: args.options.withFilename, recursive: args.options.recursive,
581
+ after: args.options.after, before: args.options.before, context: args.options.context, color: args.options.color,
582
+ };
316
583
  if (ask.patterns.length === 0)
317
584
  return envelope_error('', undefined, 'grep: a pattern to keep: grep [-ivwEFc] [-e PATTERN] PATTERN\n');
318
- if (piped !== null)
319
- return listing_grep({ ...piped, source: '' }, ask, line);
585
+ const piped = listingIn_take();
586
+ if (piped !== null && files.length === 0) {
587
+ stdin_take();
588
+ return listingLayout_refuse(layout) ?? listing_grep({ ...piped, source: '' }, ask, grepLine_render(ask, layout));
589
+ }
590
+ if (files.length > 0 || layout.recursive)
591
+ return sources_grep(ask, layout, files);
592
+ const text = stdin_take();
320
593
  if (text !== null)
321
- return text_grep(text, ask, args);
594
+ return text_grep(text, ask, layout);
322
595
  // Nothing piped: the held listing, as an index reads it.
323
596
  const held = answer_get();
324
597
  if (held === null)
325
598
  return envelope_error('', undefined, 'grep: nothing to filter: pipe text or a listing in, or list something first (query, ls, gather list)\n');
326
- return listing_grep({ kind: held.kind, data: held.data, source: held.source }, ask, line);
599
+ return listingLayout_refuse(layout) ?? listing_grep({ kind: held.kind, data: held.data, source: held.source }, ask, grepLine_render(ask, layout));
600
+ }
601
+ /**
602
+ * `grep` over words already split: the declaration's parse and handler.
603
+ *
604
+ * @param args - The words after `grep`.
605
+ * @returns The envelope, or the refusal of a bad line.
606
+ *
607
+ * @example
608
+ * ```ts
609
+ * await builtin_grep(['-i', 't1']);
610
+ * ```
611
+ */
612
+ export async function builtin_grep(args) {
613
+ return declaredCommand_run(grepCommand, args);
327
614
  }
328
615
  //# sourceMappingURL=grep.js.map