-
-
Notifications
You must be signed in to change notification settings - Fork 247
Drag sexp comments 3073 #3178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Drag sexp comments 3073 #3178
Changes from all commits
51241a9
041c195
fbdebd5
a5d8e26
232dad7
6e35fd3
f82fc4f
9aad55f
55bae2e
dbbce39
23f5641
5dd1c37
a4779fc
633f4fe
916a01d
e7be838
912577b
940faa1
5207041
0995dfb
7aefb1e
e7b0193
aef0cb2
5b714a9
cda36c4
3fa66b2
2c38eb6
0fec76e
b6bb142
83d72c5
42f50c8
ed2cbd4
642e5f0
937b20f
489fe61
83868f6
6c79d5c
d376672
c5ae562
8f0f142
0419285
31cef04
fe4fd41
4c8ae16
8fabd25
151b606
7206911
e6b65b7
cb6e713
8521b61
52cd85a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -2337,6 +2337,179 @@ export function currentSexpsRange( | |||||||||||||||||||||
| return currentSingleRange; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| type LineInfo = { | ||||||||||||||||||||||
| start: number; | ||||||||||||||||||||||
| end: number; | ||||||||||||||||||||||
| content: string; | ||||||||||||||||||||||
| trimmed: string; | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| function lineInfoAt(text: string, offset: number): LineInfo { | ||||||||||||||||||||||
| const start = text.lastIndexOf('\n', Math.max(0, offset - 1)) + 1; | ||||||||||||||||||||||
| const nl = text.indexOf('\n', offset); | ||||||||||||||||||||||
| const end = nl === -1 ? text.length : nl; | ||||||||||||||||||||||
| const content = text.substring(start, end); | ||||||||||||||||||||||
| return { start, end, content, trimmed: content.trimStart() }; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const prevLine = (text: string, lineStart: number) => | ||||||||||||||||||||||
| lineStart > 0 ? lineInfoAt(text, lineStart - 1) : null; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const nextLine = (text: string, lineEnd: number) => | ||||||||||||||||||||||
| lineEnd < text.length ? lineInfoAt(text, lineEnd + 1) : null; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const isCommentLine = (line: LineInfo) => line.trimmed.startsWith(';'); | ||||||||||||||||||||||
| const isBlankLine = (line: LineInfo) => line.trimmed === ''; | ||||||||||||||||||||||
| const isResultCommentStartLine = (line: LineInfo) => /^;{1,2}=>/.test(line.trimmed); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| function isResultCommentBlock(lines: LineInfo[]): boolean { | ||||||||||||||||||||||
| if (!lines.length || !isResultCommentStartLine(lines[0])) { | ||||||||||||||||||||||
| return false; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const semicolonPrefix = lines[0].trimmed.startsWith(';;=>') ? ';;' : ';'; | ||||||||||||||||||||||
| return lines.slice(1).every((line) => line.trimmed.startsWith(`${semicolonPrefix} `)); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| function trailingResultCommentBlockEnd(text: string, fromLineEnd: number): number | null { | ||||||||||||||||||||||
| const first = nextLine(text, fromLineEnd); | ||||||||||||||||||||||
| if (!first || !isResultCommentStartLine(first)) { | ||||||||||||||||||||||
| return null; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const semicolonPrefix = first.trimmed.startsWith(';;=>') ? ';;' : ';'; | ||||||||||||||||||||||
| let end = first.end; | ||||||||||||||||||||||
| let line = nextLine(text, first.end); | ||||||||||||||||||||||
| while (line && isCommentLine(line) && line.trimmed.startsWith(`${semicolonPrefix} `)) { | ||||||||||||||||||||||
| end = line.end; | ||||||||||||||||||||||
| line = nextLine(text, line.end); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return end; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| function extendRangeToLineIndent(text: string, [start, end]: [number, number]): [number, number] { | ||||||||||||||||||||||
| const startLine = lineInfoAt(text, start); | ||||||||||||||||||||||
| return text.substring(startLine.start, start).trim() === '' | ||||||||||||||||||||||
| ? [startLine.start, end] | ||||||||||||||||||||||
| : [start, end]; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| function normalizeSwapRangesForLeadingComments( | ||||||||||||||||||||||
| text: string, | ||||||||||||||||||||||
| leftRange: [number, number], | ||||||||||||||||||||||
| rightRange: [number, number] | ||||||||||||||||||||||
| ): [[number, number], [number, number]] { | ||||||||||||||||||||||
| const leftStartsWithComment = isCommentLine(lineInfoAt(text, leftRange[0])); | ||||||||||||||||||||||
| const rightStartsWithComment = isCommentLine(lineInfoAt(text, rightRange[0])); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return [ | ||||||||||||||||||||||
| rightStartsWithComment ? extendRangeToLineIndent(text, leftRange) : leftRange, | ||||||||||||||||||||||
| leftStartsWithComment ? extendRangeToLineIndent(text, rightRange) : rightRange, | ||||||||||||||||||||||
| ]; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Extends a form's range to include its attached line comments: | ||||||||||||||||||||||
| * - Backward: contiguous comment lines immediately above the form (only when | ||||||||||||||||||||||
| * the form's line has nothing but whitespace before the form). | ||||||||||||||||||||||
| * - Forward: a trailing `;…` on the form's last line, plus comment lines | ||||||||||||||||||||||
| * below that are NOT leading a following form (terminated by a blank line | ||||||||||||||||||||||
| * or EOF). | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function extendRangeOverAttachedComments( | ||||||||||||||||||||||
| doc: EditableDocument, | ||||||||||||||||||||||
| [start, end]: [number, number] | ||||||||||||||||||||||
| ): [number, number] { | ||||||||||||||||||||||
| const text = doc.model.getText(0, Number.MAX_SAFE_INTEGER); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| let lo = start; | ||||||||||||||||||||||
| const startLine = lineInfoAt(text, start); | ||||||||||||||||||||||
| if (text.substring(startLine.start, start).trim() === '') { | ||||||||||||||||||||||
| const leadingCommentLines: LineInfo[] = []; | ||||||||||||||||||||||
| for (let line = prevLine(text, startLine.start); line && isCommentLine(line); ) { | ||||||||||||||||||||||
| leadingCommentLines.unshift(line); | ||||||||||||||||||||||
| line = prevLine(text, line.start); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if (leadingCommentLines.length && !isResultCommentBlock(leadingCommentLines)) { | ||||||||||||||||||||||
| lo = leadingCommentLines[0].start; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| let hi = end; | ||||||||||||||||||||||
| const endLine = lineInfoAt(text, end); | ||||||||||||||||||||||
| if (/^\s*;/.test(text.substring(end, endLine.end))) { | ||||||||||||||||||||||
| hi = endLine.end; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const resultCommentsEnd = trailingResultCommentBlockEnd(text, endLine.end); | ||||||||||||||||||||||
| if (resultCommentsEnd !== null) { | ||||||||||||||||||||||
| hi = resultCommentsEnd; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const trailingEnds: number[] = []; | ||||||||||||||||||||||
| let below = nextLine(text, endLine.end); | ||||||||||||||||||||||
| while (below && isCommentLine(below)) { | ||||||||||||||||||||||
| trailingEnds.push(below.end); | ||||||||||||||||||||||
| below = nextLine(text, below.end); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if (trailingEnds.length && (!below || isBlankLine(below))) { | ||||||||||||||||||||||
| hi = trailingEnds[trailingEnds.length - 1]; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return [lo, hi]; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * If `offset` is on a line-comment line, returns the range of the form that | ||||||||||||||||||||||
| * comment is attached to: the form immediately below when contiguous (no | ||||||||||||||||||||||
| * blank line between), else the form immediately above when the comment is a | ||||||||||||||||||||||
| * trailing annotation. Returns `null` when the cursor isn't on a comment, or | ||||||||||||||||||||||
| * when no form is attached. | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function formAttachedToCommentAt(doc: EditableDocument, offset: number): [number, number] | null { | ||||||||||||||||||||||
| const text = doc.model.getText(0, Number.MAX_SAFE_INTEGER); | ||||||||||||||||||||||
| const here = lineInfoAt(text, offset); | ||||||||||||||||||||||
| if (!isCommentLine(here)) { | ||||||||||||||||||||||
| return null; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (isResultCommentStartLine(here)) { | ||||||||||||||||||||||
| let above = prevLine(text, here.start); | ||||||||||||||||||||||
| while (above && isCommentLine(above)) { | ||||||||||||||||||||||
| above = prevLine(text, above.start); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if (above && !isBlankLine(above)) { | ||||||||||||||||||||||
| const formEnd = above.start + above.content.trimEnd().length; | ||||||||||||||||||||||
| const cursor = doc.getTokenCursor(formEnd); | ||||||||||||||||||||||
| cursor.backwardSexp(); | ||||||||||||||||||||||
| return cursor.rangeForCurrentForm(cursor.offsetStart); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return null; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| let below = nextLine(text, here.end); | ||||||||||||||||||||||
| while (below && isCommentLine(below)) { | ||||||||||||||||||||||
| below = nextLine(text, below.end); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if (below && !isBlankLine(below)) { | ||||||||||||||||||||||
| const formStart = below.start + (below.content.length - below.trimmed.length); | ||||||||||||||||||||||
| return doc.getTokenCursor(formStart).rangeForCurrentForm(formStart); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| let above = prevLine(text, here.start); | ||||||||||||||||||||||
| while (above && isCommentLine(above)) { | ||||||||||||||||||||||
| above = prevLine(text, above.start); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| if (above && !isBlankLine(above)) { | ||||||||||||||||||||||
| const formEnd = above.start + above.content.trimEnd().length; | ||||||||||||||||||||||
| const cursor = doc.getTokenCursor(formEnd); | ||||||||||||||||||||||
| cursor.backwardSexp(); | ||||||||||||||||||||||
| return cursor.rangeForCurrentForm(cursor.offsetStart); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return null; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export async function dragSexprBackward( | ||||||||||||||||||||||
| doc: EditableDocument, | ||||||||||||||||||||||
| left = doc.selections[0].anchor, | ||||||||||||||||||||||
|
|
@@ -2345,21 +2518,36 @@ export async function dragSexprBackward( | |||||||||||||||||||||
| ) { | ||||||||||||||||||||||
| const cursor = doc.getTokenCursor(right); | ||||||||||||||||||||||
| const usePairs = isInPairsList(cursor, config); | ||||||||||||||||||||||
| const currentRange = currentSexpsRange(doc, cursor, right, usePairs, config); | ||||||||||||||||||||||
| const newPosOffset = right - currentRange[0]; | ||||||||||||||||||||||
| const backCursor = doc.getTokenCursor(currentRange[0]); | ||||||||||||||||||||||
| const text = doc.model.getText(0, Number.MAX_SAFE_INTEGER); | ||||||||||||||||||||||
| const baseRange = | ||||||||||||||||||||||
| formAttachedToCommentAt(doc, right) ?? currentSexpsRange(doc, cursor, right, usePairs, config); | ||||||||||||||||||||||
|
Comment on lines
+2522
to
+2523
|
||||||||||||||||||||||
| const baseRange = | |
| formAttachedToCommentAt(doc, right) ?? currentSexpsRange(doc, cursor, right, usePairs, config); | |
| const attachedCommentRange = formAttachedToCommentAt(doc, right); | |
| const text = doc.model.getText(0, Number.MAX_SAFE_INTEGER); | |
| const onCommentLine = isCommentLine(lineInfoAt(text, right)); | |
| if (onCommentLine && !attachedCommentRange) { | |
| return; | |
| } | |
| const baseRange = | |
| attachedCommentRange ?? currentSexpsRange(doc, cursor, right, usePairs, config); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extendRangeOverAttachedCommentsonly attaches comment lines below the form if they’re followed by a blank line or EOF. Calva’s evaluation result comments are produced as\n;;=> …(seeresultAsComment) and can be immediately followed by the next form without an extra blank line, in which case the;;=>lines would not be moved with the form. Consider treating;;=>(and/or;=>) result comment lines as attached-to-previous even when another form follows.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something we can consider following up with, @jramosg. Sometimes you want a tight RCF with no blank lines, and the
;=>marker is what tells us where the comment belongs.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're right, now is handled in
trailingResultCommentBlockEnd