private int ValidateBreakpointLocationWorker( IVsTextBuffer pBuffer, int iLine, int iCol, VsTextSpan[] pCodeSpan, CancellationToken cancellationToken) { if (_breakpointService == null) { return(VSConstants.E_FAIL); } var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(pBuffer); if (textBuffer != null) { var snapshot = textBuffer.CurrentSnapshot; Document document = snapshot.AsText().GetDocumentWithFrozenPartialSemanticsAsync(cancellationToken).WaitAndGetResult(cancellationToken); if (document != null) { var point = snapshot.GetPoint(iLine, iCol); var length = 0; if (pCodeSpan != null && pCodeSpan.Length > 0) { // If we have a non-empty span then it means that the debugger is asking us to adjust an // existing span. In Everett we didn't do this so we had some good and some bad // behavior. For example if you had a breakpoint on: "int i;" and you changed it to "int // i = 4;", then the breakpoint wouldn't adjust. That was bad. However, if you had the // breakpoint on an open or close curly brace then it would always "stick" to that brace // which was good. // // So we want to keep the best parts of both systems. We want to appropriately "stick" // to tokens and we also want to adjust spans intelligently. // // However, it turns out the latter is hard to do when there are parse errors in the // code. Things like missing name nodes cause a lot of havoc and make it difficult to // track a closing curly brace. // // So the way we do this is that we default to not intelligently adjusting the spans // while there are parse errors. But when there are no parse errors then the span is // adjusted. var initialBreakpointSpan = snapshot.GetSpan(pCodeSpan[0]); if (initialBreakpointSpan.Length > 0 && document.SupportsSyntaxTree) { var tree = document.GetSyntaxTreeAsync(cancellationToken).WaitAndGetResult(cancellationToken); if (tree.GetDiagnostics(cancellationToken).Any(d => d.Severity == DiagnosticSeverity.Error)) { return(VSConstants.E_FAIL); } } // If a span is provided, and the requested position falls in that span, then just // move the requested position to the start of the span. // Length will be used to determine if we need further analysis, which is only required when text spans multiple lines. if (initialBreakpointSpan.Contains(point)) { point = initialBreakpointSpan.Start; length = pCodeSpan[0].iEndLine > pCodeSpan[0].iStartLine ? initialBreakpointSpan.Length : 0; } } // NOTE(cyrusn): we need to wait here because ValidateBreakpointLocation is // synchronous. In the future, it would be nice for the debugger to provide // an async entry point for this. var breakpoint = _breakpointService.ResolveBreakpointAsync(document, new CodeAnalysis.Text.TextSpan(point.Position, length), cancellationToken).WaitAndGetResult(cancellationToken); if (breakpoint == null) { // There should *not* be a breakpoint here. E_FAIL to let the debugger know // that. return(VSConstants.E_FAIL); } if (breakpoint.IsLineBreakpoint) { // Let the debugger take care of this. They'll put a line breakpoint // here. This is useful for when the user does something like put a // breakpoint in inactive code. We want to allow this as they might // just have different defines during editing versus debugging. // TODO(cyrusn): Do we need to set the pCodeSpan in this case? return(VSConstants.E_NOTIMPL); } // There should be a breakpoint at the location passed back. if (pCodeSpan != null && pCodeSpan.Length > 0) { pCodeSpan[0] = breakpoint.TextSpan.ToSnapshotSpan(snapshot).ToVsTextSpan(); } return(VSConstants.S_OK); } } return(VSConstants.E_NOTIMPL); }