Finding code
- names, by words in the name, signature or doc
tcm find <words>- terms by the shape of their type
tcm find ':<type>'- text in the code and pages
tcm search <text>- lines of one definition
tcm view <name> --grep <text>
Agents have the same as tcm_find, tcm_search and tcm_view's grep.
By name, signature or doc
tcm find <query> lists the names with every word of the query in the name, its signature or its doc, in any case:
$ tcm find square number
Matches in the name or signature come first, then those found by their doc.
- It reads the branch it runs on. A session's changes to a page, doc or comment are found on its branch as soon
as they're saved, and on
mainonce they land.--branch <name>searches another branch's text, as that branch has it. - Signatures use current type names. After
tcm mv lib.User lib.Person,find,view,ls, the editor and the agent tools show(u: Person) => string.
Text in the code
tcm search <text> is find in files for the codebase (the editor's Search view too). It lists each line with the
text in the codebase's definitions (as they read now, with current names) and pages, by definition, numbered as
tcm view --lines counts them. tcm view <name> --grep <text> shows one definition's lines with it, and two either
side.
Both read the text the same way:
- Text is as written:
a|bis those three characters;(is a parenthesis. - A regular expression is written
/…/, with flags after it:/get|set/i. Its case follows its flags:/get/matches onlyget.
tcm search options:
--case- match case (by default any case matches;
--grepalways matches case) --word- whole words only
--under <namespace>- only a namespace, or one definition
--limit <n>- lines to show (default 200)
--regex- the old way: all the text is a regular expression, without slashes (the editor's Use Regular Expression)
--branch <name>- search another branch
Text that starts with - goes after --: tcm search -- --lines.
Finding nothing says what it looked for: no lines with the text "a|b" (text, as written: a regular expression is written /…/), or no lines matching /a|b/.
A search stops at 500 definitions or 5000 matches, and says so: it stopped at 500 definitions or 5000 matches: the rest of the codebase wasn't searched. Narrow it with more of the text, --word, or --under.
By type
tcm find ':<type>' finds terms by the shape of their type, as Unison's type search does. The query is a
TypeScript type:
$ tcm find ':(xs: T[]) => T | undefined'
type variables: T
lib.first #q5xh6tqo <A>(xs: A[]) => A | undefined
lib.head #glnvgmf6 <T>(xs: T[]) => T | undefined
$ tcm find ':(n: number, s: string) => string'
lib.repeat #66hwcb26 (s: string, n: number) => string arguments reordered
$ tcm find ':(id: string) => User'
lib.loadUser #sjksocbz (id: string) => Promise<User> async
- Type variables: names in the query that are neither codebase types nor TypeScript globals become type
variables, so
(xs: a[]) => aworks like Unison's[a] -> a. Explicit generics (<T>(xs: T[]) => T) work too. - Codebase types are found by name:
User, orlib.Userwhen the short name is ambiguous (tcm says which paths it could be). - Parameter and type-variable names don't matter:
<A>(xs: A[]) => Aand<T>(ys: T[]) => Tare the same shape. - A class itself (
typeof Server, as inhttp.Server<typeof IncomingMessage>) is compared by the class, not by its static members. - Code that came from a push or pull gets its shapes as the branch comes to name it; a search works out any still missing.
Ranking
Exact matches (the same type up to renaming) come first. Then, closest first, each labelled:
arguments reordered- the same parameters in another order
async- returns a
Promiseof the queried result type more specific- an instance of the query:
sum: (xs: number[]) => numberfor(xs: a[]) => a more general- the query is an instance of it:
last: <T>(xs: T[]) => Tfor(xs: number[]) => number partly compared- parts too deep or too big, or conditional or mapped types, were compared only as "some type"
Overloads are searched one by one; a function matches through its closest overload.
How it works
When a term is added, tcm records its type as a shape, kept beside it, not in its hash: functions, parameters,
arrays, tuples, unions, object types and literals. Type parameters become numbered variables. Codebase types are
referenced by hash, so renaming a type (tcm mv lib.User lib.Person) loses no matches and signatures show its
new name. Library types (Promise, Map, a package's types) are referenced by name.
The query is typechecked, turned into a shape the same way, and unified with each term's. Query variables can stand
for parts of a term's type (more specific), and the term's variables for parts of the query (more general).