feat(vim): added Vim ex :join and :join! support for line joining
- Implemented parsing of `:j`/`:join` and `:j!`/`:join!` ex commands and mapped them to a new `join` command shape with a whitespace-trim flag. - Added VimEngine handling for `join` to concatenate addressed lines (or the current plus next line by default), optionally normalizing whitespace and reporting line count in the status message. - Updated vim prompt guidance, changelog notes, and tests to document and verify both normal and `:join!` join behavior.
This commit is contained in:
@@ -294,6 +294,9 @@ export function parseExCommand(input: string, context?: VimExParseContext): VimE
|
||||
const flags = rest.slice(4).trim();
|
||||
return { kind: "sort", range: range ?? undefined, flags };
|
||||
}
|
||||
if (rest === "j" || rest === "join" || rest === "j!" || rest === "join!") {
|
||||
return { kind: "join", range: range ?? undefined, trimWhitespace: !rest.endsWith("!") };
|
||||
}
|
||||
|
||||
if (rest.startsWith("substitute")) {
|
||||
const segments = parseDelimitedSegments(rest.slice("substitute".length));
|
||||
|
||||
@@ -2298,6 +2298,34 @@ export class VimEngine {
|
||||
this.statusMessage = `Sorted ${endLine - startLine + 1} line${endLine === startLine ? "" : "s"}`;
|
||||
return;
|
||||
}
|
||||
case "join": {
|
||||
const currentLine = this.buffer.cursor.line + 1;
|
||||
const baseRange = this.#resolveExRange(
|
||||
command.range,
|
||||
currentLine,
|
||||
command.range ? undefined : Math.min(this.buffer.lineCount(), currentLine + 1),
|
||||
);
|
||||
const startLine = baseRange.start;
|
||||
const endLine =
|
||||
baseRange.start === baseRange.end ? Math.min(this.buffer.lineCount(), baseRange.end + 1) : baseRange.end;
|
||||
const lineCount = endLine - startLine + 1;
|
||||
if (lineCount < 2) {
|
||||
this.statusMessage = "Nothing to join";
|
||||
return;
|
||||
}
|
||||
await this.#applyAtomicChange([":join"], () => {
|
||||
const startIndex = startLine - 1;
|
||||
if (command.trimWhitespace) {
|
||||
this.buffer.joinLines(startIndex, lineCount - 1);
|
||||
return;
|
||||
}
|
||||
const joined = this.buffer.lines.slice(startIndex, endLine).join("");
|
||||
this.buffer.lines.splice(startIndex, lineCount, joined);
|
||||
this.buffer.setCursor({ line: startIndex, col: Math.max(0, joined.length - 1) });
|
||||
});
|
||||
this.statusMessage = `Joined ${lineCount} lines`;
|
||||
return;
|
||||
}
|
||||
case "append": {
|
||||
const anchorRange = this.#resolveExRange(command.range, this.buffer.cursor.line + 1);
|
||||
const anchorLine = anchorRange.end;
|
||||
|
||||
@@ -140,6 +140,7 @@ export type VimExCommand =
|
||||
| { kind: "copy"; range?: VimLineRange | "all"; destination: number }
|
||||
| { kind: "move"; range?: VimLineRange | "all"; destination: number }
|
||||
| { kind: "sort"; range?: VimLineRange | "all"; flags: string }
|
||||
| { kind: "join"; range?: VimLineRange | "all"; trimWhitespace: boolean }
|
||||
| { kind: "global"; range?: VimLineRange | "all"; pattern: string; command: string; invert: boolean }
|
||||
| { kind: "append"; range?: VimLineRange; text: string }
|
||||
| { kind: "insert-before"; range?: VimLineRange; text: string };
|
||||
|
||||
Reference in New Issue
Block a user