├── .editorconfig
├── .github
└── workflows
│ └── workflow.yml
├── .gitignore
├── LICENSE
├── README.md
├── Resources
├── CallStackWindow.ico
├── CallStackWindow_256x.png
├── Preview.png
├── StackTraceExplorer.cs
└── StackTraceExplorer.vsct
├── StackTraceExplorer.Shared
├── CustomLinkVisualLineText.cs
├── Generators
│ ├── FileLinkElementGenerator.cs
│ └── MemberLinkElementGenerator.cs
├── Helpers
│ ├── ClickHelper.cs
│ ├── SolutionHelper.cs
│ ├── StringHelper.cs
│ └── TraceHelper.cs
├── Models
│ ├── StackTrace.cs
│ └── StackTracesViewModel.cs
├── StackTraceEditor.cs
├── StackTraceExplorer.Shared.projitems
├── StackTraceExplorer.Shared.shproj
├── StackTraceExplorer.resx
├── StackTraceExplorerToolWindow.cs
├── StackTraceExplorerToolWindowCommand.cs
├── StackTraceExplorerToolWindowControl.xaml
├── StackTraceExplorerToolWindowControl.xaml.cs
└── StackTraceExplorerToolWindowPackage.cs
├── StackTraceExplorer.Tests
├── FileRegexTests.cs
├── LongestSuffixTests.cs
├── MemberRegexTests.cs
├── Properties
│ └── AssemblyInfo.cs
├── SolutionHelperTests.cs
└── StackTraceExplorer.Tests.csproj
├── StackTraceExplorer.VS2019
├── CallStackWindow.ico
├── Properties
│ └── AssemblyInfo.cs
├── StackTraceExplorer.VS2019.csproj
└── source.extension.vsixmanifest
├── StackTraceExplorer.VS2022
├── CallStackWindow.ico
├── Properties
│ └── AssemblyInfo.cs
├── StackTraceExplorer.VS2022.csproj
└── source.extension.vsixmanifest
├── StackTraceExplorer.sln
├── nuget.config
├── publish-manifest.VS2019.json
└── publish-manifest.VS2022.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # VSTHRD200: Use "Async" suffix for async methods
4 | dotnet_diagnostic.VSTHRD200.severity = none
5 |
6 | # IDE0008: Use explicit type
7 | dotnet_diagnostic.IDE0008.severity = none
8 |
9 | # VSTHRD111: Use ConfigureAwait(bool)
10 | dotnet_diagnostic.VSTHRD111.severity = none
11 |
12 | # IDE0022: Use block body for methods
13 | dotnet_diagnostic.IDE0022.severity = none
14 |
--------------------------------------------------------------------------------
/.github/workflows/workflow.yml:
--------------------------------------------------------------------------------
1 | name: StackTraceExplorer
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - 'feature/**'
8 |
9 | env:
10 | version: '3.0.${{ github.run_number }}'
11 | repoUrl: ${{ github.server_url }}/${{ github.repository }}
12 |
13 | jobs:
14 | build:
15 | name: 🛠️ Build
16 | runs-on: windows-latest
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v4
20 |
21 | - name: Update Assembly Version
22 | uses: dannevesdantas/set-version-assemblyinfo@v.1.0.0
23 | with:
24 | version: ${{ env.version }}
25 |
26 | - name: Update Vsix Version (VS2019)
27 | uses: cezarypiatek/VsixVersionAction@1.0
28 | with:
29 | version: ${{ env.version }}
30 | vsix-manifest-file: 'StackTraceExplorer.VS2019\source.extension.vsixmanifest'
31 |
32 | - name: Update Vsix Version (VS2022)
33 | uses: cezarypiatek/VsixVersionAction@1.0
34 | with:
35 | version: ${{ env.version }}
36 | vsix-manifest-file: 'StackTraceExplorer.VS2022\source.extension.vsixmanifest'
37 |
38 | - name: Setup MSBuild
39 | uses: microsoft/setup-msbuild@v2
40 |
41 | - name: NuGet restore
42 | run: nuget restore StackTraceExplorer.sln -ConfigFile nuget.config
43 |
44 | - name: Build VSIX
45 | run: msbuild StackTraceExplorer.sln /t:Rebuild /p:Configuration=Release
46 | env:
47 | DeployExtension: False
48 |
49 | - name: Publish Build Artifacts
50 | uses: actions/upload-artifact@v4
51 | with:
52 | name: StackTraceExplorer
53 | path: |
54 | **\*.vsix
55 | publish-manifest.*.json
56 | readme.md
57 |
58 | release:
59 | name: 🚀 Release
60 | needs: build
61 | runs-on: windows-latest
62 | environment: Release
63 | steps:
64 | - name: Download artifact
65 | uses: actions/download-artifact@v4
66 |
67 | - name: Tag release
68 | id: tag_release
69 | uses: mathieudutour/github-tag-action@v6.2
70 | with:
71 | custom_tag: '${{ env.version }}'
72 | github_token: ${{ secrets.GITHUB_TOKEN }}
73 |
74 | - name: Create a GitHub release
75 | uses: ncipollo/release-action@v1
76 | with:
77 | tag: ${{ steps.tag_release.outputs.new_tag }}
78 | name: ${{ steps.tag_release.outputs.new_tag }}
79 | body: ${{ steps.tag_release.outputs.changelog }}
80 | artifacts: "**/*.vsix"
81 | skipIfReleaseExists: true
82 |
83 | - name: Publish to Marketplace - VS2019
84 | uses: cezarypiatek/VsixPublisherAction@1.1
85 | with:
86 | extension-file: StackTraceExplorer/StackTraceExplorer.VS2019/bin/release/StackTraceExplorer.VS2019.vsix
87 | publish-manifest-file: StackTraceExplorer/publish-manifest.VS2019.json
88 | personal-access-code: ${{ secrets.VS_PUBLISHER_ACCESS_TOKEN }}
89 |
90 | - name: Publish to Marketplace - VS2022
91 | uses: cezarypiatek/VsixPublisherAction@1.1
92 | with:
93 | extension-file: StackTraceExplorer/StackTraceExplorer.VS2022/bin/release/StackTraceExplorer.VS2022.vsix
94 | publish-manifest-file: StackTraceExplorer/publish-manifest.VS2022.json
95 | personal-access-code: ${{ secrets.VS_PUBLISHER_ACCESS_TOKEN }}
96 |
97 | - name: Publish to Open VSIX Gallery - VS2019
98 | run: |
99 | curl -L 'https://www.vsixgallery.com/api/upload?repo=${{ env.repoUrl }}&issuetracker=${{ env.repoUrl }}/issues' -F 'file=@"StackTraceExplorer/StackTraceExplorer.VS2019/bin/release/StackTraceExplorer.VS2019.vsix"'
100 |
101 | - name: Publish to Open VSIX Gallery - VS2022
102 | run: |
103 | curl -L 'https://www.vsixgallery.com/api/upload?repo=${{ env.repoUrl }}&issuetracker=${{ env.repoUrl }}/issues' -F 'file=@"StackTraceExplorer/StackTraceExplorer.VS2022/bin/release/StackTraceExplorer.VS2022.vsix"'
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build results
2 | [Bb]in/
3 | [Oo]bj/
4 | [Ll]og/
5 | [Ll]ogs/
6 |
7 | # The packages folder can be ignored because of Package Restore
8 | **/[Pp]ackages/*
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 |
14 | # Visual Studio options directory
15 | .vs/
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 - 2021 Samir Boulema
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Stack Trace Explorer
2 | Parse those pesty unreadable long stack traces. Stack Trace Explorer provides syntax highlighting and easy navigation to elements in the stack trace.
3 |
4 | [](https://github.com/sboulema/StackTraceExplorer/actions/workflows/workflow.yml)
5 | [](https://github.com/sponsors/sboulema)
6 |
7 | ## Installing
8 | [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=SamirBoulema.StackTraceExplorer)
9 |
10 | [Github Releases](https://github.com/sboulema/StackTraceExplorer/releases)
11 |
12 | [Open VSIX Gallery](http://vsixgallery.com/extension/StackTraceExplorer.Samir%20Boulema/)
13 |
14 | ## Usage
15 | You can find the Stack Trace Explorer at
16 |
17 | `View - Other Windows - Stack Trace Explorer`
18 |
19 | or the default keybinding
20 |
21 | `Ctrl + S, T`
22 |
23 | ## Features
24 | - Clicking a filepath will open the file at the line mentioned in the stacktrace
25 | - Clicking a method will open the corresponding file at the start of that method
26 | - Wrap long stacktraces
27 | - Syntax highlighting
28 | - Dark theme support
29 | - Tabs
30 |
31 | ### Opening a new tab
32 | There are multiple ways open opening new tabs to show your stack traces:
33 |
34 | **Copy a stack trace to your clipboard:**
35 | - Click the paste as new tab but
36 | - Select a tab, make sure your cursor is not in the text editor, Paste your stack trace with `Ctrl + V`
37 |
38 | **Stack trace from file:**
39 | - Click the open file button
40 | - Drag & drop the file to the toolwindow
41 |
42 | ## Supported stack trace formats
43 | - Visual Studio
44 | - Application Insights
45 | - [Ben.Demystifier](https://github.com/benaadams/Ben.Demystifier)
46 |
47 | ## Screenshots
48 | 
49 |
50 | ## Debug extension
51 |
52 | On `StackTraceExplorer.VS2022` project property, set 'Debug' properties as the following:
53 | * set radio button to `Start external program`
54 | * browse for Visual Studio executable (like `C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\devenv.exe`)
55 | * set `/RootSuffix Exp` in `Command line arguments`
56 |
57 | ## Thanks
58 | - [Resharper](https://www.jetbrains.com/resharper/) (for the initial idea to recreate this)
59 | - [Terrajobst](https://github.com/terrajobst/stack-trace-explorer) (for some inspiration on optimizing the extension)
60 | - [dlstucki](https://github.com/dlstucki) (for a large PR optimizing StackTraceExplorer for large solutions)
61 | - [pmiossec](https://github.com/pmiossec) (for several PRs reviving StackTraceExplorer)
--------------------------------------------------------------------------------
/Resources/CallStackWindow.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sboulema/StackTraceExplorer/267f8f387860823f5bcab79ebba20564ba357488/Resources/CallStackWindow.ico
--------------------------------------------------------------------------------
/Resources/CallStackWindow_256x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sboulema/StackTraceExplorer/267f8f387860823f5bcab79ebba20564ba357488/Resources/CallStackWindow_256x.png
--------------------------------------------------------------------------------
/Resources/Preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sboulema/StackTraceExplorer/267f8f387860823f5bcab79ebba20564ba357488/Resources/Preview.png
--------------------------------------------------------------------------------
/Resources/StackTraceExplorer.cs:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------
2 | //
3 | // This file was generated by VSIX Synchronizer
4 | //
5 | // ------------------------------------------------------------------------------
6 | namespace StackTraceExplorer
7 | {
8 | using System;
9 |
10 | ///
11 | /// Helper class that exposes all GUIDs used across VS Package.
12 | ///
13 | internal sealed partial class PackageGuids
14 | {
15 | public const string guidStackTraceExplorerToolWindowPackageString = "0485ea98-864e-461f-945f-3c8f9c994842";
16 | public static Guid guidStackTraceExplorerToolWindowPackage = new Guid(guidStackTraceExplorerToolWindowPackageString);
17 |
18 | public const string guidStackTraceExplorerToolWindowPackageCmdSetString = "8de831e0-1853-4232-8c10-da7fac5a2b54";
19 | public static Guid guidStackTraceExplorerToolWindowPackageCmdSet = new Guid(guidStackTraceExplorerToolWindowPackageCmdSetString);
20 | }
21 | ///
22 | /// Helper class that encapsulates all CommandIDs uses across VS Package.
23 | ///
24 | internal sealed partial class PackageIds
25 | {
26 | public const int StackTraceExplorerToolWindowCommandId = 0x0100;
27 | }
28 | }
--------------------------------------------------------------------------------
/Resources/StackTraceExplorer.vsct:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/CustomLinkVisualLineText.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Windows;
4 | using System.Windows.Input;
5 | using System.Windows.Media;
6 | using System.Windows.Media.TextFormatting;
7 | using Community.VisualStudio.Toolkit;
8 | using ICSharpCode.AvalonEdit.Document;
9 | using ICSharpCode.AvalonEdit.Rendering;
10 | using StackTraceExplorer.Helpers;
11 |
12 | namespace StackTraceExplorer
13 | {
14 | ///
15 | /// VisualLineElement that represents a piece of text and is a clickable link.
16 | ///
17 | public class CustomLinkVisualLineText : VisualLineText
18 | {
19 | public string[] Link { get; }
20 |
21 | public bool RequireControlModifierForClick { get; set; }
22 |
23 | public Brush ForegroundBrush { get; set; }
24 |
25 | public Action ClickFunction { get; set; }
26 |
27 | public TextDocument TextDocument { get; set; }
28 |
29 | public StackTraceEditor TextEditor { get; set; }
30 |
31 | OutputWindowPane OutputWindowPane { get; }
32 |
33 | ///
34 | /// Creates a visual line text element with the specified length.
35 | /// It uses the and its
36 | /// to find the actual text string.
37 | ///
38 | public CustomLinkVisualLineText(string[] theLink, VisualLine parentVisualLine, int length,
39 | Brush foregroundBrush, Action clickFunction, bool requireControlModifierForClick,
40 | TextDocument textDocument, StackTraceEditor textEditor)
41 | : base(parentVisualLine, length)
42 | {
43 | RequireControlModifierForClick = requireControlModifierForClick;
44 | Link = theLink;
45 | ForegroundBrush = foregroundBrush;
46 | ClickFunction = clickFunction;
47 | TextDocument = textDocument;
48 | TextEditor = textEditor;
49 | }
50 |
51 | public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
52 | {
53 | TextRunProperties.SetForegroundBrush(ForegroundBrush);
54 |
55 | var lineNumber = TextDocument.GetLineByOffset(context.VisualLine.StartOffset).LineNumber;
56 |
57 | if (LinkIsClickable() &&
58 | TraceHelper.LineNumber == lineNumber &&
59 | TraceHelper.CurrentColumn >= RelativeTextOffset &&
60 | TraceHelper.CurrentColumn <= RelativeTextOffset + VisualLength)
61 | {
62 | TextRunProperties.SetTextDecorations(TextDecorations.Underline);
63 | }
64 |
65 | return base.CreateTextRun(startVisualColumn, context);
66 | }
67 |
68 | private bool LinkIsClickable()
69 | {
70 | if (!Link.Any())
71 | {
72 | return false;
73 | }
74 |
75 | return RequireControlModifierForClick
76 | ? (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control
77 | : true;
78 | }
79 |
80 | protected override void OnQueryCursor(QueryCursorEventArgs e)
81 | {
82 | if (!LinkIsClickable())
83 | {
84 | return;
85 | }
86 |
87 | e.Handled = true;
88 | e.Cursor = Cursors.Hand;
89 |
90 | TraceHelper.TextEditor = TextEditor;
91 | TraceHelper.SetCurrentMouseOffset(e);
92 |
93 | (e.Source as TextView).Redraw();
94 | }
95 |
96 | protected override void OnMouseDown(MouseButtonEventArgs e)
97 | {
98 | if (e.ChangedButton == MouseButton.Left && !e.Handled && LinkIsClickable())
99 | {
100 | e.Handled = true;
101 |
102 | ClickFunction(Link, this.TextEditor);
103 | TraceHelper.ViewModel.AddClickedLine(this);
104 |
105 | (e.Source as TextView).Redraw();
106 | }
107 | }
108 |
109 | protected override VisualLineText CreateInstance(int length)
110 | => new CustomLinkVisualLineText(Link, ParentVisualLine, length,
111 | ForegroundBrush, ClickFunction, false, TextDocument, TextEditor);
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/Generators/FileLinkElementGenerator.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 | using System.Windows.Media;
3 | using ICSharpCode.AvalonEdit.Rendering;
4 | using Microsoft.VisualStudio.PlatformUI;
5 | using Microsoft.VisualStudio.Shell;
6 | using StackTraceExplorer.Helpers;
7 |
8 | namespace StackTraceExplorer.Generators
9 | {
10 | public class FileLinkElementGenerator : VisualLineElementGenerator
11 | {
12 | // To use this class:
13 | // textEditor.TextArea.TextView.ElementGenerators.Add(new FileLinkElementGenerator());
14 |
15 | private readonly StackTraceEditor _textEditor;
16 | public static readonly Regex FilePathRegex = new Regex(@" (?(?(?:[A-Za-z]\:|\\|/)(?:[\\/a-zA-Z_\-\s0-9\.\(\)]+)+):(?:line|Zeile|строка|ligne)?\s?(?\d+))", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
17 |
18 | public FileLinkElementGenerator(StackTraceEditor textEditor)
19 | {
20 | _textEditor = textEditor;
21 | }
22 |
23 | private Match FindMatch(int startOffset)
24 | {
25 | // fetch the end offset of the VisualLine being generated
26 | var endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset;
27 | var document = CurrentContext.Document;
28 | var relevantText = document.GetText(startOffset, endOffset - startOffset);
29 | return FilePathRegex.Match(relevantText);
30 | }
31 |
32 | /// Gets the first offset >= startOffset where the generator wants to construct
33 | /// an element.
34 | /// Return -1 to signal no interest.
35 | public override int GetFirstInterestedOffset(int startOffset)
36 | {
37 | var m = FindMatch(startOffset);
38 | return m.Success ? startOffset + m.Index : -1;
39 | }
40 |
41 | /// Constructs an element at the specified offset.
42 | /// May return null if no element should be constructed.
43 | public override VisualLineElement ConstructElement(int offset)
44 | {
45 | var match = FindMatch(offset);
46 |
47 | // check whether there's a match exactly at offset
48 | if (!match.Success || match.Index != 0)
49 | {
50 | return null;
51 | }
52 |
53 | var line = new CustomLinkVisualLineText(
54 | new[] { match.Groups["path"].Value, match.Groups["line"].Value },
55 | CurrentContext.VisualLine,
56 | match.Groups["place"].Length + 1,
57 | ToBrush(EnvironmentColors.ControlLinkTextBrushKey),
58 | ClickHelper.HandleFileLinkClicked,
59 | false,
60 | CurrentContext.Document,
61 | _textEditor
62 | );
63 |
64 | if (TraceHelper.ViewModel.IsClickedLine(line))
65 | {
66 | line.ForegroundBrush = ToBrush(EnvironmentColors.ControlLinkTextBrushKey);
67 | }
68 |
69 | return line;
70 | }
71 |
72 | private static SolidColorBrush ToBrush(ThemeResourceKey key)
73 | {
74 | var color = VSColorTheme.GetThemedColor(key);
75 | return new SolidColorBrush(Color.FromArgb(color.A, color.R, color.G, color.B));
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/Generators/MemberLinkElementGenerator.cs:
--------------------------------------------------------------------------------
1 | using System.Text.RegularExpressions;
2 | using System.Windows.Media;
3 | using ICSharpCode.AvalonEdit.Rendering;
4 | using Microsoft.VisualStudio.PlatformUI;
5 | using Microsoft.VisualStudio.Shell;
6 | using StackTraceExplorer.Helpers;
7 | using System.Linq;
8 |
9 | namespace StackTraceExplorer.Generators
10 | {
11 | public class MemberLinkElementGenerator : VisualLineElementGenerator
12 | {
13 | // To use this class:
14 | // textEditor.TextArea.TextView.ElementGenerators.Add(new MemberLinkElementGenerator());
15 |
16 | private readonly StackTraceEditor _textEditor;
17 | private static readonly Regex MemberVisualStudioRegex = new Regex(@"(?(?[A-Za-z0-9<>_`+]+\.)*(?(.ctor|[A-Za-z0-9<>_\[,\]|+])+\(.*?\)))", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
18 | private static readonly Regex MemberAppInsightsRegex = new Regex(@"(?(?[A-Za-z0-9<>_`+]+\.)*(?(.ctor|[A-Za-z0-9<>_\[,\]|+])+)) \(.+:\d+\)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
19 | private static readonly Regex MemberDemystifiedRegex = new Regex(@"(async )?([A-Za-z0-9<>_`+]+ )(?(?[A-Za-z0-9<>_`+]+\.)*(?(.ctor|[A-Za-z0-9<>_\[,\]|+])+\(.*?\))) ", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
20 |
21 | private string _fullMatchText;
22 |
23 | public MemberLinkElementGenerator(StackTraceEditor textEditor)
24 | {
25 | _textEditor = textEditor;
26 | }
27 |
28 | private Match FindMatch(int startOffset)
29 | {
30 | // fetch the end offset of the VisualLine being generated
31 | var endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset;
32 | var document = CurrentContext.Document;
33 | var relevantText = document.GetText(startOffset, endOffset - startOffset);
34 | return FindMatch(relevantText);
35 | }
36 |
37 | public static Match FindMatch(string text)
38 | {
39 | var match = MemberDemystifiedRegex.Match(text);
40 | if (match.Success)
41 | return match;
42 | match = MemberAppInsightsRegex.Match(text);
43 | if (match.Success)
44 | return match;
45 | return MemberVisualStudioRegex.Match(text);
46 | }
47 |
48 | /// Gets the first offset >= startOffset where the generator wants to construct
49 | /// an element.
50 | /// Return -1 to signal no interest.
51 | public override int GetFirstInterestedOffset(int startOffset)
52 | {
53 | var m = FindMatch(startOffset);
54 | return m.Success ? startOffset + m.Index : -1;
55 | }
56 |
57 | /// Constructs an element at the specified offset.
58 | /// May return null if no element should be constructed.
59 | public override VisualLineElement ConstructElement(int offset)
60 | {
61 | var match = FindMatch(offset);
62 | // check whether there's a match exactly at offset
63 | if (!match.Success || match.Index != 0) return null;
64 |
65 | // The first match returns the full method definition
66 | if (string.IsNullOrEmpty(_fullMatchText))
67 | {
68 | _fullMatchText = match.Groups["member"].Value;
69 | }
70 |
71 | var captures = match.Groups["namespace"].Captures.Cast().Select(c => c.Value).ToList();
72 | captures.Add(match.Groups["method"].Value);
73 |
74 | var firstCapture = captures[0];
75 | var lineElement = new CustomLinkVisualLineText(
76 | new [] { _fullMatchText, firstCapture },
77 | CurrentContext.VisualLine,
78 | firstCapture.TrimEnd('.').Length,
79 | ToBrush(EnvironmentColors.ControlLinkTextBrushKey),
80 | ClickHelper.HandleMemberLinkClicked,
81 | false,
82 | CurrentContext.Document,
83 | _textEditor
84 | );
85 |
86 | // If we have created elements for the entire definition, reset.
87 | // So we can create elements for more definitions
88 | if (_fullMatchText.EndsWith(firstCapture))
89 | {
90 | _fullMatchText = null;
91 | }
92 |
93 | if (TraceHelper.ViewModel.IsClickedLine(lineElement))
94 | {
95 | lineElement.ForegroundBrush = ToBrush(EnvironmentColors.ControlLinkTextBrushKey);
96 | }
97 |
98 | return lineElement;
99 | }
100 |
101 | private static SolidColorBrush ToBrush(ThemeResourceKey key)
102 | {
103 | var color = VSColorTheme.GetThemedColor(key);
104 | return new SolidColorBrush(Color.FromArgb(color.A, color.R, color.G, color.B));
105 | }
106 | }
107 | }
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/Helpers/ClickHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using Community.VisualStudio.Toolkit;
8 | using Microsoft.VisualStudio.Shell;
9 | using Microsoft.VisualStudio.Text;
10 | using Microsoft.VisualStudio.Text.Editor;
11 | using File = System.IO.File;
12 | using Path = System.IO.Path;
13 | using Task = System.Threading.Tasks.Task;
14 |
15 | namespace StackTraceExplorer.Helpers
16 | {
17 | public static class ClickHelper
18 | {
19 | ///
20 | /// Handle click event on a file path in a stacktrace
21 | ///
22 | /// File path
23 | /// File found
24 | public static void HandleFileLinkClicked(string[] input, StackTraceEditor stackTraceEditor)
25 | {
26 | ThreadHelper.JoinableTaskFactory.Run(async () =>
27 | {
28 | var stopwatch = Stopwatch.StartNew();
29 | OutputWindowPane outputWindow = await stackTraceEditor.EnsureOutputWindowPaneAsync();
30 | try
31 | {
32 | var path = await Find(input[0], outputWindow, stopwatch);
33 |
34 | if (!File.Exists(path))
35 | {
36 | await WriteOutputAsync(outputWindow, $"FileLinkClicked: {input[0]}: Unable to resolve file ({stopwatch.Elapsed})", true);
37 | return;
38 | }
39 |
40 | var documentView = await VS.Documents.OpenAsync(path);
41 |
42 | ITextSnapshotLine line = documentView.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(int.Parse(input[1]) - 1);
43 | documentView?.TextView?.ViewScroller.EnsureSpanVisible(line.Extent);
44 | var selectionBroker = documentView.TextView?.GetMultiSelectionBroker();
45 | if (selectionBroker != null)
46 | {
47 | selectionBroker.SetSelection(new Microsoft.VisualStudio.Text.Selection(line.Extent));
48 | }
49 |
50 | await WriteOutputAsync(outputWindow, $"FileLinkClicked: {input[0]} finished ({stopwatch.Elapsed})", true);
51 | }
52 | catch (Exception e)
53 | {
54 | await WriteOutputAsync(outputWindow, $"FileLinkClicked: {input[0]} error ({stopwatch.Elapsed}):\r\n{e}", true);
55 | }
56 | });
57 | }
58 |
59 | ///
60 | /// Handle click event on a member in a stacktrace
61 | ///
62 | /// Function name
63 | public static void HandleMemberLinkClicked(string[] input, StackTraceEditor stackTraceEditor)
64 | {
65 | ThreadHelper.JoinableTaskFactory.Run(async () =>
66 | {
67 | var stopwatch = Stopwatch.StartNew();
68 | string typeOrMemberName = GetTypeOrMemberName(input);
69 | OutputWindowPane outputWindow = await stackTraceEditor.EnsureOutputWindowPaneAsync();
70 | try
71 | {
72 | if (SolutionHelper.CompilationServiceNotInitialized)
73 | {
74 | await WriteOutputAsync(outputWindow, $"MemberLinkClicked: {typeOrMemberName}: Compilation service not initialized. Build solution first...", showInStatusBar: true);
75 | return;
76 | }
77 |
78 | var member = SolutionHelper.Resolve(typeOrMemberName);
79 | if (member == null)
80 | {
81 | await WriteOutputAsync(outputWindow, $"MemberLinkClicked: {typeOrMemberName}: unable to resolve member ({stopwatch.Elapsed})", showInStatusBar: true);
82 | return;
83 | }
84 |
85 | var location = member.Locations.FirstOrDefault();
86 |
87 | var documentView = await VS.Documents.OpenAsync(location.SourceTree.FilePath);
88 | var line = documentView.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(location.GetLineSpan().StartLinePosition.Line);
89 | documentView?.TextView?.ViewScroller.EnsureSpanVisible(line.Extent);
90 | var selectionBroker = documentView.TextView?.GetMultiSelectionBroker();
91 | if (selectionBroker != null)
92 | {
93 | var snapshotSpan = new SnapshotSpan(documentView.TextView.TextSnapshot, location.SourceSpan.Start, location.SourceSpan.Length);
94 | selectionBroker.SetSelection(new Microsoft.VisualStudio.Text.Selection(snapshotSpan));
95 | }
96 |
97 | await WriteOutputAsync(outputWindow, $"MemberLinkClicked: {typeOrMemberName} finished ({stopwatch.Elapsed})", true);
98 | }
99 | catch (Exception e)
100 | {
101 | await WriteOutputAsync(outputWindow, $"MemberLinkClicked: {typeOrMemberName} error:\r\n{e}", true);
102 | }
103 | });
104 | }
105 |
106 | static async Task WriteOutputAsync(OutputWindowPane outputWindow, string message, bool showInStatusBar = false)
107 | {
108 | await outputWindow.WriteLineAsync(message);
109 | if (showInStatusBar)
110 | {
111 | await VS.StatusBar.ShowMessageAsync(message);
112 | }
113 | }
114 |
115 | private static readonly Dictionary CacheFolders = new Dictionary();
116 |
117 | ///
118 | /// Given a path to a file, try to find a project item that closely matches the file path,
119 | /// but is not an exact match
120 | ///
121 | ///
122 | ///
123 | public static async Task Find(string path, OutputWindowPane outputWindow, Stopwatch stopwatch)
124 | {
125 | if (string.IsNullOrEmpty(path))
126 | {
127 | return string.Empty;
128 | }
129 |
130 | if (File.Exists(path))
131 | {
132 | await WriteOutputAsync(outputWindow, $"FindFile: '{path}': full path exists ({stopwatch.Elapsed})");
133 | return path;
134 | }
135 |
136 | foreach (var mapping in CacheFolders)
137 | {
138 | if (path.StartsWith(mapping.Key))
139 | {
140 | var potentialPath = path.Replace(mapping.Key, mapping.Value);
141 | if (File.Exists(potentialPath))
142 | {
143 | await WriteOutputAsync(outputWindow, $"FindFile: '{potentialPath}': full path exists ({stopwatch.Elapsed})");
144 | return potentialPath;
145 | }
146 | }
147 | }
148 |
149 | string fileNameOnly = Path.GetFileName(path);
150 |
151 | var solution = await VS.Solutions.GetCurrentSolutionAsync();
152 | if (solution == null)
153 | {
154 | await WriteOutputAsync(outputWindow, $"FindFile: '{path}': No solution ({stopwatch.Elapsed})");
155 | return string.Empty;
156 | }
157 |
158 | var solutionDir = new DirectoryInfo(Path.GetDirectoryName(solution.FullPath));
159 | try
160 | {
161 | await outputWindow.WriteLineAsync($"FindFile: '{path}' looking for '{fileNameOnly}' in '{solutionDir.FullName}'");
162 | FileInfo[] fileInfos = solutionDir.GetFiles($"{fileNameOnly}", SearchOption.AllDirectories);
163 | string[] files = fileInfos.Select(fi => fi.FullName).ToArray();
164 | await outputWindow.WriteLineAsync($"FindFile: '{path}' found {files.Length} potential matches ({stopwatch.Elapsed})");
165 | if (files.Length == 0)
166 | {
167 | return string.Empty;
168 | }
169 |
170 | var candidates = new List();
171 | candidates.AddRange(files.Select(f => f.Replace('\\', '/')));
172 | candidates.AddRange(files.Select(f => f.Replace('/', '\\')));
173 |
174 | string file = StringHelper.FindLongestMatchingSuffix(path, candidates.ToArray(), StringComparison.OrdinalIgnoreCase);
175 |
176 | if (file != null)
177 | {
178 | var paths = LongestUncommonPath(path, file);
179 | CacheFolders[paths.Item1] = paths.Item2;
180 | await outputWindow.WriteLineAsync($"FindFile: Returning file: '{file}' ({stopwatch.Elapsed})");
181 | return file;
182 | }
183 | }
184 | catch (Exception e)
185 | {
186 | await outputWindow.WriteLineAsync($"FindFile: '{path}' ({stopwatch.Elapsed}) error:\r\n{e}");
187 | }
188 |
189 | await outputWindow.WriteLineAsync($"FindFile: '{path}' returning original path! Is this bad? ({stopwatch.Elapsed})'");
190 | return path;
191 | }
192 |
193 | private static (string, string) LongestUncommonPath(string s1, string s2)
194 | {
195 | var minLength = Math.Min(s1.Length, s2.Length);
196 | var s1Sep = s1.Replace('/', '\\');
197 | var s2Sep = s2.Replace('/', '\\');
198 | for (int i = 0; i < minLength; i++)
199 | {
200 | if (s1Sep[s1Sep.Length-i-1] == s2Sep[s2Sep.Length-i-1])
201 | {
202 | continue;
203 | }
204 | else
205 | {
206 | var pathsCached = (s1.Substring(0, s1.Length - i + 1), s2.Substring(0, s2.Length - i + 1));
207 | return pathsCached;
208 | }
209 | }
210 |
211 | //Should not happen...
212 | return (string.Empty, string.Empty);
213 | }
214 |
215 | ///
216 | /// Construct the member identification
217 | ///
218 | ///
219 | ///
220 | public static string GetTypeOrMemberName(string[] input)
221 | {
222 | var needle = input[1].TrimEnd('.');
223 | var index = input[0].IndexOf(needle);
224 |
225 | var result = input[0].Substring(0, index + needle.Length);
226 |
227 | return result;
228 | }
229 |
230 | internal static void ClearCache()
231 | {
232 | CacheFolders.Clear();
233 | }
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/Helpers/SolutionHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Immutable;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Microsoft.CodeAnalysis;
8 |
9 | namespace StackTraceExplorer.Helpers
10 | {
11 | public static class SolutionHelper
12 | {
13 | public static Solution Solution { get; set; }
14 | public static ImmutableArray Compilations { get; set; }
15 |
16 | public static async Task> GetCompilationsAsync(Solution solution)
17 | {
18 | var compilationTasks = new Task[solution.ProjectIds.Count];
19 | for (var i = 0; i < compilationTasks.Length; i++)
20 | {
21 | var project = solution.GetProject(solution.ProjectIds[i]);
22 | compilationTasks[i] = project.GetCompilationAsync();
23 | }
24 |
25 | _ = await Task.WhenAll(compilationTasks);
26 |
27 | Compilations = compilationTasks.Select(t => t.Result).ToImmutableArray();
28 | return Compilations;
29 | }
30 |
31 | public static bool CompilationServiceNotInitialized => Compilations.IsDefault;
32 |
33 | public static ISymbol Resolve(string memberName)
34 | {
35 | ISymbol symbol = Compilations.Select(c => Resolve(c, memberName)).FirstOrDefault(s => s != null);
36 | return symbol;
37 | }
38 |
39 | public static ISymbol Resolve(Compilation compilation, string methodName)
40 | {
41 | var parts = methodName.Replace(".ctor", "#ctor").Split('.');
42 |
43 | var currentContainer = (INamespaceOrTypeSymbol)compilation.Assembly.Modules.Single().GlobalNamespace;
44 |
45 | for (var i = 0; currentContainer != null && i < parts.Length - 1; i++)
46 | {
47 | ParseTypeName(parts[i], out var typeOrNamespaceName, out var typeArity);
48 | if (string.IsNullOrEmpty(typeOrNamespaceName) && IsCompilerGeneratedName(parts[i]))
49 | {
50 | // We could be dealing with a name like "SolutionHelperTests.<>c.b__6_5()". On the member "<>c" we need to
51 | // skip to the next part in order to find a member with the name, such as 'CreateSomeStackTraces" from the example above.
52 | continue;
53 | }
54 |
55 | currentContainer = currentContainer
56 | .GetMembers(typeOrNamespaceName)
57 | .Where(n => typeArity == 0 || (n is INamedTypeSymbol t && t.Arity == typeArity))
58 | .FirstOrDefault() as INamespaceOrTypeSymbol;
59 | }
60 |
61 | if (currentContainer == null)
62 | {
63 | return null;
64 | }
65 |
66 | string lastPart = parts.Last();
67 | var name = GetMemberName(lastPart);
68 | var members = currentContainer.GetMembers(name);
69 |
70 | if (!members.Any())
71 | {
72 | return null;
73 | }
74 |
75 | int methodArity = GetMethodArity(lastPart);
76 | IReadOnlyList parameterTypes = GetMethodParameterTypes(lastPart);
77 | bool isCompilerGenerated = IsCompilerGeneratedName(lastPart);
78 |
79 | foreach (ISymbol member in members)
80 | {
81 | switch (member.Kind)
82 | {
83 | case SymbolKind.Method:
84 | if (member is IMethodSymbol method)
85 | {
86 | // isCompilerGenerated allows for "b__7(String s)" to match "CreateSomeStackTraces" which
87 | // might have a different number of parameters than what is found here.
88 | if (IsMatch(method, parameterTypes, methodArity) || isCompilerGenerated)
89 | {
90 | return method;
91 | }
92 | }
93 |
94 | break;
95 | case SymbolKind.NamedType:
96 | ParseTypeName(lastPart, out _, out int typeArity);
97 | if (member is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.Arity == typeArity)
98 | {
99 | return member;
100 | }
101 | break;
102 | default:
103 | return member;
104 | }
105 | }
106 |
107 | return null;
108 | }
109 |
110 | ///
111 | /// Determine if the name is generated by the compiler. Examples include iterators, closures, anonymous methods, lambdas
112 | /// examples:
113 | /// Types: "<>c", "<>c__DisplayClass", and so on "<>c__DisplayClass5_0`2"
114 | /// Methods: "b__6_5()", "b__2_1(IAsyncResult r)"
115 | ///
116 | static bool IsCompilerGeneratedName(string memberName)
117 | {
118 | return memberName.StartsWith("<", StringComparison.OrdinalIgnoreCase);
119 | }
120 |
121 | private static void ParseTypeName(string typeName, out string name, out int arity)
122 | {
123 | name = typeName;
124 | arity = 0;
125 |
126 | var backtick = typeName.IndexOf('`');
127 | int angleBracketOpen = typeName.IndexOf('<');
128 | if (angleBracketOpen >= 0)
129 | {
130 | int angleBracketClose = typeName.IndexOf('>', angleBracketOpen);
131 | if (angleBracketClose > 0)
132 | {
133 | name = typeName.Substring(angleBracketOpen + 1, angleBracketClose - angleBracketOpen - 1);
134 | }
135 | }
136 |
137 | if (backtick >= 0)
138 | {
139 | name = typeName.Substring(0, backtick);
140 | var arityText = typeName.Substring(backtick + 1);
141 | arity = int.Parse(arityText);
142 | }
143 | }
144 |
145 | public static string GetMemberName(string memberNameAndSignature)
146 | {
147 | string result = memberNameAndSignature;
148 | if (IsCompilerGeneratedName(memberNameAndSignature))
149 | {
150 | // This could be an anonymous method name like "b__6_5()", in that case
151 | // return "CreateSomeStackTraces"
152 | result = memberNameAndSignature.Split(new[] { '<', '>' }, StringSplitOptions.RemoveEmptyEntries)[0];
153 | }
154 | else
155 | {
156 | int firstSeparator = memberNameAndSignature.IndexOfAny(new[] { '(', '[', '`' });
157 | if (firstSeparator != -1)
158 | {
159 | result = memberNameAndSignature.Substring(0, firstSeparator);
160 | }
161 |
162 | if (result == "#ctor")
163 | {
164 | result = ".ctor";
165 | }
166 | }
167 |
168 | return result;
169 | }
170 |
171 | private static int GetMethodArity(string methodNameAndSignature)
172 | {
173 | var parenthesis = methodNameAndSignature.IndexOf('(');
174 | if (parenthesis < 0)
175 | {
176 | return 0;
177 | }
178 |
179 | var openBracket = methodNameAndSignature.IndexOf('[', 0, parenthesis);
180 | if (openBracket < 0)
181 | {
182 | return 0;
183 | }
184 |
185 | var closeBracket = methodNameAndSignature.IndexOf(']', 0, parenthesis);
186 | if (closeBracket < 0)
187 | {
188 | return 0;
189 | }
190 |
191 | var result = 1;
192 | for (var i = openBracket; i <= closeBracket; i++)
193 | {
194 | if (methodNameAndSignature[i] == ',')
195 | {
196 | result++;
197 | }
198 | }
199 |
200 | return result;
201 | }
202 |
203 | private static IReadOnlyList GetMethodParameterTypes(string methodNameAndSignature)
204 | {
205 | var openParenthesis = methodNameAndSignature.IndexOf('(');
206 | if (openParenthesis < 0)
207 | {
208 | return Array.Empty();
209 | }
210 |
211 | var closeParenthesis = methodNameAndSignature.IndexOf(')');
212 | var signatureStart = openParenthesis + 1;
213 | var signatureLength = closeParenthesis - signatureStart;
214 | var signature = methodNameAndSignature.Substring(signatureStart, signatureLength);
215 | var parameters = signature.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
216 |
217 | for (var i = 0; i < parameters.Length; i++)
218 | {
219 | parameters[i] = parameters[i].Trim();
220 | }
221 |
222 | var result = new List(parameters.Length);
223 |
224 | foreach (var parameter in parameters)
225 | {
226 | var space = parameter.IndexOf(' ');
227 | var typeName = parameter.Substring(0, space);
228 | result.Add(typeName);
229 | }
230 |
231 | return result;
232 | }
233 |
234 | private static bool IsMatch(IMethodSymbol method, IReadOnlyList parameterTypes, int methodArity)
235 | {
236 | if (method.Arity != methodArity || method.Parameters.Length != parameterTypes.Count)
237 | {
238 | return false;
239 | }
240 |
241 | for (var i = 0; i < method.Parameters.Length; i++)
242 | {
243 | var symbolTypeName = GetTypeName(method.Parameters[i]);
244 | var frameTypename = parameterTypes[i];
245 |
246 | if (symbolTypeName != frameTypename)
247 | {
248 | return false;
249 | }
250 | }
251 |
252 | return true;
253 | }
254 |
255 | private static string GetTypeName(IParameterSymbol symbol)
256 | {
257 | var sb = new StringBuilder();
258 |
259 | if (symbol.Type is IArrayTypeSymbol array)
260 | {
261 | sb.Append(array.ElementType.MetadataName);
262 | sb.Append("[]");
263 | }
264 | else if (symbol.Type is IPointerTypeSymbol pointer)
265 | {
266 | sb.Append(pointer.PointedAtType.MetadataName);
267 | sb.Append('*');
268 | }
269 | else
270 | {
271 | sb.Append(symbol.Type.MetadataName);
272 | }
273 |
274 | if (symbol.RefKind != RefKind.None)
275 | {
276 | sb.Append("&");
277 | }
278 |
279 | return sb.ToString();
280 | }
281 | }
282 | }
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/Helpers/StringHelper.cs:
--------------------------------------------------------------------------------
1 | namespace StackTraceExplorer.Helpers
2 | {
3 | using System;
4 | using System.IO;
5 | using System.Linq;
6 |
7 | public static class StringHelper
8 | {
9 | private static readonly char[] Separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
10 | public static string FindLongestMatchingSuffix(string searchString, string[] candidates, StringComparison comparisonType)
11 | {
12 | int nextSeparatorIndex = 0;
13 | int previousSeparatorIndex = -1;
14 | while (true)
15 | {
16 | string suffixToSearchFor;
17 | if (previousSeparatorIndex == -1)
18 | {
19 | // Search for the whole string the first time
20 | suffixToSearchFor = searchString;
21 | }
22 | else
23 | {
24 | nextSeparatorIndex = searchString.IndexOfAny(Separators, previousSeparatorIndex);
25 | if (nextSeparatorIndex == -1)
26 | {
27 | break;
28 | }
29 |
30 | suffixToSearchFor = searchString.Substring(nextSeparatorIndex);
31 | }
32 |
33 | string match = candidates.FirstOrDefault(s => s.EndsWith(suffixToSearchFor, comparisonType));
34 | if (match != null)
35 | {
36 | return match;
37 | }
38 |
39 | previousSeparatorIndex = nextSeparatorIndex + 1;
40 | }
41 |
42 | throw new ArgumentException("None of the candidates match", nameof(candidates));
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/Helpers/TraceHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Input;
2 | using Microsoft.VisualStudio.ComponentModelHost;
3 | using StackTraceExplorer.Models;
4 | using TextEditor = ICSharpCode.AvalonEdit.TextEditor;
5 |
6 | namespace StackTraceExplorer.Helpers
7 | {
8 | public static class TraceHelper
9 | {
10 | public static int LineNumber;
11 | public static TextEditor TextEditor;
12 | public static int CurrentColumn;
13 | public static IComponentModel ComponentModel;
14 | public static StackTracesViewModel ViewModel;
15 |
16 | public static void SetCurrentMouseOffset(QueryCursorEventArgs e)
17 | {
18 | var pos = TextEditor.GetPositionFromPoint(e.GetPosition(TextEditor));
19 |
20 | if (pos == null)
21 | {
22 | return;
23 | }
24 |
25 | LineNumber = pos.Value.Line;
26 | CurrentColumn = pos.Value.Column;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/Models/StackTrace.cs:
--------------------------------------------------------------------------------
1 | using ICSharpCode.AvalonEdit.Document;
2 | using Microsoft.VisualStudio.PlatformUI;
3 | using StackTraceExplorer.Helpers;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text.RegularExpressions;
8 |
9 | namespace StackTraceExplorer.Shared.Models
10 | {
11 | public class StackTrace : ObservableObject
12 | {
13 | public TextDocument Document { get; set; }
14 |
15 | public List ClickedLines { get; set; } = new List();
16 |
17 | public StackTrace(string trace = null)
18 | => SetStackTrace(trace);
19 |
20 | private bool _wordWrap;
21 |
22 | public bool WordWrap
23 | {
24 | get => _wordWrap;
25 | set => SetProperty(ref _wordWrap, value);
26 | }
27 |
28 | public void SetStackTrace(string trace)
29 | {
30 | Document = new TextDocument { Text = WrapStackTrace(trace) };
31 | ClickHelper.ClearCache();
32 | NotifyPropertyChanged("Document");
33 | }
34 |
35 | public void AddClickedLine(CustomLinkVisualLineText line)
36 | => ClickedLines.Add(line);
37 |
38 | public bool IsClickedLine(CustomLinkVisualLineText line)
39 | => ClickedLines.Any(l => l.Link.SequenceEqual(line.Link));
40 |
41 | private string WrapStackTrace(string trace)
42 | {
43 | if (string.IsNullOrEmpty(trace))
44 | {
45 | return string.Empty;
46 | }
47 |
48 | if (trace.Contains(Environment.NewLine))
49 | {
50 | return trace;
51 | }
52 |
53 | var lines = Regex
54 | .Split(trace, @"(?=\s+(at|в|à)\s+)", RegexOptions.Compiled)
55 | .Where(line => !string.IsNullOrEmpty(line))
56 | .Where(line => !string.IsNullOrWhiteSpace(line))
57 | .Where(line => line != "at")
58 | .Where(line => line != "в")
59 | .Where(line => line != "à");
60 |
61 | return string.Join(Environment.NewLine, lines);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/Models/StackTracesViewModel.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.PlatformUI;
2 | using StackTraceExplorer.Shared.Models;
3 | using System.Collections.ObjectModel;
4 |
5 | namespace StackTraceExplorer.Models
6 | {
7 | public class StackTracesViewModel : ObservableObject
8 | {
9 | private ObservableCollection _stackTraces = new ObservableCollection();
10 |
11 | public ObservableCollection StackTraces
12 | {
13 | get => _stackTraces;
14 | set => SetProperty(ref _stackTraces, value);
15 | }
16 |
17 | private int _selectedStackTraceIndex;
18 |
19 | public int SelectedStackTraceIndex
20 | {
21 | get => _selectedStackTraceIndex;
22 | set => SetProperty(ref _selectedStackTraceIndex, value);
23 | }
24 |
25 | public void AddStackTrace(string trace)
26 | {
27 | _stackTraces.Add(new StackTrace(trace));
28 | NotifyPropertyChanged("StackTraces");
29 | }
30 |
31 | public void SetStackTrace(string trace)
32 | => _stackTraces[_selectedStackTraceIndex].SetStackTrace(trace);
33 |
34 | public void AddClickedLine(CustomLinkVisualLineText line)
35 | => _stackTraces[_selectedStackTraceIndex].AddClickedLine(line);
36 |
37 | public bool IsClickedLine(CustomLinkVisualLineText line)
38 | => _stackTraces[_selectedStackTraceIndex].IsClickedLine(line);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/StackTraceEditor.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Community.VisualStudio.Toolkit;
3 | using ICSharpCode.AvalonEdit;
4 | using StackTraceExplorer.Generators;
5 |
6 | namespace StackTraceExplorer
7 | {
8 | public class StackTraceEditor : TextEditor
9 | {
10 | OutputWindowPane outputWindowPane;
11 |
12 | public StackTraceEditor()
13 | {
14 | TextArea.TextView.ElementGenerators.Add(new FileLinkElementGenerator(this));
15 | TextArea.TextView.ElementGenerators.Add(new MemberLinkElementGenerator(this));
16 | }
17 |
18 | public async Task EnsureOutputWindowPaneAsync()
19 | {
20 | const string OutputWindowName = nameof(StackTraceExplorer);
21 | if (this.outputWindowPane == null)
22 | {
23 | this.outputWindowPane = await VS.Windows.CreateOutputWindowPaneAsync(OutputWindowName);
24 | }
25 |
26 | return this.outputWindowPane;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/StackTraceExplorer.Shared.projitems:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
5 | true
6 | 2732f00a-56da-436f-8cc4-baea756330d3
7 |
8 |
9 | StackTraceExplorer.Shared
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | StackTraceExplorerToolWindowControl.xaml
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | MSBuild:Compile
35 | Designer
36 |
37 |
38 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/StackTraceExplorer.Shared.shproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2732f00a-56da-436f-8cc4-baea756330d3
5 | 14.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/StackTraceExplorer.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | text/microsoft-resx
91 |
92 |
93 | 1.3
94 |
95 |
96 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
97 |
98 |
99 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
100 |
101 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/StackTraceExplorerToolWindow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using System.Windows;
6 | using Community.VisualStudio.Toolkit;
7 | using Microsoft.VisualStudio.Imaging;
8 | using Microsoft.VisualStudio.Shell;
9 |
10 | namespace StackTraceExplorer
11 | {
12 | public class StackTraceExplorerToolWindow : BaseToolWindow
13 | {
14 | private StackTraceExplorerToolWindowControl _control;
15 |
16 | public override string GetTitle(int toolWindowId) => "Stack Trace Explorer";
17 |
18 | public override Type PaneType => typeof(Pane);
19 |
20 | public override async Task CreateAsync(int toolWindowId, CancellationToken cancellationToken)
21 | {
22 | RegisterEvents();
23 |
24 | await Package.JoinableTaskFactory.SwitchToMainThreadAsync();
25 |
26 | _control = new StackTraceExplorerToolWindowControl();
27 |
28 | return _control;
29 | }
30 |
31 | [Guid("7648448a-48ab-4c10-968a-1b2ce0386050")]
32 | public class Pane : ToolWindowPane
33 | {
34 | public Pane()
35 | {
36 | BitmapImageMoniker = KnownMonikers.CallStackWindow;
37 | }
38 | }
39 |
40 | private void RegisterEvents()
41 | {
42 | VS.Events.WindowEvents.ActiveFrameChanged += WindowEvents_ActiveFrameChanged;
43 | }
44 |
45 | private void WindowEvents_ActiveFrameChanged(ActiveFrameChangeEventArgs obj)
46 | {
47 | if (obj.NewFrame.Caption.Equals("Stack Trace Explorer"))
48 | {
49 | _control.EnsureOneStackTrace();
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/StackTraceExplorerToolWindowCommand.cs:
--------------------------------------------------------------------------------
1 | using Community.VisualStudio.Toolkit;
2 | using Microsoft.VisualStudio.Shell;
3 | using Task = System.Threading.Tasks.Task;
4 |
5 | namespace StackTraceExplorer
6 | {
7 | [Command(PackageGuids.guidStackTraceExplorerToolWindowPackageCmdSetString, PackageIds.StackTraceExplorerToolWindowCommandId)]
8 | internal sealed class StackTraceExplorerToolWindowCommand : BaseCommand
9 | {
10 | protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
11 | => await StackTraceExplorerToolWindow.ShowAsync();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/StackTraceExplorerToolWindowControl.xaml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
18 |
19 |
20 |
23 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
125 |
126 |
133 |
134 |
141 |
142 |
143 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/StackTraceExplorerToolWindowControl.xaml.cs:
--------------------------------------------------------------------------------
1 | using ICSharpCode.AvalonEdit;
2 | using Microsoft.VisualStudio.LanguageServices;
3 | using StackTraceExplorer.Helpers;
4 | using StackTraceExplorer.Models;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Windows;
8 | using System.Windows.Input;
9 |
10 | namespace StackTraceExplorer
11 | {
12 | public partial class StackTraceExplorerToolWindowControl
13 | {
14 | public StackTracesViewModel ViewModel { get; set; }
15 |
16 | public StackTraceExplorerToolWindowControl()
17 | {
18 | InitializeComponent();
19 |
20 | KeyDown += StackTraceExplorerToolWindowControl_KeyDown;
21 | Drop += StackTraceExplorerToolWindowControl_Drop;
22 |
23 | ViewModel = new StackTracesViewModel();
24 | DataContext = ViewModel;
25 | TraceHelper.ViewModel = ViewModel;
26 |
27 | EnsureOneStackTrace();
28 | }
29 |
30 | ///
31 | /// Always have one tab available in the toolwindow
32 | ///
33 | public void EnsureOneStackTrace()
34 | {
35 | if (!ViewModel.StackTraces.Any())
36 | {
37 | AddStackTrace();
38 | }
39 | }
40 |
41 | ///
42 | /// Add a tab to the toolwindow with the pasted stacktrace
43 | ///
44 | /// stack trace
45 | public void AddStackTrace(string trace = "")
46 | {
47 | ViewModel.AddStackTrace(trace);
48 | StackTraceTabs.SelectedIndex = StackTraceTabs.Items.Count - 1;
49 | }
50 |
51 | ///
52 | /// Add a tab to the toolwindow after presenting an open file dialog
53 | ///
54 | public void AddStackTraceFromFile()
55 | {
56 | var openFileDialog = new System.Windows.Forms.OpenFileDialog();
57 | if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
58 | {
59 | AddStackTraceFromPath(openFileDialog.FileName);
60 | }
61 | }
62 |
63 | ///
64 | /// Add a tab to the toolwindow with file contents
65 | ///
66 | /// path to the file
67 | public void AddStackTraceFromPath(string path)
68 | {
69 | if (File.Exists(path))
70 | {
71 | AddStackTrace(File.ReadAllText(path));
72 | }
73 | }
74 |
75 | #region Events
76 | private void ButtonPaste_OnClick(object sender, RoutedEventArgs e)
77 | => ViewModel.SetStackTrace(Clipboard.GetText());
78 |
79 | private void ButtonPasteAsNew_OnClick(object sender, RoutedEventArgs e)
80 | => AddStackTrace(Clipboard.GetText());
81 |
82 | private void ButtonOpenFile_OnClick(object sender, RoutedEventArgs e)
83 | => AddStackTraceFromFile();
84 |
85 | // In use through XAML binding
86 | private async void TextEditor_TextChanged(object sender, System.EventArgs e)
87 | {
88 | var textEditor = sender as TextEditor;
89 | var trace = textEditor.Document?.Text;
90 |
91 | if (string.IsNullOrEmpty(trace))
92 | {
93 | return;
94 | }
95 |
96 | int selectionStart = textEditor.SelectionStart;
97 | textEditor.TextChanged -= TextEditor_TextChanged;
98 | ViewModel.SetStackTrace(trace);
99 | textEditor.TextChanged += TextEditor_TextChanged;
100 | textEditor.SelectionStart = selectionStart;
101 |
102 | var workspace = TraceHelper.ComponentModel.GetService();
103 | SolutionHelper.Solution = workspace.CurrentSolution;
104 | await SolutionHelper.GetCompilationsAsync(workspace.CurrentSolution);
105 | }
106 |
107 | private void StackTraceExplorerToolWindowControl_Drop(object sender, DragEventArgs e)
108 | {
109 | var dropped = (string[])e.Data.GetData(DataFormats.FileDrop);
110 | var files = dropped;
111 |
112 | if (!files.Any())
113 | {
114 | return;
115 | }
116 |
117 | foreach (var file in files)
118 | {
119 | AddStackTraceFromPath(file);
120 | }
121 |
122 | e.Handled = true;
123 | }
124 |
125 | private void CloseButton_MouseDown(object sender, MouseButtonEventArgs e)
126 | {
127 | if (StackTraceTabs.SelectedIndex >= 0)
128 | {
129 | ViewModel.StackTraces.RemoveAt(StackTraceTabs.SelectedIndex);
130 | }
131 |
132 | EnsureOneStackTrace();
133 | }
134 |
135 | private void StackTraceExplorerToolWindowControl_KeyDown(object sender, KeyEventArgs e)
136 | {
137 | if (e.Key == Key.V && Keyboard.Modifiers == ModifierKeys.Control)
138 | {
139 | AddStackTrace(Clipboard.GetText());
140 | }
141 | base.OnKeyDown(e);
142 | }
143 | #endregion
144 | }
145 | }
--------------------------------------------------------------------------------
/StackTraceExplorer.Shared/StackTraceExplorerToolWindowPackage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using System.Threading;
4 | using Community.VisualStudio.Toolkit;
5 | using Microsoft.VisualStudio.ComponentModelHost;
6 | using Microsoft.VisualStudio.Shell;
7 | using StackTraceExplorer.Helpers;
8 | using Task = System.Threading.Tasks.Task;
9 |
10 | namespace StackTraceExplorer
11 | {
12 | [Guid(PackageGuids.guidStackTraceExplorerToolWindowPackageString)]
13 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
14 | [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
15 | [ProvideToolWindow(typeof(StackTraceExplorerToolWindow.Pane))]
16 | [ProvideMenuResource("Menus.ctmenu", 1)]
17 | public sealed class StackTraceExplorerToolWindowPackage : ToolkitPackage
18 | {
19 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
20 | {
21 | StackTraceExplorerToolWindow.Initialize(this);
22 |
23 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
24 |
25 | await StackTraceExplorerToolWindowCommand.InitializeAsync(this);
26 |
27 | TraceHelper.ComponentModel = await GetServiceAsync(typeof(SComponentModel)) as IComponentModel;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Tests/FileRegexTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using StackTraceExplorer.Generators;
3 |
4 | namespace StackTraceExplorer.Tests
5 | {
6 | [TestClass]
7 | public class FileRegexTests
8 | {
9 | [DataTestMethod]
10 | [DataRow(
11 | @"at Bla.Program.InnerClass.InnerMain() in C:\repos\Bla.Program\InnerClass.cs:line 168",
12 | @"C:\repos\Bla.Program\InnerClass.cs:line 168",
13 | @"C:\repos\Bla.Program\InnerClass.cs",
14 | "168")]
15 | [DataRow(
16 | @"at Bla.Program.InnerClass.InnerMain() in D:\repos\Bla.Program\Dash-File.cs:line 3005",
17 | @"D:\repos\Bla.Program\Dash-File.cs:line 3005",
18 | @"D:\repos\Bla.Program\Dash-File.cs",
19 | "3005")]
20 | [DataRow(
21 | @"at Bla.Program.InnerClass.InnerMain() in C:\repos\Bla.Program\Dot.File.cs:line 3",
22 | @"C:\repos\Bla.Program\Dot.File.cs:line 3",
23 | @"C:\repos\Bla.Program\Dot.File.cs",
24 | "3")]
25 | [DataRow(
26 | @"at Program.ApplicationMdi.b__59_0() in E:\Repos\Underscore_File.cs:line 375",
27 | @"E:\Repos\Underscore_File.cs:line 375",
28 | @"E:\Repos\Underscore_File.cs",
29 | "375")]
30 | [DataRow(
31 | @"at CodeNav.Helpers.HistoryHelper.AddItemToHistory(CodeNav.VS2019, Version= 8.8.28.0, Culture= neutral, PublicKeyToken= null: D:\a\CodeNav\CodeNav\CodeNav.Shared\Helpers\HistoryHelper.cs:21)",
32 | @"D:\a\CodeNav\CodeNav\CodeNav.Shared\Helpers\HistoryHelper.cs:21",
33 | @"D:\a\CodeNav\CodeNav\CodeNav.Shared\Helpers\HistoryHelper.cs",
34 | "21")]
35 | [DataRow(
36 | @"at Dapper.SqlMapper+d__69`1.MoveNext (Dapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null: /_/Dapper/SqlMapper.Async.cs:1241)",
37 | @"/_/Dapper/SqlMapper.Async.cs:1241",
38 | @"/_/Dapper/SqlMapper.Async.cs",
39 | "1241")]
40 | public void ShouldMatch(string input, string expectedPlace, string expectedFile, string expectedLine)
41 | {
42 | var match = FileLinkElementGenerator.FilePathRegex.Match(input);
43 |
44 | Assert.IsTrue(match.Success, "Match was not a success!");
45 | Assert.AreEqual(expectedPlace, match.Groups["place"].Value, nameof(expectedPlace));
46 | Assert.AreEqual(expectedFile, match.Groups["path"].Value, nameof(expectedFile));
47 | Assert.AreEqual(expectedLine, match.Groups["line"].Value, nameof(expectedLine));
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Tests/LongestSuffixTests.cs:
--------------------------------------------------------------------------------
1 | namespace StackTraceExplorer.Tests
2 | {
3 | using System;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using StackTraceExplorer.Helpers;
6 |
7 | [TestClass]
8 | public class LongestSuffixTests
9 | {
10 | [DataTestMethod]
11 | [DataRow(
12 | @"D:\bldserver01\src\product\Project1\Sample.cs",
13 | new[] { @"C:\Sample.cs", @"C:\src\Project1\Sample.cs", @"D:\src\product\Project1\Sample.cs" },
14 | @"D:\src\product\Project1\Sample.cs")]
15 | [DataRow(
16 | @"D:\bldserver01\src\product\Project1\Sample.cs",
17 | new[] { @"C:\Sample.cs", @"C:\src\Project1\Sample.cs", @"C:\product\Project1\Sample.cs" },
18 | @"C:\product\Project1\Sample.cs")]
19 | [DataRow(
20 | @"D:\test\product\Project1\Sample.cs",
21 | new[] { @"C:\test\product\Project1\Sample.cs", @"D:\test\product\Project1\Sample.cs" },
22 | @"D:\test\product\Project1\Sample.cs")]
23 | [DataRow(
24 | @"/src/test/product/Project1/Sample.cs",
25 | new[] { @"/target/test/product/Project1/Sample.cs", @"/src/product/Project1/Sample.cs" },
26 | @"/target/test/product/Project1/Sample.cs")]
27 | public void LongestSuffixMatch(string path, string[] candidates, string expected)
28 | {
29 | string longestMatched = StringHelper.FindLongestMatchingSuffix(path, candidates, StringComparison.OrdinalIgnoreCase);
30 | Assert.AreEqual(expected, longestMatched);
31 | }
32 |
33 | [DataTestMethod]
34 | [DataRow(
35 | @"D:\src\Project1\DifferentFileName.cs",
36 | new[] { @"C:\DifferentFileName2.cs", @"C:\src\Project1\DifferentFileName2.cs", },
37 | "None of the candidates match")]
38 | [DataRow(
39 | @"C:\CandidateFileNameMatchingWithSuffix.cs",
40 | new[] { @"C:\OtherCandidateFileNameMatchingWithSuffix.cs", },
41 | "None of the candidates match")]
42 | public void LongestSuffixMatchNoMatches(string path, string[] candidates, string exceptionText)
43 | {
44 | var exception = Assert.ThrowsException(() => StringHelper.FindLongestMatchingSuffix(path, candidates, StringComparison.OrdinalIgnoreCase));
45 | Assert.IsTrue(exception.Message.Contains(exceptionText), $"Exception.Message should contain '{exceptionText}' (actual:{exception.Message})");
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Tests/MemberRegexTests.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Text.RegularExpressions;
3 | using FluentAssertions;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using StackTraceExplorer.Generators;
6 |
7 | namespace StackTraceExplorer.Tests
8 | {
9 | [TestClass]
10 | public class MemberRegexTests
11 | {
12 | [DataTestMethod]
13 | [DataRow(
14 | "Bla.Program.Main(String[] args)",
15 | "Bla.Program.Main(String[] args)",
16 | new[] { "Bla.", "Program." },
17 | "Main(String[] args)")]
18 | [DataRow(
19 | @"at Bla.Program.InnerClass.InnerMain() in C:\repos\Bla.Program\InnerClass.cs:line 168",
20 | "Bla.Program.InnerClass.InnerMain()",
21 | new[] { "Bla.", "Program.", "InnerClass." },
22 | "InnerMain()")]
23 | [DataRow(
24 | @"at Bla.Program.ApplicationMdi.b__59_0() in C:\Repos\Bla.Program\Views\ApplicationMdi.cs:line 375",
25 | "Bla.Program.ApplicationMdi.b__59_0()",
26 | new[] { "Bla.", "Program.", "ApplicationMdi." },
27 | "b__59_0()")]
28 | [DataRow(
29 | @"at Bla.Program.ApplicationMdi.<>c.b__59_0() in C:\Repos\Bla.Program\Views\ApplicationMdi.cs:line 375",
30 | "Bla.Program.ApplicationMdi.<>c.b__59_0()",
31 | new[] { "Bla.", "Program.", "ApplicationMdi.", "<>c." },
32 | "b__59_0()")]
33 | [DataRow(
34 | "at Company.Common.AsyncResult.End(IAsyncResult result)",
35 | "Company.Common.AsyncResult.End(IAsyncResult result)",
36 | new[] { "Company.", "Common.", "AsyncResult." },
37 | "End(IAsyncResult result)")]
38 | [DataRow(
39 | "at Company.Common.AsyncResult.End[TAsyncResult](IAsyncResult result)",
40 | "Company.Common.AsyncResult.End[TAsyncResult](IAsyncResult result)",
41 | new[] { "Company.", "Common.", "AsyncResult." },
42 | "End[TAsyncResult](IAsyncResult result)")]
43 | [DataRow(
44 | "Company.SomeType`1.StepCallback(IAsyncResult result)",
45 | "Company.SomeType`1.StepCallback(IAsyncResult result)",
46 | new[] { "Company.", "SomeType`1." },
47 | "StepCallback(IAsyncResult result)")]
48 | [DataRow(
49 | @" at StackTraceExplorer.TestClass.End[T,V](String s) in D:\StackTraceExplorer\TestClass.cs:line 38",
50 | "StackTraceExplorer.TestClass.End[T,V](String s)",
51 | new[] { "StackTraceExplorer.", "TestClass." },
52 | "End[T,V](String s)")]
53 | [DataRow(
54 | " at SolutionHelperTests.ClassWithGenericTypeArgs`2.StaticMethod[V]()",
55 | "SolutionHelperTests.ClassWithGenericTypeArgs`2.StaticMethod[V]()",
56 | new[] { "SolutionHelperTests.", "ClassWithGenericTypeArgs`2.", },
57 | "StaticMethod[V]()")]
58 | [DataRow(
59 | " at StackTraceExplorer.Tests.SolutionHelperTests.<>c.b__6_3() in D:\\SolutionHelperTests.cs:line 52",
60 | "StackTraceExplorer.Tests.SolutionHelperTests.<>c.b__6_3()",
61 | new[] { "StackTraceExplorer.", "Tests.", "SolutionHelperTests.", "<>c." },
62 | "b__6_3()")]
63 | [DataRow("at Sample.ClassWithGenericTypeArgs`1..ctor(Boolean throwException)",
64 | "Sample.ClassWithGenericTypeArgs`1..ctor(Boolean throwException)",
65 | new[] { "Sample.", "ClassWithGenericTypeArgs`1.", },
66 | ".ctor(Boolean throwException)")]
67 | [DataRow(@"StackTraceExplorer.Tests.SolutionHelperTests.ClassWithGenericTypeArgs`1.StaticMethod[C]() in D:\StackTraceExplorer.Tests\SolutionHelperTests.cs:line 114",
68 | "StackTraceExplorer.Tests.SolutionHelperTests.ClassWithGenericTypeArgs`1.StaticMethod[C]()",
69 | new[] { "StackTraceExplorer.", "Tests.", "SolutionHelperTests.", "ClassWithGenericTypeArgs`1." },
70 | "StaticMethod[C]()")]
71 | [DataRow(@"StackTraceExplorer.Tests.SolutionHelperTests.g__Parse|155_0(IEnumerable`1 lines) in C:\StackTraceExplorer.Tests\DummyFile.cs:line 21",
72 | "StackTraceExplorer.Tests.SolutionHelperTests.g__Parse|155_0(IEnumerable`1 lines)",
73 | new[] { "StackTraceExplorer.", "Tests.", "SolutionHelperTests." },
74 | "g__Parse|155_0(IEnumerable`1 lines)")]
75 | [DataRow(@"StackTraceExplorer.Tests.SolutionHelperTests.ThreadHelper+<>c__DisplayClass13_0+<b__0>d.MoveNext() in C:\StackTraceExplorer.Tests\DummyFile.cs:line 21",
76 | "StackTraceExplorer.Tests.SolutionHelperTests.ThreadHelper+<>c__DisplayClass13_0+<b__0>d.MoveNext()",
77 | new[] { "StackTraceExplorer.", "Tests.", "SolutionHelperTests.", "ThreadHelper+<>c__DisplayClass13_0+<b__0>d." },
78 | "MoveNext()")]
79 | public void ShouldMatchVisualStudioStacktraces(string input, string expectedMatch, string[] expectedCaptures, string expectedMethod)
80 | => ShouldMatchStacktraces(input, expectedMatch, expectedCaptures, expectedMethod);
81 |
82 | public void ShouldMatchStacktraces(string input, string expectedMatch, string[] expectedCaptures, string expectedMethod)
83 | {
84 | var match = MemberLinkElementGenerator.FindMatch(input);
85 |
86 | Assert.IsTrue(match.Success, "Match was not a success!");
87 | Assert.AreEqual(expectedMatch, match.Groups["member"].Value, nameof(expectedMatch));
88 |
89 | var captures = match.Groups["namespace"].Captures.Cast().Select(c => c.Value).ToArray();
90 | captures.Should().BeEquivalentTo(expectedCaptures);
91 |
92 | Assert.AreEqual(expectedMethod, match.Groups["method"].Value, nameof(expectedMethod));
93 | }
94 |
95 | [DataTestMethod]
96 | [DataRow(@"Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor+d__35.MoveNext (Microsoft.Azure.WebJobs.Host, Version=3.0.41.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35: D:\a\_work\1\s\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs:663)",
97 | "Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor+d__35.MoveNext",
98 | new[] { "Microsoft.", "Azure.", "WebJobs.", "Host.", "Executors.", "FunctionExecutor+d__35." },
99 | "MoveNext")]
100 | public void ShouldMatchAppInsightsStackTraces(string input, string expectedMatch, string[] expectedCaptures, string expectedMethod)
101 | => ShouldMatchStacktraces(input, expectedMatch, expectedCaptures, expectedMethod);
102 |
103 | [DataTestMethod]
104 | [DataRow(@"Task> GitCommands.GitModule.GetRemotesAsync()+ParseRemotes(IEnumerable lines) in C:/.../gitextensions/GitCommands/Git/GitModule.cs:line 2196",
105 | "GitCommands.GitModule.GetRemotesAsync()+ParseRemotes(IEnumerable lines)",
106 | new[] { "GitCommands.", "GitModule." },
107 | "GetRemotesAsync()+ParseRemotes(IEnumerable lines)")]
108 | public void ShouldMatchDemystifiedStackTraces(string input, string expectedMatch, string[] expectedCaptures, string expectedMethod)
109 | => ShouldMatchStacktraces(input, expectedMatch, expectedCaptures, expectedMethod);
110 |
111 | [DataTestMethod]
112 | [DataRow("whatever[0]")]
113 | [DataRow("Normal sentence (pretty much)")]
114 | [DataRow("Normal sentence [pretty much]")]
115 | public void ShouldNotMatch(string input)
116 | {
117 | var match = MemberLinkElementGenerator.FindMatch(input);
118 | Assert.IsFalse(match.Success, $"Input {input} should not match.");
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("StackTraceExplorer.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("StackTraceExplorer.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © 2018")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("865d31df-a8cc-4c50-9eb5-aa04e555360b")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Tests/SolutionHelperTests.cs:
--------------------------------------------------------------------------------
1 | namespace StackTraceExplorer.Tests
2 | {
3 | using System;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Runtime.CompilerServices;
7 | using Microsoft.CodeAnalysis;
8 | using Microsoft.CodeAnalysis.CSharp;
9 | using Microsoft.VisualStudio.TestTools.UnitTesting;
10 | using StackTraceExplorer.Helpers;
11 |
12 | [TestClass]
13 | public class SolutionHelperTests
14 | {
15 | public SolutionHelperTests()
16 | {
17 | this.Compilation = CreateCompilationForCurrentFile();
18 | }
19 |
20 | CSharpCompilation Compilation { get; }
21 |
22 | [DataTestMethod]
23 | [DataRow("End[TAsyncResult](IAsyncResult result)", "End")]
24 | [DataRow("Main(String[] args)", "Main")]
25 | [DataRow("GenericType`1", "GenericType")]
26 | [DataRow("d__0", "GetSteps")]
27 | [DataRow("get_TestProperty()", "get_TestProperty")]
28 | public void GetMemberName(string input, string expected)
29 | {
30 | string memberName = SolutionHelper.GetMemberName(input);
31 | Assert.AreEqual(expected, memberName);
32 | }
33 |
34 | [DataTestMethod]
35 | [DataRow("StackTraceExplorer.Tests." + nameof(SolutionHelperTests), "StackTraceExplorer.Tests.SolutionHelperTests")]
36 | [DataRow("StackTraceExplorer.Tests.SolutionHelperTests..ctor()", "StackTraceExplorer.Tests.SolutionHelperTests.SolutionHelperTests()")]
37 | [DataRow("StackTraceExplorer.Tests.SolutionHelperTests.ResolveMember(String memberName, String expectedMatch)", "StackTraceExplorer.Tests.SolutionHelperTests.ResolveMember(string, string)")]
38 | [DataRow("StackTraceExplorer.Tests.SolutionHelperTests.ThisMethodThrows[T](String s)", "StackTraceExplorer.Tests.SolutionHelperTests.ThisMethodThrows(string)")]
39 | [DataRow("StackTraceExplorer.Tests.SolutionHelperTests.ThisMethodThrows[T,V](String s)", "StackTraceExplorer.Tests.SolutionHelperTests.ThisMethodThrows(string)")]
40 | [DataRow("StackTraceExplorer.Tests.ClassWithGenericTypeArgs`1", "StackTraceExplorer.Tests.ClassWithGenericTypeArgs")]
41 | [DataRow("StackTraceExplorer.Tests." + nameof(ClassWithGenericTypeArgs) + "`2", "StackTraceExplorer.Tests.ClassWithGenericTypeArgs")]
42 | [DataRow("StackTraceExplorer.Tests.ClassWithGenericTypeArgs`1..ctor(Boolean throwException)", "StackTraceExplorer.Tests.ClassWithGenericTypeArgs.ClassWithGenericTypeArgs(bool)")]
43 | [DataRow("StackTraceExplorer.Tests.ClassWithGenericTypeArgs`1.StaticMethod[C]()", "StackTraceExplorer.Tests.ClassWithGenericTypeArgs.StaticMethod()")]
44 | [DataRow("StackTraceExplorer.Tests.SolutionHelperTests.<>c.<" + nameof(GenerateStackTracesForTesting) + ">b__6_3()", "StackTraceExplorer.Tests.SolutionHelperTests.GenerateStackTracesForTesting()")]
45 | [DataRow("StackTraceExplorer.Tests.SolutionHelperTests.<>c__DisplayClass6_0.<" + nameof(GenerateStackTracesForTesting) + ">b__7(String s)", "StackTraceExplorer.Tests.SolutionHelperTests.GenerateStackTracesForTesting()")]
46 | [DataRow("StackTraceExplorer.Tests.SolutionHelperTests.GetExceptionToString[T](Action`1 action, T value)", "StackTraceExplorer.Tests.SolutionHelperTests.GetExceptionToString(Action, T)")]
47 | public void ResolveMember(string memberName, string expectedMatch)
48 | {
49 | ISymbol symbol = SolutionHelper.Resolve(this.Compilation, memberName);
50 | Assert.IsNotNull(symbol, $"Symbol {memberName} should be found");
51 | Trace.WriteLine($"Found: {symbol}");
52 | Assert.AreEqual(expectedMatch, symbol.ToString(), "Resolved member was not correct");
53 | }
54 |
55 | [TestMethod]
56 | public void GenerateStackTracesForTesting()
57 | {
58 | // This isn't a test, per se, but it is useful for creating sample text to try pasting into the StackTrace window.
59 | Trace.WriteLine(GetExceptionToString(() => ClassWithGenericTypeArgs.StaticMethod()));
60 | Trace.WriteLine(GetExceptionToString(() => new ClassWithGenericTypeArgs(throwException: true)));
61 | Trace.WriteLine(GetExceptionToString(() => new ClassWithGenericTypeArgs(throwException: false).InstanceMethod()));
62 |
63 | Trace.WriteLine(GetExceptionToString(() => ClassWithGenericTypeArgs.StaticMethod()));
64 | Trace.WriteLine(GetExceptionToString(() => new ClassWithGenericTypeArgs(throwException: true)));
65 | Trace.WriteLine(GetExceptionToString((s) => new ClassWithGenericTypeArgs(throwException: false).InstanceMethod(), "someString"));
66 |
67 | // Create some funny compiler generated names
68 | Trace.WriteLine(GetExceptionToString(() => throw new InvalidOperationException(this.ToString())));
69 | foreach (string value in new[] { "Test String" })
70 | {
71 | Trace.WriteLine(GetExceptionToString((s) => throw new InvalidOperationException(value), value));
72 | }
73 | }
74 |
75 | public static CSharpCompilation CreateCompilationForCurrentFile([CallerFilePath] string fileName = "")
76 | {
77 | var compilation = CSharpCompilation.Create("TempAssembly");
78 | string sourceText = File.ReadAllText(fileName);
79 | SyntaxTree tree = CSharpSyntaxTree.ParseText(sourceText);
80 | compilation = compilation.AddSyntaxTrees(tree);
81 | return compilation;
82 | }
83 |
84 | public static string GetExceptionToString(Action action)
85 | {
86 | try
87 | {
88 | action();
89 | }
90 | catch (Exception e)
91 | {
92 | return e.ToString();
93 | }
94 |
95 | return string.Empty;
96 | }
97 |
98 | public static string GetExceptionToString(Action action, T value)
99 | {
100 | try
101 | {
102 | action(value);
103 | }
104 | catch (Exception e)
105 | {
106 | return e.ToString();
107 | }
108 |
109 | return string.Empty;
110 | }
111 |
112 | // Used in Test DataRows
113 | static void ThisMethodThrows(string s)
114 | {
115 | throw new NotImplementedException("foo");
116 | }
117 |
118 | // Used in Test DataRows
119 | static void ThisMethodThrows(string s)
120 | {
121 | throw new NotImplementedException("foo");
122 | }
123 | }
124 |
125 | class ClassWithGenericTypeArgs
126 | {
127 | public ClassWithGenericTypeArgs(bool throwException)
128 | {
129 | if (throwException)
130 | {
131 | throw new NotImplementedException();
132 | }
133 | }
134 |
135 | public string InstanceMethod()
136 | {
137 | throw new NotImplementedException();
138 | }
139 |
140 | public static string StaticMethod()
141 | {
142 | throw new NotImplementedException();
143 | }
144 | }
145 |
146 | class ClassWithGenericTypeArgs
147 | {
148 | public ClassWithGenericTypeArgs(bool throwException)
149 | {
150 | if (throwException)
151 | {
152 | throw new NotImplementedException();
153 | }
154 | }
155 |
156 | public string InstanceMethod()
157 | {
158 | throw new NotImplementedException();
159 | }
160 |
161 | public static string StaticMethod()
162 | {
163 | throw new NotImplementedException();
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/StackTraceExplorer.Tests/StackTraceExplorer.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | AnyCPU
8 | {865D31DF-A8CC-4C50-9EB5-AA04E555360B}
9 | Library
10 | Properties
11 | StackTraceExplorer.Tests
12 | StackTraceExplorer.Tests
13 | v4.7.2
14 | 512
15 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
16 | 15.0
17 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
18 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
19 | False
20 | UnitTest
21 |
22 |
23 |
24 |
25 | true
26 | full
27 | false
28 | bin\Debug\
29 | DEBUG;TRACE
30 | prompt
31 | 4
32 |
33 |
34 | pdbonly
35 | true
36 | bin\Release\
37 | TRACE
38 | prompt
39 | 4
40 |
41 |
42 |
43 | ..\packages\MSTest.TestFramework.2.1.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
44 |
45 |
46 | ..\packages\MSTest.TestFramework.2.1.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | StackTraceExplorer.cs
60 | True
61 | True
62 | StackTraceExplorer.vsct
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | .editorconfig
73 |
74 |
75 |
76 |
77 | 6.1.2.30
78 |
79 |
80 | 17.0.76.257-pre
81 |
82 |
83 |
84 | 4.0.1
85 |
86 |
87 | 4.0.1
88 |
89 |
90 | 2.2.8
91 |
92 |
93 | 2.2.8
94 |
95 |
96 |
97 |
98 | StackTraceExplorer.vsct
99 | VsctGenerator
100 | StackTraceExplorer.cs
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/StackTraceExplorer.VS2019/CallStackWindow.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sboulema/StackTraceExplorer/267f8f387860823f5bcab79ebba20564ba357488/StackTraceExplorer.VS2019/CallStackWindow.ico
--------------------------------------------------------------------------------
/StackTraceExplorer.VS2019/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("StackTraceExplorer")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("StackTraceExplorer")]
13 | [assembly: AssemblyCopyright("")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // Version information for an assembly consists of the following four values:
23 | //
24 | // Major Version
25 | // Minor Version
26 | // Build Number
27 | // Revision
28 | //
29 | // You can specify all the values or you can default the Build and Revision Numbers
30 | // by using the '*' as shown below:
31 | // [assembly: AssemblyVersion("1.0.*")]
32 | [assembly: AssemblyVersion("1.0.0.0")]
33 | [assembly: AssemblyFileVersion("1.0.0.0")]
34 |
--------------------------------------------------------------------------------
/StackTraceExplorer.VS2019/StackTraceExplorer.VS2019.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16.0
5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
6 |
7 |
8 | true
9 |
10 |
11 |
12 |
13 | 15.0
14 |
15 |
16 |
17 | true
18 |
19 |
20 |
21 |
22 |
23 |
24 | CallStackWindow.ico
25 |
26 |
27 |
28 | Debug
29 | AnyCPU
30 | 2.0
31 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
32 | {393FF8FD-00C8-471C-8C5A-EA97B229886A}
33 | Library
34 | Properties
35 | StackTraceExplorer
36 | StackTraceExplorer.VS2019
37 | v4.8
38 | true
39 | true
40 | true
41 | true
42 | true
43 | false
44 | False
45 |
46 |
47 | true
48 | full
49 | false
50 | bin\Debug\
51 | DEBUG;TRACE
52 | prompt
53 | 4
54 | False
55 |
56 |
57 | pdbonly
58 | true
59 | bin\Release\
60 | TRACE
61 | prompt
62 | 4
63 | False
64 |
65 |
66 |
67 | StackTraceExplorer.cs
68 | True
69 | True
70 | StackTraceExplorer.vsct
71 |
72 |
73 |
74 |
75 |
76 | Resources\LICENSE
77 | true
78 |
79 |
80 | Resources\CallStackWindow_256x.png
81 | true
82 |
83 |
84 | Resources\Preview.png
85 | true
86 |
87 |
88 |
89 | Designer
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | 6.1.2.30
111 |
112 |
113 | 15.0.527
114 |
115 |
116 | 16.0.29.6
117 | runtime; build; native; contentfiles; analyzers; buildtransitive
118 | all
119 |
120 |
121 | 3.11.0
122 |
123 |
124 | 2.10.0
125 |
126 |
127 | 16.10.230
128 |
129 |
130 | 17.12.2069
131 | runtime; build; native; contentfiles; analyzers; buildtransitive
132 | all
133 |
134 |
135 |
136 |
137 | StackTraceExplorer.vsct
138 | Menus.ctmenu
139 | VsctGenerator
140 | StackTraceExplorer.cs
141 |
142 |
143 |
144 |
145 |
146 |
153 |
--------------------------------------------------------------------------------
/StackTraceExplorer.VS2019/source.extension.vsixmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Stack Trace Explorer 2019
6 | Parse those pesty unreadable long stack traces. Stack Trace Explorer provides syntax highlighting and easy navigation to elements in the stack trace.
7 | https://marketplace.visualstudio.com/vsgallery/0886a4d9-35e3-431a-b86c-bf0e346ad036
8 | Resources\LICENSE
9 | https://github.com/sboulema/StackTraceExplorer/blob/master/README.md
10 | https://github.com/sboulema/StackTraceExplorer/releases
11 | Resources\CallStackWindow_256x.png
12 | Resources\Preview.png
13 | stacktrace, debug
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/StackTraceExplorer.VS2022/CallStackWindow.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sboulema/StackTraceExplorer/267f8f387860823f5bcab79ebba20564ba357488/StackTraceExplorer.VS2022/CallStackWindow.ico
--------------------------------------------------------------------------------
/StackTraceExplorer.VS2022/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("StackTraceExplorer")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("StackTraceExplorer")]
13 | [assembly: AssemblyCopyright("")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // Version information for an assembly consists of the following four values:
23 | //
24 | // Major Version
25 | // Minor Version
26 | // Build Number
27 | // Revision
28 | //
29 | // You can specify all the values or you can default the Build and Revision Numbers
30 | // by using the '*' as shown below:
31 | // [assembly: AssemblyVersion("1.0.*")]
32 | [assembly: AssemblyVersion("1.0.0.0")]
33 | [assembly: AssemblyFileVersion("1.0.0.0")]
34 |
--------------------------------------------------------------------------------
/StackTraceExplorer.VS2022/StackTraceExplorer.VS2022.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16.0
5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
6 |
7 |
8 | true
9 |
10 |
11 |
12 |
13 | 15.0
14 |
15 |
16 |
17 | true
18 |
19 |
20 |
21 |
22 |
23 |
24 | CallStackWindow.ico
25 |
26 |
27 |
28 | Debug
29 | AnyCPU
30 | 2.0
31 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
32 | {3EA63EFB-6A5B-4CDA-9F53-14D94C0DB12B}
33 | Library
34 | Properties
35 | StackTraceExplorer
36 | StackTraceExplorer.VS2022
37 | v4.8
38 | true
39 | true
40 | true
41 | true
42 | true
43 | false
44 | False
45 |
46 |
47 | true
48 | full
49 | false
50 | bin\Debug\
51 | DEBUG;TRACE
52 | prompt
53 | 4
54 | False
55 |
56 |
57 | pdbonly
58 | true
59 | bin\Release\
60 | TRACE
61 | prompt
62 | 4
63 | False
64 |
65 |
66 |
67 | StackTraceExplorer.cs
68 | True
69 | True
70 | StackTraceExplorer.vsct
71 |
72 |
73 |
74 |
75 |
76 | Resources\LICENSE
77 | true
78 |
79 |
80 |
81 | Designer
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | 6.1.2.30
103 |
104 |
105 | 17.0.527
106 |
107 |
108 | 16.0.29.6
109 | runtime; build; native; contentfiles; analyzers; buildtransitive
110 | all
111 |
112 |
113 | 4.0.1
114 |
115 |
116 | 4.0.1
117 |
118 |
119 | 17.12.2069
120 | runtime; build; native; contentfiles; analyzers; buildtransitive
121 | all
122 |
123 |
124 |
125 |
126 | Resources\CallStackWindow_256x.png
127 | true
128 |
129 |
130 | Resources\Preview.png
131 | true
132 |
133 |
134 | StackTraceExplorer.vsct
135 | Menus.ctmenu
136 | VsctGenerator
137 | StackTraceExplorer.cs
138 |
139 |
140 |
141 |
142 |
143 |
150 |
--------------------------------------------------------------------------------
/StackTraceExplorer.VS2022/source.extension.vsixmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Stack Trace Explorer 2022
6 | Parse those pesty unreadable long stack traces. Stack Trace Explorer provides syntax highlighting and easy navigation to elements in the stack trace.
7 | https://marketplace.visualstudio.com/vsgallery/0886a4d9-35e3-431a-b86c-bf0e346ad036
8 | Resources\LICENSE
9 | https://github.com/sboulema/StackTraceExplorer/blob/master/README.md
10 | https://github.com/sboulema/StackTraceExplorer/releases
11 | Resources\CallStackWindow_256x.png
12 | Resources\Preview.png
13 | stacktrace, debug
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/StackTraceExplorer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31521.260
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EE2CB841-FBAB-42FF-8233-E762CC77D778}"
7 | ProjectSection(SolutionItems) = preProject
8 | .editorconfig = .editorconfig
9 | publish-manifest.VS2019.json = publish-manifest.VS2019.json
10 | publish-manifest.VS2022.json = publish-manifest.VS2022.json
11 | README.md = README.md
12 | .github\workflows\workflow.yml = .github\workflows\workflow.yml
13 | EndProjectSection
14 | EndProject
15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StackTraceExplorer.VS2019", "StackTraceExplorer.VS2019\StackTraceExplorer.VS2019.csproj", "{393FF8FD-00C8-471C-8C5A-EA97B229886A}"
16 | EndProject
17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StackTraceExplorer.Tests", "StackTraceExplorer.Tests\StackTraceExplorer.Tests.csproj", "{865D31DF-A8CC-4C50-9EB5-AA04E555360B}"
18 | EndProject
19 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "StackTraceExplorer.Shared", "StackTraceExplorer.Shared\StackTraceExplorer.Shared.shproj", "{2732F00A-56DA-436F-8CC4-BAEA756330D3}"
20 | EndProject
21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StackTraceExplorer.VS2022", "StackTraceExplorer.VS2022\StackTraceExplorer.VS2022.csproj", "{3EA63EFB-6A5B-4CDA-9F53-14D94C0DB12B}"
22 | EndProject
23 | Global
24 | GlobalSection(SharedMSBuildProjectFiles) = preSolution
25 | StackTraceExplorer.Shared\StackTraceExplorer.Shared.projitems*{2732f00a-56da-436f-8cc4-baea756330d3}*SharedItemsImports = 13
26 | StackTraceExplorer.Shared\StackTraceExplorer.Shared.projitems*{393ff8fd-00c8-471c-8c5a-ea97b229886a}*SharedItemsImports = 4
27 | StackTraceExplorer.Shared\StackTraceExplorer.Shared.projitems*{3ea63efb-6a5b-4cda-9f53-14d94c0db12b}*SharedItemsImports = 4
28 | StackTraceExplorer.Shared\StackTraceExplorer.Shared.projitems*{865d31df-a8cc-4c50-9eb5-aa04e555360b}*SharedItemsImports = 4
29 | EndGlobalSection
30 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
31 | Debug|Any CPU = Debug|Any CPU
32 | Debug|x86 = Debug|x86
33 | Release|Any CPU = Release|Any CPU
34 | Release|x86 = Release|x86
35 | EndGlobalSection
36 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
37 | {393FF8FD-00C8-471C-8C5A-EA97B229886A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {393FF8FD-00C8-471C-8C5A-EA97B229886A}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {393FF8FD-00C8-471C-8C5A-EA97B229886A}.Debug|x86.ActiveCfg = Debug|x86
40 | {393FF8FD-00C8-471C-8C5A-EA97B229886A}.Debug|x86.Build.0 = Debug|x86
41 | {393FF8FD-00C8-471C-8C5A-EA97B229886A}.Release|Any CPU.ActiveCfg = Release|Any CPU
42 | {393FF8FD-00C8-471C-8C5A-EA97B229886A}.Release|Any CPU.Build.0 = Release|Any CPU
43 | {393FF8FD-00C8-471C-8C5A-EA97B229886A}.Release|x86.ActiveCfg = Release|x86
44 | {393FF8FD-00C8-471C-8C5A-EA97B229886A}.Release|x86.Build.0 = Release|x86
45 | {865D31DF-A8CC-4C50-9EB5-AA04E555360B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 | {865D31DF-A8CC-4C50-9EB5-AA04E555360B}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 | {865D31DF-A8CC-4C50-9EB5-AA04E555360B}.Debug|x86.ActiveCfg = Debug|Any CPU
48 | {865D31DF-A8CC-4C50-9EB5-AA04E555360B}.Debug|x86.Build.0 = Debug|Any CPU
49 | {865D31DF-A8CC-4C50-9EB5-AA04E555360B}.Release|Any CPU.ActiveCfg = Release|Any CPU
50 | {865D31DF-A8CC-4C50-9EB5-AA04E555360B}.Release|Any CPU.Build.0 = Release|Any CPU
51 | {865D31DF-A8CC-4C50-9EB5-AA04E555360B}.Release|x86.ActiveCfg = Release|Any CPU
52 | {865D31DF-A8CC-4C50-9EB5-AA04E555360B}.Release|x86.Build.0 = Release|Any CPU
53 | {3EA63EFB-6A5B-4CDA-9F53-14D94C0DB12B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54 | {3EA63EFB-6A5B-4CDA-9F53-14D94C0DB12B}.Debug|Any CPU.Build.0 = Debug|Any CPU
55 | {3EA63EFB-6A5B-4CDA-9F53-14D94C0DB12B}.Debug|x86.ActiveCfg = Debug|x86
56 | {3EA63EFB-6A5B-4CDA-9F53-14D94C0DB12B}.Debug|x86.Build.0 = Debug|x86
57 | {3EA63EFB-6A5B-4CDA-9F53-14D94C0DB12B}.Release|Any CPU.ActiveCfg = Release|Any CPU
58 | {3EA63EFB-6A5B-4CDA-9F53-14D94C0DB12B}.Release|Any CPU.Build.0 = Release|Any CPU
59 | {3EA63EFB-6A5B-4CDA-9F53-14D94C0DB12B}.Release|x86.ActiveCfg = Release|x86
60 | {3EA63EFB-6A5B-4CDA-9F53-14D94C0DB12B}.Release|x86.Build.0 = Release|x86
61 | EndGlobalSection
62 | GlobalSection(SolutionProperties) = preSolution
63 | HideSolutionNode = FALSE
64 | EndGlobalSection
65 | GlobalSection(ExtensibilityGlobals) = postSolution
66 | SolutionGuid = {6CFED3C7-F32D-41F2-B165-6CE47F688579}
67 | EndGlobalSection
68 | EndGlobal
69 |
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/publish-manifest.VS2019.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vsix-publish",
3 | "categories": [ "coding" ],
4 | "identity": {
5 | "internalName": "StackTraceExplorer"
6 | },
7 | "overview": "readme.md",
8 | "priceCategory": "free",
9 | "publisher": "SamirBoulema",
10 | "private": false,
11 | "qna": true,
12 | "repo": "https://github.com/sboulema/StackTraceExplorer"
13 | }
--------------------------------------------------------------------------------
/publish-manifest.VS2022.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/vsix-publish",
3 | "categories": [ "coding" ],
4 | "identity": {
5 | "internalName": "StackTraceExplorer2022"
6 | },
7 | "overview": "readme.md",
8 | "priceCategory": "free",
9 | "publisher": "SamirBoulema",
10 | "private": false,
11 | "qna": true,
12 | "repo": "https://github.com/sboulema/StackTraceExplorer"
13 | }
--------------------------------------------------------------------------------