Project tree — explanation
What is this about?
F9 opens a window listing the project's files, and pressing Enter on one opens it. This page is about the three decisions inside that: where the tree is rooted, why it is a window rather than a panel down the side, and why it does not notice files appearing on its own.
Why the root is the working directory
The editor already contains two different answers to "what is the project".
The language server is started in the directory of the file you opened. Golo has no project manifest, so there is nothing to walk up to, and golo lsp needs no root: it answers about the file it is given. The project settings file does not walk either: .turbo-golo/settings.toml is looked for in the working directory and nowhere else.
The tree follows the settings file, and it is worth saying why another rule was tempting. An explorer usually roots itself at something it finds by walking up — a manifest, or the nearest .git — so that opening a file from anywhere in a project shows the whole project. Golo offers no manifest to find, and rooting at .git would make the tree's root depend on a directory three levels away that you may not have thought about; it stops being predictable the moment a repository holds more than one program — a monorepo would show you the whole repository whichever script you happened to open.
The rule kept is the one that can be said in a sentence and is true everywhere in the editor: the project is the directory you started the editor in. It costs something, and the cost is named in the how-to: start from a subdirectory and you get a tree of that subdirectory. The answer is to start from the project root, which is where you would run golo --test and git anyway.
Why .git is hidden and nothing else is
The Open dialog hides every entry beginning with a dot. Copying that here was the obvious thing and would have been wrong.
.turbo-golo/settings.toml is a file this editor asks people to edit — it is why the editor colours TOML at all. .gitignore and .qlty/qlty.toml are files of the project too. A tree that hid them would make the editor's own configuration unreachable from the editor's own file browser, which is an odd place to end up.
.git is different in kind rather than in spelling: nothing inside it is meant to be opened by hand, and it holds enough objects to bury everything else in the listing. One name, hidden for a reason that can be stated. Respecting .gitignore as well was considered and turned down for now: it would hide bin/ and release/, which is genuinely nicer, and it costs a gitignore pattern engine — negation, **, anchoring — that is a feature in its own right rather than a detail of a tree.
Why it is a window, not a panel
Every other editor puts its file tree in a fixed strip down the left. That was the alternative, and it was turned down because of what it would have cost the rest of the editor.
A docked panel means the desktop is no longer a single rectangle that windows live in. Desktop would need a notion of reserved edges; Window.fitInto and the grow modes would have to respect them; maximising would mean "the whole desktop except the panel"; tiling and cascading would need to know about it. That is a change to the foundation of the whole interface, for one widget.
As an ordinary window, the tree gets everything for free and behaves like everything else: F6 reaches it, Alt-2 raises it, [x] closes it, [■] fills the desktop with it, Window ▸ Tile puts it beside your file. Nothing in ui had to change. If a docked panel is wanted later, it is a ui feature to be designed on its own terms rather than something smuggled in with a file browser.
Why there is only one
Two trees on the same project would be two views of one thing with nothing to tell them apart, and the project cannot change while the editor runs — the root is fixed at start-up. So F9 on an open tree raises it rather than making another, the same way opening a file that is already open raises its window.
Why it does not watch the disk
A tree that noticed gogolo build producing an executable would be better. Doing it properly means watching the filesystem, and in Go that means fsnotify — a third dependency, against a project that has kept to two since it started and treats adding one as a decision to be argued for.
It is not a small dependency in behaviour either: recursive watches, watch descriptors running out on large trees, and different semantics on every platform, in a feature whose failure mode is a stale line in a list.
So the tree re-reads on demand, and the editor picks the moments it can be sure about. Saving a file is one: the editor did it, so it knows. F5 and Ctrl-R are the other, because a build in a terminal window is something only the user knows has finished. Refreshing keeps the shape of the tree and re-reads only the directories that were actually opened, so it costs what is on screen rather than a walk of the project.
Why the tree has theme keys of its own
The obvious economy was to draw it with the list.* keys — a tree is a list, after all, and it would have meant no new keys for user themes to miss.
It does not work, and the reason is worth recording. list.selected is coloured to stand out against a dialog. In turbo-classic it is white on navy, and window.body is silver on navy — a tree in a window would have highlighted its selected row in exactly the background colour it sits on. The selection would have been invisible in the theme the editor ships as its default.
So tree.text, tree.directory, tree.selected and tree.unfocused exist, and a test holds every shipped theme to a minimum contrast between the first and the third, in the same way the cursor colours are checked. A user theme that sets none of them falls back along the dots to default: a readable tree without the file-and-directory distinction, rather than nothing at all.
How it relates to the rest
- Every key and every rule, exactly: Project tree reference
- Using it: How to browse a project and open files from a tree
- The other place "the project" is defined the same way: Project settings
- Where
filetreesits among the packages: Architecture
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|