Compare commits

...
5 changed files with 241 additions and 31 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Remind VS code users of clines existence(Open Cline on AutoUpdate+KeyboardShortcuts+Lightbulb icons)
+18
View File
@@ -126,6 +126,16 @@
"title": "Generate Commit Message with Cline",
"category": "Cline",
"icon": "$(robot)"
},
{
"command": "cline.explainCode",
"title": "Explain with Cline",
"category": "Cline"
},
{
"command": "cline.improveCode",
"title": "Improve with Cline",
"category": "Cline"
}
],
"keybindings": [
@@ -140,6 +150,14 @@
{
"command": "cline.generateGitCommitMessage",
"when": "scmProvider == git"
},
{
"command": "cline.focusChatInput",
"key": "cmd+'",
"mac": "cmd+'",
"win": "ctrl+'",
"linux": "ctrl+'",
"when": "!editorHasSelection"
}
],
"menus": {
+2
View File
@@ -3,6 +3,7 @@
// Import all method implementations
import { registerMethod } from "./index"
import { copyToClipboard } from "./copyToClipboard"
import { createRuleFile } from "./createRuleFile"
import { deleteRuleFile } from "./deleteRuleFile"
import { getRelativePaths } from "./getRelativePaths"
@@ -15,6 +16,7 @@ import { selectImages } from "./selectImages"
// Register all file service methods
export function registerAllMethods(): void {
// Register each method with the registry
registerMethod("copyToClipboard", copyToClipboard)
registerMethod("createRuleFile", createRuleFile)
registerMethod("deleteRuleFile", deleteRuleFile)
registerMethod("getRelativePaths", getRelativePaths)
+22
View File
@@ -149,6 +149,28 @@ export class WebviewProvider implements vscode.WebviewViewProvider {
this.controller.clearTask()
this.outputChannel.appendLine("Webview view resolved")
// Set the title to include the keyboard shortcut
if (this.view && "title" in this.view) {
// Check if the view object has a title property
const isMac = process.platform === "darwin"
const shortcutDisplay = isMac ? " (⌘+')" : " (Ctrl+')"
const baseTitle = this.context.extension.packageJSON.displayName || "Cline"
const newTitleWithShortcut = `${baseTitle}${shortcutDisplay}`
const currentTitle = this.view.title
if (typeof currentTitle === "string") {
if (!currentTitle.includes(shortcutDisplay)) {
// Title exists and is a string, but doesn't have the shortcut
this.view.title = newTitleWithShortcut
}
// If it includes shortcutDisplay, do nothing
} else {
// Title is undefined or not a string (e.g., for sidebar initially)
this.view.title = newTitleWithShortcut
}
}
}
}
+194 -31
View File
@@ -28,7 +28,7 @@ let outputChannel: vscode.OutputChannel
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
export async function activate(context: vscode.ExtensionContext) {
outputChannel = vscode.window.createOutputChannel("Cline")
context.subscriptions.push(outputChannel)
@@ -36,6 +36,9 @@ export function activate(context: vscode.ExtensionContext) {
Logger.initialize(outputChannel)
Logger.log("Cline extension activated")
// Version checking for autoupdate notification
const currentVersion = context.extension.packageJSON.version
const previousVersion = context.globalState.get<string>("clineVersion")
const sidebarWebview = new WebviewProvider(context, outputChannel)
// Initialize test mode and add disposables to context
@@ -49,6 +52,29 @@ export function activate(context: vscode.ExtensionContext) {
}),
)
// Perform post-update actions if necessary
try {
if (!previousVersion || currentVersion !== previousVersion) {
Logger.log(`Cline version changed: ${previousVersion} -> ${currentVersion}. First run or update detected.`)
const lastShownPopupNotificationVersion = context.globalState.get<string>("clineLastPopupNotificationVersion")
if (currentVersion !== lastShownPopupNotificationVersion && previousVersion) {
// Show VS Code popup notification as this version hasn't been notified yet without doing it for fresh installs
const message = `Cline has been updated to v${currentVersion}`
await vscode.commands.executeCommand("claude-dev.SidebarProvider.focus")
await new Promise((resolve) => setTimeout(resolve, 200))
vscode.window.showInformationMessage(message)
// Record that we've shown the popup for this version.
await context.globalState.update("clineLastPopupNotificationVersion", currentVersion)
}
// Always update the main version tracker for the next launch.
await context.globalState.update("clineVersion", currentVersion)
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
console.error(`Error during post-update actions: ${errorMessage}, Stack trace: ${error.stack}`)
}
context.subscriptions.push(
vscode.commands.registerCommand("cline.plusButtonClicked", async (webview: any) => {
const openChat = async (instance?: WebviewProvider) => {
@@ -257,6 +283,8 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand("cline.addToChat", async (range?: vscode.Range, diagnostics?: vscode.Diagnostic[]) => {
await vscode.commands.executeCommand("cline.focusChatInput") // Ensure Cline is visible and input focused
await pWaitFor(() => !!WebviewProvider.getVisibleInstance())
const editor = vscode.window.activeTextEditor
if (!editor) {
return
@@ -336,50 +364,100 @@ export function activate(context: vscode.ExtensionContext) {
}),
)
const CONTEXT_LINES_TO_EXPAND = 3
const START_OF_LINE_CHAR_INDEX = 0
const LINE_COUNT_ADJUSTMENT_FOR_ZERO_INDEXING = 1
// Register code action provider
context.subscriptions.push(
vscode.languages.registerCodeActionsProvider(
"*",
new (class implements vscode.CodeActionProvider {
public static readonly providedCodeActionKinds = [vscode.CodeActionKind.QuickFix]
public static readonly providedCodeActionKinds = [vscode.CodeActionKind.QuickFix, vscode.CodeActionKind.Refactor]
provideCodeActions(
document: vscode.TextDocument,
range: vscode.Range,
context: vscode.CodeActionContext,
): vscode.CodeAction[] {
// Expand range to include surrounding 3 lines
const expandedRange = new vscode.Range(
Math.max(0, range.start.line - 3),
0,
Math.min(document.lineCount - 1, range.end.line + 3),
document.lineAt(Math.min(document.lineCount - 1, range.end.line + 3)).text.length,
)
const actions: vscode.CodeAction[] = []
const editor = vscode.window.activeTextEditor // Get active editor for selection check
// Expand range to include surrounding 3 lines or use selection if broader
const selection = editor?.selection
let expandedRange = range
if (
editor &&
selection &&
!selection.isEmpty &&
selection.contains(range.start) &&
selection.contains(range.end)
) {
expandedRange = selection
} else {
expandedRange = new vscode.Range(
Math.max(0, range.start.line - CONTEXT_LINES_TO_EXPAND),
START_OF_LINE_CHAR_INDEX,
Math.min(
document.lineCount - LINE_COUNT_ADJUSTMENT_FOR_ZERO_INDEXING,
range.end.line + CONTEXT_LINES_TO_EXPAND,
),
document.lineAt(
Math.min(
document.lineCount - LINE_COUNT_ADJUSTMENT_FOR_ZERO_INDEXING,
range.end.line + CONTEXT_LINES_TO_EXPAND,
),
).text.length,
)
}
// Add to Cline (Always available)
const addAction = new vscode.CodeAction("Add to Cline", vscode.CodeActionKind.QuickFix)
addAction.command = {
command: "cline.addToChat",
title: "Add to Cline",
arguments: [expandedRange, context.diagnostics],
}
actions.push(addAction)
const fixAction = new vscode.CodeAction("Fix with Cline", vscode.CodeActionKind.QuickFix)
fixAction.command = {
command: "cline.fixWithCline",
title: "Fix with Cline",
arguments: [expandedRange, context.diagnostics],
// Explain with Cline (Always available)
const explainAction = new vscode.CodeAction("Explain with Cline", vscode.CodeActionKind.RefactorExtract) // Using a refactor kind
explainAction.command = {
command: "cline.explainCode",
title: "Explain with Cline",
arguments: [expandedRange],
}
actions.push(explainAction)
// Only show actions when there are errors
// Improve with Cline (Always available)
const improveAction = new vscode.CodeAction("Improve with Cline", vscode.CodeActionKind.RefactorRewrite) // Using a refactor kind
improveAction.command = {
command: "cline.improveCode",
title: "Improve with Cline",
arguments: [expandedRange],
}
actions.push(improveAction)
// Fix with Cline (Only if diagnostics exist)
if (context.diagnostics.length > 0) {
return [addAction, fixAction]
} else {
return []
const fixAction = new vscode.CodeAction("Fix with Cline", vscode.CodeActionKind.QuickFix)
fixAction.isPreferred = true
fixAction.command = {
command: "cline.fixWithCline",
title: "Fix with Cline",
arguments: [expandedRange, context.diagnostics],
}
actions.push(fixAction)
}
return actions
}
})(),
{
providedCodeActionKinds: [vscode.CodeActionKind.QuickFix],
providedCodeActionKinds: [
vscode.CodeActionKind.QuickFix,
vscode.CodeActionKind.RefactorExtract,
vscode.CodeActionKind.RefactorRewrite,
],
},
),
)
@@ -406,21 +484,106 @@ export function activate(context: vscode.ExtensionContext) {
}),
)
context.subscriptions.push(
vscode.commands.registerCommand("cline.explainCode", async (range: vscode.Range) => {
await vscode.commands.executeCommand("cline.focusChatInput") // Ensure Cline is visible and input focused
await pWaitFor(() => !!WebviewProvider.getVisibleInstance())
const editor = vscode.window.activeTextEditor
if (!editor) {
return
}
const selectedText = editor.document.getText(range)
if (!selectedText.trim()) {
vscode.window.showInformationMessage("Please select some code to explain.")
return
}
const filePath = editor.document.uri.fsPath
const visibleWebview = WebviewProvider.getVisibleInstance()
const fileMention = visibleWebview?.controller.getFileMentionFromPath(filePath) || filePath
const prompt = `Explain the following code from ${fileMention}:\n\`\`\`${editor.document.languageId}\n${selectedText}\n\`\`\``
await visibleWebview?.controller.initTask(prompt)
}),
)
context.subscriptions.push(
vscode.commands.registerCommand("cline.improveCode", async (range: vscode.Range) => {
await vscode.commands.executeCommand("cline.focusChatInput") // Ensure Cline is visible and input focused
await pWaitFor(() => !!WebviewProvider.getVisibleInstance())
const editor = vscode.window.activeTextEditor
if (!editor) {
return
}
const selectedText = editor.document.getText(range)
if (!selectedText.trim()) {
vscode.window.showInformationMessage("Please select some code to improve.")
return
}
const filePath = editor.document.uri.fsPath
const visibleWebview = WebviewProvider.getVisibleInstance()
const fileMention = visibleWebview?.controller.getFileMentionFromPath(filePath) || filePath
const prompt = `Improve the following code from ${fileMention} (e.g., suggest refactorings, optimizations, or better practices):\n\`\`\`${editor.document.languageId}\n${selectedText}\n\`\`\``
await visibleWebview?.controller.initTask(prompt)
}),
)
// Register the focusChatInput command handler
context.subscriptions.push(
vscode.commands.registerCommand("cline.focusChatInput", () => {
let visibleWebview = WebviewProvider.getVisibleInstance()
if (!visibleWebview) {
vscode.commands.executeCommand("claude-dev.SidebarProvider.focus")
visibleWebview = WebviewProvider.getSidebarInstance()
// showing the extension will call didBecomeVisible which focuses it already
// but it doesn't focus if a tab is selected which focusChatInput accounts for
}
vscode.commands.registerCommand("cline.focusChatInput", async () => {
let activeWebviewProvider: WebviewProvider | undefined = WebviewProvider.getVisibleInstance()
visibleWebview?.controller.postMessageToWebview({
type: "action",
action: "focusChatInput",
})
// If a tab is visible and active, ensure it's fully revealed (might be redundant but safe)
if (activeWebviewProvider?.view && activeWebviewProvider.view.hasOwnProperty("reveal")) {
const panelView = activeWebviewProvider.view as vscode.WebviewPanel
panelView.reveal(panelView.viewColumn)
} else if (!activeWebviewProvider) {
// No webview is currently visible, try to activate the sidebar
await vscode.commands.executeCommand("claude-dev.SidebarProvider.focus")
await new Promise((resolve) => setTimeout(resolve, 200)) // Allow time for focus
activeWebviewProvider = WebviewProvider.getSidebarInstance()
if (!activeWebviewProvider) {
// Sidebar didn't become active (might be closed or not in current view container)
// Check for existing tab panels
const tabInstances = WebviewProvider.getTabInstances()
if (tabInstances.length > 0) {
const potentialTabInstance = tabInstances[tabInstances.length - 1] // Get the most recent one
if (potentialTabInstance.view && potentialTabInstance.view.hasOwnProperty("reveal")) {
const panelView = potentialTabInstance.view as vscode.WebviewPanel
panelView.reveal(panelView.viewColumn)
activeWebviewProvider = potentialTabInstance
}
}
}
if (!activeWebviewProvider) {
// No existing Cline view found at all, open a new tab
await vscode.commands.executeCommand("cline.openInNewTab")
// After openInNewTab, a new webview is created. We need to get this new instance.
// It might take a moment for it to register.
await pWaitFor(
() => {
const visibleInstance = WebviewProvider.getVisibleInstance()
// Ensure a boolean is returned
return !!(visibleInstance?.view && visibleInstance.view.hasOwnProperty("reveal"))
},
{ timeout: 2000 },
)
activeWebviewProvider = WebviewProvider.getVisibleInstance()
}
}
// At this point, activeWebviewProvider should be the one we want to send the message to.
// It could still be undefined if opening a new tab failed or timed out.
if (activeWebviewProvider) {
activeWebviewProvider.controller.postMessageToWebview({
type: "action",
action: "focusChatInput",
})
} else {
console.error("FocusChatInput: Could not find or activate a Cline webview to focus.")
vscode.window.showErrorMessage(
"Could not activate Cline view. Please try opening it manually from the Activity Bar.",
)
}
}),
)