@ascribed/review 0.1.2-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +183 -0
- package/dist/github/index.d.ts +7 -0
- package/dist/github/index.js +8 -0
- package/dist/github/index.js.map +1 -0
- package/dist/github/marker.d.ts +63 -0
- package/dist/github/marker.js +156 -0
- package/dist/github/marker.js.map +1 -0
- package/dist/github/queries.d.ts +16 -0
- package/dist/github/queries.js +174 -0
- package/dist/github/queries.js.map +1 -0
- package/dist/github/read.d.ts +84 -0
- package/dist/github/read.js +141 -0
- package/dist/github/read.js.map +1 -0
- package/dist/github/repository.d.ts +30 -0
- package/dist/github/repository.js +77 -0
- package/dist/github/repository.js.map +1 -0
- package/dist/github/requests.d.ts +65 -0
- package/dist/github/requests.js +274 -0
- package/dist/github/requests.js.map +1 -0
- package/dist/github/session.d.ts +143 -0
- package/dist/github/session.js +581 -0
- package/dist/github/session.js.map +1 -0
- package/dist/github/transport.d.ts +33 -0
- package/dist/github/transport.js +206 -0
- package/dist/github/transport.js.map +1 -0
- package/dist/marks/index.d.ts +92 -0
- package/dist/marks/index.js +766 -0
- package/dist/marks/index.js.map +1 -0
- package/dist/overlay/index.d.ts +4 -0
- package/dist/overlay/index.js +7 -0
- package/dist/overlay/index.js.map +1 -0
- package/dist/overlay/markdown.d.ts +4 -0
- package/dist/overlay/markdown.js +368 -0
- package/dist/overlay/markdown.js.map +1 -0
- package/dist/overlay/overlay.d.ts +47 -0
- package/dist/overlay/overlay.js +1710 -0
- package/dist/overlay/overlay.js.map +1 -0
- package/dist/overlay/style.d.ts +1 -0
- package/dist/overlay/style.js +570 -0
- package/dist/overlay/style.js.map +1 -0
- package/dist/overlay/text.d.ts +33 -0
- package/dist/overlay/text.js +200 -0
- package/dist/overlay/text.js.map +1 -0
- package/dist/overlay/types.d.ts +60 -0
- package/dist/overlay/types.js +2 -0
- package/dist/overlay/types.js.map +1 -0
- package/dist/place/anchor.d.ts +23 -0
- package/dist/place/anchor.js +49 -0
- package/dist/place/anchor.js.map +1 -0
- package/dist/place/index.d.ts +4 -0
- package/dist/place/index.js +6 -0
- package/dist/place/index.js.map +1 -0
- package/dist/place/lines.d.ts +56 -0
- package/dist/place/lines.js +280 -0
- package/dist/place/lines.js.map +1 -0
- package/dist/place/place.d.ts +77 -0
- package/dist/place/place.js +182 -0
- package/dist/place/place.js.map +1 -0
- package/dist/shared/errors.d.ts +40 -0
- package/dist/shared/errors.js +22 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/git.d.ts +4 -0
- package/dist/shared/git.js +29 -0
- package/dist/shared/git.js.map +1 -0
- package/dist/shared/types.d.ts +56 -0
- package/dist/shared/types.js +3 -0
- package/dist/shared/types.js.map +1 -0
- package/package.json +61 -0
- package/src/marks/marks.css +361 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/** `"1 comment"`, `"2 comments"`. */
|
|
2
|
+
export function plural(count, one, many = `${one}s`) {
|
|
3
|
+
return `${count} ${count === 1 ? one : many}`;
|
|
4
|
+
}
|
|
5
|
+
/** The last segment of a content path. */
|
|
6
|
+
export function fileName(path) {
|
|
7
|
+
return path.slice(path.lastIndexOf("/") + 1);
|
|
8
|
+
}
|
|
9
|
+
/** `rollouts.md:28`, or `rollouts.md:28-30`. */
|
|
10
|
+
export function linesLabel(path, lines) {
|
|
11
|
+
const range = lines.first === lines.last ? `${lines.first}` : `${lines.first}-${lines.last}`;
|
|
12
|
+
return `${fileName(path)}:${range}`;
|
|
13
|
+
}
|
|
14
|
+
/** Where a thread is, for its card's header. */
|
|
15
|
+
export function whereLabel(thread) {
|
|
16
|
+
const file = fileName(thread.path);
|
|
17
|
+
if (thread.detached === "file")
|
|
18
|
+
return `On ${file} as a whole`;
|
|
19
|
+
if (thread.detached === "line-gone") {
|
|
20
|
+
const line = thread.originalLine ?? thread.line;
|
|
21
|
+
const commit = thread.originalCommit?.slice(0, 7);
|
|
22
|
+
if (line === null)
|
|
23
|
+
return `Was on ${file}, since removed`;
|
|
24
|
+
return `Was on ${file}, line ${line}${commit ? ` at ${commit}` : ""}, since removed`;
|
|
25
|
+
}
|
|
26
|
+
if (thread.lines === undefined)
|
|
27
|
+
return file;
|
|
28
|
+
if (thread.detached === "no-block")
|
|
29
|
+
return `${linesLabel(thread.path, thread.lines)}, not on this page`;
|
|
30
|
+
if (thread.kind === "review" && thread.side === "LEFT")
|
|
31
|
+
return file;
|
|
32
|
+
return linesLabel(thread.path, thread.lines);
|
|
33
|
+
}
|
|
34
|
+
/** Where a thread is, for a list of them: shorter, and naming removed text. */
|
|
35
|
+
export function listLabel(thread) {
|
|
36
|
+
const file = fileName(thread.path);
|
|
37
|
+
if (thread.detached !== undefined)
|
|
38
|
+
return `no block, was ${file}`;
|
|
39
|
+
if (thread.kind === "review" && thread.side === "LEFT")
|
|
40
|
+
return `removed text in ${file}`;
|
|
41
|
+
return thread.lines === undefined ? file : linesLabel(thread.path, thread.lines);
|
|
42
|
+
}
|
|
43
|
+
/** Whether the thread is on removed text. */
|
|
44
|
+
export function onRemovedText(thread) {
|
|
45
|
+
return thread.kind === "review" && thread.side === "LEFT";
|
|
46
|
+
}
|
|
47
|
+
/** Whether any comment of the thread is unsent. */
|
|
48
|
+
export function hasUnsent(thread) {
|
|
49
|
+
return thread.comments.some((comment) => comment.pending);
|
|
50
|
+
}
|
|
51
|
+
/** A comment's author's name, `"you"` for the viewer. */
|
|
52
|
+
export function authorName(author, viewer) {
|
|
53
|
+
if (author === null)
|
|
54
|
+
return "ghost";
|
|
55
|
+
return author.login === viewer ? "you" : author.login;
|
|
56
|
+
}
|
|
57
|
+
/** Two letters standing for an author, for the avatar's place. */
|
|
58
|
+
export function initials(author) {
|
|
59
|
+
return ((author?.login ?? "?")
|
|
60
|
+
.replace(/[^A-Za-z0-9]/g, "")
|
|
61
|
+
.slice(0, 2)
|
|
62
|
+
.toUpperCase() || "?");
|
|
63
|
+
}
|
|
64
|
+
/** `"just now"`, `"5 minutes ago"`, `"yesterday"`, or a date. */
|
|
65
|
+
export function relativeTime(iso, now) {
|
|
66
|
+
const then = Date.parse(iso);
|
|
67
|
+
if (Number.isNaN(then))
|
|
68
|
+
return "";
|
|
69
|
+
const seconds = Math.max(0, Math.round((now - then) / 1000));
|
|
70
|
+
if (seconds < 60)
|
|
71
|
+
return "just now";
|
|
72
|
+
const minutes = Math.round(seconds / 60);
|
|
73
|
+
if (minutes < 60)
|
|
74
|
+
return `${plural(minutes, "minute")} ago`;
|
|
75
|
+
const hours = Math.round(minutes / 60);
|
|
76
|
+
if (hours < 24)
|
|
77
|
+
return `${plural(hours, "hour")} ago`;
|
|
78
|
+
const days = Math.round(hours / 24);
|
|
79
|
+
if (days === 1)
|
|
80
|
+
return "yesterday";
|
|
81
|
+
if (days < 30)
|
|
82
|
+
return `${days} days ago`;
|
|
83
|
+
return new Date(then).toISOString().slice(0, 10);
|
|
84
|
+
}
|
|
85
|
+
/** Elements whose text starts on a line of its own. */
|
|
86
|
+
const BLOCK_TAGS = new Set([
|
|
87
|
+
"ADDRESS",
|
|
88
|
+
"ARTICLE",
|
|
89
|
+
"ASIDE",
|
|
90
|
+
"BLOCKQUOTE",
|
|
91
|
+
"DD",
|
|
92
|
+
"DETAILS",
|
|
93
|
+
"DIV",
|
|
94
|
+
"DL",
|
|
95
|
+
"DT",
|
|
96
|
+
"FIGCAPTION",
|
|
97
|
+
"FIGURE",
|
|
98
|
+
"FOOTER",
|
|
99
|
+
"H1",
|
|
100
|
+
"H2",
|
|
101
|
+
"H3",
|
|
102
|
+
"H4",
|
|
103
|
+
"H5",
|
|
104
|
+
"H6",
|
|
105
|
+
"HEADER",
|
|
106
|
+
"HR",
|
|
107
|
+
"LI",
|
|
108
|
+
"OL",
|
|
109
|
+
"P",
|
|
110
|
+
"PRE",
|
|
111
|
+
"SECTION",
|
|
112
|
+
"SUMMARY",
|
|
113
|
+
"TABLE",
|
|
114
|
+
"TR",
|
|
115
|
+
"UL",
|
|
116
|
+
]);
|
|
117
|
+
/** Classes that hide text from sight but not from screen readers. */
|
|
118
|
+
const SCREEN_READER_ONLY = ["sr-only", "visually-hidden", "sl-sr-only"];
|
|
119
|
+
/** Whether a reader can't see `el`. */
|
|
120
|
+
function unseen(el) {
|
|
121
|
+
if (el.hasAttribute("hidden") ||
|
|
122
|
+
el.getAttribute("aria-hidden") === "true" ||
|
|
123
|
+
SCREEN_READER_ONLY.some((name) => el.classList.contains(name))) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
const style = el.ownerDocument.defaultView?.getComputedStyle(el);
|
|
127
|
+
return style?.display === "none" || style?.visibility === "hidden";
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* A block's text as a reader sees it, for quoting: links as their text, a
|
|
131
|
+
* line for each paragraph, list item, or row, numbered items with their
|
|
132
|
+
* numbers, images as their alt text, and code as written. What the marks and
|
|
133
|
+
* the overlay add is left out, but for the words a change inserted, and so
|
|
134
|
+
* is anything a reader can't see.
|
|
135
|
+
*/
|
|
136
|
+
export function blockText(block) {
|
|
137
|
+
// Each line, and whether it's code, which keeps its spaces.
|
|
138
|
+
const lines = [];
|
|
139
|
+
let line = "";
|
|
140
|
+
let linePre = false;
|
|
141
|
+
const breakLine = () => {
|
|
142
|
+
if (line.trim() !== "")
|
|
143
|
+
lines.push({ text: line, pre: linePre });
|
|
144
|
+
line = "";
|
|
145
|
+
linePre = false;
|
|
146
|
+
};
|
|
147
|
+
const walk = (node, pre) => {
|
|
148
|
+
if (node.nodeType === 3) {
|
|
149
|
+
const text = node.nodeValue ?? "";
|
|
150
|
+
if (!pre) {
|
|
151
|
+
line += text.replace(/\s+/g, " ");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const [first = "", ...rest] = text.split("\n");
|
|
155
|
+
line += first;
|
|
156
|
+
linePre = true;
|
|
157
|
+
for (const next of rest) {
|
|
158
|
+
lines.push({ text: line, pre: true });
|
|
159
|
+
line = next;
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (!(node instanceof Element))
|
|
164
|
+
return;
|
|
165
|
+
const tag = node.tagName.toUpperCase();
|
|
166
|
+
if (node.hasAttribute("data-ascribe-overlay") ||
|
|
167
|
+
(node.hasAttribute("data-ascribe-ui") && tag !== "INS") ||
|
|
168
|
+
["SCRIPT", "STYLE", "TEMPLATE", "NOSCRIPT"].includes(tag) ||
|
|
169
|
+
unseen(node)) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (tag === "BR") {
|
|
173
|
+
breakLine();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (tag === "IMG") {
|
|
177
|
+
line += node.getAttribute("alt") ?? "";
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const block = BLOCK_TAGS.has(tag);
|
|
181
|
+
if (block)
|
|
182
|
+
breakLine();
|
|
183
|
+
if ((tag === "TD" || tag === "TH") && node.previousElementSibling !== null)
|
|
184
|
+
line += " | ";
|
|
185
|
+
if (tag === "LI" && node.parentElement?.tagName.toUpperCase() === "OL") {
|
|
186
|
+
const list = node.parentElement;
|
|
187
|
+
const start = Number(list.getAttribute("start") ?? "1");
|
|
188
|
+
const items = Array.from(list.children).filter((c) => c.tagName.toUpperCase() === "LI");
|
|
189
|
+
line += `${(Number.isFinite(start) ? start : 1) + items.indexOf(node)}. `;
|
|
190
|
+
}
|
|
191
|
+
for (const child of Array.from(node.childNodes))
|
|
192
|
+
walk(child, pre || tag === "PRE");
|
|
193
|
+
if (block)
|
|
194
|
+
breakLine();
|
|
195
|
+
};
|
|
196
|
+
walk(block, false);
|
|
197
|
+
breakLine();
|
|
198
|
+
return lines.map(({ text, pre }) => (pre ? text.trimEnd() : text.trim())).join("\n");
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/overlay/text.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { CommentTarget, PendingReview, ReviewEvent } from "../github/session.js";
|
|
2
|
+
import type { Anchor } from "../place/anchor.js";
|
|
3
|
+
import type { LocatedThread, PlacedThreads } from "../place/place.js";
|
|
4
|
+
import type { Thread, ThreadComment } from "../shared/types.js";
|
|
5
|
+
export type { Anchor, CommentTarget, LocatedThread, PendingReview, PlacedThreads, ReviewEvent };
|
|
6
|
+
/** What the overlay draws for the page it's on. */
|
|
7
|
+
export interface OverlayData {
|
|
8
|
+
/** The pull request: its number names it ("#128"). */
|
|
9
|
+
pullRequest: {
|
|
10
|
+
number: number;
|
|
11
|
+
url: string;
|
|
12
|
+
};
|
|
13
|
+
/** The page's threads, placed on its blocks (`ReviewSession.threads`). */
|
|
14
|
+
threads: PlacedThreads;
|
|
15
|
+
/** The viewer's pending review: what's unsent. */
|
|
16
|
+
pending: PendingReview;
|
|
17
|
+
/** The signed-in viewer's login. */
|
|
18
|
+
viewer: string;
|
|
19
|
+
}
|
|
20
|
+
/** A thread on the pull request, and the pages it's on. */
|
|
21
|
+
export interface ThreadSummary {
|
|
22
|
+
thread: LocatedThread;
|
|
23
|
+
/** The pages that show it, by content path; empty when no page is known to. */
|
|
24
|
+
pages: {
|
|
25
|
+
path: string;
|
|
26
|
+
title: string | null;
|
|
27
|
+
}[];
|
|
28
|
+
}
|
|
29
|
+
/** A failure the host reports: `message` is a sentence to show; `code` is a `ReviewError` code. */
|
|
30
|
+
export interface HostError {
|
|
31
|
+
message: string;
|
|
32
|
+
code?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Where the overlay gets its data and sends what the reviewer does. A host
|
|
36
|
+
* keeps GitHub to itself: the overlay never sees a token or makes a request.
|
|
37
|
+
*/
|
|
38
|
+
export interface OverlayHost {
|
|
39
|
+
load(): Promise<OverlayData>;
|
|
40
|
+
/** How a comment on the block would go: as a thread, in the review's summary, or not yet. */
|
|
41
|
+
commentTarget(anchor: Anchor): Promise<CommentTarget>;
|
|
42
|
+
/** Comments on a block. `quote` is the block's text as the page shows it. */
|
|
43
|
+
comment(anchor: Anchor, body: string, quote?: string): Promise<Thread>;
|
|
44
|
+
reply(threadId: string, body: string, when: "now" | "withReview"): Promise<ThreadComment>;
|
|
45
|
+
/** Every thread on the pull request's pages, with the pages each is on. */
|
|
46
|
+
allThreads(): Promise<ThreadSummary[]>;
|
|
47
|
+
resolve(threadId: string, resolved: boolean): Promise<void>;
|
|
48
|
+
submit(event: ReviewEvent, body?: string): Promise<void>;
|
|
49
|
+
/** Deletes the pending review and everything in it. */
|
|
50
|
+
discard(): Promise<void>;
|
|
51
|
+
openSource(anchor: Anchor): void;
|
|
52
|
+
/** Shows a thread that's on another page: the host opens that page, and its overlay goes to the thread. */
|
|
53
|
+
openThread(threadId: string, page: string): void;
|
|
54
|
+
/** Opens a link from a comment. Without it, the link opens as the page would open it. */
|
|
55
|
+
openLink?(url: string): void;
|
|
56
|
+
/** Tells the reviewer something that happened, as the host shows notices. */
|
|
57
|
+
notify?(message: string): void;
|
|
58
|
+
/** Calls `listener` when the page re-rendered or the threads changed. */
|
|
59
|
+
onDidChange(listener: () => void): void;
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/overlay/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Where a rendered block came from: its `data-ascribe-source` and `data-ascribe-via`. */
|
|
2
|
+
export interface Anchor {
|
|
3
|
+
/** `<path>:<first>-<last>`. */
|
|
4
|
+
source: string;
|
|
5
|
+
/** The includes it came through, outermost first, each `<path>:<line>`. */
|
|
6
|
+
via: string[];
|
|
7
|
+
}
|
|
8
|
+
/** A block's source, parsed. `path` is decoded. */
|
|
9
|
+
export interface SourceRange {
|
|
10
|
+
path: string;
|
|
11
|
+
first: number;
|
|
12
|
+
last: number;
|
|
13
|
+
}
|
|
14
|
+
/** Parses `<path>:<first>-<last>`, or returns `undefined` if it isn't one. */
|
|
15
|
+
export declare function parseSource(source: string): SourceRange | undefined;
|
|
16
|
+
/** Writes a block's source for a decoded content path. */
|
|
17
|
+
export declare function formatSource(range: SourceRange): string;
|
|
18
|
+
/** Percent-encodes each segment of a `/`-separated path, as anchors do. */
|
|
19
|
+
export declare function encodePath(path: string): string;
|
|
20
|
+
/** The inverse of `encodePath`. */
|
|
21
|
+
export declare function decodePath(path: string): string;
|
|
22
|
+
/** A string that identifies an anchor, `via` included. */
|
|
23
|
+
export declare function anchorKey(anchor: Anchor): string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Source anchors: `<path>:<first>-<last>` for a block, `<path>:<line>` for an
|
|
2
|
+
// include. The path is a content path, `/`-separated, each segment
|
|
3
|
+
// percent-encoded except for ASCII letters, digits, `-`, `.`, `_`, and `~`.
|
|
4
|
+
/** Parses `<path>:<first>-<last>`, or returns `undefined` if it isn't one. */
|
|
5
|
+
export function parseSource(source) {
|
|
6
|
+
const colon = source.lastIndexOf(":");
|
|
7
|
+
if (colon <= 0)
|
|
8
|
+
return undefined;
|
|
9
|
+
const match = /^(\d+)-(\d+)$/.exec(source.slice(colon + 1));
|
|
10
|
+
if (match === null)
|
|
11
|
+
return undefined;
|
|
12
|
+
const first = Number(match[1]);
|
|
13
|
+
const last = Number(match[2]);
|
|
14
|
+
if (first < 1 || last < first)
|
|
15
|
+
return undefined;
|
|
16
|
+
let path;
|
|
17
|
+
try {
|
|
18
|
+
path = decodePath(source.slice(0, colon));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return { path, first, last };
|
|
24
|
+
}
|
|
25
|
+
/** Writes a block's source for a decoded content path. */
|
|
26
|
+
export function formatSource(range) {
|
|
27
|
+
return `${encodePath(range.path)}:${range.first}-${range.last}`;
|
|
28
|
+
}
|
|
29
|
+
/** Percent-encodes each segment of a `/`-separated path, as anchors do. */
|
|
30
|
+
export function encodePath(path) {
|
|
31
|
+
return path
|
|
32
|
+
.split("/")
|
|
33
|
+
.map((segment) => Array.from(new TextEncoder().encode(segment), (byte) => {
|
|
34
|
+
const char = String.fromCharCode(byte);
|
|
35
|
+
return /[A-Za-z0-9\-._~]/.test(char)
|
|
36
|
+
? char
|
|
37
|
+
: `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
38
|
+
}).join(""))
|
|
39
|
+
.join("/");
|
|
40
|
+
}
|
|
41
|
+
/** The inverse of `encodePath`. */
|
|
42
|
+
export function decodePath(path) {
|
|
43
|
+
return path.split("/").map(decodeURIComponent).join("/");
|
|
44
|
+
}
|
|
45
|
+
/** A string that identifies an anchor, `via` included. */
|
|
46
|
+
export function anchorKey(anchor) {
|
|
47
|
+
return [anchor.source, ...anchor.via].join(" ");
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=anchor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor.js","sourceRoot":"","sources":["../../src/place/anchor.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mEAAmE;AACnE,4EAA4E;AAiB5E,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACf,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE;QACrD,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;YAClC,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CACZ;SACA,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { anchorKey, decodePath, encodePath, formatSource, parseSource, type Anchor, type SourceRange, } from "./anchor.js";
|
|
2
|
+
export { lineHunks, lineMap, linesAt, parseHunks, shiftLine, type Hunk, type LineMap, type Shifted, type Sides, } from "./lines.js";
|
|
3
|
+
export { locateThreads, placeOnPage, type DetachReason, type LocateContext, type LocatedThread, type PageRef, type PlacedThreads, } from "./place.js";
|
|
4
|
+
export type { Author, Side, Thread, ThreadComment } from "../shared/types.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Placing review threads on rendered blocks: source anchors, moving lines
|
|
2
|
+
// between a commit and the working tree, and matching threads to a page.
|
|
3
|
+
export { anchorKey, decodePath, encodePath, formatSource, parseSource, } from "./anchor.js";
|
|
4
|
+
export { lineHunks, lineMap, linesAt, parseHunks, shiftLine, } from "./lines.js";
|
|
5
|
+
export { locateThreads, placeOnPage, } from "./place.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/place/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AACzE,OAAO,EACL,SAAS,EACT,UAAU,EACV,UAAU,EACV,YAAY,EACZ,WAAW,GAGZ,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,SAAS,EACT,OAAO,EACP,OAAO,EACP,UAAU,EACV,SAAS,GAKV,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,aAAa,EACb,WAAW,GAMZ,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** One hunk of a unified diff: lines `oldStart`.. on one side became `newStart`.. on the other. */
|
|
2
|
+
export interface Hunk {
|
|
3
|
+
oldStart: number;
|
|
4
|
+
oldCount: number;
|
|
5
|
+
newStart: number;
|
|
6
|
+
newCount: number;
|
|
7
|
+
}
|
|
8
|
+
/** The hunks of a unified diff of one file, in order. */
|
|
9
|
+
export declare function parseHunks(diff: string): Hunk[];
|
|
10
|
+
/** Where a line is on the other side of a diff. */
|
|
11
|
+
export interface Shifted {
|
|
12
|
+
line: number;
|
|
13
|
+
/** Whether the diff replaced the line: its text changed, and `line` is where the new text is. */
|
|
14
|
+
replaced: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** The text of both sides of a diff, line by line, for telling replaced lines from deleted ones. */
|
|
17
|
+
export interface Sides {
|
|
18
|
+
old: readonly string[];
|
|
19
|
+
new: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Where line `line` of the old side is on the new side. A line the diff
|
|
23
|
+
* replaced maps onto the line that replaced it, with `replaced` set; a line it
|
|
24
|
+
* only deleted maps to `undefined`. Hunks come from a diff with no context
|
|
25
|
+
* lines.
|
|
26
|
+
*
|
|
27
|
+
* Old lines pair with new ones by position, clamped to the last new line.
|
|
28
|
+
* But a hunk with fewer new lines than old can have deleted some lines and
|
|
29
|
+
* reworded others, so with `sides` each of its old lines pairs with the new
|
|
30
|
+
* line most like it, in order, and an old line like none of them was deleted.
|
|
31
|
+
* When none of them pairs (the text was rewritten), or the hunk is too large
|
|
32
|
+
* to compare, it's by position again.
|
|
33
|
+
*/
|
|
34
|
+
export declare function shiftLine(hunks: readonly Hunk[], line: number, sides?: Sides): Shifted | undefined;
|
|
35
|
+
/** Maps line numbers of a file from one side to the other. */
|
|
36
|
+
export interface LineMap {
|
|
37
|
+
/** Where the line is on the other side, or `undefined` if it was deleted. */
|
|
38
|
+
map(line: number): Shifted | undefined;
|
|
39
|
+
/** Whether the file is the same on both sides. */
|
|
40
|
+
identity: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A map of the lines of the repository-relative `file` between `commit` and
|
|
44
|
+
* the working tree, in the direction given. Every line maps to nothing when
|
|
45
|
+
* the commit isn't in the repository or the file is missing on either side.
|
|
46
|
+
*/
|
|
47
|
+
export declare function lineMap(root: string, commit: string, file: string, direction: "to-worktree" | "to-commit"): Promise<LineMap>;
|
|
48
|
+
/** Lines `first`..`last` of the repository-relative `file` at `commit`, or `undefined`. */
|
|
49
|
+
export declare function linesAt(root: string, commit: string, file: string, first: number, last: number): Promise<string | undefined>;
|
|
50
|
+
/**
|
|
51
|
+
* The hunks that turn `before` into `after`, line by line, as
|
|
52
|
+
* `git diff --unified=0` would report them: for text in hand, such as a file
|
|
53
|
+
* and an editor's unsaved copy of it. Lines common to both ends are skipped
|
|
54
|
+
* first; a middle too large to compare line by line is one hunk.
|
|
55
|
+
*/
|
|
56
|
+
export declare function lineHunks(before: string, after: string): Hunk[];
|