Skip to content

Commit cedc0e1

Browse files
Adjust context help alignment for consistent indentation (#55)
This pull request corrects the indentation logic in the context help display to ensure consistent alignment between text lines. It refactors the whitespace handling and improves the visual structure of the context help content.
1 parent 8db4fb5 commit cedc0e1

File tree

2 files changed

+42
-130
lines changed

2 files changed

+42
-130
lines changed

package-lock.json

Lines changed: 15 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ccs/commands/contextHelp.ts

Lines changed: 27 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@ export async function resolveContextExpression(): Promise<void> {
1414
}
1515

1616
const { document, selection } = editor;
17-
const contextExpression = selection.isEmpty
18-
? document.lineAt(selection.active.line).text.trim()
19-
: document.getText(selection).trim();
17+
const contextInfo = getContextExpressionInfo(document, selection);
18+
const contextExpression = contextInfo.text;
2019

21-
if (!contextExpression) {
20+
if (!contextExpression.trim()) {
2221
void vscode.window.showErrorMessage("Context expression is empty.");
2322
return;
2423
}
@@ -41,35 +40,16 @@ export async function resolveContextExpression(): Promise<void> {
4140
}
4241

4342
const textExpression = normalizedTextExpression.replace(/\r?\n/g, eol);
44-
let formattedTextExpression = textExpression;
43+
const formattedTextExpression = textExpression;
4544

4645
let rangeToReplace: vscode.Range;
4746
if (selection.isEmpty) {
4847
const fallbackLine = document.lineAt(selection.active.line);
49-
const fallbackRange = fallbackLine.range;
50-
51-
rangeToReplace = getRangeToReplaceForLine(document, selection.active.line, contextExpression) ?? fallbackRange;
52-
53-
const preservedPrefix = document.getText(new vscode.Range(fallbackLine.range.start, rangeToReplace.start));
54-
55-
formattedTextExpression = normalizeInsertionWithPrefix(formattedTextExpression, preservedPrefix, eol);
48+
rangeToReplace = fallbackLine.range;
5649
} else {
57-
// Multi-line or partial selection
58-
const firstSelLine = document.lineAt(selection.start.line);
59-
const preservedPrefix = document.getText(new vscode.Range(firstSelLine.range.start, selection.start));
60-
const leadingWS = firstSelLine.text.match(/^[\t ]*/)?.[0] ?? "";
61-
62-
// 1) Normalize snippet to avoid duplicating "."/";" according to the prefix that will remain in the file
63-
formattedTextExpression = normalizeInsertionWithPrefix(formattedTextExpression, preservedPrefix, eol);
64-
65-
// 2) Only prefix indentation if the selection started at column 0 (i.e., NO preserved prefix)
66-
formattedTextExpression = maybePrefixFirstLineIndent(
67-
formattedTextExpression,
68-
preservedPrefix.length === 0 ? leadingWS : "",
69-
eol
70-
);
71-
72-
rangeToReplace = new vscode.Range(selection.start, selection.end);
50+
const start = document.lineAt(selection.start.line).range.start;
51+
const replacementEnd = contextInfo.replacementEnd ?? document.lineAt(selection.end.line).range.end;
52+
rangeToReplace = new vscode.Range(start, replacementEnd);
7353
}
7454

7555
await editor.edit((editBuilder) => {
@@ -92,112 +72,32 @@ export async function resolveContextExpression(): Promise<void> {
9272
}
9373
}
9474

95-
function getRangeToReplaceForLine(
96-
document: vscode.TextDocument,
97-
lineNumber: number,
98-
contextExpression: string
99-
): vscode.Range | undefined {
100-
if (!contextExpression) {
101-
return undefined;
102-
}
75+
type ContextExpressionInfo = {
76+
text: string;
77+
replacementEnd?: vscode.Position;
78+
};
10379

104-
const line = document.lineAt(lineNumber);
105-
const expressionIndex = line.text.indexOf(contextExpression);
106-
if (expressionIndex === -1) {
107-
return undefined;
80+
function getContextExpressionInfo(document: vscode.TextDocument, selection: vscode.Selection): ContextExpressionInfo {
81+
if (selection.isEmpty) {
82+
return {
83+
text: document.lineAt(selection.active.line).text,
84+
};
10885
}
10986

110-
const prefixLength = getPrefixLengthToPreserve(contextExpression);
111-
const startCharacter = expressionIndex + prefixLength;
112-
const endCharacter = expressionIndex + contextExpression.length;
113-
114-
const start = line.range.start.translate(0, startCharacter);
115-
const end = line.range.start.translate(0, endCharacter);
116-
return new vscode.Range(start, end);
117-
}
118-
119-
/**
120-
* Based on the preserved line prefix, remove from the BEGINNING of the snippet's first line:
121-
* - if the prefix ends with ";": remove ^[\t ]*(?:\.\s*)*;\s*
122-
* - otherwise, if it ends with dots: remove ^[\t ]*(?:\.\s*)+
123-
* - neutral case: try to remove comment; otherwise remove dots
124-
*/
125-
function normalizeInsertionWithPrefix(text: string, preservedPrefix: string, eol: string): string {
126-
const lines = text.split(/\r?\n/);
127-
if (lines.length === 0) return text;
128-
129-
const preservedEnd = preservedPrefix.replace(/\s+$/g, "");
130-
131-
const endsWithSemicolon = /(?:\.\s*)*;\s*$/.test(preservedEnd);
132-
const endsWithDotsOnly = !endsWithSemicolon && /(?:\.\s*)+$/.test(preservedEnd);
87+
const startLine = selection.start.line;
88+
const start = document.lineAt(startLine).range.start;
13389

134-
if (endsWithSemicolon) {
135-
lines[0] = lines[0].replace(/^[\t ]*(?:\.\s*)*;\s*/, "");
136-
} else if (endsWithDotsOnly) {
137-
lines[0] = lines[0].replace(/^[\t ]*(?:\.\s*)+/, "");
138-
} else {
139-
const removedComment = lines[0].replace(/^[\t ]*(?:\.\s*)?;\s*/, "");
140-
if (removedComment !== lines[0]) {
141-
lines[0] = removedComment;
142-
} else {
143-
lines[0] = lines[0].replace(/^[\t ]*(?:\.\s*)+/, "");
144-
}
90+
let lastLine = selection.end.line;
91+
if (selection.end.character === 0 && selection.end.line > selection.start.line) {
92+
lastLine = selection.end.line - 1;
14593
}
14694

147-
return lines.join(eol);
148-
}
149-
150-
/**
151-
* Prefix indentation (tabs/spaces) ONLY if provided.
152-
* Useful when the selection started at column 0 (no preserved prefix).
153-
*/
154-
function maybePrefixFirstLineIndent(text: string, leadingWS: string, eol: string): string {
155-
if (!text || !leadingWS) return text;
156-
const lines = text.split(/\r?\n/);
157-
if (lines.length === 0) return text;
158-
159-
// Do not force replacement if there is already some whitespace; just prefix it.
160-
lines[0] = leadingWS + lines[0];
161-
return lines.join(eol);
162-
}
163-
164-
/**
165-
* Keep: preserve level dots / indentation and, if present, '; ' before the typed content.
166-
* Returns how many characters of the contextExpression belong to that prefix.
167-
*/
168-
function getPrefixLengthToPreserve(contextExpression: string): number {
169-
let index = 0;
170-
171-
while (index < contextExpression.length) {
172-
const char = contextExpression[index];
173-
174-
if (char === ".") {
175-
index++;
176-
while (index < contextExpression.length && contextExpression[index] === " ") {
177-
index++;
178-
}
179-
continue;
180-
}
181-
182-
if (char === " " || char === "\t") {
183-
index++;
184-
continue;
185-
}
186-
187-
break;
188-
}
189-
190-
if (index < contextExpression.length && contextExpression[index] === ";") {
191-
index++;
192-
while (
193-
index < contextExpression.length &&
194-
(contextExpression[index] === " " || contextExpression[index] === "\t")
195-
) {
196-
index++;
197-
}
198-
}
95+
const end = document.lineAt(lastLine).range.end;
19996

200-
return index;
97+
return {
98+
text: document.getText(new vscode.Range(start, end)),
99+
replacementEnd: end,
100+
};
201101
}
202102

203103
function extractGifUri(text: string): {

0 commit comments

Comments
 (0)