diff --git a/README.md b/README.md index 70adbb2..c212004 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,43 @@ | `build_cancel` | Cancel a running build | | `build_status` | Get current build status | +### 🧭 Navigation Tools + +| Tool | Description | +|------|-------------| +| `goto_definition` | Navigate to the definition of a symbol | +| `find_references` | Find all references to a symbol | +| `symbol_document` | Get all symbols defined in a document | +| `symbol_workspace` | Search for symbols across the solution | + +### 🐛 Debugger Tools + +| Tool | Description | +|------|-------------| +| `debugger_status` | Get current debugger state | +| `debugger_launch` | Start debugging (F5) | +| `debugger_launch_without_debugging` | Start without debugger (Ctrl+F5) | +| `debugger_continue` | Continue execution (F5) | +| `debugger_break` | Pause execution (Ctrl+Alt+Break) | +| `debugger_stop` | Stop debugging (Shift+F5) | +| `debugger_step_over` | Step over (F10) | +| `debugger_step_into` | Step into (F11) | +| `debugger_step_out` | Step out (Shift+F11) | +| `debugger_add_breakpoint` | Add a breakpoint at a file and line | +| `debugger_remove_breakpoint` | Remove a breakpoint | +| `debugger_list_breakpoints` | List all breakpoints | +| `debugger_get_locals` | Get local variables in current frame | +| `debugger_get_callstack` | Get the call stack | + +### 🔍 Diagnostics Tools + +| Tool | Description | +|------|-------------| +| `errors_list` | Read build errors, warnings, and messages from the Error List | +| `output_read` | Read content from an Output window pane | +| `output_write` | Write a message to an Output window pane | +| `output_list_panes` | List all available Output window panes | + ## đŸ› ī¸ Installation ### Visual Studio Marketplace @@ -103,20 +140,36 @@ Download the latest `.vsix` from the [Releases](https://github.com/CodingWithCal 2. Go to **Tools > MCP Server > Start Server** (or enable auto-start in settings) 3. The MCP server starts on `http://localhost:5050` -### 🤖 Configuring Claude Desktop +### 🤖 Configuring Claude Desktop & Claude Code -Add this to your Claude Desktop MCP settings: +Add this to your Claude Desktop or Claude Code MCP settings (preferred HTTP method): ```json { "mcpServers": { - "visual-studio": { + "visualstudio": { + "type": "http", + "url": "http://localhost:5050" + } + } +} +``` + +**Legacy SSE method** (deprecated, but still supported): + +```json +{ + "mcpServers": { + "visualstudio": { + "type": "sse", "url": "http://localhost:5050/sse" } } } ``` +> â„šī¸ **Note:** The HTTP method is the preferred standard. SSE (Server-Sent Events) is a legacy protocol and should only be used for backward compatibility. + ### âš™ī¸ Settings Configure the extension at **Tools > Options > MCP Server**: diff --git a/src/CodingWithCalvin.MCPServer.Server/Program.cs b/src/CodingWithCalvin.MCPServer.Server/Program.cs index 892bc70..f73330e 100644 --- a/src/CodingWithCalvin.MCPServer.Server/Program.cs +++ b/src/CodingWithCalvin.MCPServer.Server/Program.cs @@ -98,7 +98,8 @@ static async Task RunServerAsync(string pipeName, string host, int port, string .WithTools() .WithTools() .WithTools() - .WithTools(); + .WithTools() + .WithTools(); var app = builder.Build(); diff --git a/src/CodingWithCalvin.MCPServer.Server/RpcClient.cs b/src/CodingWithCalvin.MCPServer.Server/RpcClient.cs index d2751d9..98a68e7 100644 --- a/src/CodingWithCalvin.MCPServer.Server/RpcClient.cs +++ b/src/CodingWithCalvin.MCPServer.Server/RpcClient.cs @@ -65,7 +65,7 @@ public Task> GetAvailableToolsAsync() } var tools = new List(); - var toolTypes = new[] { typeof(Tools.SolutionTools), typeof(Tools.DocumentTools), typeof(Tools.BuildTools), typeof(Tools.NavigationTools), typeof(Tools.DebuggerTools) }; + var toolTypes = new[] { typeof(Tools.SolutionTools), typeof(Tools.DocumentTools), typeof(Tools.BuildTools), typeof(Tools.NavigationTools), typeof(Tools.DebuggerTools), typeof(Tools.DiagnosticsTools) }; foreach (var toolType in toolTypes) { @@ -149,4 +149,11 @@ public Task FindReferencesAsync(string path, int line, int col public Task> DebugGetBreakpointsAsync() => Proxy.DebugGetBreakpointsAsync(); public Task> DebugGetLocalsAsync() => Proxy.DebugGetLocalsAsync(); public Task> DebugGetCallStackAsync() => Proxy.DebugGetCallStackAsync(); + + public Task GetErrorListAsync(string? severity = null, int maxResults = 100) + => Proxy.GetErrorListAsync(severity, maxResults); + public Task ReadOutputPaneAsync(string paneIdentifier) => Proxy.ReadOutputPaneAsync(paneIdentifier); + public Task WriteOutputPaneAsync(string paneIdentifier, string message, bool activate = false) + => Proxy.WriteOutputPaneAsync(paneIdentifier, message, activate); + public Task> GetOutputPanesAsync() => Proxy.GetOutputPanesAsync(); } diff --git a/src/CodingWithCalvin.MCPServer.Server/Tools/DiagnosticsTools.cs b/src/CodingWithCalvin.MCPServer.Server/Tools/DiagnosticsTools.cs new file mode 100644 index 0000000..2de5f38 --- /dev/null +++ b/src/CodingWithCalvin.MCPServer.Server/Tools/DiagnosticsTools.cs @@ -0,0 +1,79 @@ +using System.ComponentModel; +using System.Text.Json; +using System.Threading.Tasks; +using ModelContextProtocol.Server; + +namespace CodingWithCalvin.MCPServer.Server.Tools; + +[McpServerToolType] +public class DiagnosticsTools +{ + private readonly RpcClient _rpcClient; + private readonly JsonSerializerOptions _jsonOptions; + + public DiagnosticsTools(RpcClient rpcClient) + { + _rpcClient = rpcClient; + _jsonOptions = new JsonSerializerOptions { WriteIndented = true }; + } + + [McpServerTool(Name = "errors_list", ReadOnly = true)] + [Description("Get errors, warnings, and messages from the Error List. Returns diagnostics with file, line, description, and severity. Filter by severity to focus on specific issues.")] + public async Task GetErrorListAsync( + [Description("Filter by severity: \"Error\", \"Warning\", \"Message\", or null for all. Case-insensitive.")] + string? severity = null, + [Description("Maximum number of items to return. Defaults to 100.")] + int maxResults = 100) + { + var result = await _rpcClient.GetErrorListAsync(severity, maxResults); + + // Always return the JSON result (includes debug info if TotalCount is 0) + return JsonSerializer.Serialize(result, _jsonOptions); + } + + [McpServerTool(Name = "output_read", ReadOnly = true)] + [Description("Read content from an Output window pane. Specify pane by GUID or well-known name (\"Build\", \"Debug\", \"General\"). Note: Some panes may not support reading due to VS API limitations.")] + public async Task ReadOutputPaneAsync( + [Description("Output pane identifier: GUID string or well-known name (\"Build\", \"Debug\", \"General\").")] + string paneIdentifier) + { + var result = await _rpcClient.ReadOutputPaneAsync(paneIdentifier); + + if (string.IsNullOrEmpty(result.Content)) + { + return $"Output pane '{paneIdentifier}' is empty or does not support reading"; + } + + return JsonSerializer.Serialize(result, _jsonOptions); + } + + [McpServerTool(Name = "output_write", Destructive = false, Idempotent = false)] + [Description("Write a message to an Output window pane. Custom panes are auto-created. System panes (Build, Debug) must already exist. Message is appended to existing content.")] + public async Task WriteOutputPaneAsync( + [Description("Output pane identifier: GUID string or name. Custom GUIDs/names will create new panes if needed.")] + string paneIdentifier, + [Description("Message to write. Appended to existing content.")] + string message, + [Description("Whether to activate (bring to front) the Output window. Defaults to false.")] + bool activate = false) + { + var success = await _rpcClient.WriteOutputPaneAsync(paneIdentifier, message, activate); + return success + ? $"Message written to output pane: {paneIdentifier}" + : $"Failed to write to output pane: {paneIdentifier}"; + } + + [McpServerTool(Name = "output_list_panes", ReadOnly = true)] + [Description("List available Output window panes. Returns well-known panes (Build, Debug, General) with their names and GUIDs.")] + public async Task GetOutputPanesAsync() + { + var panes = await _rpcClient.GetOutputPanesAsync(); + + if (panes.Count == 0) + { + return "No output panes available"; + } + + return JsonSerializer.Serialize(panes, _jsonOptions); + } +} diff --git a/src/CodingWithCalvin.MCPServer.Shared/Models/DiagnosticsModels.cs b/src/CodingWithCalvin.MCPServer.Shared/Models/DiagnosticsModels.cs new file mode 100644 index 0000000..e67d188 --- /dev/null +++ b/src/CodingWithCalvin.MCPServer.Shared/Models/DiagnosticsModels.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; + +namespace CodingWithCalvin.MCPServer.Shared.Models; + +public class ErrorListResult +{ + public List Items { get; set; } = new(); + public int TotalCount { get; set; } + public int ErrorCount { get; set; } + public int WarningCount { get; set; } + public int MessageCount { get; set; } + public bool Truncated { get; set; } +} + +public class ErrorItemInfo +{ + public string Severity { get; set; } = string.Empty; // "Error", "Warning", "Message" + public string Description { get; set; } = string.Empty; + public string ErrorCode { get; set; } = string.Empty; // e.g., "CS0103" + public string Project { get; set; } = string.Empty; + public string FilePath { get; set; } = string.Empty; + public int Line { get; set; } + public int Column { get; set; } +} + +public class OutputPaneInfo +{ + public string Name { get; set; } = string.Empty; + public string Guid { get; set; } = string.Empty; +} + +public class OutputReadResult +{ + public string Content { get; set; } = string.Empty; + public string PaneName { get; set; } = string.Empty; + public int LinesRead { get; set; } +} diff --git a/src/CodingWithCalvin.MCPServer.Shared/RpcContracts.cs b/src/CodingWithCalvin.MCPServer.Shared/RpcContracts.cs index 523ece7..b390d4c 100644 --- a/src/CodingWithCalvin.MCPServer.Shared/RpcContracts.cs +++ b/src/CodingWithCalvin.MCPServer.Shared/RpcContracts.cs @@ -56,6 +56,12 @@ public interface IVisualStudioRpc Task> DebugGetBreakpointsAsync(); Task> DebugGetLocalsAsync(); Task> DebugGetCallStackAsync(); + + // Diagnostics tools + Task GetErrorListAsync(string? severity = null, int maxResults = 100); + Task ReadOutputPaneAsync(string paneIdentifier); + Task WriteOutputPaneAsync(string paneIdentifier, string message, bool activate = false); + Task> GetOutputPanesAsync(); } /// diff --git a/src/CodingWithCalvin.MCPServer/CodingWithCalvin.MCPServer.csproj b/src/CodingWithCalvin.MCPServer/CodingWithCalvin.MCPServer.csproj index e35cc89..df769f7 100644 --- a/src/CodingWithCalvin.MCPServer/CodingWithCalvin.MCPServer.csproj +++ b/src/CodingWithCalvin.MCPServer/CodingWithCalvin.MCPServer.csproj @@ -9,6 +9,7 @@ + diff --git a/src/CodingWithCalvin.MCPServer/Services/IVisualStudioService.cs b/src/CodingWithCalvin.MCPServer/Services/IVisualStudioService.cs index ff7dc4d..a702a28 100644 --- a/src/CodingWithCalvin.MCPServer/Services/IVisualStudioService.cs +++ b/src/CodingWithCalvin.MCPServer/Services/IVisualStudioService.cs @@ -52,4 +52,9 @@ public interface IVisualStudioService Task> DebugGetBreakpointsAsync(); Task> DebugGetLocalsAsync(); Task> DebugGetCallStackAsync(); + + Task GetErrorListAsync(string? severity = null, int maxResults = 100); + Task ReadOutputPaneAsync(string paneIdentifier); + Task WriteOutputPaneAsync(string paneIdentifier, string message, bool activate = false); + Task> GetOutputPanesAsync(); } diff --git a/src/CodingWithCalvin.MCPServer/Services/RpcServer.cs b/src/CodingWithCalvin.MCPServer/Services/RpcServer.cs index 544f190..f079c2f 100644 --- a/src/CodingWithCalvin.MCPServer/Services/RpcServer.cs +++ b/src/CodingWithCalvin.MCPServer/Services/RpcServer.cs @@ -209,4 +209,11 @@ public Task FindReferencesAsync(string path, int line, int col public Task> DebugGetBreakpointsAsync() => _vsService.DebugGetBreakpointsAsync(); public Task> DebugGetLocalsAsync() => _vsService.DebugGetLocalsAsync(); public Task> DebugGetCallStackAsync() => _vsService.DebugGetCallStackAsync(); + + public Task GetErrorListAsync(string? severity = null, int maxResults = 100) + => _vsService.GetErrorListAsync(severity, maxResults); + public Task ReadOutputPaneAsync(string paneIdentifier) => _vsService.ReadOutputPaneAsync(paneIdentifier); + public Task WriteOutputPaneAsync(string paneIdentifier, string message, bool activate = false) + => _vsService.WriteOutputPaneAsync(paneIdentifier, message, activate); + public Task> GetOutputPanesAsync() => _vsService.GetOutputPanesAsync(); } diff --git a/src/CodingWithCalvin.MCPServer/Services/VisualStudioService.cs b/src/CodingWithCalvin.MCPServer/Services/VisualStudioService.cs index 68fbb6f..919839e 100644 --- a/src/CodingWithCalvin.MCPServer/Services/VisualStudioService.cs +++ b/src/CodingWithCalvin.MCPServer/Services/VisualStudioService.cs @@ -4,12 +4,22 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Threading.Tasks; using CodingWithCalvin.MCPServer.Shared.Models; using CodingWithCalvin.Otel4Vsix; using EnvDTE; using EnvDTE80; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Package; using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Shell.TableManager; +using Microsoft.VisualStudio.Text.Editor; + +using Microsoft.VisualStudio.Shell.TableControl; +using Microsoft.VisualStudio.Shell.TableManager; namespace CodingWithCalvin.MCPServer.Services; @@ -1507,4 +1517,402 @@ public async Task> DebugGetCallStackAsync() return results; } + + public async Task GetErrorListAsync(string? severity = null, int maxResults = 100) + { + var result = new ErrorListResult(); + + try + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + // Get the Error List service + var errorListService = ServiceProvider.GetService(typeof(SVsErrorList)); + if (errorListService == null) + { + result.Items.Add(new ErrorItemInfo + { + Description = "Error List service not available", + Severity = "Message" + }); + return result; + } + + // Cast to IErrorList to access the TableControl + IErrorList errorList = errorListService as IErrorList; + if (errorList == null) + { + result.Items.Add(new ErrorItemInfo + { + Description = "Could not access Error List", + Severity = "Message" + }); + return result; + } + + IWpfTableControl tableControl = errorList.TableControl; + if (tableControl == null) + { + result.Items.Add(new ErrorItemInfo + { + Description = "Could not access Error List table control", + Severity = "Message" + }); + return result; + } + + int count = 0; + var severityFilter = severity?.ToLowerInvariant(); + + // Enumerate through error list entries + foreach (ITableEntryHandle entry in tableControl.Entries) + { + if (count >= maxResults) + break; + + try + { + // Get error properties from the table entry + string errorCode = ""; + string projectName = ""; + string text = ""; + string documentName = ""; + int line = 0; + int column = 0; + string severityStr = "Message"; + + // Extract all available properties + if (entry.TryGetValue(StandardTableKeyNames.ErrorCode, out object codeObj)) + { + errorCode = codeObj as string ?? ""; + } + + if (entry.TryGetValue(StandardTableKeyNames.ProjectName, out object projectObj)) + { + projectName = projectObj as string ?? ""; + } + + if (entry.TryGetValue(StandardTableKeyNames.Text, out object textObj)) + { + text = textObj as string ?? ""; + } + + if (entry.TryGetValue(StandardTableKeyNames.DocumentName, out object docObj)) + { + documentName = docObj as string ?? ""; + } + + // Get line number + if (entry.TryGetValue(StandardTableKeyNames.Line, out object lineObj) && lineObj is int lineInt) + { + line = lineInt; + } + + // Get column number + if (entry.TryGetValue(StandardTableKeyNames.Column, out object colObj) && colObj is int colInt) + { + column = colInt; + } + + // Get error severity + if (entry.TryGetValue(StandardTableKeyNames.ErrorSeverity, out object severityObj) && + severityObj is __VSERRORCATEGORY errorCategory) + { + severityStr = errorCategory switch + { + __VSERRORCATEGORY.EC_ERROR => "Error", + __VSERRORCATEGORY.EC_WARNING => "Warning", + __VSERRORCATEGORY.EC_MESSAGE => "Message", + _ => "Message" + }; + } + + // Apply severity filter if specified + if (!string.IsNullOrEmpty(severityFilter) && + !severityStr.Equals(severityFilter, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + // Add the error item to results + result.Items.Add(new ErrorItemInfo + { + FilePath = documentName, + Line = line, + Column = column, + Description = text, + Severity = severityStr, + ErrorCode = errorCode, + Project = projectName + }); + + count++; + + // Count by severity + if (severityStr == "Error") result.ErrorCount++; + else if (severityStr == "Warning") result.WarningCount++; + else result.MessageCount++; + } + catch (Exception itemEx) + { + VsixTelemetry.TrackException(itemEx); + } + } + + result.TotalCount = count; + + if (count == 0) + { + result.Items.Add(new ErrorItemInfo + { + Description = "No errors or warnings in the Error List. Build the project to populate the Error List.", + Severity = "Message" + }); + } + } + catch (Exception ex) + { + VsixTelemetry.TrackException(ex); + result.Items.Add(new ErrorItemInfo + { + Description = $"Error accessing Error List: {ex.Message}", + Severity = "Message" + }); + } + + return result; + } + + public async Task> GetOutputPanesAsync() + { + var panes = new List(); + + try + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + var dte = await GetDteAsync(); + + if (dte.ToolWindows?.OutputWindow?.OutputWindowPanes != null) + { + // Enumerate all actual panes in the Output window + foreach (EnvDTE.OutputWindowPane pane in dte.ToolWindows.OutputWindow.OutputWindowPanes) + { + panes.Add(new OutputPaneInfo + { + Name = pane.Name, + Guid = pane.Guid ?? string.Empty // Custom panes may not have a GUID + }); + } + } + } + catch (Exception ex) + { + VsixTelemetry.TrackException(ex); + } + + return panes; + } + + public async Task ReadOutputPaneAsync(string paneIdentifier) + { + var result = new OutputReadResult { PaneName = paneIdentifier }; + + try + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + var dte = await GetDteAsync(); + + if (dte.ToolWindows?.OutputWindow == null) + { + result.Content = "Output window not available"; + return result; + } + + // Find the matching pane by name (works for both well-known and custom panes) + EnvDTE.OutputWindowPane targetPane = null; + + foreach (EnvDTE.OutputWindowPane outputPane in dte.ToolWindows.OutputWindow.OutputWindowPanes) + { + if (outputPane.Name == paneIdentifier) + { + targetPane = outputPane; + break; + } + } + + if (targetPane == null) + { + result.Content = $"Output pane '{paneIdentifier}' not found"; + return result; + } + + // Read the text using the documented approach: + // 1) Get the TextDocument + // 2) Get StartPoint and create EditPoint + // 3) Call GetText with EndPoint + try + { + // Check if TextDocument is available + if (targetPane.TextDocument == null) + { + result.Content = $"Output pane '{paneIdentifier}' is empty or not yet initialized. " + + "Trigger an action for this pane (e.g., start debugging, build, or write to it)."; + return result; + } + + try + { + EnvDTE.TextDocument textDoc = targetPane.TextDocument; + EnvDTE.EditPoint startPoint = textDoc.StartPoint.CreateEditPoint(); + EnvDTE.TextPoint endPoint = textDoc.EndPoint; + + string text = startPoint.GetText(endPoint); + result.Content = text; + return result; + } + catch (System.Runtime.InteropServices.COMException comEx) when (comEx.HResult == unchecked((int)0x80004005)) + { + // E_FAIL: TextDocument exists but is not accessible (pane not initialized) + result.Content = $"Output pane '{paneIdentifier}' is not yet initialized. " + + "Trigger an action for this pane to generate content."; + return result; + } + } + catch (Exception innerEx) + { + result.Content = $"Could not read TextDocument: {innerEx.Message}"; + return result; + } + } + catch (Exception ex) + { + VsixTelemetry.TrackException(ex); + result.Content = $"Error reading output pane: {ex.Message}"; + } + + return result; + } + + public async Task WriteOutputPaneAsync(string paneIdentifier, string message, bool activate = false) + { + try + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + var outputWindow = ServiceProvider.GetService(typeof(SVsOutputWindow)) as IVsOutputWindow; + if (outputWindow == null) + { + return false; + } + + // Parse identifier as GUID or name + Guid paneGuid = Guid.Empty; + bool isCustomPane = false; + + if (Guid.TryParse(paneIdentifier, out var parsedGuid)) + { + paneGuid = parsedGuid; + isCustomPane = !IsWellKnownPane(paneGuid); + } + else + { + // Map well-known names to GUIDs + paneGuid = paneIdentifier.ToLowerInvariant() switch + { + "build" => VSConstants.OutputWindowPaneGuid.BuildOutputPane_guid, + "debug" => VSConstants.OutputWindowPaneGuid.DebugPane_guid, + "general" => VSConstants.OutputWindowPaneGuid.GeneralPane_guid, + _ => Guid.NewGuid() // Create custom pane with new GUID + }; + + isCustomPane = paneIdentifier.ToLowerInvariant() switch + { + "build" or "debug" or "general" => false, + _ => true + }; + } + + // Try to get existing pane + var paneGuidRef = paneGuid; + int hr = outputWindow.GetPane(ref paneGuidRef, out IVsOutputWindowPane? pane); + + // If pane doesn't exist and it's a custom pane, create it + if (hr != 0 && isCustomPane) + { + hr = outputWindow.CreatePane(ref paneGuid, paneIdentifier, 1, 1); + if (hr != 0) + { + return false; + } + + // Get the newly created pane + paneGuidRef = paneGuid; + hr = outputWindow.GetPane(ref paneGuidRef, out pane); + if (hr != 0 || pane == null) + { + return false; + } + } + else if (hr != 0 || pane == null) + { + // System pane not found - don't create + return false; + } + + // Write message to pane + pane.OutputStringThreadSafe(message + "\r\n"); + + // Activate pane if requested + if (activate) + { + pane.Activate(); + } + + return true; + } + catch (Exception ex) + { + VsixTelemetry.TrackException(ex); + return false; + } + } + + private IVsOutputWindowPane? GetPaneByIdentifier(IVsOutputWindow outputWindow, string paneIdentifier) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (Guid.TryParse(paneIdentifier, out var paneGuid)) + { + var guidRef = paneGuid; + outputWindow.GetPane(ref guidRef, out var pane); + return pane; + } + + // Map well-known names + var guid = paneIdentifier.ToLowerInvariant() switch + { + "build" => VSConstants.OutputWindowPaneGuid.BuildOutputPane_guid, + "debug" => VSConstants.OutputWindowPaneGuid.DebugPane_guid, + "general" => VSConstants.OutputWindowPaneGuid.GeneralPane_guid, + _ => Guid.Empty + }; + + if (guid != Guid.Empty) + { + var guidRef = guid; + outputWindow.GetPane(ref guidRef, out var pane); + return pane; + } + + return null; + } + + private bool IsWellKnownPane(Guid paneGuid) + { + return paneGuid == VSConstants.OutputWindowPaneGuid.BuildOutputPane_guid || + paneGuid == VSConstants.OutputWindowPaneGuid.DebugPane_guid || + paneGuid == VSConstants.OutputWindowPaneGuid.GeneralPane_guid; + } }