Browse Source

Implemented re-parsing when switching the project configuration.

newNRvisualizers
Daniel Grunwald 13 years ago
parent
commit
a25ebfe842
  1. 53
      TODOnewNR.txt
  2. 1
      src/AddIns/BackendBindings/CSharpBinding/Project/Resources/CSharp-Semantic.xshd
  3. 113
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs
  4. 2
      src/AddIns/BackendBindings/CSharpBinding/Tests/OverrideCompletionTests.cs
  5. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  6. 9
      src/Main/Base/Project/Src/Project/CompilableProject.cs
  7. 2
      src/Main/Base/Project/Src/Project/MSBuildInternals.cs
  8. 198
      src/Main/Base/Project/Src/Services/ParserService/ParseProjectContent.cs
  9. 60
      src/Main/Base/Project/Src/Util/AtomicBoolean.cs
  10. 4
      src/Main/Core/Project/Src/AddInTree/AddInManager.cs

53
TODOnewNR.txt

@ -22,14 +22,55 @@ Stuff that was renamed/moved:
IReturnType -> ITypeReference (unresolved) or IType (resolved) IReturnType -> ITypeReference (unresolved) or IType (resolved)
Location -> TextLocation in ICSharpCode.NRefactory Location -> TextLocation in ICSharpCode.NRefactory
TextLocation -> moved to ICSharpCode.NRefactory TextLocation -> moved to ICSharpCode.NRefactory
ParserService -> SD.ParserService
Functionality changes: Functionality changes:
The result of a parser run (ParseInformation) now may contain a fully parsed AST.
The ParserService may cache such full ASTs, but may also drop them from memory at any time. Static services replaced with interfaces:
This will be implemented by keeping the last N accessed files in the cache. (currently we just keep the caches around forever) To make writing unit tests easier, the static services in SharpDevelop are getting
Every parse information also contains an IParsedFile instance with the type system information. replaced with interfaces. The class "SD" has static properties to get references
The IParsedFile is stored permanently (both in ParserService and in the IProjectContents). to the services, so the call "ResourceService.GetString()" becomes "SD.ResourceService.GetString()".
In unit tests, Rhino.Mocks can be used to easily create mocks of the services:
SD.InitializeForUnitTests(); // removes services from previous test cases
SD.Services.AddService(typeof(IParserService), MockRepository.GenerateStrictMock<IParserService>());
SD.ParserService.Stub(p => p.GetCachedParseInformation(textEditor.FileName)).Return(parseInfo);
SD.ParserService.Stub(p => p.GetCompilationForFile(textEditor.FileName)).Return(compilation);
It is possible to define a service interface in ICSharpCode.SharpDevelop.dll and have the implementation
somewhere else (SD will find it using the AddInTree).
This allows for AddIns to consume each other's functionality (e.g. debugger accessing the decompiler service)
without having to define a custom extension point.
The long-term goal is to have only interfaces and helper classes in ICSharpCode.SharpDevelop.dll (the API for AddIns)
and have the implementation details in SharpDevelop.exe (which AddIns aren't supposed to reference).
SD.MainThread:
The new best way to invoke a call on the main thread is:
SD.MainThread.InvokeAsync(delegate { ... }).FireAndForget();
Note that InvokeAsync returns a Task (like all .NET 4.5 *Async APIs). If any exceptions occur while
executing the delegate, they will get stored in the task object. This can cause the exception to get
silently ignored if the task object isn't used later. The "FireAndForget()" extension method solves
this problem by reporting any (future) errors to the message service.
SD.PropertyService:
The Get()/Set() methods no longer support nested Properties objects or lists of elements -
you will need to use the new dedicated GetList()/SetList()/NestedProperties() methods for that.
The Get() method no longer causes the default value to be stored in the container; and GetList()
results in a read-only list - an explicit SetList() call is required to store the resulting value again.
However, a nested properties container still is connected with its parent, and any changes done
to the nested container will get saves without having to call the SetNestedProperties() method.
SD.ParserService:
The result of a parser run (ParseInformation) now may contain a fully parsed AST.
The ParserService may cache such full ASTs, but may also drop them from memory at any time.
This will be implemented by keeping the last N accessed files in the cache. (currently we just keep the caches around forever)
Every parse information also contains an IParsedFile instance with the type system information.
The IParsedFile is stored permanently (both in ParserService and in the IProjectContents).
Context Actions vs. Member Context Menu: Context Actions vs. Member Context Menu:

1
src/AddIns/BackendBindings/CSharpBinding/Project/Resources/CSharp-Semantic.xshd

@ -35,6 +35,7 @@
<Color name="ValueTypes" fontWeight="bold" foreground="#004085" exampleText="System.#{#DateTime#}# date;"/> <Color name="ValueTypes" fontWeight="bold" foreground="#004085" exampleText="System.#{#DateTime#}# date;"/>
<Color name="MethodCall" foreground="MidnightBlue" fontWeight="bold" exampleText="o.#{#ToString#}#();"/> <Color name="MethodCall" foreground="MidnightBlue" fontWeight="bold" exampleText="o.#{#ToString#}#();"/>
<Color name="FieldAccess" fontStyle="italic" exampleText="return this.#{#name#}#;"/> <Color name="FieldAccess" fontStyle="italic" exampleText="return this.#{#name#}#;"/>
<Color name="InactiveCode" foreground="Gray" exampleText="Deactivated by #if"/>
<RuleSet name="CommentMarkerSet"> <RuleSet name="CommentMarkerSet">
<Keywords fontWeight="bold" foreground="Red"> <Keywords fontWeight="bold" foreground="Red">

113
src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpSemanticHighlighter.cs

@ -33,10 +33,12 @@ namespace CSharpBinding
readonly HighlightingColor fieldAccessColor; readonly HighlightingColor fieldAccessColor;
readonly HighlightingColor valueKeywordColor; readonly HighlightingColor valueKeywordColor;
readonly HighlightingColor parameterModifierColor; readonly HighlightingColor parameterModifierColor;
readonly HighlightingColor inactiveCodeColor;
List<IDocumentLine> invalidLines = new List<IDocumentLine>(); List<IDocumentLine> invalidLines = new List<IDocumentLine>();
List<CachedLine> cachedLines = new List<CachedLine>(); List<CachedLine> cachedLines = new List<CachedLine>();
bool hasCrashed; bool hasCrashed;
bool forceParseOnNextRefresh;
int lineNumber; int lineNumber;
HighlightedLine line; HighlightedLine line;
@ -61,6 +63,7 @@ namespace CSharpBinding
this.fieldAccessColor = highlightingDefinition.GetNamedColor("FieldAccess"); this.fieldAccessColor = highlightingDefinition.GetNamedColor("FieldAccess");
this.valueKeywordColor = highlightingDefinition.GetNamedColor("NullOrValueKeywords"); this.valueKeywordColor = highlightingDefinition.GetNamedColor("NullOrValueKeywords");
this.parameterModifierColor = highlightingDefinition.GetNamedColor("ParameterModifiers"); this.parameterModifierColor = highlightingDefinition.GetNamedColor("ParameterModifiers");
this.inactiveCodeColor = highlightingDefinition.GetNamedColor("InactiveCode");
SD.ParserService.ParseInformationUpdated += ParserService_ParseInformationUpdated; SD.ParserService.ParseInformationUpdated += ParserService_ParseInformationUpdated;
SD.ParserService.LoadSolutionProjectsThread.Finished += ParserService_LoadSolutionProjectsThreadEnded; SD.ParserService.LoadSolutionProjectsThread.Finished += ParserService_LoadSolutionProjectsThreadEnded;
@ -146,6 +149,7 @@ namespace CSharpBinding
{ {
cachedLines.Clear(); cachedLines.Clear();
invalidLines.Clear(); invalidLines.Clear();
forceParseOnNextRefresh = true;
syntaxHighlighter.InvalidateAll(); syntaxHighlighter.InvalidateAll();
} }
@ -208,7 +212,13 @@ namespace CSharpBinding
return cachedLine.HighlightedLine; return cachedLine.HighlightedLine;
} }
var parseInfo = SD.ParserService.GetCachedParseInformation(textEditor.FileName, textEditor.Document.Version) as CSharpFullParseInformation; CSharpFullParseInformation parseInfo;
if (forceParseOnNextRefresh) {
forceParseOnNextRefresh = false;
parseInfo = SD.ParserService.Parse(textEditor.FileName, textEditor.Document) as CSharpFullParseInformation;
} else {
parseInfo = SD.ParserService.GetCachedParseInformation(textEditor.FileName, textEditor.Document.Version) as CSharpFullParseInformation;
}
if (parseInfo == null) { if (parseInfo == null) {
if (!invalidLines.Contains(documentLine)) if (!invalidLines.Contains(documentLine))
invalidLines.Add(documentLine); invalidLines.Add(documentLine);
@ -278,10 +288,10 @@ namespace CSharpBinding
{ {
if (color == null) if (color == null)
return; return;
if (start.Line == lineNumber && end.Line == lineNumber) { if (start.Line <= lineNumber && end.Line >= lineNumber) {
int lineStartOffset = line.DocumentLine.Offset; int lineStartOffset = line.DocumentLine.Offset;
int startOffset = lineStartOffset + start.Column - 1; int startOffset = lineStartOffset + (start.Line == lineNumber ? start.Column - 1 : 0);
int endOffset = lineStartOffset + end.Column - 1; int endOffset = lineStartOffset + (end.Line == lineNumber ? end.Column - 1 : line.DocumentLine.Length);
if (line.Sections.Count > 0) { if (line.Sections.Count > 0) {
HighlightedSection prevSection = line.Sections.Last(); HighlightedSection prevSection = line.Sections.Last();
if (startOffset < prevSection.Offset + prevSection.Length) if (startOffset < prevSection.Offset + prevSection.Length)
@ -347,12 +357,18 @@ namespace CSharpBinding
{ {
Expression target = invocationExpression.Target; Expression target = invocationExpression.Target;
if (target is IdentifierExpression || target is MemberReferenceExpression || target is PointerReferenceExpression) { if (target is IdentifierExpression || target is MemberReferenceExpression || target is PointerReferenceExpression) {
var invocationRR = resolver.Resolve(invocationExpression) as CSharpInvocationResolveResult;
if (invocationRR != null && IsInactiveConditionalMethod(invocationRR.Member)) {
// mark the whole invocation expression as inactive code
Colorize(invocationExpression, inactiveCodeColor);
return;
}
// apply color to target's target // apply color to target's target
target.GetChildByRole(Roles.TargetExpression).AcceptVisitor(this); target.GetChildByRole(Roles.TargetExpression).AcceptVisitor(this);
// highlight the method call // highlight the method call
var identifier = target.GetChildByRole(Roles.Identifier); var identifier = target.GetChildByRole(Roles.Identifier);
var invocationRR = resolver.Resolve(invocationExpression) as CSharpInvocationResolveResult;
if (invocationRR != null && !invocationRR.IsDelegateInvocation) { if (invocationRR != null && !invocationRR.IsDelegateInvocation) {
Colorize(identifier, methodCallColor); Colorize(identifier, methodCallColor);
} else { } else {
@ -364,8 +380,38 @@ namespace CSharpBinding
} else { } else {
target.AcceptVisitor(this); target.AcceptVisitor(this);
} }
// Visit arguments and comments within the arguments:
invocationExpression.Arguments.AcceptVisitor(this); for (AstNode child = target.NextSibling; child != null; child = child.NextSibling) {
child.AcceptVisitor(this);
}
}
bool IsInactiveConditionalMethod(IParameterizedMember member)
{
if (member.EntityType != EntityType.Method || member.ReturnType.Kind != TypeKind.Void)
return false;
while (member.IsOverride)
member = (IParameterizedMember)InheritanceHelper.GetBaseMember(member);
return IsInactiveConditional(member.Attributes);
}
bool IsInactiveConditional(IList<IAttribute> attributes)
{
bool hasConditionalAttribute = false;
foreach (var attr in attributes) {
if (attr.AttributeType.Name == "ConditionalAttribute" && attr.AttributeType.Namespace == "System.Diagnostics" && attr.PositionalArguments.Count == 1) {
string symbol = attr.PositionalArguments[0].ConstantValue as string;
if (symbol != null) {
hasConditionalAttribute = true;
var cu = this.resolver.RootNode as CompilationUnit;
if (cu != null) {
if (cu.ConditionalSymbols.Contains(symbol))
return false; // conditional is active
}
}
}
}
return hasConditionalAttribute;
} }
public override void VisitAccessor(Accessor accessor) public override void VisitAccessor(Accessor accessor)
@ -380,34 +426,40 @@ namespace CSharpBinding
public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{ {
//methodDeclaration.Attributes.AcceptVisitor(this); for (AstNode child = methodDeclaration.FirstChild; child != null; child = child.NextSibling) {
methodDeclaration.ReturnType.AcceptVisitor(this); if (child.StartLocation.Line <= lineNumber && child.EndLocation.Line >= lineNumber) {
methodDeclaration.PrivateImplementationType.AcceptVisitor(this); if (child.Role == Roles.Identifier) {
Colorize(methodDeclaration.NameToken, methodCallColor); // child == methodDeclaration.NameToken
methodDeclaration.TypeParameters.AcceptVisitor(this); Colorize(child, methodCallColor);
methodDeclaration.Parameters.AcceptVisitor(this); } else {
methodDeclaration.Constraints.AcceptVisitor(this); child.AcceptVisitor(this);
methodDeclaration.Body.AcceptVisitor(this); }
}
}
} }
public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{ {
//typeDeclaration.Attributes.AcceptVisitor(this); // Type declarations often contain #if directives, so we must make sure
// to also visit the comments.
if (typeDeclaration.ClassType == ClassType.Enum || typeDeclaration.ClassType == ClassType.Struct) for (AstNode child = typeDeclaration.FirstChild; child != null; child = child.NextSibling) {
Colorize(typeDeclaration.NameToken, valueTypeColor); if (child.StartLocation.Line <= lineNumber && child.EndLocation.Line >= lineNumber) {
else if (child.Role == Roles.Identifier) {
Colorize(typeDeclaration.NameToken, referenceTypeColor); // child == typeDeclaration.NameToken
if (typeDeclaration.ClassType == ClassType.Enum || typeDeclaration.ClassType == ClassType.Struct)
typeDeclaration.TypeParameters.AcceptVisitor(this); Colorize(typeDeclaration.NameToken, valueTypeColor);
typeDeclaration.BaseTypes.AcceptVisitor(this); else
typeDeclaration.Constraints.AcceptVisitor(this); Colorize(typeDeclaration.NameToken, referenceTypeColor);
typeDeclaration.Members.AcceptVisitor(this); } else {
child.AcceptVisitor(this);
}
}
}
} }
public override void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration) public override void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration)
{ {
//typeParameterDeclaration.Attributes.AcceptVisitor(this); typeParameterDeclaration.Attributes.AcceptVisitor(this);
if (typeParameterDeclaration.Variance == VarianceModifier.Contravariant) if (typeParameterDeclaration.Variance == VarianceModifier.Contravariant)
Colorize(typeParameterDeclaration.VarianceToken, parameterModifierColor); Colorize(typeParameterDeclaration.VarianceToken, parameterModifierColor);
@ -440,6 +492,13 @@ namespace CSharpBinding
} }
variableInitializer.Initializer.AcceptVisitor(this); variableInitializer.Initializer.AcceptVisitor(this);
} }
public override void VisitComment(Comment comment)
{
if (comment.CommentType == CommentType.InactiveCode) {
Colorize(comment, inactiveCodeColor);
}
}
#endregion #endregion
} }
} }

2
src/AddIns/BackendBindings/CSharpBinding/Tests/OverrideCompletionTests.cs

@ -57,7 +57,7 @@ class DerivedClass : BaseClass {
var pc = new CSharpProjectContent().UpdateProjectContent(null, parseInfo.ParsedFile); var pc = new CSharpProjectContent().UpdateProjectContent(null, parseInfo.ParsedFile);
pc = pc.AddAssemblyReferences(new[] { Corlib }); pc = pc.AddAssemblyReferences(new[] { Corlib });
var compilation = pc.CreateCompilation(); var compilation = pc.CreateCompilation();
SD.Services.AddService(typeof(IParserService), MockRepository.GenerateStub<IParserService>()); SD.Services.AddService(typeof(IParserService), MockRepository.GenerateStrictMock<IParserService>());
SD.ParserService.Stub(p => p.GetCachedParseInformation(textEditor.FileName)).Return(parseInfo); SD.ParserService.Stub(p => p.GetCachedParseInformation(textEditor.FileName)).Return(parseInfo);
SD.ParserService.Stub(p => p.GetCompilationForFile(textEditor.FileName)).Return(compilation); SD.ParserService.Stub(p => p.GetCompilationForFile(textEditor.FileName)).Return(compilation);
CSharpCompletionBinding completion = new CSharpCompletionBinding(); CSharpCompletionBinding completion = new CSharpCompletionBinding();

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

@ -740,6 +740,7 @@
<Compile Include="Src\Gui\Dialogs\ReferenceDialog\AsyncDiscoveryState.cs" /> <Compile Include="Src\Gui\Dialogs\ReferenceDialog\AsyncDiscoveryState.cs" />
<Compile Include="Src\Gui\Dialogs\ReferenceDialog\DiscoveryNetworkCredential.cs" /> <Compile Include="Src\Gui\Dialogs\ReferenceDialog\DiscoveryNetworkCredential.cs" />
<Compile Include="Src\Services\ProjectService\ProjectLoader.cs" /> <Compile Include="Src\Services\ProjectService\ProjectLoader.cs" />
<Compile Include="Src\Util\AtomicBoolean.cs" />
<Compile Include="Src\Util\MultiDictionary.cs" /> <Compile Include="Src\Util\MultiDictionary.cs" />
<Compile Include="Src\Util\DotnetDetection.cs" /> <Compile Include="Src\Util\DotnetDetection.cs" />
<Compile Include="Src\Util\FakeXmlViewContent.cs" /> <Compile Include="Src\Util\FakeXmlViewContent.cs" />

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

@ -238,9 +238,14 @@ namespace ICSharpCode.SharpDevelop.Project
lock (SyncRoot) { lock (SyncRoot) {
if (parseProjectContentContainer == null) if (parseProjectContentContainer == null)
return; // parsing hasn't started yet; no need to re-parse return; // parsing hasn't started yet; no need to re-parse
if (references) {
parseProjectContentContainer.ReparseReferences();
}
if (code) {
parseProjectContentContainer.SetCompilerSettings(CreateCompilerSettings());
parseProjectContentContainer.ReparseCode();
}
} }
#warning Reparse
throw new NotImplementedException();
} }
[Browsable(false)] [Browsable(false)]

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

@ -205,7 +205,7 @@ namespace ICSharpCode.SharpDevelop.Project
} }
List<string> targets = new List<string>(); List<string> targets = new List<string>();
if (baseProject.HasProjectType(ProjectTypeGuids.PortableLibrary)) { if (baseProject.MinimumSolutionVersion >= Solution.SolutionVersionVS2010) {
targets.Add("ResolveReferences"); targets.Add("ResolveReferences");
targets.Add("DesignTimeResolveAssemblyReferences"); targets.Add("DesignTimeResolveAssemblyReferences");
} else { } else {

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

@ -46,6 +46,7 @@ namespace ICSharpCode.SharpDevelop.Parser
string cacheFileName; string cacheFileName;
#region Constructor + Dispose
public ParseProjectContentContainer(MSBuildBasedProject project, IProjectContent initialProjectContent) public ParseProjectContentContainer(MSBuildBasedProject project, IProjectContent initialProjectContent)
{ {
if (project == null) if (project == null)
@ -92,6 +93,7 @@ namespace ICSharpCode.SharpDevelop.Parser
if (serializeOnDispose) if (serializeOnDispose)
SerializeAsync(cacheFileName, pc).FireAndForget(); SerializeAsync(cacheFileName, pc).FireAndForget();
} }
#endregion
#region Caching logic (serialization) #region Caching logic (serialization)
@ -228,13 +230,7 @@ namespace ICSharpCode.SharpDevelop.Parser
} }
} }
bool IsParseableFile(FileProjectItem projectItem) #region Initialize
{
if (projectItem == null || string.IsNullOrEmpty(projectItem.FileName))
return false;
return projectItem.ItemType == ItemType.Compile || projectItem.ItemType == ItemType.Page;
}
void Initialize(IProgressMonitor progressMonitor, List<FileName> filesToParse) void Initialize(IProgressMonitor progressMonitor, List<FileName> filesToParse)
{ {
ICollection<ProjectItem> projectItems = project.Items; ICollection<ProjectItem> projectItems = project.Items;
@ -248,16 +244,29 @@ namespace ICSharpCode.SharpDevelop.Parser
using (IProgressMonitor initReferencesProgressMonitor = progressMonitor.CreateSubTask(LoadingReferencesWorkAmount * scalingFactor), using (IProgressMonitor initReferencesProgressMonitor = progressMonitor.CreateSubTask(LoadingReferencesWorkAmount * scalingFactor),
parseProgressMonitor = progressMonitor.CreateSubTask(projectItems.Count * scalingFactor)) parseProgressMonitor = progressMonitor.CreateSubTask(projectItems.Count * scalingFactor))
{ {
var resolveReferencesTask = ResolveReferencesAsync(projectItems, initReferencesProgressMonitor); var resolveReferencesTask = Task.Run(
delegate {
DoResolveReferences(projectItems, initReferencesProgressMonitor);
}, initReferencesProgressMonitor.CancellationToken);
ParseFiles(filesToParse, parseProgressMonitor); ParseFiles(filesToParse, parseProgressMonitor);
resolveReferencesTask.Wait(); resolveReferencesTask.Wait();
} }
} }
#endregion
void ParseFiles(IReadOnlyList<FileName> filesToParse, IProgressMonitor progressMonitor) #region ParseFiles
bool IsParseableFile(FileProjectItem projectItem)
{ {
if (projectItem == null || string.IsNullOrEmpty(projectItem.FileName))
return false;
return projectItem.ItemType == ItemType.Compile || projectItem.ItemType == ItemType.Page;
}
void ParseFiles(List<FileName> filesToParse, IProgressMonitor progressMonitor)
{
IParserService parserService = SD.ParserService;
IProjectContent cachedPC = TryReadFromCache(cacheFileName); IProjectContent cachedPC = TryReadFromCache(cacheFileName);
ParseableFileContentFinder finder = new ParseableFileContentFinder(); ParseableFileContentFinder finder = new ParseableFileContentFinder();
@ -279,7 +288,7 @@ namespace ICSharpCode.SharpDevelop.Parser
if (content == null && cachedPC != null) { if (content == null && cachedPC != null) {
parsedFile = cachedPC.GetFile(fileName); parsedFile = cachedPC.GetFile(fileName);
if (parsedFile != null && parsedFile.LastWriteTime == File.GetLastWriteTimeUtc(fileName)) { if (parsedFile != null && parsedFile.LastWriteTime == File.GetLastWriteTimeUtc(fileName)) {
SD.ParserService.RegisterParsedFile(fileName, project, parsedFile); parserService.RegisterParsedFile(fileName, project, parsedFile);
wasLoadedFromCache = true; wasLoadedFromCache = true;
} }
} }
@ -292,7 +301,7 @@ namespace ICSharpCode.SharpDevelop.Parser
} }
} }
if (content != null) { if (content != null) {
parsedFile = SD.ParserService.ParseFile(fileName, content, project); parsedFile = parserService.ParseFile(fileName, content, project);
} }
} }
lock (progressLock) { lock (progressLock) {
@ -314,88 +323,111 @@ namespace ICSharpCode.SharpDevelop.Parser
} }
} }
Task ResolveReferencesAsync(ICollection<ProjectItem> projectItems, IProgressMonitor progressMonitor) AtomicBoolean reparseCodeStartedButNotYetRunning;
public void ReparseCode()
{ {
return Task.Run( if (reparseCodeStartedButNotYetRunning.Set()) {
delegate { var filesToParse = (
var referenceItems = project.ResolveAssemblyReferences(progressMonitor.CancellationToken); from item in project.Items.OfType<FileProjectItem>()
const double assemblyResolvingProgress = 0.3; // 30% asm resolving, 70% asm loading where IsParseableFile(item)
progressMonitor.Progress += assemblyResolvingProgress; select FileName.Create(item.FileName)
progressMonitor.CancellationToken.ThrowIfCancellationRequested(); ).ToList();
SD.ParserService.LoadSolutionProjectsThread.AddJob(
List<string> assemblyFiles = new List<string>(); monitor => {
List<IAssemblyReference> newReferences = new List<IAssemblyReference>(); reparseCodeStartedButNotYetRunning.Reset();
DoReparseCode(filesToParse, monitor);
foreach (var reference in referenceItems) { },
ProjectReferenceProjectItem projectReference = reference as ProjectReferenceProjectItem; "Loading " + project.Name + "...", filesToParse.Count);
if (projectReference != null) { }
newReferences.Add(projectReference); }
} else {
assemblyFiles.Add(reference.FileName); void DoReparseCode(List<FileName> filesToParse, IProgressMonitor progressMonitor)
} {
IParserService parserService = SD.ParserService;
ParseableFileContentFinder finder = new ParseableFileContentFinder();
double fileCountInverse = 1.0 / filesToParse.Count;
object progressLock = new object();
Parallel.ForEach(
filesToParse,
new ParallelOptions {
MaxDegreeOfParallelism = Environment.ProcessorCount,
CancellationToken = progressMonitor.CancellationToken
},
fileName => {
ITextSource content = finder.Create(fileName);
if (content != null) {
parserService.ParseFile(fileName, content, project);
} }
lock (progressLock) {
foreach (string file in assemblyFiles) { progressMonitor.Progress += fileCountInverse;
progressMonitor.CancellationToken.ThrowIfCancellationRequested();
if (File.Exists(file)) {
var pc = SD.AssemblyParserService.GetAssembly(FileName.Create(file), progressMonitor.CancellationToken);
if (pc != null) {
newReferences.Add(pc);
}
}
progressMonitor.Progress += (1.0 - assemblyResolvingProgress) / assemblyFiles.Count;
} }
lock (lockObj) { });
projectContent = projectContent.RemoveAssemblyReferences(this.references).AddAssemblyReferences(newReferences); }
this.references = newReferences.ToArray(); #endregion
SD.ParserService.InvalidateCurrentSolutionSnapshot();
#region ResolveReferences
void DoResolveReferences(ICollection<ProjectItem> projectItems, IProgressMonitor progressMonitor)
{
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>();
foreach (var reference in referenceItems) {
ProjectReferenceProjectItem projectReference = reference as ProjectReferenceProjectItem;
if (projectReference != null) {
newReferences.Add(projectReference);
} else {
assemblyFiles.Add(reference.FileName);
}
}
foreach (string file in assemblyFiles) {
progressMonitor.CancellationToken.ThrowIfCancellationRequested();
if (File.Exists(file)) {
var pc = SD.AssemblyParserService.GetAssembly(FileName.Create(file), progressMonitor.CancellationToken);
if (pc != null) {
newReferences.Add(pc);
} }
}, progressMonitor.CancellationToken); }
progressMonitor.Progress += (1.0 - assemblyResolvingProgress) / assemblyFiles.Count;
}
lock (lockObj) {
if (!disposed) {
projectContent = projectContent.RemoveAssemblyReferences(this.references).AddAssemblyReferences(newReferences);
this.references = newReferences.ToArray();
SD.ParserService.InvalidateCurrentSolutionSnapshot();
}
}
} }
// ensure that com references are built serially because we cannot invoke multiple instances of MSBuild AtomicBoolean reparseReferencesStartedButNotYetRunning;
static Queue<Action> callAfterAddComReference = new Queue<Action>();
static bool buildingComReference;
public void ReparseReferences()
{
if (reparseReferencesStartedButNotYetRunning.Set()) {
SD.ParserService.LoadSolutionProjectsThread.AddJob(
monitor => {
reparseReferencesStartedButNotYetRunning.Reset();
DoResolveReferences(project.Items, monitor);
},
"Loading " + project.Name + "...", LoadingReferencesWorkAmount);
}
}
#endregion
#region Project Item Added/Removed
void OnProjectItemAdded(object sender, ProjectItemEventArgs e) void OnProjectItemAdded(object sender, ProjectItemEventArgs e)
{ {
if (e.Project != project) return; if (e.Project != project) return;
ReferenceProjectItem reference = e.ProjectItem as ReferenceProjectItem; ReferenceProjectItem reference = e.ProjectItem as ReferenceProjectItem;
if (reference != null) { if (reference != null) {
if (reference.ItemType == ItemType.COMReference) { ReparseReferences();
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");
TaskService.BuildMessageViewCategory.AppendText(message);
BuildCallback afterBuildCallback = delegate {
ReparseReferences();
lock (callAfterAddComReference) {
if (callAfterAddComReference.Count > 0) {
// run next enqueued action
callAfterAddComReference.Dequeue()();
} else {
buildingComReference = false;
}
}
};
BuildEngine.BuildInGui(project, new BuildOptions(BuildTarget.ResolveComReferences, afterBuildCallback));
};
// enqueue actions when adding multiple COM references so that multiple builds of the same project
// are not started parallely
lock (callAfterAddComReference) {
if (buildingComReference) {
callAfterAddComReference.Enqueue(action);
} else {
buildingComReference = true;
action();
}
}
} else {
ReparseReferences();
}
} }
FileProjectItem fileProjectItem = e.ProjectItem as FileProjectItem; FileProjectItem fileProjectItem = e.ProjectItem as FileProjectItem;
if (IsParseableFile(fileProjectItem)) { if (IsParseableFile(fileProjectItem)) {
@ -404,11 +436,6 @@ namespace ICSharpCode.SharpDevelop.Parser
} }
} }
void ReparseReferences()
{
throw new NotImplementedException();
}
void OnProjectItemRemoved(object sender, ProjectItemEventArgs e) void OnProjectItemRemoved(object sender, ProjectItemEventArgs e)
{ {
if (e.Project != project) return; if (e.Project != project) return;
@ -427,5 +454,6 @@ namespace ICSharpCode.SharpDevelop.Parser
SD.ParserService.RemoveOwnerProject(FileName.Create(e.ProjectItem.FileName), project); SD.ParserService.RemoveOwnerProject(FileName.Create(e.ProjectItem.FileName), project);
} }
} }
#endregion
} }
} }

60
src/Main/Base/Project/Src/Util/AtomicBoolean.cs

@ -0,0 +1,60 @@
// 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 System.Threading;
namespace ICSharpCode.SharpDevelop
{
/// <summary>
/// A boolean that starts 'false' and can be atomically set/reset.
/// </summary>
public struct AtomicBoolean
{
int val;
/// <summary>
/// Gets/Sets the value.
/// </summary>
public bool Value {
get {
return Volatile.Read(ref val) != 0;
}
set {
Volatile.Write(ref val, value ? 1 : 0);
}
}
/// <summary>
/// Sets the value to true.
/// </summary>
/// <returns>True if the value was successfully set from false to true,
/// false if the value already was true.</returns>
public bool Set()
{
return Interlocked.Exchange(ref val, 1) == 0;
}
/// <summary>
/// Sets the value to false.
/// </summary>
/// <returns>True if the value was successfully set from true to false,
/// false if the value already was false.</returns>
public bool Reset()
{
return Interlocked.Exchange(ref val, 0) != 0;
}
/// <inheritdoc/>
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return (obj is AtomicBoolean) && val == ((AtomicBoolean)obj).val;
}
}
}

4
src/Main/Core/Project/Src/AddInTree/AddInManager.cs

@ -265,7 +265,7 @@ namespace ICSharpCode.Core
/// </summary> /// </summary>
/// <param name="addIns"> /// <param name="addIns">
/// The list of AddIns to add. (use <see cref="AddIn"/> instances /// The list of AddIns to add. (use <see cref="AddIn"/> instances
/// created by <see cref="AddIn.Load(TextReader,string,XmlNameTable)"/>). /// created by <see cref="AddIn.Load(IAddInTree,TextReader,string,XmlNameTable)"/>).
/// </param> /// </param>
public static void AddExternalAddIns(IList<AddIn> addIns) public static void AddExternalAddIns(IList<AddIn> addIns)
{ {
@ -289,7 +289,7 @@ namespace ICSharpCode.Core
/// AddIns. /// AddIns.
/// </summary> /// </summary>
/// The list of AddIns to remove. /// The list of AddIns to remove.
/// (use external AddIns from the <see cref="AddInTree.AddIns"/> collection). /// (use external AddIns from the <see cref="IAddInTree.AddIns"/> collection).
public static void RemoveExternalAddIns(IList<AddIn> addIns) public static void RemoveExternalAddIns(IList<AddIn> addIns)
{ {
List<string> addInFiles = new List<string>(); List<string> addInFiles = new List<string>();

Loading…
Cancel
Save