diff --git a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControl.cs b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControl.cs
index e765ba0054..373cd6850a 100644
--- a/src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControl.cs
+++ b/src/Libraries/ICSharpCode.TextEditor/Project/Src/Gui/TextEditorControl.cs
@@ -179,7 +179,7 @@ namespace ICSharpCode.TextEditor
}
}
- public void SetHighlighting(string name)
+ public virtual void SetHighlighting(string name)
{
Document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(name);
}
diff --git a/src/Main/Base/Project/Src/Project/BuildEngine.cs b/src/Main/Base/Project/Src/Project/BuildEngine.cs
index 02201f59e9..8c3a03cb74 100644
--- a/src/Main/Base/Project/Src/Project/BuildEngine.cs
+++ b/src/Main/Base/Project/Src/Project/BuildEngine.cs
@@ -17,31 +17,52 @@ namespace ICSharpCode.SharpDevelop.Project
///
/// Supports building a project with dependencies or the whole solution.
/// Creates a build graph and then builds it using topological sort.
- ///
/// Supports building multiple projects in parallel.
+ ///
+ /// This class is not related to MSBuild: it simply "builds" a project by calling IBuildable.StartBuild.
///
public sealed class BuildEngine
{
#region Building in the SharpDevelop GUI
static CancellableProgressMonitor guiBuildProgressMonitor;
+ ///
+ /// Starts to run a build inside the SharpDevelop GUI.
+ /// Only one build can run inside the GUI at one time.
+ ///
+ /// The project/solution to build.
+ /// The build options.
public static void BuildInGui(IBuildable project, BuildOptions options)
{
+ if (project == null)
+ throw new ArgumentNullException("project");
+ if (options == null)
+ throw new ArgumentNullException("options");
WorkbenchSingleton.AssertMainThread();
if (guiBuildProgressMonitor != null) {
BuildResults results = new BuildResults();
results.Add(new BuildError(null, Core.ResourceService.GetString("MainWindow.CompilerMessages.MSBuildAlreadyRunning")));
results.Result = BuildResultCode.MSBuildAlreadyRunning;
- options.Callback(results);
+ if (options.Callback != null) {
+ options.Callback(results);
+ }
} else {
guiBuildProgressMonitor = new CancellableProgressMonitor(StatusBarService.CreateProgressMonitor());
Gui.WorkbenchSingleton.Workbench.GetPad(typeof(Gui.CompilerMessageView)).BringPadToFront();
+ GuiBuildStarted.RaiseEvent(null, new BuildEventArgs(project, options));
StartBuild(project, options,
new MessageViewSink(TaskService.BuildMessageViewCategory),
guiBuildProgressMonitor);
}
}
+ // TODO: in 4.0, replace ProjectService.BuildStarted/BuildFinished with these events
+ public static event EventHandler GuiBuildStarted;
+ public static event EventHandler GuiBuildFinished;
+
+ ///
+ /// Gets whether there is a build currently running inside the SharpDevelop GUI.
+ ///
public static bool IsGuiBuildRunning {
get {
WorkbenchSingleton.AssertMainThread();
@@ -49,6 +70,10 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
+ ///
+ /// Cancels the build currently running inside the SharpDevelop GUI.
+ /// This method does nothing if no build is running.
+ ///
public static void CancelGuiBuild()
{
WorkbenchSingleton.AssertMainThread();
@@ -77,7 +102,16 @@ namespace ICSharpCode.SharpDevelop.Project
public void Done(bool success)
{
- WorkbenchSingleton.SafeThreadAsyncCall(delegate { guiBuildProgressMonitor = null; });
+ throw new InvalidOperationException("The Done(IBuildable,BuildOptions,BuildResults) method should be called instead.");
+ }
+
+ public void Done(IBuildable buildable, BuildOptions options, BuildResults results)
+ {
+ WorkbenchSingleton.SafeThreadAsyncCall(
+ delegate {
+ guiBuildProgressMonitor = null;
+ GuiBuildFinished.RaiseEvent(null, new BuildEventArgs(buildable, options, results));
+ });
}
}
@@ -152,6 +186,15 @@ namespace ICSharpCode.SharpDevelop.Project
#endregion
#region StartBuild
+ ///
+ /// Starts to run a build.
+ ///
+ /// The project/solution to build
+ /// The build options that should be used
+ /// The build feedback sink that receives the build output.
+ /// The output is nearly sent "as it comes in": sometimes output must wait because the BuildEngine
+ /// will ensure that output from two projects building in parallel isn't interleaved.
+ /// The progress monitor that receives build progress.
public static void StartBuild(IBuildable project, BuildOptions options, IBuildFeedbackSink realtimeBuildFeedbackSink, IProgressMonitor progressMonitor)
{
if (project == null)
@@ -180,8 +223,7 @@ namespace ICSharpCode.SharpDevelop.Project
else
engine.results.Add(new BuildError(null, "Cyclic dependency"));
engine.results.Result = BuildResultCode.BuildFileError;
- realtimeBuildFeedbackSink.Done(false);
- options.Callback(engine.results);
+ engine.ReportDone();
return;
}
@@ -288,12 +330,12 @@ namespace ICSharpCode.SharpDevelop.Project
readonly BuildOptions options;
IProgressMonitor progressMonitor;
BuildNode rootNode;
- IBuildable rootProject;
- BuildResults results = new BuildResults();
+ readonly IBuildable rootProject;
+ readonly BuildResults results = new BuildResults();
DateTime buildStart;
- List projectsCurrentlyBuilding = new List();
- List projectsReadyForBuildStart = new List();
+ readonly List projectsCurrentlyBuilding = new List();
+ readonly List projectsReadyForBuildStart = new List();
int workersToStart, runningWorkers;
private BuildEngine(BuildOptions options, IBuildable rootProject)
@@ -497,21 +539,35 @@ namespace ICSharpCode.SharpDevelop.Project
results.Result = BuildResultCode.Error;
ReportMessageInternal("${res:MainWindow.CompilerMessages.BuildFailed}" + buildTime);
- //StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildFailed}");
} else {
results.Result = BuildResultCode.Success;
ReportMessageInternal("${res:MainWindow.CompilerMessages.BuildFinished}" + buildTime);
- //StatusBarService.SetMessage("${res:MainWindow.CompilerMessages.BuildFinished}");
}
if (progressMonitor != null) {
progressMonitor.Done();
}
+ ReportDone();
+ }
+
+ ///
+ /// Notifies the callback hooks that the build is done.
+ ///
+ void ReportDone()
+ {
if (combinedBuildFeedbackSink != null) {
- combinedBuildFeedbackSink.Done(results.Result == BuildResultCode.Success);
+ if (combinedBuildFeedbackSink is MessageViewSink) {
+ // Special case GUI-builds so that they have more information available:
+ // (non-GUI builds can get the same information from the options.Callback,
+ // but the GUI cannot use the callback because the build options are set by
+ // the code triggering the build)
+ ((MessageViewSink)combinedBuildFeedbackSink).Done(rootProject, options, results);
+ } else {
+ combinedBuildFeedbackSink.Done(results.Result == BuildResultCode.Success);
+ }
}
if (options.Callback != null) {
- Gui.WorkbenchSingleton.SafeThreadAsyncCall(r => options.Callback(r), results);
+ Gui.WorkbenchSingleton.SafeThreadAsyncCall(delegate { options.Callback(results); });
}
}
#endregion
diff --git a/src/Main/Base/Project/Src/Project/BuildResults.cs b/src/Main/Base/Project/Src/Project/BuildResults.cs
index c2e67edc10..6d2721d62f 100644
--- a/src/Main/Base/Project/Src/Project/BuildResults.cs
+++ b/src/Main/Base/Project/Src/Project/BuildResults.cs
@@ -60,7 +60,7 @@ namespace ICSharpCode.SharpDevelop.Project
///
/// Adds a project to the list of built projects.
- /// This method is thread-sage.
+ /// This method is thread-safe.
///
public void AddBuiltProject(IBuildable buildable)
{
diff --git a/src/Main/Base/Project/Src/Project/IBuildFeedbackSink.cs b/src/Main/Base/Project/Src/Project/IBuildFeedbackSink.cs
index d4667e7b06..3b32909983 100644
--- a/src/Main/Base/Project/Src/Project/IBuildFeedbackSink.cs
+++ b/src/Main/Base/Project/Src/Project/IBuildFeedbackSink.cs
@@ -11,6 +11,7 @@ namespace ICSharpCode.SharpDevelop.Project
{
///
/// Interface for reporting build results in real-time.
+ /// Project-specific build engines use this interface to report results to the main build engine.
///
public interface IBuildFeedbackSink
{
diff --git a/src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs b/src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs
index daab240096..9f32dfd36d 100644
--- a/src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs
+++ b/src/Main/Base/Project/Src/Services/ProjectService/CompileModifiedProjectsOnly.cs
@@ -45,7 +45,7 @@ namespace ICSharpCode.SharpDevelop.Project
ProjectService.SolutionClosed += MarkAllForRecompilation;
ProjectService.SolutionConfigurationChanged += MarkAllForRecompilation;
ProjectService.SolutionSaved += MarkAllForRecompilation;
- ProjectService.EndBuild += ProjectService_EndBuild;
+ BuildEngine.GuiBuildFinished += BuildEngine_GuiBuildFinished;
FileUtility.FileSaved += OnFileSaved;
}
@@ -55,7 +55,7 @@ namespace ICSharpCode.SharpDevelop.Project
// first call to init causes static ctor calls
}
- static void ProjectService_EndBuild(object sender, BuildEventArgs e)
+ static void BuildEngine_GuiBuildFinished(object sender, BuildEventArgs e)
{
// at the end of an successful build, mark all built projects as unmodified
if (e.Results.Result == BuildResultCode.Success) {
@@ -69,6 +69,12 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
}
+ // at the end of a cleaning build, mark all projects as requiring a rebuild
+ if (e.Options.ProjectTarget == BuildTarget.Clean || e.Options.TargetForDependencies == BuildTarget.Clean) {
+ lock (unmodifiedProjects) {
+ unmodifiedProjects.Clear();
+ }
+ }
}
static IProject GetProjectFromBuildable(IBuildable b)
diff --git a/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs b/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs
index 81b057f323..469f7127b5 100644
--- a/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs
+++ b/src/Main/Base/Project/Src/Services/ProjectService/ProjectService.cs
@@ -662,10 +662,41 @@ namespace ICSharpCode.SharpDevelop.Project
public class BuildEventArgs : EventArgs
{
+ ///
+ /// The project/solution to be built.
+ ///
+ public readonly IBuildable Buildable;
+
+ ///
+ /// The build options.
+ ///
+ public readonly BuildOptions Options;
+
+ ///
+ /// Gets the build results.
+ /// This property is null for build started events.
+ ///
public readonly BuildResults Results;
+ public BuildEventArgs(IBuildable buildable, BuildOptions options)
+ : this(buildable, options, null)
+ {
+ }
+
+ public BuildEventArgs(IBuildable buildable, BuildOptions options, BuildResults results)
+ {
+ if (buildable == null)
+ throw new ArgumentNullException("buildable");
+ if (options == null)
+ throw new ArgumentNullException("options");
+ this.Buildable = buildable;
+ this.Options = options;
+ this.Results = results;
+ }
+
public BuildEventArgs(BuildResults results)
{
+ // TODO: remove this constructor in 4.0
this.Results = results;
}
}
diff --git a/src/Main/Base/Project/Src/TextEditor/Commands/TextAreaContextmenuCommands.cs b/src/Main/Base/Project/Src/TextEditor/Commands/TextAreaContextmenuCommands.cs
index 7ce2355ca6..d24bb0f0dc 100644
--- a/src/Main/Base/Project/Src/TextEditor/Commands/TextAreaContextmenuCommands.cs
+++ b/src/Main/Base/Project/Src/TextEditor/Commands/TextAreaContextmenuCommands.cs
@@ -58,14 +58,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands
}
item.Checked = true;
try {
- IHighlightingStrategy strat = HighlightingStrategyFactory.CreateHighlightingStrategy(item.Text);
- if (strat == null) {
- throw new Exception("Strategy can't be null");
- }
- control.Document.HighlightingStrategy = strat;
- if (control is SharpDevelopTextAreaControl) {
- ((SharpDevelopTextAreaControl)control).InitializeAdvancedHighlighter();
- }
+ control.SetHighlighting(item.Text);
} catch (HighlightingDefinitionInvalidException ex) {
MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
diff --git a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs
index b39da24834..d184961719 100644
--- a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs
+++ b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/SharpDevelopTextAreaControl.cs
@@ -462,6 +462,18 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
InitializeAdvancedHighlighter();
}
+ public bool HighlightingExplicitlySet { get; set; }
+
+ ///
+ /// Explicitly set the highlighting to use. Will be persisted.
+ ///
+ public override void SetHighlighting(string name)
+ {
+ base.SetHighlighting(name);
+ this.HighlightingExplicitlySet = true;
+ InitializeAdvancedHighlighter();
+ }
+
public void InitializeAdvancedHighlighter()
{
if (advancedHighlighter != null) {
diff --git a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/TextEditorDisplayBinding.cs b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/TextEditorDisplayBinding.cs
index d0a6fca9c9..8917deb83f 100644
--- a/src/Main/Base/Project/Src/TextEditor/Gui/Editor/TextEditorDisplayBinding.cs
+++ b/src/Main/Base/Project/Src/TextEditor/Gui/Editor/TextEditorDisplayBinding.cs
@@ -265,7 +265,9 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
Properties properties = new Properties();
properties.Set("CaretOffset", textEditorControl.ActiveTextAreaControl.Caret.Offset);
properties.Set("VisibleLine", textEditorControl.ActiveTextAreaControl.TextArea.TextView.FirstVisibleLine);
- properties.Set("HighlightingLanguage", textEditorControl.Document.HighlightingStrategy.Name);
+ if (textEditorControl.HighlightingExplicitlySet) {
+ properties.Set("HighlightingLanguage", textEditorControl.Document.HighlightingStrategy.Name);
+ }
return properties;
}
@@ -274,10 +276,16 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Gui.Editor
textEditorControl.ActiveTextAreaControl.Caret.Position = textEditorControl.Document.OffsetToPosition(Math.Min(textEditorControl.Document.TextLength, Math.Max(0, properties.Get("CaretOffset", textEditorControl.ActiveTextAreaControl.Caret.Offset))));
// textAreaControl.SetDesiredColumn();
- if (textEditorControl.Document.HighlightingStrategy.Name != properties.Get("HighlightingLanguage", textEditorControl.Document.HighlightingStrategy.Name)) {
- IHighlightingStrategy highlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(properties.Get("HighlightingLanguage", textEditorControl.Document.HighlightingStrategy.Name));
- if (highlightingStrategy != null) {
- textEditorControl.Document.HighlightingStrategy = highlightingStrategy;
+ string highlightingName = properties.Get("HighlightingLanguage", string.Empty);
+ if (!string.IsNullOrEmpty(highlightingName)) {
+ if (highlightingName == textEditorControl.Document.HighlightingStrategy.Name) {
+ textEditorControl.HighlightingExplicitlySet = true;
+ } else {
+ IHighlightingStrategy highlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategy(highlightingName);
+ if (highlightingStrategy != null) {
+ textEditorControl.HighlightingExplicitlySet = true;
+ textEditorControl.Document.HighlightingStrategy = highlightingStrategy;
+ }
}
}
textEditorControl.ActiveTextAreaControl.TextArea.TextView.FirstVisibleLine = properties.Get("VisibleLine", 0);
diff --git a/src/Main/Base/Project/Src/Util/ExtensionMethods.cs b/src/Main/Base/Project/Src/Util/ExtensionMethods.cs
index 138e4d25ba..0f325c6412 100644
--- a/src/Main/Base/Project/Src/Util/ExtensionMethods.cs
+++ b/src/Main/Base/Project/Src/Util/ExtensionMethods.cs
@@ -20,6 +20,42 @@ namespace ICSharpCode.SharpDevelop
///
public static class ExtensionMethods
{
+ ///
+ /// Raises the event.
+ /// Does nothing if eventHandler is null.
+ /// Because the event handler is passed as parameter, it is only fetched from the event field one time.
+ /// This makes
+ /// MyEvent.RaiseEvent(x,y);
+ /// thread-safe
+ /// whereas
+ /// if (MyEvent != null) MyEvent(x,y);
+ /// would not be safe.
+ ///
+ public static void RaiseEvent(this EventHandler eventHandler, object sender, EventArgs e)
+ {
+ if (eventHandler != null) {
+ eventHandler(sender, e);
+ }
+ }
+
+ ///
+ /// Raises the event.
+ /// Does nothing if eventHandler is null.
+ /// Because the event handler is passed as parameter, it is only fetched from the event field one time.
+ /// This makes
+ /// MyEvent.RaiseEvent(x,y);
+ /// thread-safe
+ /// whereas
+ /// if (MyEvent != null) MyEvent(x,y);
+ /// would not be safe.
+ ///
+ public static void RaiseEvent(this EventHandler eventHandler, object sender, T e) where T : EventArgs
+ {
+ if (eventHandler != null) {
+ eventHandler(sender, e);
+ }
+ }
+
///
/// Runs an action for all elements in the input.
///