void SelectionChangedHandler(object sender, EventArgs e) { if (suppressChangeEvent) { return; } var pos = SelectedRow; if (pos == null) { return; } var nav = store.GetNavigatorAt(pos); var span = nav.GetValue(spanField); var roslynSnapshot = lastSourceText.FindCorrespondingEditorTextSnapshot(); if (roslynSnapshot == null) { return; } var editorSpan = new SnapshotSpan(roslynSnapshot, span.Start, span.Length); suppressChangeEvent = true; try { editorTracker.TextView.Selection.Select(editorSpan, false); } finally { suppressChangeEvent = false; } }
/// <summary> /// Displays highlights at the given locations. /// </summary> public void HighlightText( SourceText text, IDictionary <HighlightType, IEnumerable <TextSpan> > highlights) { Contract.Requires <ArgumentNullException>(text != null, nameof(text)); Contract.Requires <ArgumentNullException>(highlights != null, nameof(highlights)); var snapshot = text.FindCorrespondingEditorTextSnapshot(); if (snapshot == null) { return; } var vsHighlights = new Dictionary <HighlightType, NormalizedSnapshotSpanCollection>(); foreach (var highlight in highlights) { var vsSpans = highlight.Value .Select(span => new SnapshotSpan(snapshot, span.Start, span.End - span.Start)); var vsSpanCollection = new NormalizedSnapshotSpanCollection(vsSpans); vsHighlights.Add(highlight.Key, vsSpanCollection); } this.highlightService.HighlightText(snapshot, vsHighlights); }
public IEnumerable <ActiveStatementTextSpan> GetSpans(SourceText source) { var document = source.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) { return(SpecializedCollections.EmptyEnumerable <ActiveStatementTextSpan>()); } // We might be asked for spans in a different workspace than // the one we maintain tracking spans for (for example, a preview). if (document.Project.Solution.Workspace != _editSession.BaseSolution.Workspace) { return(SpecializedCollections.EmptyEnumerable <ActiveStatementTextSpan>()); } ITrackingSpan[] documentTrackingSpans; lock (_trackingSpans) { if (!_trackingSpans.TryGetValue(document.Id, out documentTrackingSpans) || documentTrackingSpans == null) { return(SpecializedCollections.EmptyEnumerable <ActiveStatementTextSpan>()); } } Debug.Assert(documentTrackingSpans.Length > 0); var snapshot = source.FindCorrespondingEditorTextSnapshot(); // The document might have been reopened with a new text buffer // and we haven't created tracking spans for the new text buffer yet. if (snapshot == null || snapshot.TextBuffer != documentTrackingSpans[0].TextBuffer) { return(SpecializedCollections.EmptyEnumerable <ActiveStatementTextSpan>()); } var baseStatements = _editSession.BaseActiveStatements[document.Id]; Debug.Assert(documentTrackingSpans.Length == baseStatements.Length); var result = new ActiveStatementTextSpan[documentTrackingSpans.Length]; for (int i = 0; i < documentTrackingSpans.Length; i++) { Debug.Assert(documentTrackingSpans[i].TextBuffer == snapshot.TextBuffer); result[i] = new ActiveStatementTextSpan( baseStatements[i].Flags, documentTrackingSpans[i].GetSpan(snapshot).Span.ToTextSpan()); } return(result); }
private void OnDiagnosticsUpdated(object sender, DiagnosticsUpdatedArgs e) { // Do some quick checks to avoid doing any further work for diagnostics we don't // care about. var ourDocument = _subjectBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); if (ourDocument == null || ourDocument.Project.Solution.Workspace != e.Workspace || ourDocument.Id != e.DocumentId) { return; } // Make sure we can find an editor snapshot for these errors. Otherwise we won't // be able to make ITagSpans for them. If we can't, just bail out. This happens // when the solution crawler is very far behind. However, it will have a more // up to date document within it that it will eventually process. Until then // we just keep around the stale tags we have. // // Note: if the Solution or Document is null here, then that means the document // was removed. In that case, we do want to proceed so that we'll produce 0 // tags and we'll update the editor appropriately. SourceText sourceText = null; ITextSnapshot editorSnapshot = null; if (e.Solution != null) { var diagnosticDocument = e.Solution.GetDocument(e.DocumentId); if (diagnosticDocument != null) { if (!diagnosticDocument.TryGetText(out sourceText)) { return; } editorSnapshot = sourceText.FindCorrespondingEditorTextSnapshot(); if (editorSnapshot == null) { return; } } } if (this.IsForeground()) { OnDiagnosticsUpdatedOnForeground(sender, e, sourceText, editorSnapshot); } else { RegisterNotification(() => OnDiagnosticsUpdatedOnForeground(sender, e, sourceText, editorSnapshot)); } }
private ImmutableArray <CompletionItem> GetItems(SourceText text, Document document, int position, CompletionTrigger triggerInfo, CancellationToken cancellationToken) { var line = text.Lines.GetLineFromPosition(position); var lineText = text.ToString(TextSpan.FromBounds(line.Start, position)); var match = s_directiveRegex.Match(lineText); if (!match.Success) { return(ImmutableArray <CompletionItem> .Empty); } var quotedPathGroup = match.Groups[1]; var quotedPath = quotedPathGroup.Value; var endsWithQuote = PathCompletionUtilities.EndsWithQuote(quotedPath); if (endsWithQuote && (position >= line.Start + match.Length)) { return(ImmutableArray <CompletionItem> .Empty); } var buffer = text.Container.GetTextBuffer(); var snapshot = text.FindCorrespondingEditorTextSnapshot(); if (snapshot == null) { return(ImmutableArray <CompletionItem> .Empty); } var fileSystem = CurrentWorkingDirectoryDiscoveryService.GetService(snapshot); // TODO: https://github.com/dotnet/roslyn/issues/5263 // Avoid dependency on a specific resolver. // The search paths should be provided by specialized workspaces: // - InteractiveWorkspace for interactive window // - ScriptWorkspace for loose .csx files (we don't have such workspace today) var searchPaths = (document.Project.CompilationOptions.SourceReferenceResolver as SourceFileResolver)?.SearchPaths ?? ImmutableArray <string> .Empty; var helper = new FileSystemCompletionHelper( this, GetTextChangeSpan(text, position, quotedPathGroup), fileSystem, Glyph.OpenFolder, Glyph.CSharpFile, searchPaths: searchPaths, allowableExtensions: new[] { ".csx" }, itemRules: s_rules); var pathThroughLastSlash = this.GetPathThroughLastSlash(text, position, quotedPathGroup); return(helper.GetItems(pathThroughLastSlash, documentPath: null)); }
private ImmutableArray <CompletionItem> GetItems(SourceText text, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var line = text.Lines.GetLineFromPosition(position); var lineText = text.ToString(TextSpan.FromBounds(line.Start, position)); var match = s_directiveRegex.Match(lineText); if (!match.Success) { return(ImmutableArray <CompletionItem> .Empty); } var quotedPathGroup = match.Groups[1]; var quotedPath = quotedPathGroup.Value; var endsWithQuote = PathCompletionUtilities.EndsWithQuote(quotedPath); if (endsWithQuote && (position >= line.Start + match.Length)) { return(ImmutableArray <CompletionItem> .Empty); } var buffer = text.Container.GetTextBuffer(); var snapshot = text.FindCorrespondingEditorTextSnapshot(); if (snapshot == null) { return(ImmutableArray <CompletionItem> .Empty); } var fileSystem = CurrentWorkingDirectoryDiscoveryService.GetService(snapshot); var searchPaths = ImmutableArray.Create(fileSystem.CurrentDirectory); var helper = new FileSystemCompletionHelper( this, GetTextChangeSpan(text, position, quotedPathGroup), fileSystem, Glyph.OpenFolder, Glyph.CSharpFile, searchPaths: searchPaths, allowableExtensions: new[] { ".csx" }, itemRules: ItemRules.Instance); var pathThroughLastSlash = this.GetPathThroughLastSlash(text, position, quotedPathGroup); return(helper.GetItems(pathThroughLastSlash, documentPath: null)); }
/// <summary> /// Selects a text span in a document. /// </summary> public void SelectText(SourceText text, TextSpan selectedSpan) { Contract.Requires <ArgumentNullException>(text != null, nameof(text)); var snapshot = text.FindCorrespondingEditorTextSnapshot(); var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); var windowFrame = this.OpenDocumentGetFrame(document); var textView = VsShellUtilities.GetTextView(windowFrame); var lineSpan = text.Lines.GetLinePositionSpan(selectedSpan); textView.SetSelection( lineSpan.Start.Line, lineSpan.Start.Character, lineSpan.End.Line, lineSpan.End.Character); }
private bool TryGetOldSnapshot(Document document, out SourceText text, out ITextSnapshot oldSnapshot) { if (!document.TryGetText(out text)) { oldSnapshot = null; return(false); } oldSnapshot = text.FindCorrespondingEditorTextSnapshot(); if (oldSnapshot == null) { return(false); } // two should be same Contract.ThrowIfFalse(text.Length == oldSnapshot.Length); return(true); }
public bool TryGetSpan(ActiveStatementId id, SourceText source, out TextSpan span) { lock (_trackingSpans) { if (_trackingSpans.TryGetValue(id.DocumentId, out var documentSpans) && documentSpans != null) { var trackingSpan = documentSpans[id.Ordinal]; var snapshot = source.FindCorrespondingEditorTextSnapshot(); if (snapshot != null && snapshot.TextBuffer == trackingSpan.TextBuffer) { span = trackingSpan.GetSpan(snapshot).Span.ToTextSpan(); return(true); } } } span = default; return(false); }
internal static string GetBaseDirectory(SourceText text, Document document) { string result; if (document.Project.IsSubmission) { var buffer = text.Container.GetTextBuffer(); var snapshot = text.FindCorrespondingEditorTextSnapshot(); if (snapshot == null) { return(null); } result = CurrentWorkingDirectoryDiscoveryService.GetService(snapshot).WorkingDirectory; } else { result = PathUtilities.GetDirectoryName(document.FilePath); } return(PathUtilities.IsAbsolute(result) ? result : null); }
public void UpdateActiveStatementSpans(SourceText source, IEnumerable <KeyValuePair <ActiveStatementId, TextSpan> > spans) { bool leafUpdated = false; bool updated = false; ITrackingSpan[] documentSpans; lock (_trackingSpans) { foreach (var span in spans) { ActiveStatementId id = span.Key; if (_trackingSpans.TryGetValue(id.DocumentId, out documentSpans) && documentSpans != null) { var snapshot = source.FindCorrespondingEditorTextSnapshot(); // Avoid updating spans if the buffer has changed. // Buffer change is handled by DocumentOpened event. if (snapshot != null && snapshot.TextBuffer == documentSpans[id.Ordinal].TextBuffer) { documentSpans[id.Ordinal] = snapshot.CreateTrackingSpan(span.Value.ToSpan(), SpanTrackingMode.EdgeExclusive); if (!leafUpdated) { var baseStatements = _editSession.BaseActiveStatements[id.DocumentId]; leafUpdated = (baseStatements[id.Ordinal].Flags & ActiveStatementFlags.LeafFrame) != 0; } updated = true; } } } } if (updated) { _service.OnTrackingSpansChanged(leafUpdated); } }
private static ITextSnapshot GetRoslynSnapshot(SourceText sourceText) => sourceText.FindCorrespondingEditorTextSnapshot();
/// <summary> /// We do all mutation work on the UI thread. That ensures that all mutation /// is processed serially *and* it means we can safely examine things like /// subject buffers and open documents without any threat of race conditions. /// Note that we do not do any expensive work here. We just store (or remove) /// the data we were given in appropriate buckets. /// /// For example, we create a TaggerProvider per unique DiagnosticUpdatedArgs.Id /// we get. So if we see a new id we simply create a tagger for it and pass it /// these args to store. Otherwise we pass these args to the existing tagger. /// /// Similarly, clearing out data is just a matter of us clearing our reference /// to the data. /// </summary> private void OnDiagnosticsUpdatedOnForeground(DiagnosticsUpdatedArgs e) { this.AssertIsForeground(); if (_disposed) { return; } // Do some quick checks to avoid doing any further work for diagnostics we don't // care about. var ourDocument = _subjectBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); var ourDocumentId = ourDocument?.Id; if (ourDocumentId != _currentDocumentId) { // Our buffer has started tracking some other document entirely. // We have to clear out all of the diagnostics we have currently stored. RemoveAllCachedDiagnostics(); } _currentDocumentId = ourDocumentId; // Now see if the document we're tracking corresponds to the diagnostics // we're hearing about. If not, just ignore them. if (ourDocument == null || ourDocument.Project.Solution.Workspace != e.Workspace || ourDocument.Id != e.DocumentId) { return; } // We're hearing about diagnostics for our document. We may be hearing // about new diagnostics coming, or existing diagnostics being cleared // out. // First see if this is a document/project removal. If so, clear out any state we // have associated with any analyzers we have for that document/project. ProcessRemovedDiagnostics(e); // Make sure we can find an editor snapshot for these errors. Otherwise we won't // be able to make ITagSpans for them. If we can't, just bail out. This happens // when the solution crawler is very far behind. However, it will have a more // up to date document within it that it will eventually process. Until then // we just keep around the stale tags we have. // // Note: if the Solution or Document is null here, then that means the document // was removed. In that case, we do want to proceed so that we'll produce 0 // tags and we'll update the editor appropriately. SourceText sourceText = null; ITextSnapshot editorSnapshot = null; if (e.Solution != null) { var diagnosticDocument = e.Solution.GetDocument(e.DocumentId); if (diagnosticDocument != null) { if (!diagnosticDocument.TryGetText(out sourceText)) { return; } editorSnapshot = sourceText.FindCorrespondingEditorTextSnapshot(); if (editorSnapshot == null) { return; } // Make sure the editor we've got associated with these diagnostics is the // same one we're a tagger for. It is possible for us to hear about diagnostics // for the *same* Document that are not from the *same* buffer. For example, // say we have the following chain of events: // // Document is opened. // Diagnostics start analyzing. // Document is closed. // Document is opened. // Diagnostics finish and report for document. // // We'll hear about diagnostics for the original Document/Buffer that was // opened. But we'll be trying to apply them to this current Document/Buffer. // That won't work since these will be different buffers (and thus, we won't // be able to map the diagnostic spans appropriately). // // Note: returning here is safe. Because the file is closed/opened, The // Diagnostics Service will reanalyze it. It will then report the new results // which we will hear about and use. if (editorSnapshot.TextBuffer != _subjectBuffer) { return; } } } OnDiagnosticsUpdatedOnForeground(e, sourceText, editorSnapshot); }