internal void RaiseParseInformationUpdated(ParseInformationEventArgs e)
 {
     // RaiseParseInformationUpdated is called inside a lock, but we don't want to raise the event inside that lock.
     // To ensure events are raised in the same order, we always invoke on the main thread.
     SD.MainThread.InvokeAsyncAndForget(delegate {
         if (!LoadSolutionProjectsThread.IsRunning)
         {
             string addition;
             if (e.OldUnresolvedFile == null)
             {
                 addition = " (new)";
             }
             else if (e.NewUnresolvedFile == null)
             {
                 addition = " (removed)";
             }
             else
             {
                 addition = " (updated)";
             }
             LoggingService.Debug("ParseInformationUpdated " + e.FileName + addition);
         }
         ParseInformationUpdated(null, e);
     });
 }
        public void RegisterUnresolvedFile(IProject project, IUnresolvedFile unresolvedFile)
        {
            if (project == null)
            {
                throw new ArgumentNullException("project");
            }
            if (unresolvedFile == null)
            {
                throw new ArgumentNullException("unresolvedFile");
            }
            FreezableHelper.Freeze(unresolvedFile);
            var newParseInfo = new ParseInformation(unresolvedFile, null, false);

            rwLock.EnterWriteLock();
            try {
                int index = FindIndexForProject(project);
                if (index >= 0)
                {
                    currentVersion = null;
                    var args = new ParseInformationEventArgs(project, entries[index].UnresolvedFile, newParseInfo);
                    entries[index] = new ProjectEntry(project, unresolvedFile, null);
                    project.OnParseInformationUpdated(args);
                    parserService.RaiseParseInformationUpdated(args);
                }
            } finally {
                rwLock.ExitWriteLock();
            }
        }
		async void UpdateTick(ParseInformationEventArgs e)
		{
			bool isActive = ctl.IsVisible && !SD.ParserService.LoadSolutionProjectsThread.IsRunning;
			timer.IsEnabled = isActive;
			if (!isActive) return;
			LoggingService.Debug("DefinitionViewPad.Update");
			
			ResolveResult res = await ResolveAtCaretAsync(e);
			if (res == null) return;
			var pos = res.GetDefinitionRegion();
			if (pos.IsEmpty) return; // TODO : try to decompile?
			OpenFile(pos);
		}
		internal void RaiseParseInformationUpdated(ParseInformationEventArgs e)
		{
			// RaiseParseInformationUpdated is called inside a lock, but we don't want to raise the event inside that lock.
			// To ensure events are raised in the same order, we always invoke on the main thread.
			SD.MainThread.InvokeAsyncAndForget(delegate {
			                                   	if (!LoadSolutionProjectsThread.IsRunning) {
			                                   		string addition;
			                                   		if (e.OldUnresolvedFile == null) {
			                                   			addition = " (new)";
			                                   		} else if (e.NewUnresolvedFile == null) {
			                                   			addition = " (removed)";
			                                   		} else {
			                                   			addition = " (updated)";
			                                   		}
			                                   		LoggingService.Debug("ParseInformationUpdated " + e.FileName + addition);
			                                   	}
			                                   	ParseInformationUpdated(null, e);
			                                   });
		}
		public void RegisterUnresolvedFile(IProject project, IUnresolvedFile unresolvedFile)
		{
			if (project == null)
				throw new ArgumentNullException("project");
			if (unresolvedFile == null)
				throw new ArgumentNullException("unresolvedFile");
			FreezableHelper.Freeze(unresolvedFile);
			var newParseInfo = new ParseInformation(unresolvedFile, null, false);
			lock (this) {
				int index = FindIndexForProject(project);
				if (index >= 0) {
					currentVersion = null;
					var args = new ParseInformationEventArgs(project, entries[index].UnresolvedFile, newParseInfo);
					entries[index] = new ProjectEntry(project, unresolvedFile, null);
					project.OnParseInformationUpdated(args);
					parserService.RaiseParseInformationUpdated(args);
				}
			}
		}
		ProjectEntry DoParse(ITextSource fileContent, IProject parentProject, bool fullParseInformationRequested,
		                     CancellationToken cancellationToken)
		{
			if (parser == null)
				return default(ProjectEntry);
			
			if (fileContent == null) {
				// No file content was specified. Because the callers of this method already check for currently open files,
				// we can assume that the file isn't open and simply read it from disk.
				try {
					fileContent = SD.FileService.GetFileContentFromDisk(fileName, cancellationToken);
				} catch (IOException) {
					// It is possible that the file gets deleted/becomes inaccessible while a background parse
					// operation is enqueued, so we have to handle IO exceptions.
					return default(ProjectEntry);
				} catch (UnauthorizedAccessException) {
					return default(ProjectEntry);
				}
			}
			
			ProjectEntry result;
			lock (this) {
				int index = FindIndexForProject(parentProject);
				int versionComparison = CompareVersions(fileContent.Version);
				if (versionComparison > 0 || index < 0) {
					// We're going backwards in time, or are requesting a project that is not an owner
					// for this entry.
					var parseInfo = ParseWithExceptionHandling(fileContent, fullParseInformationRequested, parentProject, cancellationToken);
					FreezableHelper.Freeze(parseInfo.UnresolvedFile);
					return new ProjectEntry(parentProject, parseInfo.UnresolvedFile, parseInfo);
				} else {
					if (versionComparison == 0 && index >= 0) {
						// Ensure we have parse info for the specified project (entry.UnresolvedFile is null for newly registered projects)
						// If full parse info is requested, ensure we have full parse info.
						if (entries[index].UnresolvedFile != null && !(fullParseInformationRequested && entries[index].CachedParseInformation == null)) {
							// We already have the requested version parsed, just return it:
							return entries[index];
						}
					}
				}
				
				ParseInformationEventArgs[] results = new ParseInformationEventArgs[entries.Count];
				for (int i = 0; i < entries.Count; i++) {
					var parseInfo = ParseWithExceptionHandling(fileContent, fullParseInformationRequested, entries[i].Project, cancellationToken);
					if (parseInfo == null)
						throw new NullReferenceException(parser.GetType().Name + ".Parse() returned null");
					if (fullParseInformationRequested && !parseInfo.IsFullParseInformation)
						throw new InvalidOperationException(parser.GetType().Name + ".Parse() did not return full parse info as requested.");
					OnDiskTextSourceVersion onDiskVersion = fileContent.Version as OnDiskTextSourceVersion;
					if (onDiskVersion != null)
						parseInfo.UnresolvedFile.LastWriteTime = onDiskVersion.LastWriteTime;
					FreezableHelper.Freeze(parseInfo.UnresolvedFile);
					results[i] = new ParseInformationEventArgs(entries[i].Project, entries[i].UnresolvedFile, parseInfo);
				}
				
				// Only if all parse runs succeeded, register the parse information.
				currentVersion = fileContent.Version;
				for (int i = 0; i < entries.Count; i++) {
					if (fullParseInformationRequested || (entries[i].CachedParseInformation != null && results[i].NewParseInformation.IsFullParseInformation))
						entries[i] = new ProjectEntry(entries[i].Project, results[i].NewUnresolvedFile, results[i].NewParseInformation);
					else
						entries[i] = new ProjectEntry(entries[i].Project, results[i].NewUnresolvedFile, null);
					if (entries[i].Project != null)
						entries[i].Project.OnParseInformationUpdated(results[i]);
					parserService.RaiseParseInformationUpdated(results[i]);
				}
				result = entries[index];
			}  // exit lock
			parserService.RegisterForCacheExpiry(this);
			return result;
		}
		Task<ResolveResult> ResolveAtCaretAsync(ParseInformationEventArgs e)
		{
			IWorkbenchWindow window = SD.Workbench.ActiveWorkbenchWindow;
			if (window == null)
				return Task.FromResult<ResolveResult>(null);
			IViewContent viewContent = window.ActiveViewContent;
			if (viewContent == null)
				return Task.FromResult<ResolveResult>(null);
			ITextEditor editor = viewContent.GetService<ITextEditor>();
			if (editor == null)
				return Task.FromResult<ResolveResult>(null);
			
			// e might be null when this is a manually triggered update
			// don't resolve when an unrelated file was changed
			if (e != null && editor.FileName != e.FileName)
				return Task.FromResult<ResolveResult>(null);
			
			return SD.ParserService.ResolveAsync(editor.FileName, editor.Caret.Location, editor.Document);
		}
		void OnParserUpdateStep(object sender, ParseInformationEventArgs e)
		{
			UpdateTick(e);
		}
        ProjectEntry DoParse(ITextSource fileContent, IProject parentProject, bool fullParseInformationRequested,
                             CancellationToken cancellationToken)
        {
            if (parser == null)
            {
                return(default(ProjectEntry));
            }

            if (fileContent == null)
            {
                // No file content was specified. Because the callers of this method already check for currently open files,
                // we can assume that the file isn't open and simply read it from disk.
                try {
                    fileContent = SD.FileService.GetFileContentFromDisk(fileName, cancellationToken);
                } catch (IOException) {
                    // It is possible that the file gets deleted/becomes inaccessible while a background parse
                    // operation is enqueued, so we have to handle IO exceptions.
                    return(default(ProjectEntry));
                } catch (UnauthorizedAccessException) {
                    return(default(ProjectEntry));
                }
            }

            ProjectEntry result;

            rwLock.EnterUpgradeableReadLock();
            try {
                int index             = FindIndexForProject(parentProject);
                int versionComparison = CompareVersions(fileContent.Version);
                if (versionComparison > 0 || index < 0)
                {
                    // We're going backwards in time, or are requesting a project that is not an owner
                    // for this entry.
                    var parseInfo = ParseWithExceptionHandling(fileContent, fullParseInformationRequested, parentProject, cancellationToken);
                    FreezableHelper.Freeze(parseInfo.UnresolvedFile);
                    return(new ProjectEntry(parentProject, parseInfo.UnresolvedFile, parseInfo));
                }
                else
                {
                    if (versionComparison == 0 && index >= 0)
                    {
                        // Ensure we have parse info for the specified project (entry.UnresolvedFile is null for newly registered projects)
                        // If full parse info is requested, ensure we have full parse info.
                        if (entries[index].UnresolvedFile != null && !(fullParseInformationRequested && entries[index].CachedParseInformation == null))
                        {
                            // We already have the requested version parsed, just return it:
                            return(entries[index]);
                        }
                    }
                }

                ParseInformationEventArgs[] results = new ParseInformationEventArgs[entries.Count];
                for (int i = 0; i < entries.Count; i++)
                {
                    var parseInfo = ParseWithExceptionHandling(fileContent, fullParseInformationRequested, entries[i].Project, cancellationToken);
                    if (parseInfo == null)
                    {
                        throw new NullReferenceException(parser.GetType().Name + ".Parse() returned null");
                    }
                    if (fullParseInformationRequested && !parseInfo.IsFullParseInformation)
                    {
                        throw new InvalidOperationException(parser.GetType().Name + ".Parse() did not return full parse info as requested.");
                    }
                    OnDiskTextSourceVersion onDiskVersion = fileContent.Version as OnDiskTextSourceVersion;
                    if (onDiskVersion != null)
                    {
                        parseInfo.UnresolvedFile.LastWriteTime = onDiskVersion.LastWriteTime;
                    }
                    FreezableHelper.Freeze(parseInfo.UnresolvedFile);
                    results[i] = new ParseInformationEventArgs(entries[i].Project, entries[i].UnresolvedFile, parseInfo);
                }

                // Only if all parse runs succeeded, register the parse information.
                rwLock.EnterWriteLock();
                try {
                    currentVersion = fileContent.Version;
                    for (int i = 0; i < entries.Count; i++)
                    {
                        if (fullParseInformationRequested || (entries[i].CachedParseInformation != null && results[i].NewParseInformation.IsFullParseInformation))
                        {
                            entries[i] = new ProjectEntry(entries[i].Project, results[i].NewUnresolvedFile, results[i].NewParseInformation);
                        }
                        else
                        {
                            entries[i] = new ProjectEntry(entries[i].Project, results[i].NewUnresolvedFile, null);
                        }
                        if (entries[i].Project != null)
                        {
                            entries[i].Project.OnParseInformationUpdated(results[i]);
                        }
                        parserService.RaiseParseInformationUpdated(results[i]);
                    }
                    result = entries[index];
                } finally {
                    rwLock.ExitWriteLock();
                }
            } finally {
                rwLock.ExitUpgradeableReadLock();
            }
            parserService.RegisterForCacheExpiry(this);
            return(result);
        }
		public override void OnParseInformationUpdated(ParseInformationEventArgs args)
		{
			var c = projectContentContainer;
			if (c != null)
				c.ParseInformationUpdated(args.OldUnresolvedFile, args.NewUnresolvedFile);
			// OnParseInformationUpdated is called inside a lock, but we don't want to raise the event inside that lock.
			// To ensure events are raised in the same order, we always invoke on the main thread.
			SD.MainThread.InvokeAsyncAndForget(delegate { ParseInformationUpdated(null, args); });
		}