From 99d4703b3b9d9c75e0b69798d47b05621585b405 Mon Sep 17 00:00:00 2001 From: Xuepoo Date: Fri, 17 Jul 2026 14:25:31 -0400 Subject: [PATCH] chore: release version bump --- .changeset/substitute-command.md | 7 ++ .changeset/vim-native-motions-batch-1.md | 6 -- packages/core/CHANGELOG.md | 6 ++ packages/core/package.json | 2 +- packages/core/src/editor.ts | 92 ++++++++++++++++++++++++ packages/lsp-client/CHANGELOG.md | 7 ++ packages/lsp-client/package.json | 4 +- packages/plugin-api/CHANGELOG.md | 7 ++ packages/plugin-api/package.json | 4 +- packages/renderer-vecto/CHANGELOG.md | 8 +++ packages/renderer-vecto/package.json | 4 +- 11 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 .changeset/substitute-command.md delete mode 100644 .changeset/vim-native-motions-batch-1.md diff --git a/.changeset/substitute-command.md b/.changeset/substitute-command.md new file mode 100644 index 0000000..ac6b9b9 --- /dev/null +++ b/.changeset/substitute-command.md @@ -0,0 +1,7 @@ +--- +'@vemjs/core': patch +--- + +Implement `:s`/`:%s` substitute command with `/pattern/replacement/flags` syntax. +Supports `g` (global), `i` (case-insensitive) flags, and Vim-style backreferences +(`$1`..`$9`, `$&`, `$``, `$'`). diff --git a/.changeset/vim-native-motions-batch-1.md b/.changeset/vim-native-motions-batch-1.md deleted file mode 100644 index b912941..0000000 --- a/.changeset/vim-native-motions-batch-1.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@vemjs/core': minor -'@vemjs/renderer-vecto': patch ---- - -Implement first batch of Vim-native motions and commands: zz/zt/zb, H/M/L, J, ~, {/}, gf, gv, gi, [[/]]/[]/][, /, m{a-zA-Z}/'{a-zA-Z}, ZZ/ZQ. diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 8748cd6..b95b02f 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,11 @@ # @vemjs/core +## 0.4.0 + +### Minor Changes + +- 9ea701f: Implement first batch of Vim-native motions and commands: zz/zt/zb, H/M/L, J, ~, {/}, gf, gv, gi, [[/]]/[]/][, /, m{a-zA-Z}/'{a-zA-Z}, ZZ/ZQ. + ## 0.3.1 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index 844205e..fa5baf9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@vemjs/core", - "version": "0.3.1", + "version": "0.4.0", "description": "Pure state machine for the Vem modal text editor", "type": "module", "main": "./dist/index.js", diff --git a/packages/core/src/editor.ts b/packages/core/src/editor.ts index b2870f8..3855140 100644 --- a/packages/core/src/editor.ts +++ b/packages/core/src/editor.ts @@ -1511,11 +1511,103 @@ export class VemEditorState { this.triggerSplit('horizontal'); } else if (base === 'set') { this.executeSetOption(arg); + } else if (base === 's') { + // :s/pattern/replacement/flags — line substitute + // :%s/pattern/replacement/flags — global substitute + let isGlobal = false; + let pattern = ''; + let replacement = ''; + let flags = ''; + + // Detect %s (global range) + let searchStart = 0; + if (trimmed.startsWith('%')) { + isGlobal = true; + searchStart = 1; + } + + const body = trimmed.substring(searchStart); + if (body.startsWith('s') && body.length > 2 && body[1] === '/') { + const sep = '/'; + const endIdx = body.indexOf(sep, 2); + if (endIdx !== -1) { + pattern = body.substring(2, endIdx); + const replEnd = body.indexOf(sep, endIdx + 1); + if (replEnd !== -1) { + replacement = body.substring(endIdx + 1, replEnd); + flags = body.substring(replEnd + 1); + } else { + replacement = body.substring(endIdx + 1); + } + } + } + + if (pattern !== undefined) { + try { + const caseInsensitive = flags.includes('i'); + const globalFlag = flags.includes('g'); + + // Build line range + let startLine = isGlobal ? 0 : this.cursor.line; + let endLine = isGlobal ? this.buffer.getLineCount() - 1 : this.cursor.line; + + this.saveStateForUndo(); + + let totalMatches = 0; + for (let l = startLine; l <= endLine; l++) { + const line = this.buffer.getLine(l); + // Vim's `:s` without `g` uses a non-global regex (first match only) + const lineRe = new RegExp(pattern, caseInsensitive ? 'i' : ''); + // Vim's `:s/.../.../g` uses a global regex (all matches) + const globalRe = new RegExp(pattern, caseInsensitive ? 'gi' : 'g'); + const [newLine, count] = this.substituteLine( + line, + globalFlag ? globalRe : lineRe, + replacement, + ); + if (count > 0) { + this.buffer.setLine(l, newLine); + totalMatches += count; + } + } + + if (totalMatches > 0) { + this.modified = true; + this.statusMessage = `${totalMatches} substitution${totalMatches > 1 ? 's' : ''} on ${ + isGlobal ? 'all lines' : 'this line' + }`; + } else if (pattern) { + this.statusMessage = `E486: Pattern not found: ${pattern}`; + } + } catch (e: any) { + this.statusMessage = `E475: Invalid argument: ${e.message}`; + } + } } else { this.statusMessage = `E492: Not an editor command: ${name}`; } } + private substituteLine(line: string, regex: RegExp, replacement: string): [string, number] { + let count = 0; + const result = line.replace(regex, (...args: any[]) => { + count++; + const match = args[0] as string; + const groups: string[] = args.slice(1, -2); + const offset = args[args.length - 2] as number; + const input = args[args.length - 1] as string; + return replacement.replace(/\$(\d|[&`'])/g, (m, ref) => { + if (ref === '&') return match; + if (ref === '`') return input.substring(0, offset); + if (ref === "'") return input.substring(offset + match.length); + const n = parseInt(ref, 10); + if (!isNaN(n) && n < groups.length) return groups[n] ?? ''; + return m; + }); + }); + return [result, count]; + } + private executeSetOption(option: string): void { // Vim's real syntax for OS-clipboard integration: `:set clipboard=unnamed` // (or `unnamedplus`) routes y/d/c/x/p/P through the system clipboard diff --git a/packages/lsp-client/CHANGELOG.md b/packages/lsp-client/CHANGELOG.md index b7d6b56..b82f757 100644 --- a/packages/lsp-client/CHANGELOG.md +++ b/packages/lsp-client/CHANGELOG.md @@ -1,5 +1,12 @@ # @vemjs/lsp-client +## 0.1.7 + +### Patch Changes + +- Updated dependencies [9ea701f] + - @vemjs/core@0.4.0 + ## 0.1.6 ### Patch Changes diff --git a/packages/lsp-client/package.json b/packages/lsp-client/package.json index e52053f..378887a 100644 --- a/packages/lsp-client/package.json +++ b/packages/lsp-client/package.json @@ -1,6 +1,6 @@ { "name": "@vemjs/lsp-client", - "version": "0.1.6", + "version": "0.1.7", "description": "LSP protocol client for the Vem editor", "type": "module", "main": "./dist/index.js", @@ -12,7 +12,7 @@ "build": "tsc" }, "dependencies": { - "@vemjs/core": "^0.3.0" + "@vemjs/core": "^0.4.0" }, "publishConfig": { "access": "public" diff --git a/packages/plugin-api/CHANGELOG.md b/packages/plugin-api/CHANGELOG.md index aed76cd..814f82b 100644 --- a/packages/plugin-api/CHANGELOG.md +++ b/packages/plugin-api/CHANGELOG.md @@ -1,5 +1,12 @@ # @vemjs/plugin-api +## 0.1.8 + +### Patch Changes + +- Updated dependencies [9ea701f] + - @vemjs/core@0.4.0 + ## 0.1.7 ### Patch Changes diff --git a/packages/plugin-api/package.json b/packages/plugin-api/package.json index d2a65e6..126b956 100644 --- a/packages/plugin-api/package.json +++ b/packages/plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@vemjs/plugin-api", - "version": "0.1.7", + "version": "0.1.8", "description": "SDK interfaces for 3rd-party plugins on the Vem editor", "type": "module", "main": "./dist/index.js", @@ -12,7 +12,7 @@ "build": "tsc" }, "dependencies": { - "@vemjs/core": "^0.3.1" + "@vemjs/core": "^0.4.0" }, "publishConfig": { "access": "public" diff --git a/packages/renderer-vecto/CHANGELOG.md b/packages/renderer-vecto/CHANGELOG.md index 2f0c78a..deedda6 100644 --- a/packages/renderer-vecto/CHANGELOG.md +++ b/packages/renderer-vecto/CHANGELOG.md @@ -1,5 +1,13 @@ # @vemjs/renderer-vecto +## 0.4.4 + +### Patch Changes + +- 9ea701f: Implement first batch of Vim-native motions and commands: zz/zt/zb, H/M/L, J, ~, {/}, gf, gv, gi, [[/]]/[]/][, /, m{a-zA-Z}/'{a-zA-Z}, ZZ/ZQ. +- Updated dependencies [9ea701f] + - @vemjs/core@0.4.0 + ## 0.4.3 ### Patch Changes diff --git a/packages/renderer-vecto/package.json b/packages/renderer-vecto/package.json index 80e81ec..ce5a73c 100644 --- a/packages/renderer-vecto/package.json +++ b/packages/renderer-vecto/package.json @@ -1,6 +1,6 @@ { "name": "@vemjs/renderer-vecto", - "version": "0.4.3", + "version": "0.4.4", "description": "VectoUI integration renderer for the Vem text editor", "type": "module", "main": "./dist/index.js", @@ -14,7 +14,7 @@ "dependencies": { "@vectojs/core": "^1.9.2", "@vectojs/ui": "^1.9.5", - "@vemjs/core": "^0.3.1" + "@vemjs/core": "^0.4.0" }, "publishConfig": { "access": "public"