diff options
author | Greg Munn <gregm@microsoft.com> | 2020-01-21 22:48:21 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-21 22:48:21 +0300 |
commit | d184f327b5ff4069517665a161fa9e48b2e8913d (patch) | |
tree | db946418f5636b5dcb9d77504cbca5c332afc356 | |
parent | 6a92a4f2b72e0a8537f7ecd81e9d39c4316762f5 (diff) | |
parent | f71636b585019ec97643b98ffd5701f8b5b21b40 (diff) |
Merge pull request #9429 from mono/jstedfast-debugger-sourcelink-in-nosourceview
[Debugger] Move the Download-via-SourceLink prompt into the NoSourceV…
3 files changed, 256 insertions, 135 deletions
diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs index 8fb53e652f..d92f35181a 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs @@ -26,6 +26,11 @@ // // +/* + * Some places we should be doing some error handling we used to toss + * exceptions, now we error out silently, this needs a real solution. + */ + using System; using System.Xml; using System.Linq; @@ -39,8 +44,8 @@ using Mono.Addins; using Mono.Debugging.Client; -using Microsoft.VisualStudio.Text; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; using MonoDevelop.Ide; using MonoDevelop.Core; @@ -48,8 +53,8 @@ using MonoDevelop.Ide.Gui; using MonoDevelop.Projects; using MonoDevelop.Components; using MonoDevelop.Core.Execution; -using MonoDevelop.Ide.TextEditing; using MonoDevelop.Ide.Gui.Content; +using MonoDevelop.Ide.TextEditing; using MonoDevelop.Debugger.Viewers; using MonoDevelop.Core.Instrumentation; diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Initializer.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Initializer.cs index f33ea3148d..b4ea663cd5 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Initializer.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Initializer.cs @@ -26,16 +26,17 @@ // using System; -using MonoDevelop.Ide.Gui; -using MonoDevelop.Components.Commands; -using Mono.Debugging.Client; -using MonoDevelop.Ide.Commands; -using MonoDevelop.Core; -using MonoDevelop.Ide; using System.IO; +using System.Threading.Tasks; + +using Mono.Debugging.Client; -using Xwt; +using MonoDevelop.Ide; +using MonoDevelop.Core; +using MonoDevelop.Ide.Gui; using MonoDevelop.Core.Web; +using MonoDevelop.Ide.Commands; +using MonoDevelop.Components.Commands; namespace MonoDevelop.Debugger { @@ -68,41 +69,40 @@ namespace MonoDevelop.Debugger async void OnFrameChanged (object s, EventArgs a) { - if (changingFrame) { + if (changingFrame) return; - } + + changingFrame = true; + try { - changingFrame = true; if (disassemblyDoc != null && DebuggingService.IsFeatureSupported (DebuggerFeatures.Disassembly)) disassemblyView.Update (); var frame = DebuggingService.CurrentFrame; - if (frame == null) { + if (frame == null) return; - } + var debuggerOptions = DebuggingService.GetUserOptions (); FilePath file = frame.SourceLocation.FileName; - int line = frame.SourceLocation.Line; + if (line != -1) { if (!file.IsNullOrEmpty && File.Exists (file)) { var doc = await IdeApp.Workbench.OpenDocument (file, null, line, 1, OpenDocumentOptions.Debugger); - if (doc != null) { + if (doc != null) return; - } } - bool alternateLocationExists = false; + if (frame.SourceLocation.FileHash != null) { var newFilePath = SourceCodeLookup.FindSourceFile (file, frame.SourceLocation.FileHash); if (newFilePath != null && File.Exists (newFilePath)) { frame.UpdateSourceFile (newFilePath); var doc = await IdeApp.Workbench.OpenDocument (newFilePath, null, line, 1, OpenDocumentOptions.Debugger); - if (doc != null) { + if (doc != null) return; - } } } - var debuggerOptions = DebuggingService.GetUserOptions (); + var automaticSourceDownload = debuggerOptions.AutomaticSourceLinkDownload; var sourceLink = frame.SourceLocation.SourceLink; @@ -111,53 +111,23 @@ namespace MonoDevelop.Debugger Document doc = null; // ~/Library/Caches/VisualStudio/8.0/Symbols/org/projectname/git-sha/path/to/file.cs if (!File.Exists (downloadLocation)) { - if (automaticSourceDownload == AutomaticSourceDownload.Always) { - doc = await DownloadAndOpenFileAsync (frame, line, sourceLink); - } else { - var hyperlink = $"<a href='{ sourceLink.Uri }'>{ Path.GetFileName (sourceLink.RelativeFilePath) }</a>"; - var stackframeText = $"<b>{frame.FullStackframeText}</b>"; - - var text = GettextCatalog.GetString - ("{0} is a call to external source code. Would you like to get '{1}' and view it?", stackframeText, hyperlink); - var message = new Ide.GenericMessage { - Text = GettextCatalog.GetString ("External source code available"), - SecondaryText = text - }; - message.AddOption (nameof (automaticSourceDownload), GettextCatalog.GetString ("Always get source code automatically"), false); - message.Buttons.Add (AlertButton.Cancel); - message.Buttons.Add (new AlertButton (GettextCatalog.GetString ("Get and Open"))); - message.DefaultButton = 1; - - var didNotCancel = MessageService.GenericAlert (message) != AlertButton.Cancel; - if (didNotCancel) { - if (message.GetOptionValue (nameof (automaticSourceDownload))) { - debuggerOptions.AutomaticSourceLinkDownload = AutomaticSourceDownload.Always; - DebuggingService.SetUserOptions (debuggerOptions); - } - doc = await DownloadAndOpenFileAsync (frame, line, sourceLink); - } - } + if (automaticSourceDownload == AutomaticSourceDownload.Always) + doc = await NoSourceView.DownloadAndOpenAsync (frame); } else { // The file has previously been downloaded for a different solution. // We need to map the cached location frame.UpdateSourceFile (downloadLocation); doc = await IdeApp.Workbench.OpenDocument (downloadLocation, null, line, 1, OpenDocumentOptions.Debugger); } - if (doc != null) { + if (doc != null) return; - } } } - bool disassemblyNotSupported = false; - // If we don't have an address space, we can't disassemble - if (string.IsNullOrEmpty (frame.AddressSpace)) - disassemblyNotSupported = true; - - if (!DebuggingService.CurrentSessionSupportsFeature (DebuggerFeatures.Disassembly)) - disassemblyNotSupported = true; + bool disassemblySupported = !string.IsNullOrEmpty (frame.AddressSpace) && + DebuggingService.CurrentSessionSupportsFeature (DebuggerFeatures.Disassembly); - if (disassemblyNotSupported && disassemblyDoc != null) { + if (!disassemblySupported && disassemblyDoc != null) { disassemblyDoc.Close ().Ignore (); disassemblyDoc = null; disassemblyView = null; @@ -167,14 +137,14 @@ namespace MonoDevelop.Debugger if (disassemblyDoc == null) { if (noSourceDoc == null) { noSourceView = new NoSourceView (); - noSourceView.Update (disassemblyNotSupported); + noSourceView.Update (debuggerOptions, frame, disassemblySupported); noSourceDoc = await IdeApp.Workbench.OpenDocument (noSourceView, true); noSourceDoc.Closed += delegate { noSourceDoc = null; noSourceView = null; }; } else { - noSourceView.Update (disassemblyNotSupported); + noSourceView.Update (debuggerOptions, frame, disassemblySupported); noSourceDoc.Select (); } } else { @@ -185,34 +155,6 @@ namespace MonoDevelop.Debugger } } - async System.Threading.Tasks.Task<Document> DownloadAndOpenFileAsync (StackFrame frame, int line, SourceLink sourceLink) - { - var pm = IdeApp.Workbench.ProgressMonitors.GetStatusProgressMonitor ( - GettextCatalog.GetString ("Downloading {0}", sourceLink.Uri), - Stock.StatusDownload, - true - ); - - Document doc = null; - try { - var downloadLocation = sourceLink.GetDownloadLocation (symbolCachePath); - Directory.CreateDirectory (Path.GetDirectoryName (downloadLocation)); - DocumentRegistry.SkipNextChange (downloadLocation); - var client = HttpClientProvider.CreateHttpClient (sourceLink.Uri); - using (var stream = await client.GetStreamAsync (sourceLink.Uri).ConfigureAwait (false)) - using (var fs = new FileStream (downloadLocation, FileMode.Create)) { - await stream.CopyToAsync (fs).ConfigureAwait (false); - } - frame.UpdateSourceFile (downloadLocation); - doc = await Runtime.RunInMainThread (() => IdeApp.Workbench.OpenDocument (downloadLocation, null, line, 1, OpenDocumentOptions.Debugger)); - } catch (Exception ex) { - LoggingService.LogInternalError ("Error downloading SourceLink file", ex); - } finally { - pm.Dispose (); - } - return doc; - } - async void OnShowDisassembly (object s, EventArgs a) { if (disassemblyDoc == null) { @@ -230,11 +172,11 @@ namespace MonoDevelop.Debugger static void SetSourceCodeFrame () { - Backtrace bt = DebuggingService.CurrentCallStack; + var bt = DebuggingService.CurrentCallStack; if (bt != null) { for (int n = 0; n < bt.FrameCount; n++) { - StackFrame sf = bt.GetFrame (n); + var sf = bt.GetFrame (n); if (!sf.IsExternalCode && sf.SourceLocation.Line != -1 && diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/NoSourceView.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/NoSourceView.cs index c2fbbfff1a..96ff22c23f 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/NoSourceView.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/NoSourceView.cs @@ -23,95 +23,184 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. + using System; using System.IO; -using MonoDevelop.Components; -using MonoDevelop.Core; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using Xwt; + +using Mono.Debugging.Client; + using MonoDevelop.Ide; +using MonoDevelop.Core; using MonoDevelop.Ide.Gui; -using Xwt; +using MonoDevelop.Core.Web; +using MonoDevelop.Components; using MonoDevelop.Ide.Gui.Documents; -using System.Threading; -using System.Threading.Tasks; namespace MonoDevelop.Debugger { public class NoSourceView : DocumentController { - XwtControl xwtControl; - ScrollView scrollView = new ScrollView (); + readonly ScrollView scrollView = new ScrollView (); + readonly XwtControl control; + DebuggerSessionOptions options; + Label manageOptionsLabel; + CheckBox sourceLinkCheckbox; + Button sourceLinkButton; + Label sourceLinkLabel; + Button disassemblyButton; + Label disassemblyLabel; + Button browseButton; + StackFrame frame; + bool downloading; + public NoSourceView () { - xwtControl = new XwtControl (scrollView); + control = new XwtControl (scrollView); } - public void Update (bool disassemblyNotSupported) + public void Update (DebuggerSessionOptions options, StackFrame frame, bool disassemblySupported) { - scrollView.Content = CreateContent (disassemblyNotSupported); + scrollView.Content = CreateContent (options, frame, disassemblySupported); } - Widget CreateContent (bool disassemblyNotSupported) + Widget CreateContent (DebuggerSessionOptions options, StackFrame frame, bool disassemblySupported) { - var fileName = GetFilename (DebuggingService.CurrentFrame?.SourceLocation?.FileName); - var box = new VBox (); - box.Margin = 30; - box.Spacing = 10; + var fileName = GetFilename (frame.SourceLocation?.FileName); + this.options = options; + this.frame = frame; + + var vbox = new VBox { + Spacing = 10, + Margin = 30 + }; + if (!string.IsNullOrEmpty (fileName)) { DocumentTitle = GettextCatalog.GetString ("Source Not Found"); - var headerLabel = new Label (); - headerLabel.Markup = GettextCatalog.GetString ("{0} file not found", $"<b>{fileName}</b>"); - box.PackStart (headerLabel); - var actionsBox = new HBox (); - var buttonBrowseAndFind = new Button (GettextCatalog.GetString ("Browse and find {0}", fileName)); - buttonBrowseAndFind.Clicked += OpenFindSourceFileDialog; - actionsBox.PackStart (buttonBrowseAndFind); - box.PackStart (actionsBox); + var headerLabel = new Label { + Markup = GettextCatalog.GetString ("{0} file not found", $"<b>{fileName}</b>") + }; + headerLabel.Font = headerLabel.Font.WithScaledSize (2); + vbox.PackStart (headerLabel); + + if (frame.SourceLocation?.SourceLink != null && options.AutomaticSourceLinkDownload == AutomaticSourceDownload.Ask) { + var sourceLinkVbox = new VBox { + MarginBottom = 20, + MarginTop = 10, + Spacing = 10, + }; + + sourceLinkLabel = new Label { + Markup = GettextCatalog.GetString ("External source code is available. Would you like to download {0} and view it?", $"<a href=\"clicked\">{fileName}</a>"), + Name = "SourceLinkLabel" + }; + sourceLinkLabel.LinkClicked += OnDownloadSourceClicked; + + sourceLinkVbox.PackStart (sourceLinkLabel); + + var sourceLinkHbox = new HBox { + Spacing = 10 + }; + + sourceLinkButton = new Button (GettextCatalog.GetString ("Download {0}", fileName)); + sourceLinkButton.Clicked += OnDownloadSourceClicked; + sourceLinkHbox.PackStart (sourceLinkButton); + + sourceLinkCheckbox = new CheckBox (GettextCatalog.GetString ("Always download source code automatically")); + sourceLinkHbox.PackStart (sourceLinkCheckbox); + + sourceLinkVbox.PackStart (sourceLinkHbox); + + vbox.PackStart (sourceLinkVbox); + + var separator = new HSeparator (); + vbox.PackStart (separator); + } + + var buttonBox = new HBox (); + + browseButton = new Button (GettextCatalog.GetString ("Browse…")); + browseButton.Clicked += OnBrowseClicked; + buttonBox.PackStart (browseButton); + + if (disassemblySupported) { + disassemblyButton = new Button (GettextCatalog.GetString ("Go to Disassembly")); + disassemblyButton.Clicked += OnGoToDisassemblyClicked; + buttonBox.PackStart (disassemblyButton); + } + + var hbox = new HBox { + MarginTop = 20, + Spacing = 10 + }; + + hbox.PackStart (buttonBox); + if (IdeApp.ProjectOperations.CurrentSelectedSolution != null) { - var manageLookupsLabel = new Label (); - manageLookupsLabel.Markup = GettextCatalog.GetString ("Manage the locations used to find source files in the {0}", "<a href=\"clicked\">" + GettextCatalog.GetString ("Solution Options") + "</a>"); - manageLookupsLabel.LinkClicked += (sender, e) => { - if (IdeApp.ProjectOperations.CurrentSelectedSolution == null) - return; - IdeApp.ProjectOperations.ShowOptions (IdeApp.ProjectOperations.CurrentSelectedSolution, "DebugSourceFiles"); + manageOptionsLabel = new Label { + Markup = GettextCatalog.GetString ("Manage the locations used to find source files in the {0}.", "<a href=\"clicked\">" + GettextCatalog.GetString ("Solution Options") + "</a>"), + MarginLeft = 10 }; - box.PackStart (manageLookupsLabel); + manageOptionsLabel.LinkClicked += OnManageSolutionOptionsClicked; + hbox.PackStart (manageOptionsLabel); } - headerLabel.Font = headerLabel.Font.WithScaledSize (2); + + vbox.PackStart (hbox); } else { DocumentTitle = GettextCatalog.GetString ("Source Not Available"); var headerLabel = new Label (GettextCatalog.GetString ("Source Not Available")); - box.PackStart (headerLabel); + vbox.PackStart (headerLabel); var label = new Label (GettextCatalog.GetString ("Source information is missing from the debug information for this module")); - box.PackStart (label); headerLabel.Font = label.Font.WithScaledSize (2); + vbox.PackStart (label); + + if (disassemblySupported) { + disassemblyLabel = new Label { + Markup = GettextCatalog.GetString ("View disassembly in the {0}", "<a href=\"clicked\">" + GettextCatalog.GetString ("Disassembly Tab") + "</a>") + }; + disassemblyLabel.LinkClicked += OnGoToDisassemblyClicked; + vbox.PackStart (disassemblyLabel); + } } - if (!disassemblyNotSupported) { - var labelDisassembly = new Label (); - labelDisassembly.Markup = GettextCatalog.GetString ("View disassembly in the {0}", "<a href=\"clicked\">" + GettextCatalog.GetString ("Disassembly Tab") + "</a>"); - labelDisassembly.LinkClicked += (sender, e) => { - DebuggingService.ShowDisassembly (); - Document.Close (false).Ignore (); - }; - box.PackStart (labelDisassembly); - } - return box; + + return vbox; } string GetFilename (string fileName) { if (fileName == null) return null; + var index = fileName.LastIndexOfAny (new char [] { '/', '\\' }); if (index != -1) return fileName.Substring (index + 1); + return fileName; } - private async void OpenFindSourceFileDialog (object sender, EventArgs e) + void OnGoToDisassemblyClicked (object sender, EventArgs e) + { + DebuggingService.ShowDisassembly (); + Document.Close (false).Ignore (); + } + + void OnManageSolutionOptionsClicked (object sender, EventArgs e) + { + if (IdeApp.ProjectOperations.CurrentSelectedSolution == null) + return; + + IdeApp.ProjectOperations.ShowOptions (IdeApp.ProjectOperations.CurrentSelectedSolution, "DebugSourceFiles"); + } + + async void OnBrowseClicked (object sender, EventArgs e) { var sf = DebuggingService.CurrentFrame; if (sf == null) { - LoggingService.LogWarning ($"CurrentFrame was null in {nameof (OpenFindSourceFileDialog)}"); + LoggingService.LogWarning ($"CurrentFrame was null in {nameof (OnBrowseClicked)}"); return; } var dlg = new Ide.Gui.Dialogs.OpenFileDialog (GettextCatalog.GetString ("File to Open") + " " + sf.SourceLocation.FileName, FileChooserAction.Open) { @@ -135,6 +224,7 @@ namespace MonoDevelop.Debugger var doc = await IdeApp.Workbench.OpenDocument (newFilePath, null, sf.SourceLocation.Line, 1, OpenDocumentOptions.Debugger); if (doc != null) { + // close the NoSourceView document tab await Document.Close (false); } } @@ -146,9 +236,93 @@ namespace MonoDevelop.Debugger } } + public static async Task<Document> DownloadAndOpenAsync (StackFrame frame) + { + var symbolCachePath = UserProfile.Current.CacheDir.Combine ("Symbols"); + var sourceLink = frame.SourceLocation.SourceLink; + + var pm = IdeApp.Workbench.ProgressMonitors.GetStatusProgressMonitor ( + GettextCatalog.GetString ("Downloading {0}", sourceLink.Uri), + Stock.StatusDownload, + true + ); + + Document doc = null; + try { + var downloadLocation = sourceLink.GetDownloadLocation (symbolCachePath); + Directory.CreateDirectory (Path.GetDirectoryName (downloadLocation)); + DocumentRegistry.SkipNextChange (downloadLocation); + + var client = HttpClientProvider.CreateHttpClient (sourceLink.Uri); + + using (var stream = await client.GetStreamAsync (sourceLink.Uri).ConfigureAwait (false)) { + using (var fs = new FileStream (downloadLocation, FileMode.Create)) + await stream.CopyToAsync (fs).ConfigureAwait (false); + } + + frame.UpdateSourceFile (downloadLocation); + + int line = frame.SourceLocation.Line; + + doc = await Runtime.RunInMainThread (() => IdeApp.Workbench.OpenDocument (downloadLocation, null, line, 1, OpenDocumentOptions.Debugger)); + } catch (Exception ex) { + LoggingService.LogInternalError ("Error downloading SourceLink file", ex); + } finally { + pm.Dispose (); + } + + return doc; + } + + async void OnDownloadSourceClicked (object sender, EventArgs e) + { + if (downloading) + return; + + downloading = true; + + try { + if (sourceLinkCheckbox != null && sourceLinkCheckbox.Active) { + options.AutomaticSourceLinkDownload = AutomaticSourceDownload.Always; + DebuggingService.SetUserOptions (options); + } + + var doc = await DownloadAndOpenAsync (frame); + + if (doc != null) { + // close the NoSourceView document tab + await Document.Close (false); + } + } finally { + downloading = false; + } + } + protected override Task<Control> OnGetViewControlAsync (CancellationToken token, DocumentViewContent view) { - return Task.FromResult<Control> (xwtControl); + return Task.FromResult<Control> (control); + } + + protected override void OnDispose () + { + if (sourceLinkButton != null) { + sourceLinkLabel.LinkClicked -= OnDownloadSourceClicked; + sourceLinkButton.Clicked -= OnDownloadSourceClicked; + } + + if (browseButton != null) + browseButton.Clicked -= OnBrowseClicked; + + if (disassemblyButton != null) + disassemblyButton.Clicked -= OnGoToDisassemblyClicked; + + if (disassemblyLabel != null) + disassemblyLabel.LinkClicked -= OnGoToDisassemblyClicked; + + if (manageOptionsLabel != null) + manageOptionsLabel.LinkClicked -= OnManageSolutionOptionsClicked; + + base.OnDispose (); } } } |