/// <summary> /// Clears all tracked buffers for the given project entry and moniker for /// the error source. /// </summary> public void ClearErrorSource(AnalysisEntry entry, string moniker) { Clear(entry, moniker); lock (_errorSources) { _errorSources.Remove(new EntryKey(entry, moniker)); } }
public ExpressionAtPoint(AnalysisEntry entry, string text, ITrackingSpan span, SourceSpan sourceSpan) { Entry = entry; Text = text; Span = span; SourceSpan = sourceSpan; }
internal static async Task ParseBuffersAsync( PythonEditorServices services, AnalysisEntry entry, IEnumerable <ITextSnapshot> snapshots ) { var updates = snapshots.Select(s => GetUpdateForSnapshot(services, s)).Where(u => u != null).ToList(); if (!updates.Any()) { return; } entry.Analyzer._analysisComplete = false; Interlocked.Increment(ref entry.Analyzer._parsePending); var res = await entry.Analyzer.SendRequestAsync( new AP.FileUpdateRequest() { fileId = entry.FileId, updates = updates.ToArray() } ); if (res != null) { Debug.Assert(res.failed != true); entry.Analyzer.OnAnalysisStarted(); ValidateBufferContents(snapshots, res.newCode); } else { Interlocked.Decrement(ref entry.Analyzer._parsePending); } }
private async void OnNewAnalysis(AnalysisEntry entry, ITextBuffer buffer) { if (!_enabled && !_alwaysCreateSquiggle) { _taskProvider.Clear(entry, VsProjectAnalyzer.UnresolvedImportMoniker); return; } var missingImports = await entry.Analyzer.GetMissingImportsAsync(entry, buffer); if (missingImports != null) { var missing = missingImports.Data; if (missing.unresolved.Any()) { var translator = missingImports.GetTracker(missingImports.Data.version); if (translator != null) { var f = new TaskProviderItemFactory(translator); _taskProvider.ReplaceItems( entry, VsProjectAnalyzer.UnresolvedImportMoniker, missingImports.Data.unresolved.Select(t => f.FromUnresolvedImport( _serviceProvider, entry.Analyzer.InterpreterFactory as IPythonInterpreterFactoryWithDatabase, t.name, new SourceSpan( new SourceLocation(t.startIndex, t.startLine, t.startColumn), new SourceLocation(t.endIndex, t.endLine, t.endColumn) ) )).ToList() ); } } else { _taskProvider.Clear(entry, VsProjectAnalyzer.UnresolvedImportMoniker); } } }
/// <summary> /// Gets the analysis entry for the given view. /// /// For files on disk this is pretty easy - we analyze each file on it's own in a buffer parser. /// Therefore we map filename -> analyzer and then get the analysis from the analyzer. If we /// determine an analyzer but the file isn't loaded into it for some reason this would return null. /// We can also apply some policy to buffers depending upon the view that they're hosted in. For /// example if a buffer is outside of any projects, but hosted in a difference view with a buffer /// that is in a project, then we'll look in the view that has the project. /// /// For interactive windows we will use the analyzer that's configured for the window. /// </summary> public bool TryGetAnalysisEntry(ITextView textView, out AnalysisEntry entry) { if (textView == null) { throw new ArgumentNullException(nameof(textView)); } if (TryGetAnalysisEntry(textView.TextBuffer, out entry)) { return(true); } if (textView != null) { // If we have a difference viewer we'll match the LHS w/ the RHS var viewer = _diffService?.TryGetViewerForTextView(textView); if (viewer != null) { if (TryGetAnalysisEntry(viewer.DifferenceBuffer.RightBuffer, out entry)) { return(true); } if (TryGetAnalysisEntry(viewer.DifferenceBuffer.LeftBuffer, out entry)) { return(true); } } } entry = null; return(false); }
private IEnumerable <CompletionResult> GetAvailableCompletions(AnalysisEntry analysis, SnapshotPoint point) { var analyzer = analysis.Analyzer; lock (analyzer) { var location = VsProjectAnalyzer.TranslateIndex( point.Position, point.Snapshot, analysis ); var parameters = Enumerable.Empty <CompletionResult>(); var sigs = analyzer.WaitForRequest(analyzer.GetSignaturesAsync(analysis, View, _snapshot, Span), "GetCompletions.GetSignatures"); if (sigs != null && sigs.Signatures.Any()) { parameters = sigs.Signatures .SelectMany(s => s.Parameters) .Select(p => p.Name) .Distinct() .Select(n => new CompletionResult(n, PythonMemberType.Field)); } return(analyzer.WaitForRequest(analyzer.GetAllAvailableMembersAsync(analysis, location, _options.MemberOptions), "GetCompletions.GetAllAvailableMembers") .MaybeEnumerate() .Union(parameters, CompletionComparer.MemberEquality)); } }
public ExpressionAtPoint(AnalysisEntry entry, string text, ITrackingSpan span, SourceLocation location) { Entry = entry; Text = text; Span = span; Location = location; }
public void ListenForNextNewAnalysis(AnalysisEntry entry, ITextBuffer buffer) { if (entry != null && !string.IsNullOrEmpty(entry.Path)) { buffer.RegisterForNewAnalysis(newEntry => OnNewAnalysis(newEntry, buffer)); if (entry.IsAnalyzed) { OnNewAnalysis(entry, buffer); } } }
public BufferParser(AnalysisEntry entry) { Debug.Assert(entry != null); _services = entry.Analyzer._services; _timer = new Timer(ReparseTimer, null, Timeout.Infinite, Timeout.Infinite); _buffers = Array.Empty <PythonTextBufferInfo>(); AnalysisEntry = entry; }
public static async Task<BufferParser> CreateAsync(AnalysisEntry analysis, VsProjectAnalyzer parser, ITextBuffer buffer) { var res = new BufferParser(analysis, parser, buffer); using (new DebugTimer("BufferParser.ParseBuffers", 100)) { // lock not necessary for _bufferInfo, no one has access to us yet... await res.ParseBuffers(new[] { buffer.CurrentSnapshot }, new[] { res._bufferInfo[buffer] }); } return res; }
internal static async Task <BufferParser> CreateAsync(AnalysisEntry analysis, VsProjectAnalyzer parser, ITextBuffer buffer) { var res = new BufferParser(analysis, parser, buffer); using (new DebugTimer("BufferParser.ParseBuffers", 100)) { // lock not necessary for _bufferInfo, no one has access to us yet... await res.ParseBuffers(new[] { buffer.CurrentSnapshot }, new[] { res._bufferInfo[buffer] }); } return(res); }
private BufferParser(AnalysisEntry analysis, VsProjectAnalyzer parser, ITextBuffer buffer) { Debug.Assert(analysis != null); _parser = parser; _timer = new Timer(ReparseTimer, null, Timeout.Infinite, Timeout.Infinite); _buffers = new[] { buffer }; AnalysisEntry = analysis; InitBuffer(buffer, 0); }
/// <summary> /// Removes the buffer from tracking for reporting squiggles and error list entries /// for the given project entry and moniker for the error source. /// </summary> public void RemoveBufferForErrorSource(AnalysisEntry entry, string moniker, ITextBuffer buffer) { lock (_errorSources) { var key = new EntryKey(entry, moniker); HashSet <ITextBuffer> buffers; if (_errorSources.TryGetValue(key, out buffers)) { buffers.Remove(buffer); } } }
protected override async Task OnNewAnalysis(PythonTextBufferInfo bi, AnalysisEntry entry) { if (!Enabled && !_alwaysCreateSquiggle || bi?.Document == null || bi.Buffer?.Properties == null) { TaskProvider.Clear(bi.Filename, VsProjectAnalyzer.InvalidEncodingMoniker); return; } var snapshot = bi.CurrentSnapshot; var message = CheckEncoding(snapshot, bi.Document.Encoding, out var magicEncodingName, out var magicEncodingIndex); if (message != null) { if (!bi.Buffer.Properties.TryGetProperty <string>(VsProjectAnalyzer.InvalidEncodingMoniker, out var prevMessage) || prevMessage != message) { bi.Buffer.Properties[VsProjectAnalyzer.InvalidEncodingMoniker] = message; SourceSpan span; if (string.IsNullOrEmpty(magicEncodingName)) { var pt = new SnapshotPoint(snapshot, magicEncodingIndex).ToSourceLocation(); span = new SourceSpan(pt, new SourceLocation(pt.Line, int.MaxValue)); } else { span = new SnapshotSpan(snapshot, magicEncodingIndex, magicEncodingName.Length).ToSourceSpan(); } TaskProvider.ReplaceItems( bi.Filename, VsProjectAnalyzer.InvalidEncodingMoniker, new List <TaskProviderItem> { new TaskProviderItem( Services.Site, VsProjectAnalyzer.InvalidEncodingMoniker, message, span, VSTASKPRIORITY.TP_NORMAL, VSTASKCATEGORY.CAT_CODESENSE, true, bi.LocationTracker, snapshot.Version.VersionNumber ) }); } } else { TaskProvider.Clear(bi.Filename, VsProjectAnalyzer.InvalidEncodingMoniker); bi.Buffer.Properties.RemoveProperty(VsProjectAnalyzer.InvalidEncodingMoniker); } }
/// <summary> /// Adds the buffer to be tracked for reporting squiggles and error list entries /// for the given project entry and moniker for the error source. /// </summary> public void AddBufferForErrorSource(AnalysisEntry entry, string moniker, ITextBuffer buffer) { lock (_errorSources) { var key = new EntryKey(entry, moniker); HashSet <ITextBuffer> buffers; if (!_errorSources.TryGetValue(key, out buffers)) { _errorSources[new EntryKey(entry, moniker)] = buffers = new HashSet <ITextBuffer>(); } buffers.Add(buffer); } }
private BufferParser(AnalysisEntry analysis, VsProjectAnalyzer parser, ITextBuffer buffer) { Debug.Assert(analysis != null); _parser = parser; _timer = new Timer(ReparseTimer, null, Timeout.Infinite, Timeout.Infinite); _buffers = new[] { buffer }; AnalysisEntry = analysis; analysis.BufferParser = this; InitBuffer(buffer, 0); }
private const int ReparseDelay = 1000; // delay in MS before we re-parse a buffer w/ non-line changes. public BufferParser(AnalysisEntry analysis, VsProjectAnalyzer parser, ITextBuffer buffer) { _parser = parser; _timer = new Timer(ReparseTimer, null, Timeout.Infinite, Timeout.Infinite); _buffers = new[] { buffer }; _analysis = analysis; AttachedViews = 1; analysis.BufferParser = this; InitBuffer(buffer, 0); // lock not necessary for _bufferInfo, no one has access to us yet... ParseBuffers(new[] { buffer.CurrentSnapshot }, new[] { _bufferInfo[buffer] }).DoNotWait(); }
public BufferParser(AnalysisEntry analysis, VsProjectAnalyzer parser, ITextBuffer buffer) { Debug.Assert(analysis != null); _parser = parser; _timer = new Timer(ReparseTimer, null, Timeout.Infinite, Timeout.Infinite); _buffers = new[] { buffer }; AnalysisEntry = analysis; analysis.BufferParser = this; InitBuffer(buffer, 0); // lock not necessary for _bufferInfo, no one has access to us yet... ParseBuffers(new[] { buffer.CurrentSnapshot }, new[] { _bufferInfo[buffer] }).DoNotWait(); }
public DropDownBarClient(IServiceProvider serviceProvider, IWpfTextView textView, AnalysisEntry analysisEntry) { Utilities.ArgumentNotNull(nameof(serviceProvider), serviceProvider); Utilities.ArgumentNotNull(nameof(textView), textView); Utilities.ArgumentNotNull(nameof(analysisEntry), analysisEntry); _serviceProvider = serviceProvider; _uiThread = _serviceProvider.GetUIThread(); _analysisEntry = analysisEntry; textView.TextBuffer.RegisterForParseTree(ParserOnNewParseTree); _textView = textView; _dispatcher = Dispatcher.CurrentDispatcher; _textView.Caret.PositionChanged += CaretPositionChanged; for (int i = 0; i < NavigationLevels; i++) { _curSelection[i] = -1; } }
private void NewAnalysisEntry(AnalysisEntry entry) { var analyzer = entry.Analyzer; var newVersion = _version; if (newVersion != _version) { _tokenCache.Clear(); Debug.Assert(analyzer != null); _version = analyzer.InterpreterFactory.GetLanguageVersion(); var changed = ClassificationChanged; if (changed != null) { var snapshot = _buffer.CurrentSnapshot; changed(this, new ClassificationChangedEventArgs(new SnapshotSpan(snapshot, 0, snapshot.Length))); } } }
private async void OnNewAnalysis(AnalysisEntry entry, ITextBuffer buffer) { if (!_alwaysCreateSquiggle) { var service = _serviceProvider.GetPythonToolsService(); if (service == null || !service.GeneralOptions.UnresolvedImportWarning) { return; } } var missingImports = await entry.Analyzer.GetMissingImportsAsync(entry, buffer); if (missingImports != null) { var missing = missingImports.Data; if (missing.unresolved.Any()) { var translator = missingImports.GetTracker(missingImports.Data.version); if (translator != null) { var f = new TaskProviderItemFactory(translator); _taskProvider.ReplaceItems( entry, VsProjectAnalyzer.UnresolvedImportMoniker, missingImports.Data.unresolved.Select(t => f.FromUnresolvedImport( _serviceProvider, entry.Analyzer.InterpreterFactory as IPythonInterpreterFactoryWithDatabase, t.name, new SourceSpan( new SourceLocation(t.startIndex, t.startLine, t.startColumn), new SourceLocation(t.endIndex, t.endLine, t.endColumn) ) )).ToList() ); } } else { _taskProvider.Clear(entry, VsProjectAnalyzer.UnresolvedImportMoniker); } } }
/// <summary> /// Gets the analysis entry for the given buffer. /// /// This will only succeed if the buffer is a file on disk. It is not able to support things like /// difference views because we don't know what view this buffer is hosted in. This method should /// only be used when we don't know the current view for the buffer. Otherwise, use /// <see cref="TryGetAnalysisEntry(ITextView, ITextBuffer, out AnalysisEntry)"/> /// </summary> public bool TryGetAnalysisEntry(ITextBuffer textBuffer, out AnalysisEntry entry) { if (textBuffer == null) { entry = null; return(false); } // If we have a REPL evaluator we'll use its analyzer IPythonInteractiveIntellisense evaluator; if ((evaluator = textBuffer.GetInteractiveWindow()?.Evaluator as IPythonInteractiveIntellisense) != null) { entry = evaluator.Analyzer?.GetAnalysisEntryFromPath(evaluator.AnalysisFilename); if (entry != null) { return(true); } } var bufferInfo = PythonTextBufferInfo.TryGetForBuffer(textBuffer); if (bufferInfo?.AnalysisEntry != null) { entry = bufferInfo.AnalysisEntry; return(true); } // Fall back on finding the analyzer and path separately ProjectAnalyzer analyzer; string path; if (TryGetAnalyzer(textBuffer, out analyzer, out path)) { entry = (analyzer as VsProjectAnalyzer)?.GetAnalysisEntryFromPath(path); if (entry != null) { return(true); } } entry = null; return(false); }
protected override async Task OnNewAnalysis(PythonTextBufferInfo bi, AnalysisEntry entry) { if (!Enabled && !_alwaysCreateSquiggle || entry == null) { TaskProvider.Clear(bi.Filename, VsProjectAnalyzer.UnresolvedImportMoniker); return; } var missingImports = await entry.Analyzer.GetMissingImportsAsync(entry, bi.Buffer); if (missingImports == null) { return; } if (missingImports.Data.unresolved.Any()) { var translator = missingImports.GetTracker(missingImports.Data.version); if (translator != null) { var f = new TaskProviderItemFactory(translator); TaskProvider.ReplaceItems( bi.Filename, VsProjectAnalyzer.UnresolvedImportMoniker, missingImports.Data.unresolved.Select(t => f.FromUnresolvedImport( Services.Site, entry.Analyzer.InterpreterFactory as IPythonInterpreterFactoryWithDatabase, t.name, new SourceSpan( new SourceLocation(t.startLine, t.startColumn), new SourceLocation(t.endLine, t.endColumn) ) )).ToList() ); } } else { TaskProvider.Clear(bi.Filename, VsProjectAnalyzer.UnresolvedImportMoniker); } }
private async void OnNewAnalysis(AnalysisEntry entry) { if (!_provider._colorNames) { bool raise = false; lock (_spanCacheLock) { if (_spanCache != null) { _spanCache = null; raise = true; } } if (raise) { OnNewClassifications(_buffer.CurrentSnapshot); } return; } var classifications = await entry.Analyzer.GetAnalysisClassificationsAsync( entry, _buffer, _provider._colorNamesWithAnalysis ); if (classifications != null) { Debug.WriteLine("Received {0} classifications", classifications.Data.classifications.Length); // sort the spans by starting position so we can use binary search when handing them out Array.Sort( classifications.Data.classifications, (x, y) => x.start - y.start ); lock (_spanCacheLock) { _spanCache = classifications.Data.classifications; _spanTranslator = classifications.GetTracker(classifications.Data.version); } if (_spanTranslator != null) { OnNewClassifications(_buffer.CurrentSnapshot); } } }
/// <summary> /// Gets the analysis entry for the given view and buffer. /// /// For files on disk this is pretty easy - we analyze each file on it's own in a buffer parser. /// Therefore we map filename -> analyzer and then get the analysis from the analyzer. If we /// determine an analyzer but the file isn't loaded into it for some reason this would return null. /// We can also apply some policy to buffers depending upon the view that they're hosted in. For /// example if a buffer is outside of any projects, but hosted in a difference view with a buffer /// that is in a project, then we'll look in the view that has the project. /// /// For interactive windows we will use the analyzer that's configured for the window. /// </summary> public bool TryGetAnalysisEntry(ITextView textView, ITextBuffer textBuffer, out AnalysisEntry entry) { ProjectAnalyzer analyzer; string filename; if ((textView == null || !TryGetAnalyzer(textView, out analyzer, out filename)) && !TryGetAnalyzer(textBuffer, out analyzer, out filename)) { entry = null; return(false); } var vsAnalyzer = analyzer as VsProjectAnalyzer; if (vsAnalyzer != null) { entry = vsAnalyzer.GetAnalysisEntryFromPath(filename); return(entry != null); } entry = null; return(false); }
//public void SetAnalyzer(ITextBuffer textBuffer, VsProjectAnalyzer analyzer) { // if (textBuffer == null) { // throw new ArgumentNullException(nameof(textBuffer)); // } // // if (analyzer == null) { // textBuffer.Properties.RemoveProperty(typeof(VsProjectAnalyzer)); // return; // } // // textBuffer.Properties[typeof(VsProjectAnalyzer)] = analyzer; // // TaskCompletionSource<object> tcs; // if (textBuffer.Properties.TryGetProperty(_waitForAnalyzerKey, out tcs)) { // tcs.TrySetResult(null); // textBuffer.Properties.RemoveProperty(_waitForAnalyzerKey); // } //} /// <summary> /// Gets the analysis entry for the given view and buffer. /// /// For files on disk this is pretty easy - we analyze each file on it's own in a buffer parser. /// Therefore we map filename -> analyzer and then get the analysis from the analyzer. If we /// determine an analyzer but the file isn't loaded into it for some reason this would return null. /// We can also apply some policy to buffers depending upon the view that they're hosted in. For /// example if a buffer is outside of any projects, but hosted in a difference view with a buffer /// that is in a project, then we'll look in the view that has the project. /// /// For interactive windows we will use the analyzer that's configured for the window. /// </summary> public bool TryGetAnalysisEntry(ITextView textView, ITextBuffer textBuffer, out AnalysisEntry entry) { var bi = PythonTextBufferInfo.TryGetForBuffer(textBuffer ?? textView?.TextBuffer); if (bi == null && textView != null) { // If we have a difference viewer we'll match the LHS w/ the RHS var viewer = _diffService?.TryGetViewerForTextView(textView); if (viewer != null) { if (TryGetAnalysisEntry(viewer.DifferenceBuffer.RightBuffer, out entry)) { return(true); } if (TryGetAnalysisEntry(viewer.DifferenceBuffer.LeftBuffer, out entry)) { return(true); } } } entry = bi?.AnalysisEntry; return(entry != null); }
public EntryEventArgs(AnalysisEntry entry) { Entry = entry; }
/// <summary> /// Removes all items for the specified entry. /// </summary> public void Clear(AnalysisEntry entry, string moniker) { SendMessage(WorkerMessage.Clear(entry, moniker)); }
/// <summary> /// Adds items to the specified entry's existing items. /// </summary> public void AddItems(AnalysisEntry entry, string moniker, List <TaskProviderItem> items) { SendMessage(WorkerMessage.Append(entry, moniker, items)); }
/// <summary> /// Replaces the items for the specified entry. /// </summary> public void ReplaceItems(AnalysisEntry entry, string moniker, List <TaskProviderItem> items) { SendMessage(WorkerMessage.Replace(entry, moniker, items)); }
public static WorkerMessage Append(AnalysisEntry entry, string moniker, List <TaskProviderItem> items) { return(new AppendMessage(new EntryKey(entry, moniker), items)); }
public static WorkerMessage Clear(AnalysisEntry entry, string moniker) { return(new ClearMessage(new EntryKey(entry, moniker))); }
public EntryKey(AnalysisEntry entry, string moniker) { Entry = entry; Moniker = moniker; }
public MonitoredBufferResult(BufferParser bufferParser, AnalysisEntry projectEntry) { BufferParser = bufferParser; AnalysisEntry = projectEntry; }
public ExtractedMethodCreator(VsProjectAnalyzer analyzer, AnalysisEntry file, ITextView view, ITextBuffer buffer, AP.ExtractMethodResponse initialExtraction) { _analyzer = analyzer; _analysisEntry = file; _view = view; _buffer = buffer; LastExtraction = initialExtraction; }
protected abstract Task OnNewAnalysis(PythonTextBufferInfo bi, AnalysisEntry entry);
private void OnNewAnalysisEntry(AnalysisEntry obj) { SuggestedActionsChanged?.Invoke(this, EventArgs.Empty); }
/// <summary> /// Wired to parser event for when the parser has completed parsing a new tree and we need /// to update the navigation bar with the new data. /// </summary> private async void ParserOnNewParseTree(AnalysisEntry entry) { var dropDownBar = _dropDownBar; if (dropDownBar == null) { return; } var navigations = await _uiThread.InvokeTask(() => _analysisEntry.Analyzer.GetNavigationsAsync(_textView)); lock (_navigationsLock) { _navigations = navigations; for (int i = 0; i < _curSelection.Length; i++) { _curSelection[i] = -1; } } Action callback = () => CaretPositionChanged( this, new CaretPositionChangedEventArgs( _textView, _textView.Caret.Position, _textView.Caret.Position ) ); try { await _dispatcher.BeginInvoke(callback, DispatcherPriority.Background); } catch (TaskCanceledException) { } }
private async Task ParseBuffers(ITextSnapshot[] snapshots, BufferInfo[] bufferInfos) { var indentationSeverity = _parser.PyService.GeneralOptions.IndentationInconsistencySeverity; AnalysisEntry entry = AnalysisEntry; List <AP.FileUpdate> updates = new List <AP.FileUpdate>(); lock (this) { for (int i = 0; i < snapshots.Length; i++) { var snapshot = snapshots[i]; var bufferInfo = bufferInfos[i]; if (snapshot.TextBuffer.Properties.ContainsProperty(DoNotParse) || snapshot.IsReplBufferWithCommand()) { continue; } var lastSent = GetLastSentSnapshot(bufferInfo.Buffer); if (lastSent == null || lastSent.TextBuffer != snapshot.TextBuffer) { // First time parsing from a live buffer, send the entire // file and set our initial snapshot. We'll roll forward // to new snapshots when we receive the errors event. This // just makes sure that the content is in sync. updates.Add( new AP.FileUpdate() { content = snapshot.GetText(), version = snapshot.Version.VersionNumber, bufferId = bufferInfo.Id, kind = AP.FileUpdateKind.reset } ); } else { if (lastSent.Version == snapshot.Version) { // this snapshot is up to date... continue; } List <AP.VersionChanges> versions = new List <AnalysisProtocol.VersionChanges>(); for (var curVersion = lastSent.Version; curVersion != snapshot.Version; curVersion = curVersion.Next) { versions.Add( new AP.VersionChanges() { changes = GetChanges(curVersion) } ); } updates.Add( new AP.FileUpdate() { versions = versions.ToArray(), version = snapshot.Version.VersionNumber, bufferId = bufferInfo.Id, kind = AP.FileUpdateKind.changes } ); } Debug.WriteLine("Added parse request {0}", snapshot.Version.VersionNumber); entry.AnalysisCookie = new SnapshotCookie(snapshot); // TODO: What about multiple snapshots? SetLastSentSnapshot(snapshot); } } if (updates.Count != 0) { _parser._analysisComplete = false; Interlocked.Increment(ref _parser._parsePending); var res = await _parser.SendRequestAsync( new AP.FileUpdateRequest() { fileId = entry.FileId, updates = updates.ToArray() } ); if (res != null) { Debug.Assert(res.failed != true); _parser.OnAnalysisStarted(); #if DEBUG for (int i = 0; i < bufferInfos.Length; i++) { var snapshot = snapshots[i]; var buffer = bufferInfos[i]; string newCode; if (res.newCode.TryGetValue(buffer.Id, out newCode)) { Debug.Assert(newCode == snapshot.GetText(), "Buffer content mismatch - safe to ignore"); } } #endif } else { Interlocked.Decrement(ref _parser._parsePending); } } }