Skip to content

Commit f891aef

Browse files
committed
feat: Add SVN commit support with GUI interface similar to Git commits
- Add SVN utility functions in src/utils/svn.ts mirroring git.ts functionality - Add SvnCommit type and svnCommitSearchResults to ExtensionMessage - Add searchSvnCommits message type to WebviewMessage - Update webviewMessageHandler to handle SVN commit searches - Update context-mentions to support SVN revision patterns (r123, @svn-changes) - Update ChatTextArea to trigger SVN searches and display results - Add comprehensive test suite for SVN functionality - Implement SVN repository info extraction and working state detection Fixes #6100
1 parent 9956cc1 commit f891aef

File tree

9 files changed

+786
-6
lines changed

9 files changed

+786
-6
lines changed

src/core/mentions/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { isBinaryFile } from "isbinaryfile"
77
import { mentionRegexGlobal, unescapeSpaces } from "../../shared/context-mentions"
88

99
import { getCommitInfo, getWorkingState } from "../../utils/git"
10+
import { getSvnCommitInfo, getSvnWorkingState } from "../../utils/svn"
1011
import { getWorkspacePath } from "../../utils/path"
1112

1213
import { openFile } from "../../integrations/misc/open-file"
@@ -95,8 +96,12 @@ export async function parseMentions(
9596
return `Workspace Problems (see below for diagnostics)`
9697
} else if (mention === "git-changes") {
9798
return `Working directory changes (see below for details)`
99+
} else if (mention === "svn-changes") {
100+
return `SVN working directory changes (see below for details)`
98101
} else if (/^[a-f0-9]{7,40}$/.test(mention)) {
99102
return `Git commit '${mention}' (see below for commit info)`
103+
} else if (/^r?\d+$/.test(mention)) {
104+
return `SVN revision '${mention}' (see below for revision info)`
100105
} else if (mention === "terminal") {
101106
return `Terminal Output (see below for output)`
102107
}
@@ -177,13 +182,27 @@ export async function parseMentions(
177182
} catch (error) {
178183
parsedText += `\n\n<git_working_state>\nError fetching working state: ${error.message}\n</git_working_state>`
179184
}
185+
} else if (mention === "svn-changes") {
186+
try {
187+
const workingState = await getSvnWorkingState(cwd)
188+
parsedText += `\n\n<svn_working_state>\n${workingState}\n</svn_working_state>`
189+
} catch (error) {
190+
parsedText += `\n\n<svn_working_state>\nError fetching SVN working state: ${error.message}\n</svn_working_state>`
191+
}
180192
} else if (/^[a-f0-9]{7,40}$/.test(mention)) {
181193
try {
182194
const commitInfo = await getCommitInfo(mention, cwd)
183195
parsedText += `\n\n<git_commit hash="${mention}">\n${commitInfo}\n</git_commit>`
184196
} catch (error) {
185197
parsedText += `\n\n<git_commit hash="${mention}">\nError fetching commit info: ${error.message}\n</git_commit>`
186198
}
199+
} else if (/^r?\d+$/.test(mention)) {
200+
try {
201+
const commitInfo = await getSvnCommitInfo(mention, cwd)
202+
parsedText += `\n\n<svn_revision revision="${mention}">\n${commitInfo}\n</svn_revision>`
203+
} catch (error) {
204+
parsedText += `\n\n<svn_revision revision="${mention}">\nError fetching SVN revision info: ${error.message}\n</svn_revision>`
205+
}
187206
} else if (mention === "terminal") {
188207
try {
189208
const terminalOutput = await getLatestTerminalOutput()

src/core/webview/webviewMessageHandler.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { fileExistsAtPath } from "../../utils/fs"
3737
import { playTts, setTtsEnabled, setTtsSpeed, stopTts } from "../../utils/tts"
3838
import { singleCompletionHandler } from "../../utils/single-completion-handler"
3939
import { searchCommits } from "../../utils/git"
40+
import { searchSvnCommits } from "../../utils/svn"
4041
import { exportSettings, importSettingsWithFeedback } from "../config/importExport"
4142
import { getOpenAiModels } from "../../api/providers/openai"
4243
import { getVsCodeLmModels } from "../../api/providers/vscode-lm"
@@ -1384,6 +1385,24 @@ export const webviewMessageHandler = async (
13841385
}
13851386
break
13861387
}
1388+
case "searchSvnCommits": {
1389+
const cwd = provider.cwd
1390+
if (cwd) {
1391+
try {
1392+
const svnCommits = await searchSvnCommits(message.query || "", cwd)
1393+
await provider.postMessageToWebview({
1394+
type: "svnCommitSearchResults",
1395+
svnCommits,
1396+
})
1397+
} catch (error) {
1398+
provider.log(
1399+
`Error searching SVN commits: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
1400+
)
1401+
vscode.window.showErrorMessage(t("common:errors.search_commits"))
1402+
}
1403+
}
1404+
break
1405+
}
13871406
case "searchFiles": {
13881407
const workspacePath = getWorkspacePath()
13891408

src/shared/ExtensionMessage.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type {
1313
} from "@roo-code/types"
1414

1515
import { GitCommit } from "../utils/git"
16+
import { SvnCommit } from "../utils/svn"
1617

1718
import { McpServer } from "./mcp"
1819
import { Mode } from "./modes"
@@ -61,6 +62,7 @@ export interface ExtensionMessage {
6162
| "mcpServers"
6263
| "enhancedPrompt"
6364
| "commitSearchResults"
65+
| "svnCommitSearchResults"
6466
| "listApiConfig"
6567
| "routerModels"
6668
| "openAiModels"
@@ -137,6 +139,7 @@ export interface ExtensionMessage {
137139
vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[]
138140
mcpServers?: McpServer[]
139141
commits?: GitCommit[]
142+
svnCommits?: SvnCommit[]
140143
listApiConfig?: ProviderSettingsEntry[]
141144
mode?: Mode
142145
customMode?: ModeConfig

src/shared/WebviewMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export interface WebviewMessage {
129129
| "mcpEnabled"
130130
| "enableMcpServerCreation"
131131
| "searchCommits"
132+
| "searchSvnCommits"
132133
| "alwaysApproveResubmit"
133134
| "requestDelaySeconds"
134135
| "setApiConfigPassword"

src/shared/context-mentions.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,20 @@ Mention regex:
4646
- URLs that start with a protocol (like 'http://') followed by any non-whitespace characters (including query parameters).
4747
- The exact word 'problems'.
4848
- The exact word 'git-changes'.
49-
- The exact word 'terminal'.
49+
- The exact word 'svn-changes'.
50+
- The exact word 'terminal'.
5051
- It ensures that any trailing punctuation marks (such as ',', '.', '!', etc.) are not included in the matched mention, allowing the punctuation to follow the mention naturally in the text.
5152
5253
- **Global Regex**:
5354
- `mentionRegexGlobal`: Creates a global version of the `mentionRegex` to find all matches within a given string.
5455
5556
*/
5657
export const mentionRegex =
57-
/(?<!\\)@((?:\/|\w+:\/\/)(?:[^\s\\]|\\ )+?|[a-f0-9]{7,40}\b|problems\b|git-changes\b|terminal\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/
58+
/(?<!\\)@((?:\/|\w+:\/\/)(?:[^\s\\]|\\ )+?|[a-f0-9]{7,40}\b|r?\d+\b|problems\b|git-changes\b|svn-changes\b|terminal\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/
5859
export const mentionRegexGlobal = new RegExp(mentionRegex.source, "g")
5960

6061
export interface MentionSuggestion {
61-
type: "file" | "folder" | "git" | "problems"
62+
type: "file" | "folder" | "git" | "svn" | "problems"
6263
label: string
6364
description?: string
6465
value: string
@@ -95,6 +96,33 @@ export function formatGitSuggestion(commit: {
9596
}
9697
}
9798

99+
export interface SvnMentionSuggestion extends MentionSuggestion {
100+
type: "svn"
101+
revision: string
102+
author: string
103+
date: string
104+
message: string
105+
}
106+
107+
export function formatSvnSuggestion(commit: {
108+
revision: string
109+
author: string
110+
date: string
111+
message: string
112+
}): SvnMentionSuggestion {
113+
return {
114+
type: "svn",
115+
label: commit.message,
116+
description: `${commit.revision} by ${commit.author} on ${commit.date}`,
117+
value: commit.revision,
118+
icon: "$(git-commit)", // Using same icon as Git for consistency
119+
revision: commit.revision,
120+
author: commit.author,
121+
date: commit.date,
122+
message: commit.message,
123+
}
124+
}
125+
98126
// Helper function to unescape paths with backslash-escaped spaces
99127
export function unescapeSpaces(path: string): string {
100128
return path.replace(/\\ /g, " ")

0 commit comments

Comments
 (0)