Browse Source

Register parsed files in project content.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
5eb2d97b33
  1. 12
      TODOnewNR.txt
  2. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs
  3. 4
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  4. 4
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/QuickClassBrowser.cs
  5. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  6. 2
      src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs
  7. 2
      src/Main/Base/Project/Src/Gui/Pads/DefinitionViewPad.cs
  8. 4
      src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/Commands/ReferenceFolderNodeCommands.cs
  9. 9
      src/Main/Base/Project/Src/Project/CompilableProject.cs
  10. 2
      src/Main/Base/Project/Src/Project/CustomTool.cs
  11. 10
      src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
  12. 2
      src/Main/Base/Project/Src/Services/File/OpenedFile.cs
  13. 96
      src/Main/Base/Project/Src/Services/ParserService/Fusion.cs
  14. 125
      src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs
  15. 18
      src/Main/Base/Project/Src/Services/ParserService/ParsedFileListener.cs
  16. 109
      src/Main/Base/Project/Src/Services/ParserService/ParserService.cs
  17. 10
      src/Main/Base/Project/Src/Util/ExtensionMethods.cs

12
TODOnewNR.txt

@ -6,20 +6,22 @@ Commented code, needs to be ported and re-enabled: @@ -6,20 +6,22 @@ Commented code, needs to be ported and re-enabled:
Context Actions (EditorContext etc.)
RefactoringService
FindReferencesAndRenameHelper
NamespaceRefactoringsService
NamespaceRefactoringService
RefactoringMenuBuilder
TaskService.UpdateCommentTags
CodeManipulation.cs (btw, I think this doesn't belong into AvalonEdit.AddIn - more a job for a language binding)
--> See TODO-list on Google Docs
Stuff that was renamed/moved:
ICSharpCode.SharpDevelop.Dom -> the type system and resolvers now are part of ICSharpCode.NRefactory
IDocument, ITextEditor -> moved to ICSharpCode.Editor assembly
IDocument -> moved to ICSharpCode.NRefactory.Editor
IClass -> ITypeDefinition
ICompilationUnit -> IParsedFile
ITextBuffer -> ITextSource (in ICSharpCode.Editor assembly)
ITextBuffer -> ITextSource (in ICSharpCode.NRefactory.Editor)
IReturnType -> ITypeReference (unresolved) or IType (resolved)
Location -> TextLocation or AstLocation. Use AstLocation only when directly working with the AST, and TextLocation otherwise.
TextLocation -> moved to ICSharpCode.Editor assembly
Location -> TextLocation in ICSharpCode.NRefactory
TextLocation -> moved to ICSharpCode.NRefactory
Functionality changes:
The result of a parser run (ParseInformation) now may contain a fully parsed AST.

2
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/AvalonEditViewContent.cs

@ -176,7 +176,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -176,7 +176,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
codeEditor.FileName = newFileName;
ParserService.ParseAsync(file.FileName, codeEditor.Document);
ParserService.ParseAsync(file.FileName, codeEditor.Document).FireAndForget();
}
}

4
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs

@ -475,7 +475,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -475,7 +475,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
// Immediately parse on enter.
// This ensures we have up-to-date CC info about the method boundary when a user
// types near the end of a method.
ParserService.ParseAsync(this.FileName, this.Document.CreateSnapshot());
ParserService.ParseAsync(this.FileName, this.Document.CreateSnapshot()).FireAndForget();
}
}
}
@ -534,7 +534,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -534,7 +534,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
ParseInformation parseInfo = ParserService.GetCachedParseInformation(this.FileName);
if (parseInfo == null) {
// if parse info is not yet available, start parsing on background
ParserService.ParseAsync(this.FileName, primaryTextEditorAdapter.Document);
ParserService.ParseAsync(this.FileName, primaryTextEditorAdapter.Document).FireAndForget();
// we'll receive the result using the ParseInformationUpdated event
}
ParseInformationUpdated(parseInfo);

4
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/QuickClassBrowser.cs

@ -166,6 +166,8 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -166,6 +166,8 @@ namespace ICSharpCode.AvalonEdit.AddIn
void AddClasses(IEnumerable<IUnresolvedTypeDefinition> classes)
{
foreach (var c in classes) {
if (c.IsSynthetic)
continue;
classItems.Add(new EntityItem(c));
AddClasses(c.NestedTypes);
}
@ -252,6 +254,8 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -252,6 +254,8 @@ namespace ICSharpCode.AvalonEdit.AddIn
if (compoundClass != null) {
var ambience = project.GetAmbience();
foreach (var member in compoundClass.Members) {
if (member.IsSynthetic)
continue;
bool isInSamePart = string.Equals(member.UnresolvedMember.ParsedFile.FileName, selectedClass.ParsedFile.FileName, StringComparison.OrdinalIgnoreCase);
memberItems.Add(new EntityItem(member, ambience) { IsInSamePart = isInSamePart });
}

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -386,6 +386,7 @@ @@ -386,6 +386,7 @@
<Compile Include="Src\Services\ParserService\DomAssemblyName.cs" />
<Compile Include="Src\Services\ParserService\Fusion.cs" />
<Compile Include="Src\Services\ParserService\GacInterop.cs" />
<Compile Include="Src\Services\ParserService\ParsedFileListener.cs" />
<Compile Include="Src\Services\ParserService\IParser.cs" />
<Compile Include="Src\Services\ParserService\ParseInformation.cs" />
<Compile Include="Src\Services\ParserService\LoadSolutionProjects.cs" />

2
src/Main/Base/Project/Src/Gui/Dialogs/NewFileDialog.cs

@ -395,7 +395,7 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -395,7 +395,7 @@ namespace ICSharpCode.SharpDevelop.Gui
File.Copy(binaryFileName, parsedFileName);
else
File.WriteAllText(parsedFileName, parsedContent, ParserService.DefaultFileEncoding);
ParserService.ParseFileAsync(FileName.Create(parsedFileName), new StringTextSource(parsedContent));
ParserService.ParseFileAsync(FileName.Create(parsedFileName), new StringTextSource(parsedContent)).FireAndForget();
} else {
if (!String.IsNullOrEmpty(binaryFileName)) {
LoggingService.Warn("binary file was skipped");

2
src/Main/Base/Project/Src/Gui/Pads/DefinitionViewPad.cs

@ -75,7 +75,7 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -75,7 +75,7 @@ namespace ICSharpCode.SharpDevelop.Gui
if (!ctl.IsVisible) return;
LoggingService.Debug("DefinitionViewPad.Update");
ResolveResult res = ResolveAtCaret(e);
ResolveResult res = /* TODO await */ ResolveAtCaret(e);
if (res == null) return;
var pos = res.GetDefinitionRegion();
if (pos.IsEmpty) return;

4
src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/Commands/ReferenceFolderNodeCommands.cs

@ -68,7 +68,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands @@ -68,7 +68,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
}
// Update code completion.
ParserService.ParseFileAsync(FileName.Create(webReference.WebProxyFileName));
ParserService.ParseFileAsync(FileName.Create(webReference.WebProxyFileName)).FireAndForget();
}
} catch (WebException ex) {
MessageService.ShowException(ex, String.Format(StringParser.Parse("${res:ICSharpCode.SharpDevelop.Commands.ProjectBrowser.RefreshWebReference.ReadServiceDescriptionError}"), url.UpdateFromURL));
@ -125,7 +125,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands @@ -125,7 +125,7 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
AddWebReferenceToProjectBrowser(node, refDialog.WebReference);
// Add proxy to code completion.
ParserService.ParseFileAsync(FileName.Create(refDialog.WebReference.WebProxyFileName));
ParserService.ParseFileAsync(FileName.Create(refDialog.WebReference.WebProxyFileName)).FireAndForget();
node.Project.Save();
}

9
src/Main/Base/Project/Src/Project/CompilableProject.cs

@ -507,6 +507,15 @@ namespace ICSharpCode.SharpDevelop.Project @@ -507,6 +507,15 @@ namespace ICSharpCode.SharpDevelop.Project
{
}
public override void Dispose()
{
lock (SyncRoot) {
if (parseProjectContentContainer != null)
parseProjectContentContainer.Dispose();
}
base.Dispose();
}
#region IUpgradableProject
[Browsable(false)]
public virtual bool UpgradeDesired {

2
src/Main/Base/Project/Src/Project/CustomTool.cs

@ -201,7 +201,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -201,7 +201,7 @@ namespace ICSharpCode.SharpDevelop.Project
},
outputFileName, FileErrorPolicy.Inform);
EnsureOutputFileIsInProject(baseItem, outputFileName);
ParserService.ParseAsync(FileName.Create(outputFileName), new StringTextSource(codeOutput));
ParserService.ParseAsync(FileName.Create(outputFileName), new StringTextSource(codeOutput)).FireAndForget();
}
public void GenerateCodeDomAsync(FileProjectItem baseItem, string outputFileName, Func<CodeCompileUnit> func)

10
src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs

@ -138,16 +138,6 @@ namespace ICSharpCode.SharpDevelop.Project @@ -138,16 +138,6 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
volatile string mscorlibPath;
/// <summary>
/// Gets the path to mscorlib.
/// This property is set only after ResolveAssemblyReferences() is called.
/// </summary>
public string MscorlibPath {
get { return mscorlibPath; }
}
public override void ResolveAssemblyReferences()
{
MSBuildInternals.ResolveAssemblyReferences(this);

2
src/Main/Base/Project/Src/Services/File/OpenedFile.cs

@ -461,7 +461,7 @@ namespace ICSharpCode.SharpDevelop @@ -461,7 +461,7 @@ namespace ICSharpCode.SharpDevelop
// We discarded some information when closing the file,
// so we need to re-parse it.
if (File.Exists(this.FileName))
ParserService.ParseAsync(this.FileName);
ParserService.ParseAsync(this.FileName).FireAndForget();
else
ParserService.ClearParseInformation(this.FileName);
}

96
src/Main/Base/Project/Src/Services/ParserService/Fusion.cs

@ -5,57 +5,11 @@ @@ -5,57 +5,11 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
namespace ICSharpCode.SharpDevelop.Parser
{
// .NET Fusion COM interfaces
[ComImport(), Guid("E707DCDE-D1CD-11D2-BAB9-00C04F8ECEAE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAssemblyCache
{
[PreserveSig()]
int UninstallAssembly(uint dwFlags,
[MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName,
IntPtr pvReserved,
out uint pulDisposition);
[PreserveSig()]
int QueryAssemblyInfo(uint dwFlags,
[MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName,
IntPtr pAsmInfo);
[PreserveSig()]
int CreateAssemblyCacheItem(uint dwFlags,
IntPtr pvReserved,
out IAssemblyCacheItem ppAsmItem,
[MarshalAs(UnmanagedType.LPWStr)] string pszAssemblyName);
[PreserveSig()]
int CreateAssemblyScavenger(out object ppAsmScavenger);
[PreserveSig()]
int InstallAssembly(uint dwFlags,
[MarshalAs(UnmanagedType.LPWStr)] string pszManifestFilePath,
IntPtr pvReserved);
}
[ComImport(), Guid("9E3AAEB4-D1CD-11D2-BAB9-00C04F8ECEAE"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAssemblyCacheItem
{
void CreateStream([MarshalAs(UnmanagedType.LPWStr)] string pszName,
uint dwFormat,
uint dwFlags,
uint dwMaxSize,
out IStream ppStream);
void IsNameEqual(IAssemblyName pName);
void Commit(uint dwFlags);
void MarkAssemblyVisible(uint dwFlags);
}
[ComImport(), Guid("CD193BC0-B4BC-11D2-9833-00C04FC31D2E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAssemblyName
{
@ -134,47 +88,8 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -134,47 +88,8 @@ namespace ICSharpCode.SharpDevelop.Parser
int Clone(out IAssemblyEnum ppEnum);
}
[ComImport(), Guid("1D23DF4D-A1E2-4B8B-93D6-6EA3DC285A54"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IHistoryReader
{
[PreserveSig()]
int GetFilePath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzFilePath,
ref uint pdwSize);
[PreserveSig()]
int GetApplicationName([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzAppName,
ref uint pdwSize);
[PreserveSig()]
int GetEXEModulePath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzExePath,
ref uint pdwSize);
void GetNumActivations(out uint pdwNumActivations);
void GetActivationDate(uint dwIdx, // One-based!
out long /* FILETIME */ pftDate);
[PreserveSig()]
int GetRunTimeVersion(ref long /* FILETIME */ pftActivationDate,
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzRunTimeVersion,
ref uint pdwSize);
void GetNumAssemblies(ref long /* FILETIME */ pftActivationDate,
out uint pdwNumAsms);
void GetHistoryAssembly(ref long /* FILETIME */ pftActivationDate,
uint dwIdx, // One-based!
[MarshalAs(UnmanagedType.IUnknown)] out object ppHistAsm);
}
internal static class Fusion
{
[DllImport("fusion.dll", CharSet=CharSet.Auto)]
internal static extern int CreateAssemblyCache(out IAssemblyCache ppAsmCache,
uint dwReserved);
// dwFlags: 1 = Enumerate native image (NGEN) assemblies
// 2 = Enumerate GAC assemblies
// 4 = Enumerate Downloaded assemblies
@ -186,17 +101,6 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -186,17 +101,6 @@ namespace ICSharpCode.SharpDevelop.Parser
uint dwFlags,
int pvReserved);
[DllImport("fusion.dll", CharSet=CharSet.Auto)]
internal static extern int CreateAssemblyNameObject(out IAssemblyName ppName,
string szAssemblyName,
uint dwFlags,
int pvReserved);
// ?????
[DllImport("fusion.dll")]
internal static extern int CreateApplicationContext(out IApplicationContext ppAppContext,
uint dw);
[DllImport("fusion.dll")]
internal static extern int GetCachePath(uint flags,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzDir,

125
src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs

@ -19,7 +19,14 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -19,7 +19,14 @@ namespace ICSharpCode.SharpDevelop.Parser
public class ParseProjectContentContainer : IDisposable
{
readonly MSBuildBasedProject project;
/// <summary>
/// Lock for accessing mutable fields of this class.
/// To avoids deadlocks, the ParserService must not be called while holding this lock.
/// </summary>
readonly object lockObj = new object();
readonly ParsedFileListener myListener;
IProjectContent projectContent;
IAssemblyReference[] references = { MinimalCorlib.Instance };
bool initializing;
@ -31,18 +38,33 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -31,18 +38,33 @@ namespace ICSharpCode.SharpDevelop.Parser
throw new ArgumentNullException("project");
this.project = project;
this.projectContent = initialProjectContent.SetAssemblyName(project.AssemblyName);
this.myListener = new ParsedFileListener(OnParsedFileUpdated);
this.initializing = true;
LoadSolutionProjects.AddJob(Initialize, "Loading " + project.Name + "...", GetInitializationWorkAmount());
}
void OnParsedFileUpdated(IParsedFile oldFile, IParsedFile newFile)
{
// This method is called by the parser service within the parser service lock.
lock (lockObj) {
if (!disposed)
projectContent = projectContent.UpdateProjectContent(oldFile, newFile);
}
}
public void Dispose()
{
ProjectService.ProjectItemAdded -= OnProjectItemAdded;
ProjectService.ProjectItemRemoved -= OnProjectItemRemoved;
lock (lockObj) {
ProjectService.ProjectItemAdded -= OnProjectItemAdded;
ProjectService.ProjectItemRemoved -= OnProjectItemRemoved;
if (disposed)
return;
disposed = true;
}
foreach (var fileName in GetFilesToParse(project.Items)) {
ParserService.RemoveParsedFileListener(fileName, myListener);
}
initializing = false;
}
@ -68,9 +90,9 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -68,9 +90,9 @@ namespace ICSharpCode.SharpDevelop.Parser
if (disposed) {
throw new ObjectDisposedException("ParseProjectContent");
}
ProjectService.ProjectItemAdded += OnProjectItemAdded;
ProjectService.ProjectItemRemoved += OnProjectItemRemoved;
}
ProjectService.ProjectItemAdded += OnProjectItemAdded;
ProjectService.ProjectItemRemoved += OnProjectItemRemoved;
double scalingFactor = 1.0 / (project.Items.Count + LoadingReferencesWorkAmount);
using (IProgressMonitor initReferencesProgressMonitor = progressMonitor.CreateSubTask(LoadingReferencesWorkAmount * scalingFactor),
parseProgressMonitor = progressMonitor.CreateSubTask(projectItems.Count * scalingFactor))
@ -84,19 +106,23 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -84,19 +106,23 @@ namespace ICSharpCode.SharpDevelop.Parser
initializing = false;
}
IEnumerable<FileName> GetFilesToParse(IEnumerable<ProjectItem> projectItems)
{
return (
from p in projectItems
where p.ItemType == ItemType.Compile && !String.IsNullOrEmpty(p.FileName)
select FileName.Create(p.FileName));
}
void ParseFiles(ICollection<ProjectItem> projectItems, IProgressMonitor progressMonitor)
{
ParseableFileContentFinder finder = new ParseableFileContentFinder();
var fileContents = (
from p in projectItems.AsParallel().WithCancellation(progressMonitor.CancellationToken)
where !ItemType.NonFileItemTypes.Contains(p.ItemType) && !String.IsNullOrEmpty(p.FileName)
select FileName.Create(p.FileName)
).ToList();
var fileList = GetFilesToParse(projectItems).ToList();
object progressLock = new object();
double fileCountInverse = 1.0 / fileContents.Count;
double fileCountInverse = 1.0 / fileList.Count;
Parallel.ForEach(
fileContents,
fileList,
new ParallelOptions {
MaxDegreeOfParallelism = Environment.ProcessorCount,
CancellationToken = progressMonitor.CancellationToken
@ -105,9 +131,13 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -105,9 +131,13 @@ namespace ICSharpCode.SharpDevelop.Parser
// Don't read files we don't have a parser for.
// This avoids loading huge files (e.g. sdps) when we have no intention of parsing them.
if (ParserService.HasParser(fileName)) {
ParserService.AddParsedFileListener(fileName, myListener, startAsyncParse: false);
ITextSource content = finder.Create(fileName);
if (content != null)
if (content != null) {
// Parse the file on this thread so that AddParsedFileListener() does not
// start an asynchronous parse operation.
ParserService.ParseFile(fileName, content);
}
}
lock (progressLock) {
progressMonitor.Progress += fileCountInverse;
@ -120,29 +150,20 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -120,29 +150,20 @@ namespace ICSharpCode.SharpDevelop.Parser
{
return System.Threading.Tasks.Task.Factory.StartNew(
delegate {
project.ResolveAssemblyReferences();
const double assemblyResolvingProgress = 0.3;
var referenceItems = project.ResolveAssemblyReferences(progressMonitor.CancellationToken);
const double assemblyResolvingProgress = 0.3; // 30% asm resolving, 70% asm loading
progressMonitor.Progress += assemblyResolvingProgress;
progressMonitor.CancellationToken.ThrowIfCancellationRequested();
List<string> assemblyFiles = new List<string>();
List<IAssemblyReference> newReferences = new List<IAssemblyReference>();
string mscorlib = project.MscorlibPath;
if (mscorlib != null)
assemblyFiles.Add(mscorlib);
foreach (ProjectItem item in projectItems) {
ReferenceProjectItem reference = item as ReferenceProjectItem;
if (reference != null) {
if (ItemType.ReferenceItemTypes.Contains(reference.ItemType)) {
ProjectReferenceProjectItem projectReference = reference as ProjectReferenceProjectItem;
if (projectReference != null) {
newReferences.Add(projectReference);
} else {
assemblyFiles.Add(reference.FileName);
}
}
foreach (var reference in referenceItems) {
ProjectReferenceProjectItem projectReference = reference as ProjectReferenceProjectItem;
if (projectReference != null) {
newReferences.Add(projectReference);
} else {
assemblyFiles.Add(reference.FileName);
}
}
@ -164,7 +185,7 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -164,7 +185,7 @@ namespace ICSharpCode.SharpDevelop.Parser
}
// ensure that com references are built serially because we cannot invoke multiple instances of MSBuild
static Queue<System.Windows.Forms.MethodInvoker> callAfterAddComReference = new Queue<System.Windows.Forms.MethodInvoker>();
static Queue<Action> callAfterAddComReference = new Queue<Action>();
static bool buildingComReference;
void OnProjectItemAdded(object sender, ProjectItemEventArgs e)
@ -174,7 +195,7 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -174,7 +195,7 @@ namespace ICSharpCode.SharpDevelop.Parser
ReferenceProjectItem reference = e.ProjectItem as ReferenceProjectItem;
if (reference != null) {
if (reference.ItemType == ItemType.COMReference) {
System.Windows.Forms.MethodInvoker action = delegate {
Action action = delegate {
// Compile project to ensure interop library is generated
project.Save(); // project is not yet saved when ItemAdded fires, so save it here
string message = StringParser.Parse("\n${res:MainWindow.CompilerMessages.CreatingCOMInteropAssembly}\n");
@ -207,13 +228,9 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -207,13 +228,9 @@ namespace ICSharpCode.SharpDevelop.Parser
ReparseReferences();
}
}
if (e.ProjectItem.ItemType == ItemType.Import) {
throw new NotImplementedException();
//UpdateDefaultImports(project.Items);
} else if (e.ProjectItem.ItemType == ItemType.Compile) {
if (System.IO.File.Exists(e.ProjectItem.FileName)) {
ParserService.ParseFileAsync(FileName.Create(e.ProjectItem.FileName));
}
if (e.ProjectItem.ItemType == ItemType.Compile) {
var fileName = FileName.Create(e.ProjectItem.FileName);
ParserService.AddParsedFileListener(fileName, myListener, startAsyncParse: true);
}
}
@ -235,37 +252,9 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -235,37 +252,9 @@ namespace ICSharpCode.SharpDevelop.Parser
}
}
if (e.ProjectItem.ItemType == ItemType.Import) {
throw new NotImplementedException();
//UpdateDefaultImports(project.Items);
} else if (e.ProjectItem.ItemType == ItemType.Compile) {
ParserService.ClearParseInformation(FileName.Create(e.ProjectItem.FileName));
if (e.ProjectItem.ItemType == ItemType.Compile) {
ParserService.RemoveParsedFileListener(FileName.Create(e.ProjectItem.FileName), myListener);
}
}
/*
int languageDefaultImportCount = -1;
void UpdateDefaultImports(ICollection<ProjectItem> items)
{
if (languageDefaultImportCount < 0) {
languageDefaultImportCount = (DefaultImports != null) ? DefaultImports.Usings.Count : 0;
}
if (languageDefaultImportCount == 0) {
DefaultImports = null;
} else {
while (DefaultImports.Usings.Count > languageDefaultImportCount) {
DefaultImports.Usings.RemoveAt(languageDefaultImportCount);
}
}
foreach (ProjectItem item in items) {
if (item.ItemType == ItemType.Import) {
if (DefaultImports == null) {
DefaultImports = new DefaultUsing(this);
}
DefaultImports.Usings.Add(item.Include);
}
}
}*/
}
}

18
src/Main/Base/Project/Src/Services/ParserService/ParsedFileListener.cs

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.SharpDevelop.Parser
{
/// <summary>
/// Listener for parse info changes.
/// Caution: The callback is invoked within the parser service's lock. Beware of deadlocks!
/// The method is called on the thread that performed the parse operation, which might be the main thread
/// or a background thread.
/// If possible, use the <see cref="ParserService.ParseInformationUpdated"/> event instead, which is called on the main thread
/// and after the parser service released its lock.
/// </summary>
public delegate void ParsedFileListener(IParsedFile oldFile, IParsedFile newFile);
}

109
src/Main/Base/Project/Src/Services/ParserService/ParserService.cs

@ -261,6 +261,7 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -261,6 +261,7 @@ namespace ICSharpCode.SharpDevelop.Parser
volatile ParseInformation cachedParseInformation;
ITextSourceVersion bufferVersion;
bool disposed;
ParsedFileListener activeListeners;
public FileEntry(FileName fileName)
{
@ -268,6 +269,38 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -268,6 +269,38 @@ namespace ICSharpCode.SharpDevelop.Parser
this.parser = CreateParser(fileName);
}
public void AddParsedFileListener(ParsedFileListener listener, bool startAsyncParse)
{
bool isNewEntry;
lock (this) {
isNewEntry = (this.activeListeners == null);
this.activeListeners += listener;
if (mainParsedFile != null) {
// We already have parse info: tell the new listener
listener(null, mainParsedFile);
// No need to start async parse.
isNewEntry = false;
}
}
if (isNewEntry && startAsyncParse)
ParseAsync(null).FireAndForget();
}
public void RemoveParsedFileListener(ParsedFileListener listener)
{
lock (this) {
this.activeListeners += listener;
}
}
public bool HasParseListeners {
get {
lock (this) {
return this.activeListeners != null;
}
}
}
/// <summary>
/// Intended for unit tests only
/// </summary>
@ -418,26 +451,10 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -418,26 +451,10 @@ namespace ICSharpCode.SharpDevelop.Parser
}
}
#warning How to update the PCs?
/*for (int i = 0; i < newUnits.Length; i++) {
IProjectContent pc = projectContents[i];
// update the compilation unit
IParsedFile oldUnit = oldUnits.FirstOrDefault(o => o.ProjectContent == pc);
// ensure the new unit is frozen beforewe make it visible to the outside world
newUnits[i].Freeze();
pc.UpdateProjectContent(oldUnit, newUnits[i]);
}*/
if (this.activeListeners != null)
this.activeListeners(mainParsedFile, resultParseInfo.ParsedFile);
RaiseParseInformationUpdated(new ParseInformationEventArgs(mainParsedFile, resultParseInfo));
/*
// remove all old units that don't exist anymore
foreach (IParsedFile oldUnit in oldUnits) {
if (!newUnits.Any(n => n.ProjectContent == oldUnit.ProjectContent)) {
oldUnit.ProjectContent.UpdateProjectContent(oldUnit, null);
RaiseParseInformationUpdated(new ParseInformationEventArgs(oldUnit, null, false));
}
}*/
this.bufferVersion = fileContentVersion;
this.mainParsedFile = resultUnit;
// Cached the new parse information around if it was requested, or if we had already cached parse information previously.
@ -454,16 +471,14 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -454,16 +471,14 @@ namespace ICSharpCode.SharpDevelop.Parser
IParsedFile parseInfo;
lock (this) {
// by setting the disposed flag, we'll cause all running ParseFile() calls to return null and not
// call into the parser anymore, so we can do the remainder of the clean-up work outside the lock
// call into the parser anymore
this.disposed = true;
parseInfo = this.mainParsedFile;
this.bufferVersion = null;
this.mainParsedFile = null;
if (parseInfo != null && this.activeListeners != null)
this.activeListeners(parseInfo, null);
}
/*foreach (IParsedFile oldUnit in oldUnits) {
oldUnit.ProjectContent.UpdateProjectContent(oldUnit, null);
bool isPrimary = parseInfo == oldUnit;
}*/
RaiseParseInformationUpdated(new ParseInformationEventArgs(parseInfo, null));
}
@ -593,7 +608,9 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -593,7 +608,9 @@ namespace ICSharpCode.SharpDevelop.Parser
/// Removes all parse information (both IParsedFile and ParseInformation) for the specified file.
/// This method is thread-safe.
/// </summary>
public static void ClearParseInformation(FileName fileName)
/// <param name="force">Whether to remove the parse information even if there are listeners
/// registered for it.</param>
public static void ClearParseInformation(FileName fileName, bool force = false)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
@ -603,11 +620,17 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -603,11 +620,17 @@ namespace ICSharpCode.SharpDevelop.Parser
FileEntry entry;
lock (syncLock) {
if (fileEntryDict.TryGetValue(fileName, out entry)) {
fileEntryDict.Remove(fileName);
// avoid concurrent deregistration of parse listener between entry.HasParseListeners and entry.Clear()
lock (entry) {
if (force || !entry.HasParseListeners) {
fileEntryDict.Remove(fileName);
// Call entry.Clear() within the lock so that we don't have a race condition
// when the entry is immediately recreated by another thread.
entry.Clear();
}
}
}
}
if (entry != null)
entry.Clear();
}
/// <summary>
@ -954,5 +977,37 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -954,5 +977,37 @@ namespace ICSharpCode.SharpDevelop.Parser
tcs.SetResult(result);
return tcs.Task;
}
/// <summary>
/// Adds a parsed file listener for the specified file.
/// If parse information for the file is already known, this method immediately invokes
/// <c>listener(null, existingParsedFile);</c>.
/// </summary>
/// <param name="startAsyncParse">If no parse information is already known, this parameter
/// controls whether an asynchronous parse operation is started.</param>
public static void AddParsedFileListener(FileName fileName, ParsedFileListener listener, bool startAsyncParse)
{
if (listener == null)
throw new ArgumentNullException("listener");
GetFileEntry(fileName, true).AddParsedFileListener(listener, startAsyncParse);
}
/// <summary>
/// Removes a parsed file listener for the specified file.
/// This method invokes <c>listener(existingParsedFile, null);</c>. (unless existingParsedFile==null)
/// </summary>
/// <remarks>
/// When the last file listener is removed, the stored parse information is cleared.
/// </remarks>
public static void RemoveParsedFileListener(FileName fileName, ParsedFileListener listener)
{
if (listener == null)
throw new ArgumentNullException("listener");
FileEntry entry = GetFileEntry(fileName, false);
if (entry == null)
return;
entry.RemoveParsedFileListener(listener);
ClearParseInformation(fileName, force: false);
}
}
}

10
src/Main/Base/Project/Src/Util/ExtensionMethods.cs

@ -69,6 +69,16 @@ namespace ICSharpCode.SharpDevelop @@ -69,6 +69,16 @@ namespace ICSharpCode.SharpDevelop
}
}
/// <summary>
/// If the task throws an exception, notifies the message service.
/// Call this method on asynchronous tasks if you do not care about the result, but do not want
/// unhandled exceptions to go unnoticed.
/// </summary>
public static void FireAndForget(this System.Threading.Tasks.Task task)
{
task.ContinueWith(t => { if (t.Exception != null) Core.MessageService.ShowException(t.Exception); });
}
/// <summary>
/// Runs an action for all elements in the input.
/// </summary>

Loading…
Cancel
Save