on codex/animated-progress-indicators help sign in
j/config.j
615 lines 27 kB history raw
1-- config.j — everything j knows.
2--
3-- j reads this file on every run. An expression can use a name only if this
4-- file declares it (a builtin, implemented by j) or defines it (written here,
5-- in the language). Nothing else exists.
6--
7-- Conventions in this file:
8-- Name = … a shape: a record layout, matched by its field names
9-- name : Type a signature. Alone, it declares a builtin implemented
10-- by j. Before a definition, it documents that definition.
11-- Either way j checks arguments and results against it at
12-- each call, one level deep: kind, list-ness, field names.
13-- name = expr a definition. Functions are lambdas: name = \x -> …
14--
15-- Edits. Most definitions are Edits: functions from a Repo to a Repo. On the
16-- command line a function result is applied to the current repository; a Repo
17-- result is persisted. Anything else is printed. `e2 . e1` runs e1 then e2;
18-- `e or id` runs e and leaves things alone if it crashes.
19--
20-- Four definitions here are read by j itself: `user` (who commits), `tree`
21-- (how a Repo is printed), `immutable` (which commits may never be
22-- rewritten), and `labelled` (what a %name literal means).
25------------------------------------------------------------------------------
26-- You
27------------------------------------------------------------------------------
29user : { name : Text, email : Text }
30user = { name = "Your Name", email = "you@example.com" }
33------------------------------------------------------------------------------
34-- Shapes
35------------------------------------------------------------------------------
37Path = [Text] -- a file path as its components; written ./a/b
38Label = Text -- a bookmark name on the remote
40Entry = { path : Path, content : Blob }
41Snapshot = [Entry] -- the files of one commit; paths unique;
42 -- a deleted, resolved path has no entry
43Change = { from : Snapshot, to : Snapshot } -- the files before and after
45Commit = { files : Snapshot
46 , message : Text
47 , labels : [Label] -- the remote's names for this commit;
48 -- read-only: only fetch and push change them
49 , id : Id } -- each id is on exactly one commit
51Subtree = { root : Commit, children : [Subtree] }
53-- A Repo is the whole history, seen from one commit: the focused subtree plus
54-- the path back to the top. Each Frame is one ancestor with a hole where the
55-- focused subtree was; left and right are the siblings on either side. Sibling
56-- order is presentation only.
57Frame = { parent : Commit, left : [Subtree], right : [Subtree] }
58Repo = { root : Commit, children : [Subtree], context : [Frame] }
59 -- context = [] at the top of history
61Detached = { subtree : Subtree, rest : Repo }
62Rebase = { from : Snapshot, onto : Snapshot } -- the base a subtree was written on, and the new one
63Meta = { hash : Text, author : Text, email : Text, time : Int }
64 -- what jj records about a commit beyond the model
66Edit = Repo -> Repo -- a command; may crash
67Fileset = Path -> Bool -- which paths a command applies to
68Revset = Repo -> [Id] -- a set of commits, computed from a repo
70Push = { id : Id, name : Label } -- set the remote's bookmark `name` to `id`
71Drop = { delete : Label } -- delete the remote's bookmark
74------------------------------------------------------------------------------
75-- Builtins: functions and control
76------------------------------------------------------------------------------
78(.) : (b -> c) -> (a -> b) -> a -> c -- composition, right to left
79id : a -> a
80const : a -> b -> a
81crash : Text -> a -- abort the program with a message
83-- `a or b` is a keyword, not a builtin: the value of a; or, if evaluating a
84-- crashed, the value of b. When a is a function and b is one too, the result
85-- is pointwise, (f or g) x = f x or g x, so `edit or id` tries an edit and
86-- keeps the repository if it fails. Edits compose with `.`: `tree . squash`
87-- squashes, then renders.
89------------------------------------------------------------------------------
90-- Builtins: comparison, booleans, integers
91------------------------------------------------------------------------------
93(==) : a -> a -> Bool -- structural; functions crash
94(/=) : a -> a -> Bool
95(&&) : Bool -> Bool -> Bool -- short-circuit
96(||) : Bool -> Bool -> Bool -- short-circuit
97not : Bool -> Bool
98(+) : Int -> Int -> Int
99(-) : Int -> Int -> Int
100(*) : Int -> Int -> Int
101(<) : Int -> Int -> Bool
102(<=) : Int -> Int -> Bool
103(>) : Int -> Int -> Bool
104(>=) : Int -> Int -> Bool
105show : a -> Text -- any value, in this language's syntax
107------------------------------------------------------------------------------
108-- Builtins: lists and text
109------------------------------------------------------------------------------
111(::) : a -> [a] -> [a] -- cons
112map : (a -> b) -> [a] -> [b]
113filter : (a -> Bool) -> [a] -> [a]
114length : [a] -> Int
115null : [a] -> Bool
116head : [a] -> a -- crash on []
117tail : [a] -> [a] -- crash on []
118last : [a] -> a -- crash on []
119nth : Int -> [a] -> a -- 0-based; crash out of range
120take : Int -> [a] -> [a]
121drop : Int -> [a] -> [a]
122member : a -> [a] -> Bool
123range : Int -> Int -> [Int] -- range a b = [a (a+1) … (b-1)]
124foldl : (b -> a -> b) -> b -> [a] -> b -- foldl f z [x y] = f (f z x) y
126-- ++ and concat work on lists and on text. concat [] crashes: supply a
127-- default with or, e.g. concat xs or []
128(++) : m -> m -> m
129concat : [m] -> m
131startsWith : Text -> Text -> Bool
132endsWith : Text -> Text -> Bool
133splitOn : Text -> Text -> [Text] -- splitOn "/" "a/b" = ["a" "b"]
135------------------------------------------------------------------------------
136-- Builtins: the repository
137------------------------------------------------------------------------------
139-- Replay a change onto a snapshot: "onto, with the change from `from` to `to`
140-- applied." Where the change collides with what is already there, the path's
141-- blob is unresolved rather than an error, except that a collision making a
142-- path a file on one side and a directory on another, as adding ./a/b onto a
143-- file ./a would, crashes, naming the path (§7.3).
144replay : Snapshot -> Change -> Snapshot
145unresolved : Blob -> Bool
146touchedPaths : Change -> [Path] -- paths whose content differs, one pass;
147 -- blobs compared by content id, bytes never read
148blob : Text -> Blob -- a resolved regular file
149text : Blob -> Text -- its content (markers if unresolved)
150by : Id -> Repo -> Repo -- focus the commit with this id; crash if absent
151lookupCommit : Id -> Repo -> Commit -- indexed commit lookup without moving the focus
152lookupContent : Path -> Snapshot -> Blob -- indexed file content; an empty blob if absent
153subtreeCommits : a -> [Commit] -- every commit of a tree in preorder, in one
154 -- pass: what `commits` (below) is
155meta : Id -> Meta -- git hash, author, email, committer time;
156 -- crash for a commit not yet persisted
157validate : Repo -> Repo -- the repo unchanged, or the crash persisting
158 -- it would produce (shape, root, labels, immutability,
159 -- focus). `tree . validate . rebase trunk` is an
160 -- honest dry run.
161diff : Blob -> Blob -> Text -- unified diff from the first to the second
162difft : Path -> Blob -> Blob -> Text -- difftastic's rendering; needs difft on PATH.
163 -- Options via DFT_* environment variables.
164treeWith : { detail : Int, margin : Bool, elide : Bool, icons : Bool, color : Text
165 , lanes : Int, author : Bool, date : Bool, files : Bool }
166 -> Repo -> Text -- the history as a tree; see the spec
168-- Every subvalue of a given shape, however deeply nested. A shape is a type
169-- name used as a value: extract Commit x, extract Id x, extract Push ys.
170extract : Shape -> a -> [b]
172-- Ids: an existing commit is written @kpqx (any unique prefix; checked before
173-- the program runs). A bare @ is a new id, distinct each time it is evaluated.
174-- A label is written %main and is a Revset: the commit the remote calls "main".
177------------------------------------------------------------------------------
178-- Zipper. A Repo is focused on one commit; these move or detach the focus.
179-- Commands never select a commit by position: use ids, labels, or predicates.
180------------------------------------------------------------------------------
182-- To the parent. Crashes at the top.
183up : Edit
184up = \repo ->
185 let f = head repo.context
186 in { root = f.parent
187 , children = f.left ++ [{ root = repo.root, children = repo.children }] ++ f.right
188 , context = tail repo.context }
190-- Detach the focused subtree; the focus moves to the parent. Crashes at the top.
191detach : Repo -> Detached
192detach = \repo ->
193 let f = head repo.context
194 in { subtree = { root = repo.root, children = repo.children }
195 , rest = { root = f.parent, children = f.left ++ f.right, context = tail repo.context } }
197-- To the top of history.
198top : Edit
199top = \repo -> top (up repo) or repo
201-- Every commit of a subtree (or of the focused subtree of a Repo), in preorder.
202-- Takes anything with `root` and `children`, so the signature is left open.
203-- The builtin is \t -> t.root :: (concat (map commits t.children) or [])
204-- in one pass; written that way it copies the list below every commit, and
205-- so takes time quadratic in the length of the history.
206commits : a -> [Commit]
207commits = subtreeCommits
210------------------------------------------------------------------------------
211-- Structure. Editing at the focus.
212------------------------------------------------------------------------------
214mapRoot : (Commit -> Commit) -> Edit
215mapRoot = \f repo -> repo { root = f repo.root }
217mapChildren : ([Subtree] -> [Subtree]) -> Edit
218mapChildren = \f repo -> repo { children = f repo.children }
220-- Attach a subtree as a new child of the focus; the focus does not move.
221attach : Subtree -> Edit
222attach = \t -> mapChildren (++ [t])
224-- Remove the focused commit; its children join the parent. Focus to the parent.
225remove : Edit
226remove = \repo -> let x = detach repo
227 in x.rest { children = x.rest.children ++ x.subtree.children }
229-- Attach a commit as a new child of the focus, and move to it.
230addChild : Commit -> Edit
231addChild = \c repo -> by c.id (attach ({ root = c, children = [] }) repo)
233-- A new commit with these files, no message, no labels, and a new id.
234newCommit : Snapshot -> Commit
235newCommit = \files -> { files = files, message = "", labels = [], id = @ }
238------------------------------------------------------------------------------
239-- Snapshots, changes, and filesets
240------------------------------------------------------------------------------
242-- A path is written ./src/lexer.rs, which is ["src" "lexer.rs"]; ./ alone is
243-- the root, []. For names the literal cannot spell, splitOn "/" "…" builds one.
245restrict : Fileset -> Snapshot -> Snapshot -- entries matching m
246restrict = \m -> filter (\e -> m e.path)
248select : Fileset -> Snapshot -> Snapshot -> Snapshot -- m from a, the rest from b
249select = \m a b -> restrict m a ++ restrict (neg m) b
251conflicted : Snapshot -> [Path]
252conflicted = \s -> map (.path) (filter (\e -> unresolved e.content) s)
254-- What the focus changes: its parent's files, and its own.
255changeOf : Repo -> Change
256changeOf = \repo -> { from = (up repo).root.files or [], to = repo.root.files }
258-- The same change, backwards.
259invert : Change -> Change
260invert = \ch -> { from = ch.to, to = ch.from }
262everything : Fileset
263everything = const true
265neg : Fileset -> Fileset
266neg = \m -> not . m
268both : Fileset -> Fileset -> Fileset
269both = \m n p -> m p && n p
271either : Fileset -> Fileset -> Fileset
272either = \m n p -> m p || n p
274under : Path -> Fileset -- inside directory d
275under = \d p -> take (length d) p == d
277ext : Text -> Fileset -- file extension e
278ext = \e p -> endsWith ("." ++ e) (last p)
281------------------------------------------------------------------------------
282-- Revsets. A revset is a function from a Repo to a list of ids, so it can be
283-- relative to the focus (parents, kids) or absolute (all, %main).
284------------------------------------------------------------------------------
286here : Revset
287here = \repo -> [repo.root.id]
289parents : Revset
290parents = \repo -> [(up repo).root.id] or []
292kids : Revset
293kids = \repo -> map (\t -> t.root.id) repo.children
295descendants : Revset -- includes the focus
296descendants = \repo -> map (.id) (commits repo)
298ancestors : Revset -- includes the focus
299ancestors = \repo -> repo.root.id :: map (\f -> f.parent.id) repo.context
301siblings : Revset
302siblings = \repo -> filter (\i -> i /= repo.root.id) (kids (up repo)) or []
304all : Revset
305all = \repo -> descendants (top repo)
307stack : Revset -- the line through the focus
308stack = union ancestors descendants
310labelled : Label -> Revset -- %main is labelled "main"
311labelled = \n repo -> map (.id) (filter (\c -> member n c.labels) (commits (top repo)))
313-- Evaluate f starting from each commit of rs, and collect.
314from : Revset -> Revset -> Revset
315from = \rs f repo -> concat (map (\i -> f (by i repo)) (rs repo)) or []
317descendantsOf : Revset -> Revset
318descendantsOf = \rs -> from rs descendants
320ancestorsOf : Revset -> Revset
321ancestorsOf = \rs -> from rs ancestors
323union : Revset -> Revset -> Revset
324union = \a b repo -> let xs = a repo in xs ++ filter (\i -> not (member i xs)) (b repo)
326intersect : Revset -> Revset -> Revset
327intersect = \a b repo -> let ys = b repo in filter (\i -> member i ys) (a repo)
329minus : Revset -> Revset -> Revset
330minus = \a b repo -> let ys = b repo in filter (\i -> not (member i ys)) (a repo)
332matching : (Commit -> Bool) -> Revset -> Revset
333matching = \p rs repo -> filter (\i -> p (commitAt i repo)) (rs repo)
335conflicts : Revset
336conflicts = matching (\c -> not (null (conflicted c.files))) all
338-- The first of several revsets that is non-empty.
339firstOf : [Revset] -> Revset
340firstOf = \rss repo -> foldl (\acc rs -> if null acc then rs repo else acc) [] rss
342-- The main line: whichever of these names the remote has, first one wins.
343-- Empty in a repository with no remote, in which case nothing is immutable.
344trunk : Revset
345trunk = firstOf [%main %master %trunk]
347-- Commits that may never be rewritten, abandoned, or focused. j evaluates this
348-- before persisting anything and refuses to change what it names. Merge
349-- commits and their ancestors are immutable regardless. The focus must always
350-- be mutable, so it is `new . goto trunk`, never `goto trunk` alone.
351immutable : Revset
352immutable = ancestorsOf trunk
355------------------------------------------------------------------------------
356-- Focusing. `goto` is the only place a revset must have exactly one member.
357------------------------------------------------------------------------------
359-- Move to the single commit selected by a revset.
360-- Example: j 'goto %feature'; use j 'new . goto trunk' to start on the main line.
361goto : Revset -> Edit
362goto = \rs repo ->
363 let is = rs repo
364 in if length is == 1
365 then by (head is) repo
366 else crash ("expected one revision, got " ++ show (length is))
368commitAt : Id -> Repo -> Commit
369commitAt = lookupCommit
371prev : Edit -- the parent
372prev = goto parents
374next : Edit -- the only child
375next = goto kids
377tip : Edit -- end of the line of only-children
378tip = \repo -> tip (next repo) or repo
380into : Id -> Edit -- a child, by id
381into = \i repo -> if member i (kids repo) then by i repo
382 else crash "into: not a child of the focus"
384child : (Commit -> Bool) -> Edit -- the one child satisfying p
385child = \p -> goto (matching p kids)
387-- Run an edit somewhere else, then come back (by id, so the edit may move things).
388at : Revset -> Edit -> Edit
389at = \rs e repo -> let me = repo.root.id in by me (e (goto rs repo))
391-- Run an edit at every commit of a revset, returning to the focus.
392forEach : Revset -> Edit -> Edit
393forEach = \rs e repo -> foldl (\acc i -> at (const [i]) e acc) repo (rs repo)
395eachChild : Edit -> Edit
396eachChild = forEach kids
398guard : (Repo -> Bool) -> Edit -- crash unless p holds
399guard = \p repo -> if p repo then repo else crash "guard"
401clean : Repo -> Bool
402clean = \repo -> null (conflicted repo.root.files)
405------------------------------------------------------------------------------
406-- Rebasing
407------------------------------------------------------------------------------
409-- Rewrite a subtree as if it had been written on `onto` instead of `from`.
410-- Each commit's change is replayed onto the new base; its children are then
411-- replayed from its old files onto its new ones.
412replayTree : Rebase -> Subtree -> Subtree
413replayTree = \rb t ->
414 let c = t.root
415 c' = c { files = replay rb.onto ({ from = rb.from, to = c.files }) }
416 in { root = c', children = map (replayTree ({ from = c.files, onto = c'.files })) t.children }
418-- Replay the focused subtree onto the commit newP, in place. The subtree stays
419-- attached where it is; only its files change. Move it afterwards with detach
420-- and attach. Crashes at the top.
421replayOnto : Commit -> Edit
422replayOnto = \newP repo ->
423 let t = replayTree ({ from = (up repo).root.files, onto = newP.files })
424 ({ root = repo.root, children = repo.children })
425 in repo { root = t.root, children = t.children }
427-- Replace the focused commit with f applied to it, replaying its children.
428rewrite : (Commit -> Commit) -> Edit
429rewrite = \f repo ->
430 let c = repo.root
431 c' = f c
432 in (eachChild (replayOnto c') repo) { root = c' }
435------------------------------------------------------------------------------
436-- Commands
437------------------------------------------------------------------------------
439-- Set the message of the focused commit.
440-- Example: j 'describe "fix login"'
441describe : Text -> Edit
442describe = \m -> mapRoot (\c -> c { message = m })
444-- Start an empty child of the focus and move to it.
445-- Examples: j new; j 'describe "next change" . new'
446new : Edit
447new = \repo -> addChild (newCommit repo.root.files) repo
449-- Remove the focused commit. Its children are rebased onto its parent, and a
450-- new empty commit on that parent becomes the focus (the parent may be
451-- immutable, and the focus never is). Refused for a commit the remote has a
452-- name for: push something else under that name first, or forget the name.
453-- Example: j abandon; preview first with j 'tree . abandon'.
454abandon : Edit
455abandon = \repo ->
456 let c = repo.root
457 p = (up repo).root
458 in if not (null c.labels)
459 then crash ("abandon: the remote names this commit " ++ show c.labels)
460 else new (remove (eachChild (replayOnto p) repo))
462-- Push the files matching m from the focus down into its parent.
463-- Example: j 'contract (ext "rs")'
464contract : Fileset -> Edit
465contract = \m repo -> let a = repo.root
466 in at parents (rewrite (\q -> q { files = select m a.files q.files })) repo
468-- Fold the focus entirely into its parent; a new empty commit becomes the focus.
469-- Example: j squash; preview first with j 'tree . validate . squash'.
470squash : Edit
471squash = abandon . contract everything
473-- Push the files matching m from the focus up into a new child, which becomes
474-- the focus and inherits the old focus's children. Labels stay with the
475-- original commit, since they are the remote's.
476-- Example: j 'split (ext "rs")'; use (under ./src) to select a directory.
477split : Fileset -> Edit
478split = \m repo ->
479 let a = repo.root
480 p = (up repo).root
481 a' = a { files = select m p.files a.files }
482 x = newCommit a.files
483 in by x.id (repo { root = a', children = [{ root = x, children = repo.children }] })
485-- Move the focused subtree onto another commit. Returns to the moved commit.
486-- Example: j 'rebase trunk'; preview with j 'tree . validate . rebase trunk'.
487rebase : Revset -> Edit
488rebase = \rs repo ->
489 let d = (goto rs repo).root
490 in if member d.id (descendants repo)
491 then crash "rebase: the destination is inside the subtree being moved"
492 else let me = repo.root.id
493 x = detach (replayOnto d repo)
494 in by me (attach x.subtree (by d.id x.rest))
496-- Apply the focused commit's change onto another commit, as a new child there.
497-- Example: j 'pick %main'
498pick : Revset -> Edit
499pick = \rs repo ->
500 let ch = changeOf repo
501 t = (goto rs repo).root
502 in at rs (addChild (newCommit (replay t.files ch))) repo
504-- A new child of the focus that undoes the change of the given commit.
505-- Example: j 'backout %feature'
506backout : Revset -> Edit
507backout = \rs repo ->
508 let ch = changeOf (goto rs repo)
509 in addChild (newCommit (replay repo.root.files (invert ch))) repo
512------------------------------------------------------------------------------
513-- Pushing. `j push EXPR` evaluates EXPR to a list of Push and Drop records.
514------------------------------------------------------------------------------
516-- Label every commit of a revset. `j 'push (label "feature" here)'` labels @.
517label : Label -> Revset -> Repo -> [Push]
518label = \n rs repo -> map (\i -> { id = i, name = n }) (rs repo)
520-- Remove a label on the remote. `j 'push (unlabel "feature")'` after the merge.
521unlabel : Label -> [Drop]
522unlabel = \n -> [{ delete = n }]
524-- Move a label: `j 'push (rename "old" "new")'`.
525rename : Label -> Label -> Repo -> [b]
526rename = \old new repo -> label new (labelled old) repo ++ unlabel old
528-- Every label the remote already has, at its current local commit.
529-- `j push relabel` after rewriting a stack moves the remote's bookmarks along.
530relabel : Repo -> [Push]
531relabel = \repo -> concat (map (\c -> map (\n -> { id = c.id, name = n }) c.labels)
532 (extract Commit repo)) or []
535------------------------------------------------------------------------------
536-- Inspection. These return printable values rather than a Repo.
537------------------------------------------------------------------------------
539focus : Repo -> Commit -- the focused commit
540focus = \repo -> repo.root
542files : Repo -> Snapshot -- its files
543files = \repo -> repo.root.files
545-- The entry at path p in snapshot s, as a list: [] if absent.
546entryAt : Path -> Snapshot -> [Entry]
547entryAt = \p s -> filter (\e -> e.path == p) s
549-- The content at path p, or an empty file if absent.
550contentAt : Path -> Snapshot -> Blob
551contentAt = lookupContent
553-- The paths a change touches.
554touched : Change -> [Path]
555touched = touchedPaths
557-- Paths whose content differs between the focus and its parent.
558-- Example: j changed
559changed : Repo -> [Path]
560changed = \repo -> touched (changeOf repo)
562-- What the focus changes, rendered by difftastic, one record per changed path.
563-- Replace difft with (\p -> diff) to use the built-in unified diff instead.
564diffs : Repo -> [{ path : Path, diff : Text }]
565diffs = \repo ->
566 let ch = changeOf repo
567 in map (\p -> { path = p, diff = difft p (contentAt p ch.from) (contentAt p ch.to) }) (touched ch)
569-- The same, as one page of text. A Text result prints raw, so `j review` is
570-- readable in the terminal. A focus that changes nothing reads "no changes";
571-- a difft that cannot be run (not on PATH) is a crash saying so, and
572-- otherwise whatever difft prints, even nothing, is the page.
573-- Example: j review (requires difftastic's difft on PATH).
574review : Repo -> Text
575review = \repo ->
576 let ds = diffs repo
577 in if null ds then "no changes\n" else concat (map (\d -> d.diff) ds)
579-- A summary of the focus.
580-- Example: j status
581status : Repo -> { id : Id, message : Text, labels : [Label], changed : [Path], conflicts : [Path] }
582status = \repo ->
583 { id = repo.root.id
584 , message = repo.root.message
585 , labels = repo.root.labels
586 , changed = changed repo
587 , conflicts = conflicted repo.root.files }
589-- A summary of every commit, top first, in preorder.
590-- Example: j log
591log : Repo -> [{ id : Id, message : Text, labels : [Label], focus : Bool }]
592log = \repo ->
593 map (\c -> { id = c.id
594 , message = c.message
595 , labels = c.labels
596 , focus = c.id == repo.root.id })
597 (commits (top repo))
599-- The history as a tree. Edit these records to change what `j tree` shows.
600-- (The parentheses are required: `f { … }` would be a record update of f.)
601-- The default `tree` shows every commit, sorted by time (parents always above
602-- their children); `treeCompact` folds uninteresting runs and far subtrees.
603-- Examples: j tree; j 'tree . squash' previews an edit without saving it.
604tree : Repo -> Text
605tree = treeWith ({ detail = 1, margin = false, elide = false, icons = false, color = "auto", lanes = 4, author = false, date = false, files = false })
607treeCompact : Repo -> Text
608treeCompact = treeWith ({ detail = 1, margin = false, elide = true, icons = false, color = "auto", lanes = 4, author = false, date = false, files = false })
610treeFull : Repo -> Text
611treeFull = treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto", lanes = 4, author = false, date = false, files = false })
613-- The full tree plus the extra data columns: date, files changed, author.
614treeData : Repo -> Text
615treeData = treeWith ({ detail = 2, margin = true, elide = false, icons = false, color = "auto", lanes = 4, author = true, date = true, files = true })