diff --git a/src/extension.ts b/src/extension.ts index 99734a2..d9ae321 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,8 +1,36 @@ import * as vscode from 'vscode'; -import { exec } from 'child_process'; +import { exec, execSync } from 'child_process'; import * as os from 'os'; import * as fs from 'fs'; +function isWSL(): boolean { + return process.platform === 'linux' && fs.existsSync('/proc/version') && + fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft'); +} + +function getWSLWindowsPath(): string { + try { + const result = execSync('cmd.exe /c echo %LOCALAPPDATA%', { encoding: 'utf8' }); + return result.trim(); + } catch (error) { + console.error('Failed to get Windows path:', error); + return ''; + } +} + +function convertWSLPathToWindows(path: string): string { + try { + // Convert path using wslpath + const windowsPath = execSync(`wslpath -w "${path}"`, { encoding: 'utf8' }).trim(); + + // Replace backslashes to ensure correct path format + return windowsPath.replace(/\\/g, '\\\\'); + } catch (error) { + console.error('Failed to convert WSL path:', error); + return path; + } +} + function getMacIdeaPath(): string { const commonPaths = [ '/Applications/IDEA.app', @@ -98,7 +126,10 @@ export function activate(context: vscode.ExtensionContext) { } let command: string; - if (os.platform() === 'darwin') { + if (isWSL()) { + const windowsFilePath = convertWSLPathToWindows(filePath); + command = `cmd.exe /c "${ideaPath}" --line ${line} --column ${column} "${windowsFilePath}"`; + } else if (os.platform() === 'darwin') { const ideaUrl = `idea://open?file=${encodeURIComponent(filePath)}&line=${line}&column=${column}`; // If IDEA is already open, using the 'idea' command will show two IDEA icons in the dock temporarily // Using the 'open' command instead will prevent this issue @@ -141,7 +172,11 @@ export function activate(context: vscode.ExtensionContext) { } let command: string; - if (os.platform() === 'darwin') { + if (isWSL()) { + const windowsProjectPath = convertWSLPathToWindows(projectPath); + // Use --project parameter to open project + command = `cmd.exe /c "${ideaPath}" --project "${windowsProjectPath}"`; + } else if (os.platform() === 'darwin') { const ideaUrl = `idea://open?file=${encodeURIComponent(projectPath)}`; command = `open -a "${ideaPath}" "${ideaUrl}"`; } else { @@ -162,4 +197,4 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(openProjectDisposable); } -export function deactivate() {} +export function deactivate() {} \ No newline at end of file diff --git a/src/test/extension.test.ts b/src/test/extension.test.ts index 9e7999e..0757c03 100644 --- a/src/test/extension.test.ts +++ b/src/test/extension.test.ts @@ -3,6 +3,7 @@ import * as vscode from 'vscode'; import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs'; +import { execSync } from 'child_process'; suite('Switch2IDEA Extension Test Suite', () => { // 在所有测试开始前激活扩展 @@ -122,4 +123,184 @@ suite('Switch2IDEA Extension Test Suite', () => { await config.update('ideaPath', originalPath, vscode.ConfigurationTarget.Global); } }); + + test('Should handle WSL path conversion', async () => { + if (process.platform === 'linux' && fs.existsSync('/proc/version') && + fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft')) { + // Create test file + const tmpDir = os.tmpdir(); + const testFileName = 'wsl_test.txt'; + const testFilePath = path.join(tmpDir, testFileName); + + try { + // Create test file + fs.writeFileSync(testFilePath, 'test content'); + + // Test path conversion + const windowsPath = execSync(`wslpath -w "${testFilePath}"`, { encoding: 'utf8' }).trim(); + assert.ok(windowsPath.startsWith('\\\\wsl.localhost\\'), 'Path should be converted to WSL format'); + + // Open file + const doc = await vscode.workspace.openTextDocument(testFilePath); + const editor = await vscode.window.showTextDocument(doc); + + // Execute command + await vscode.commands.executeCommand('Switch2IDEA.openFileInIDEA'); + + // Verify command execution completed + assert.ok(true); + } finally { + // Cleanup test file + try { + fs.unlinkSync(testFilePath); + } catch (e) { + console.error('Failed to cleanup test file:', e); + } + } + } + }); + + test('Should handle WSL project opening', async () => { + if (process.platform === 'linux' && fs.existsSync('/proc/version') && + fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft')) { + // Create temporary project directory + const tmpDir = os.tmpdir(); + const projectDir = path.join(tmpDir, 'wsl_project_test'); + + try { + // Create project directory and test file + fs.mkdirSync(projectDir); + fs.writeFileSync(path.join(projectDir, 'test.txt'), 'test content'); + + // Test project path conversion + const windowsProjectPath = execSync(`wslpath -w "${projectDir}"`, { encoding: 'utf8' }).trim(); + assert.ok(windowsProjectPath.startsWith('\\\\wsl.localhost\\'), 'Project path should be converted to WSL format'); + + // Execute project open command + await vscode.commands.executeCommand('Switch2IDEA.openProjectInIDEA'); + + // Verify command execution completed + assert.ok(true); + } finally { + // Cleanup test directory + try { + fs.rmSync(projectDir, { recursive: true, force: true }); + } catch (e) { + console.error('Failed to cleanup test directory:', e); + } + } + } + }); + + test('Should handle Windows path in WSL environment', async () => { + if (process.platform === 'linux' && fs.existsSync('/proc/version') && + fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft')) { + // Get Windows path + const windowsPath = execSync('cmd.exe /c echo %LOCALAPPDATA%', { encoding: 'utf8' }).trim(); + assert.ok(windowsPath, 'Should be able to get Windows path'); + + // Test IDEA path construction + const ideaPath = `${windowsPath}\\Programs\\IntelliJ IDEA\\bin\\idea64.exe`; + assert.ok(ideaPath.includes('\\'), 'IDEA path should contain backslashes'); + + // Create test file + const tmpDir = os.tmpdir(); + const testFileName = 'windows_path_test.txt'; + const testFilePath = path.join(tmpDir, testFileName); + + try { + fs.writeFileSync(testFilePath, 'test content'); + + // Open file + const doc = await vscode.workspace.openTextDocument(testFilePath); + const editor = await vscode.window.showTextDocument(doc); + + // Execute command + await vscode.commands.executeCommand('Switch2IDEA.openFileInIDEA'); + + // Verify command execution completed + assert.ok(true); + } finally { + // Cleanup test file + try { + fs.unlinkSync(testFilePath); + } catch (e) { + console.error('Failed to cleanup test file:', e); + } + } + } + }); + + test('Should handle special characters in WSL paths', async () => { + if (process.platform === 'linux' && fs.existsSync('/proc/version') && + fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft')) { + // Create test file with special characters + const tmpDir = os.tmpdir(); + const testFileName = 'test file with spaces and @#$%^&.txt'; + const testFilePath = path.join(tmpDir, testFileName); + + try { + fs.writeFileSync(testFilePath, 'test content'); + + // Test path conversion + const windowsPath = execSync(`wslpath -w "${testFilePath}"`, { encoding: 'utf8' }).trim(); + assert.ok(windowsPath, 'Path should be converted successfully'); + + // Open file + const doc = await vscode.workspace.openTextDocument(testFilePath); + const editor = await vscode.window.showTextDocument(doc); + + // Execute command + await vscode.commands.executeCommand('Switch2IDEA.openFileInIDEA'); + + // Verify command execution completed + assert.ok(true); + } finally { + // Cleanup test file + try { + fs.unlinkSync(testFilePath); + } catch (e) { + console.error('Failed to cleanup test file:', e); + } + } + } + }); + + test('Should handle command execution errors gracefully', async () => { + // Temporarily set an invalid IDEA path + const config = vscode.workspace.getConfiguration('switch2idea'); + const originalPath = config.get('ideaPath'); + + try { + await config.update('ideaPath', 'invalid_path', vscode.ConfigurationTarget.Global); + + // Create test file + const tmpDir = os.tmpdir(); + const testFilePath = path.join(tmpDir, 'error_test.txt'); + + try { + fs.writeFileSync(testFilePath, 'test content'); + + // Open file + const doc = await vscode.workspace.openTextDocument(testFilePath); + const editor = await vscode.window.showTextDocument(doc); + + // Execute command + await vscode.commands.executeCommand('Switch2IDEA.openFileInIDEA'); + + // Verify command execution completed (should not throw exception even if failed) + assert.ok(true); + } finally { + // Cleanup test file + try { + fs.unlinkSync(testFilePath); + } catch (e) { + console.error('Failed to cleanup test file:', e); + } + } + } finally { + // Restore original settings + await config.update('ideaPath', originalPath, vscode.ConfigurationTarget.Global); + } + }); });