Sessions read #N as every reader of references does
A session tied itself to what #N named with a scanner of its own: it took references inside code, ignored owner/name#N and workspace#N, and read seven digits where references have nine. It reads them with the shared reader now. The rules' notes name the TypeScript copy where it is, and say that #N names another repository's pull request only above the floor.
- G1T-Session:
- ses_01m56vxcstesp8b57jt5edge6y
4 files+77−150/4 viewed
| 1 | 1 | //! What `#N` in text names, the one set of rules every reader of references | |
| 2 | 2 | //! follows: closing keywords in pull requests and commits (work), Markdown | |
| 3 | − | //! on the site, chat and the CLI. `packages/contracts/src/refs.ts` mirrors | |
| 4 | − | //! it. | |
| 3 | + | //! on the site, chat and the CLI. `apps/web/app/lib/refs.ts` mirrors it. | |
| 5 | 4 | //! | |
| 6 | 5 | //! A reference is `#N`, `owner/name#N` (read in that repository's context) | |
| 7 | 6 | //! or `workspace#N` (in that workspace's). Read in a repository R of | |
| ⋯ | |||
| 10 | 9 | //! 1. a pull request of R numbered N; | |
| 11 | 10 | //! 2. a work item filed in R whose number there was N (an old issue); | |
| 12 | 11 | //! 3. the work item numbered N in W; | |
| 13 | − | //! 4. a pull request numbered N in another repository of W (new pull | |
| 14 | − | //! requests are numbered in the workspace, so this is unique). | |
| 12 | + | //! 4. a pull request numbered N in another repository of W, when N is | |
| 13 | + | //! above W's floor: new pull requests are numbered in the workspace, so | |
| 14 | + | //! those are unique; the numbers its repositories used on their own | |
| 15 | + | //! before collide, and name none of them from elsewhere. | |
| 15 | 16 | //! | |
| 16 | 17 | //! Read in a workspace alone, `#N` is the work item N in W, else a pull | |
| 17 | − | //! request numbered N in W. The work service's `resolve_ref` does the | |
| 18 | + | //! request numbered N in W, above its floor. The work service's `resolve_ref` does the | |
| 18 | 19 | //! lookups. | |
| 19 | 20 | //! | |
| 20 | 21 | //! What sits in code, a `span` or a fenced block, is no reference: it is | |
| 247 | 247 | self.link(session_id, repo_id, LinkKind::Item, 0, item_id, via).await; | |
| 248 | 248 | } | |
| 249 | 249 | ||
| 250 | − | /// Ties a session to what `#N` in `text` names, read in its repository | |
| 251 | − | /// (`refs`): one of its pull requests, or a work item of its workspace. | |
| 250 | + | /// Ties a session to what the references in `text` name, read in its | |
| 251 | + | /// repository as every reader of them reads them | |
| 252 | + | /// (`g1t_contracts::work::refs`, outside code): one of its workspace's | |
| 253 | + | /// pull requests, or work items. | |
| 252 | 254 | pub(crate) async fn link_refs(&self, session_id: &str, repo_id: &str, text: &str) -> Result<()> { | |
| 253 | 255 | let Some(workspace) = items::repo_workspace(self, repo_id).await? else { | |
| 254 | 256 | return Ok(()); | |
| 255 | 257 | }; | |
| 256 | − | for number in refs_in(text).into_iter().take(MAX_REFS) { | |
| 257 | − | match items::resolve(self, repo_id, &workspace, number).await? { | |
| 258 | − | Some(Named::PullHere) => self.link_pull(session_id, repo_id, number, LinkVia::Ref).await, | |
| 259 | − | Some(Named::Item(id)) => self.link_item(session_id, repo_id, &id, LinkVia::Ref).await, | |
| 260 | − | _ => {} | |
| 258 | + | for reference in refs::refs_in_text(text).into_iter().take(MAX_REFS) { | |
| 259 | + | let Some((read_in, named)) = items::resolve_in(self, repo_id, &workspace, &reference).await? else { continue }; | |
| 260 | + | match named { | |
| 261 | + | Named::PullHere => self.link_pull(session_id, &read_in, reference.number, LinkVia::Ref).await, | |
| 262 | + | Named::Item(id) => self.link_item(session_id, repo_id, &id, LinkVia::Ref).await, | |
| 263 | + | Named::PullThere(_) => {} | |
| 261 | 264 | } | |
| 262 | 265 | } | |
| 263 | 266 | Ok(()) | |
| ⋯ | |||
| 753 | 756 | session.subagents = counts.get(&session.id).copied().unwrap_or_default(); | |
| 754 | 757 | } | |
| 755 | 758 | } | |
| 759 | + | ||
| 760 | + | #[cfg(test)] | |
| 761 | + | mod ref_tests { | |
| 762 | + | use g1t_contracts::work::refs::{self, Ref, RefScope}; | |
| 763 | + | ||
| 764 | + | #[test] | |
| 765 | + | fn a_session_reads_references_as_every_reader_does() { | |
| 766 | + | // What link_refs reads: never inside code, and owner/name#N and | |
| 767 | + | // workspace#N as what they name. | |
| 768 | + | let found = refs::refs_in_text("Fixing `#5` in acme/web#7, see acme#9 and #123456789"); | |
| 769 | + | let scopes: Vec<(String, u32)> = found | |
| 770 | + | .iter() | |
| 771 | + | .map(|Ref { scope, number }| { | |
| 772 | + | let scope = match scope { | |
| 773 | + | RefScope::Here => "here".to_owned(), | |
| 774 | + | RefScope::Repo(path) => format!("{}/{}", path.namespace, path.name), | |
| 775 | + | RefScope::Workspace(slug) => slug.clone(), | |
| 776 | + | }; | |
| 777 | + | (scope, *number) | |
| 778 | + | }) | |
| 779 | + | .collect(); | |
| 780 | + | assert_eq!(scopes, vec![("acme/web".to_owned(), 7), ("acme".to_owned(), 9), ("here".to_owned(), 123_456_789)]); | |
| 781 | + | } | |
| 782 | + | } | |
| 143 | 143 | ||
| 144 | 144 | // --- References and landing ------------------------------------------------ | |
| 145 | 145 | ||
| 146 | − | /// What `#number` names, read in repository `repo_id` of `workspace`. | |
| 147 | − | pub(crate) async fn resolve(work: &Work, repo_id: &str, workspace: &str, number: u32) -> Result<Option<Named>> { | |
| 148 | − | Ok(work.lookup_ref(Some(repo_id), workspace, number).await?.named(true)) | |
| 146 | + | /// What `reference` names, read in repository `repo_id` of `workspace`, | |
| 147 | + | /// with the repository it is read in (another of the workspace's, for | |
| 148 | + | /// `owner/name#N`): none for another workspace's, or a repository not | |
| 149 | + | /// known. | |
| 150 | + | pub(crate) async fn resolve_in(work: &Work, repo_id: &str, workspace: &str, reference: &Ref) -> Result<Option<(String, Named)>> { | |
| 151 | + | work.resolve_in(repo_id, workspace, reference).await | |
| 149 | 152 | } | |
| 150 | 153 | ||
| 151 | 154 | /// The work item `reference` names, read in `repo`, by id: one of its |
| 219 | 219 | }) | |
| 220 | 220 | } | |
| 221 | 221 | ||
| 222 | + | /// What `reference` names read in `repo_id` of `workspace`, and the | |
| 223 | + | /// repository it is read in ([`super::resolve_in`]). | |
| 224 | + | pub(super) async fn resolve_in(&self, repo_id: &str, workspace: &str, reference: &Ref) -> Result<Option<(String, Named)>> { | |
| 225 | + | let read_in = match &reference.scope { | |
| 226 | + | RefScope::Here => Some(repo_id.to_owned()), | |
| 227 | + | RefScope::Workspace(named) => { | |
| 228 | + | if !named.eq_ignore_ascii_case(workspace) { | |
| 229 | + | return Ok(None); | |
| 230 | + | } | |
| 231 | + | None | |
| 232 | + | } | |
| 233 | + | RefScope::Repo(path) => { | |
| 234 | + | if !path.namespace.eq_ignore_ascii_case(workspace) { | |
| 235 | + | return Ok(None); | |
| 236 | + | } | |
| 237 | + | let known = self | |
| 238 | + | .db | |
| 239 | + | .prepare("SELECT repo_id AS value FROM repo_workspaces WHERE lower(path) = lower(?)") | |
| 240 | + | .bind(&[format!("{}/{}", path.namespace, path.name).into()])? | |
| 241 | + | .first::<ValueRow>(None) | |
| 242 | + | .await?; | |
| 243 | + | match known { | |
| 244 | + | Some(row) => Some(row.value), | |
| 245 | + | None => return Ok(None), | |
| 246 | + | } | |
| 247 | + | } | |
| 248 | + | }; | |
| 249 | + | let found = self.lookup_ref(read_in.as_deref(), workspace, reference.number).await?; | |
| 250 | + | Ok(found.named(read_in.is_some()).map(|named| (read_in.unwrap_or_else(|| repo_id.to_owned()), named))) | |
| 251 | + | } | |
| 252 | + | ||
| 222 | 253 | /// The work items `text`, written in `repo`, closes and is part of, | |
| 223 | 254 | /// with `own` (a pull request's own work item) closed first. Each of | |
| 224 | 255 | /// `who` (whoever wrote it, merges it or pushed it) must be able to |
Comments
Sign in to comment.