17. Finding code
17.1 By name, signature or doc
tcm find <query> (the agents' tcm_find) lists the names with every word of query in the name, its signature
text or its doc, in any case: tcm find square number. Those it's in the name or signature of come first, then
those it takes the doc to find. Docs, and the code tcm search reads, are as the branch it runs on shows them: a
session's changes to a page, a doc or a comment are found on its branch as soon as they're saved, and not on main
until they land. Searching another branch (--branch) reads its text as it has it, rendered, never from main's
files (or any other branch's).
A signature names the codebase's types as they're named now: after tcm mv lib.User lib.Person, find, view,
ls, the editor and the agent tools show (u: Person) => string. (A signature is the type as TypeScript printed it
when the definition was made, kept beside it, not part of its hash; the types it names are found by hash, from its
shape, and shown with their current names.)
17.2 Text in the code
tcm search <text> (the agents' tcm_search, the editor's Search view) is find in files for the codebase: each
line of its definitions (as they read now, with current names) and pages with the text in it, by definition, numbered
as tcm view --lines counts them. tcm view <name> --grep <text> (tcm_view's grep) shows a definition's lines
with it, and two either side. Both take what to find the same way:
- Text, as written:
a|bis those three characters,(a parenthesis. - A regular expression is written
/…/, flags after it:/get|set/i. Its case is its flags':/get/matches onlyget. - Text is matched in any case by
tcm search(--casefor its case) and in its case by--grep.--wordkeeps to whole words.--regexis the old way, all the text a regular expression without slashes (and the editor's Use Regular Expression).
Finding nothing says which it looked for: no lines with the text "a|b" (text, as written: a regular expression is written /…/), or no lines matching /a|b/.
--under <namespace> searches only a namespace, or one definition. A search stops at 500 definitions or 5000
matches, and then 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. Of what it finds, the first 200 lines are
shown (--limit <n> for more).
17.3 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
- Code that came from elsewhere (pushed or pulled) comes without the shapes this matches, and gets them as the branch comes to name it; a search works out any still missing.
- 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 names don't matter, nor do type-variable names:
<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.
17.3.1 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.
17.3.2 How it works
When a term is added, tcm records its type as a shape (metadata, not part of the 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) doesn't lose any
matches; library types (Promise, Map, a package's types) by name.
The query is typechecked against the #codebase shim, turned into the same kind of shape, and unified with
each term's: query variables can stand for parts of a definition's type (more specific) and the definition's
variables for parts of the query (more general).