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: @@ -22,14 +22,55 @@ Stuff that was renamed/moved:
IReturnType -> ITypeReference (unresolved) or IType (resolved)
Location -> TextLocation in ICSharpCode.NRefactory
TextLocation -> moved to ICSharpCode.NRefactory
ParserService -> SD.ParserService
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.
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).
Static services replaced with interfaces:
To make writing unit tests easier, the static services in SharpDevelop are getting
replaced with interfaces. The class "SD" has static properties to get references
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:

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

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

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

@ -33,10 +33,12 @@ namespace CSharpBinding @@ -33,10 +33,12 @@ namespace CSharpBinding
readonly HighlightingColor fieldAccessColor;
readonly HighlightingColor valueKeywordColor;
readonly HighlightingColor parameterModifierColor;
readonly HighlightingColor inactiveCodeColor;
List<IDocumentLine> invalidLines = new List<IDocumentLine>();
List<CachedLine> cachedLines = new List<CachedLine>();
bool hasCrashed;
bool forceParseOnNextRefresh;
int lineNumber;
HighlightedLine line;
@ -61,6 +63,7 @@ namespace CSharpBinding @@ -61,6 +63,7 @@ namespace CSharpBinding
this.fieldAccessColor = highlightingDefinition.GetNamedColor("FieldAccess");
this.valueKeywordColor = highlightingDefinition.GetNamedColor("NullOrValueKeywords");
this.parameterModifierColor = highlightingDefinition.GetNamedColor("ParameterModifiers");
this.inactiveCodeColor = highlightingDefinition.GetNamedColor("InactiveCode");
SD.ParserService.ParseInformationUpdated += ParserService_ParseInformationUpdated;
SD.ParserService.LoadSolutionProjectsThread.Finished += ParserService_LoadSolutionProjectsThreadEnded;
@ -146,6 +149,7 @@ namespace CSharpBinding @@ -146,6 +149,7 @@ namespace CSharpBinding
{
cachedLines.Clear();
invalidLines.Clear();
forceParseOnNextRefresh = true;
syntaxHighlighter.InvalidateAll();
}
@ -208,7 +212,13 @@ namespace CSharpBinding @@ -208,7 +212,13 @@ namespace CSharpBinding
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 (!invalidLines.Contains(documentLine))
invalidLines.Add(documentLine);
@ -278,10 +288,10 @@ namespace CSharpBinding @@ -278,10 +288,10 @@ namespace CSharpBinding
{
if (color == null)
return;
if (start.Line == lineNumber && end.Line == lineNumber) {
if (start.Line <= lineNumber && end.Line >= lineNumber) {
int lineStartOffset = line.DocumentLine.Offset;
int startOffset = lineStartOffset + start.Column - 1;
int endOffset = lineStartOffset + end.Column - 1;
int startOffset = lineStartOffset + (start.Line == lineNumber ? start.Column - 1 : 0);
int endOffset = lineStartOffset + (end.Line == lineNumber ? end.Column - 1 : line.DocumentLine.Length);
if (line.Sections.Count > 0) {
HighlightedSection prevSection = line.Sections.Last();
if (startOffset < prevSection.Offset + prevSection.Length)
@ -347,12 +357,18 @@ namespace CSharpBinding @@ -347,12 +357,18 @@ namespace CSharpBinding
{
Expression target = invocationExpression.Target;
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
target.GetChildByRole(Roles.TargetExpression).AcceptVisitor(this);
// highlight the method call
var identifier = target.GetChildByRole(Roles.Identifier);
var invocationRR = resolver.Resolve(invocationExpression) as CSharpInvocationResolveResult;
if (invocationRR != null && !invocationRR.IsDelegateInvocation) {
Colorize(identifier, methodCallColor);
} else {
@ -364,8 +380,38 @@ namespace CSharpBinding @@ -364,8 +380,38 @@ namespace CSharpBinding
} else {
target.AcceptVisitor(this);
}
invocationExpression.Arguments.AcceptVisitor(this);
// Visit arguments and comments within the arguments:
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)
@ -380,34 +426,40 @@ namespace CSharpBinding @@ -380,34 +426,40 @@ namespace CSharpBinding
public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
//methodDeclaration.Attributes.AcceptVisitor(this);
methodDeclaration.ReturnType.AcceptVisitor(this);
methodDeclaration.PrivateImplementationType.AcceptVisitor(this);
Colorize(methodDeclaration.NameToken, methodCallColor);
methodDeclaration.TypeParameters.AcceptVisitor(this);
methodDeclaration.Parameters.AcceptVisitor(this);
methodDeclaration.Constraints.AcceptVisitor(this);
methodDeclaration.Body.AcceptVisitor(this);
for (AstNode child = methodDeclaration.FirstChild; child != null; child = child.NextSibling) {
if (child.StartLocation.Line <= lineNumber && child.EndLocation.Line >= lineNumber) {
if (child.Role == Roles.Identifier) {
// child == methodDeclaration.NameToken
Colorize(child, methodCallColor);
} else {
child.AcceptVisitor(this);
}
}
}
}
public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
{
//typeDeclaration.Attributes.AcceptVisitor(this);
if (typeDeclaration.ClassType == ClassType.Enum || typeDeclaration.ClassType == ClassType.Struct)
Colorize(typeDeclaration.NameToken, valueTypeColor);
else
Colorize(typeDeclaration.NameToken, referenceTypeColor);
typeDeclaration.TypeParameters.AcceptVisitor(this);
typeDeclaration.BaseTypes.AcceptVisitor(this);
typeDeclaration.Constraints.AcceptVisitor(this);
typeDeclaration.Members.AcceptVisitor(this);
// Type declarations often contain #if directives, so we must make sure
// to also visit the comments.
for (AstNode child = typeDeclaration.FirstChild; child != null; child = child.NextSibling) {
if (child.StartLocation.Line <= lineNumber && child.EndLocation.Line >= lineNumber) {
if (child.Role == Roles.Identifier) {
// child == typeDeclaration.NameToken
if (typeDeclaration.ClassType == ClassType.Enum || typeDeclaration.ClassType == ClassType.Struct)
Colorize(typeDeclaration.NameToken, valueTypeColor);
else
Colorize(typeDeclaration.NameToken, referenceTypeColor);
} else {
child.AcceptVisitor(this);
}
}
}
}
public override void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration)
{
//typeParameterDeclaration.Attributes.AcceptVisitor(this);
typeParameterDeclaration.Attributes.AcceptVisitor(this);
if (typeParameterDeclaration.Variance == VarianceModifier.Contravariant)
Colorize(typeParameterDeclaration.VarianceToken, parameterModifierColor);
@ -440,6 +492,13 @@ namespace CSharpBinding @@ -440,6 +492,13 @@ namespace CSharpBinding
}
variableInitializer.Initializer.AcceptVisitor(this);
}
public override void VisitComment(Comment comment)
{
if (comment.CommentType == CommentType.InactiveCode) {
Colorize(comment, inactiveCodeColor);
}
}
#endregion
}
}

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

@ -57,7 +57,7 @@ class DerivedClass : BaseClass { @@ -57,7 +57,7 @@ class DerivedClass : BaseClass {
var pc = new CSharpProjectContent().UpdateProjectContent(null, parseInfo.ParsedFile);
pc = pc.AddAssemblyReferences(new[] { Corlib });
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.GetCompilationForFile(textEditor.FileName)).Return(compilation);
CSharpCompletionBinding completion = new CSharpCompletionBinding();

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

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

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

@ -238,9 +238,14 @@ namespace ICSharpCode.SharpDevelop.Project @@ -238,9 +238,14 @@ namespace ICSharpCode.SharpDevelop.Project
lock (SyncRoot) {
if (parseProjectContentContainer == null)
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)]

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

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

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

@ -46,6 +46,7 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -46,6 +46,7 @@ namespace ICSharpCode.SharpDevelop.Parser
string cacheFileName;
#region Constructor + Dispose
public ParseProjectContentContainer(MSBuildBasedProject project, IProjectContent initialProjectContent)
{
if (project == null)
@ -92,6 +93,7 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -92,6 +93,7 @@ namespace ICSharpCode.SharpDevelop.Parser
if (serializeOnDispose)
SerializeAsync(cacheFileName, pc).FireAndForget();
}
#endregion
#region Caching logic (serialization)
@ -228,13 +230,7 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -228,13 +230,7 @@ namespace ICSharpCode.SharpDevelop.Parser
}
}
bool IsParseableFile(FileProjectItem projectItem)
{
if (projectItem == null || string.IsNullOrEmpty(projectItem.FileName))
return false;
return projectItem.ItemType == ItemType.Compile || projectItem.ItemType == ItemType.Page;
}
#region Initialize
void Initialize(IProgressMonitor progressMonitor, List<FileName> filesToParse)
{
ICollection<ProjectItem> projectItems = project.Items;
@ -248,16 +244,29 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -248,16 +244,29 @@ namespace ICSharpCode.SharpDevelop.Parser
using (IProgressMonitor initReferencesProgressMonitor = progressMonitor.CreateSubTask(LoadingReferencesWorkAmount * 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);
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);
ParseableFileContentFinder finder = new ParseableFileContentFinder();
@ -279,7 +288,7 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -279,7 +288,7 @@ namespace ICSharpCode.SharpDevelop.Parser
if (content == null && cachedPC != null) {
parsedFile = cachedPC.GetFile(fileName);
if (parsedFile != null && parsedFile.LastWriteTime == File.GetLastWriteTimeUtc(fileName)) {
SD.ParserService.RegisterParsedFile(fileName, project, parsedFile);
parserService.RegisterParsedFile(fileName, project, parsedFile);
wasLoadedFromCache = true;
}
}
@ -292,7 +301,7 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -292,7 +301,7 @@ namespace ICSharpCode.SharpDevelop.Parser
}
}
if (content != null) {
parsedFile = SD.ParserService.ParseFile(fileName, content, project);
parsedFile = parserService.ParseFile(fileName, content, project);
}
}
lock (progressLock) {
@ -314,88 +323,111 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -314,88 +323,111 @@ namespace ICSharpCode.SharpDevelop.Parser
}
}
Task ResolveReferencesAsync(ICollection<ProjectItem> projectItems, IProgressMonitor progressMonitor)
AtomicBoolean reparseCodeStartedButNotYetRunning;
public void ReparseCode()
{
return Task.Run(
delegate {
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);
}
if (reparseCodeStartedButNotYetRunning.Set()) {
var filesToParse = (
from item in project.Items.OfType<FileProjectItem>()
where IsParseableFile(item)
select FileName.Create(item.FileName)
).ToList();
SD.ParserService.LoadSolutionProjectsThread.AddJob(
monitor => {
reparseCodeStartedButNotYetRunning.Reset();
DoReparseCode(filesToParse, monitor);
},
"Loading " + project.Name + "...", filesToParse.Count);
}
}
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);
}
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.Progress += (1.0 - assemblyResolvingProgress) / assemblyFiles.Count;
lock (progressLock) {
progressMonitor.Progress += fileCountInverse;
}
lock (lockObj) {
projectContent = projectContent.RemoveAssemblyReferences(this.references).AddAssemblyReferences(newReferences);
this.references = newReferences.ToArray();
SD.ParserService.InvalidateCurrentSolutionSnapshot();
});
}
#endregion
#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
static Queue<Action> callAfterAddComReference = new Queue<Action>();
static bool buildingComReference;
AtomicBoolean reparseReferencesStartedButNotYetRunning;
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)
{
if (e.Project != project) return;
ReferenceProjectItem reference = e.ProjectItem as ReferenceProjectItem;
if (reference != null) {
if (reference.ItemType == ItemType.COMReference) {
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();
}
ReparseReferences();
}
FileProjectItem fileProjectItem = e.ProjectItem as FileProjectItem;
if (IsParseableFile(fileProjectItem)) {
@ -404,11 +436,6 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -404,11 +436,6 @@ namespace ICSharpCode.SharpDevelop.Parser
}
}
void ReparseReferences()
{
throw new NotImplementedException();
}
void OnProjectItemRemoved(object sender, ProjectItemEventArgs e)
{
if (e.Project != project) return;
@ -427,5 +454,6 @@ namespace ICSharpCode.SharpDevelop.Parser @@ -427,5 +454,6 @@ namespace ICSharpCode.SharpDevelop.Parser
SD.ParserService.RemoveOwnerProject(FileName.Create(e.ProjectItem.FileName), project);
}
}
#endregion
}
}

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

@ -0,0 +1,60 @@ @@ -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 @@ -265,7 +265,7 @@ namespace ICSharpCode.Core
/// </summary>
/// <param name="addIns">
/// 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>
public static void AddExternalAddIns(IList<AddIn> addIns)
{
@ -289,7 +289,7 @@ namespace ICSharpCode.Core @@ -289,7 +289,7 @@ namespace ICSharpCode.Core
/// AddIns.
/// </summary>
/// 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)
{
List<string> addInFiles = new List<string>();

Loading…
Cancel
Save