/// <summary> /// Idle time event handler. Retrieves results from the validation task queue, /// creates new error tags (squiggles) and fires an event telling editor that /// tags changed. The code must operate on UI thread and hence it is called on idle. /// </summary> /// <param name="sender"></param> /// <param name="eventArgs"></param> private void OnIdle(object sender, EventArgs eventArgs) { if (_settings.SyntaxCheckEnabled && _textBuffer != null) { if (_resultsQueue.Count > 0) { var addedTags = new List <IEditorTaskListItem>(); var changedRange = _errorTags.BeginUpdate(); changedRange = TextRange.Intersection(changedRange, 0, _textBuffer.CurrentSnapshot.Length); var timer = Stopwatch.StartNew(); timer.Reset(); while (timer.ElapsedMilliseconds < 100) { if (!_resultsQueue.TryDequeue(out var error)) { break; } if (string.IsNullOrEmpty(error.Message)) { // Empty message means remove all errors. changedRange = new TextRange(0, _textBuffer.CurrentSnapshot.Length); } else { var tag = new EditorErrorTag(_document.EditorTree, error); if (tag.Length > 0) { if (changedRange.End == 0) { changedRange = tag; } else { changedRange = changedRange.Union(tag); } _errorTags.Add(tag); addedTags.Add(tag); } } } _errorTags.EndUpdate(changedRange.Length > 0); // Clip range to the current snapshot var start = Math.Max(changedRange.Start, 0); start = Math.Min(start, _textBuffer.CurrentSnapshot.Length); var end = Math.Min(changedRange.End, _textBuffer.CurrentSnapshot.Length); if (changedRange.Length > 0) { TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(_textBuffer.CurrentSnapshot, start, end - start))); } BeginUpdatingTasks?.Invoke(this, EventArgs.Empty); try { if (addedTags.Count > 0) { TasksAdded?.Invoke(this, new TasksListItemsChangedEventArgs(addedTags)); } if (_errorTags.RemovedTags.Count > 0) { var removedTags = new List <IEditorTaskListItem>(); while (_errorTags.RemovedTags.Count > 0) { if (_errorTags.RemovedTags.TryDequeue(out var tag)) { removedTags.Add(tag); } } if (removedTags.Count > 0) { TasksRemoved?.Invoke(this, new TasksListItemsChangedEventArgs(removedTags)); } } } finally { EndUpdatingTasks?.Invoke(this, EventArgs.Empty); } timer.Stop(); } } }
/// <summary> /// Idle time event handler. Retrieves results from the validation task queue, /// creates new error tags (squiggles) and fires an event telling editor that /// tags changed. The code must operate on UI thread and hence it is called on idle. /// </summary> /// <param name="sender"></param> /// <param name="eventArgs"></param> private void OnIdle(object sender, EventArgs eventArgs) { if (_settings.SyntaxCheckEnabled && _textBuffer != null) { if (ResultsQueue.Count > 0) { _fireCodeMarkerUponCompletion = true; var addedTags = new List <IEditorTaskListItem>(); var changedRange = _errorTags.BeginUpdate(); changedRange = TextRange.Intersection(changedRange, 0, _textBuffer.CurrentSnapshot.Length); var timer = Stopwatch.StartNew(); timer.Reset(); while (timer.ElapsedMilliseconds < 100) { if (!ResultsQueue.TryDequeue(out var error)) { break; } if (string.IsNullOrEmpty(error.Message)) { // Empty message means remove all error for the element. var removedRange = TextRange.EmptyRange; // _errorTags.RemoveTagsForNode(error.NodeKey); // Only update changedRange if there were errors removed if (removedRange.End > 0) { if (changedRange.End == 0) { changedRange = removedRange; } else { changedRange = changedRange.Union(removedRange); } } } else { var tag = new EditorErrorTag(_document.EditorTree, error); if (tag.Length > 0) { if (changedRange.End == 0) { changedRange = tag; } else { changedRange = changedRange.Union(tag); } _errorTags.Add(tag); addedTags.Add(tag); } } } _errorTags.EndUpdate(changedRange.Length > 0); // Clip range to the current snapshot var start = Math.Max(changedRange.Start, 0); start = Math.Min(start, _textBuffer.CurrentSnapshot.Length); var end = Math.Min(changedRange.End, _textBuffer.CurrentSnapshot.Length); if (changedRange.Length > 0) { TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(_textBuffer.CurrentSnapshot, start, end - start))); } BeginUpdatingTasks?.Invoke(this, EventArgs.Empty); try { if (addedTags.Count > 0) { TasksAdded?.Invoke(this, new TasksListItemsChangedEventArgs(addedTags)); } if (_errorTags.RemovedTags.Count > 0) { var removedTags = new List <IEditorTaskListItem>(); while (_errorTags.RemovedTags.Count > 0) { if (_errorTags.RemovedTags.TryDequeue(out var tag)) { removedTags.Add(tag); } } if (removedTags.Count > 0) { TasksRemoved?.Invoke(this, new TasksListItemsChangedEventArgs(removedTags)); } } } finally { EndUpdatingTasks?.Invoke(this, EventArgs.Empty); } timer.Stop(); } if (_fireCodeMarkerUponCompletion && (ResultsQueue.Count == 0)) { // Use this flag so we don't incessantly fire this code marker on every idle. // TODO: Even this isn't quite correct, as it's possible that a validator // yet pushed all it's entries into the results queue. There should really // be a notification from the validators to indicate their completeness. If there // were such a notification, then we could actually even unhook ourselves from idle. _fireCodeMarkerUponCompletion = false; } } }