From 65019e2fa32257c474c10f11b7e2915703622358 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 16 May 2009 14:47:36 +0000 Subject: [PATCH] Do not save the highlighter being used if the user didn't change it. Fixed SD2-1540: When "build only modified projects" is used, projects are not rebuilt after a "Clean" operation git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@4081 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Project/Src/Gui/TextEditorControl.cs | 2 +- .../Base/Project/Src/Project/BuildEngine.cs | 82 ++++++++++++++++--- .../Base/Project/Src/Project/BuildResults.cs | 2 +- .../Project/Src/Project/IBuildFeedbackSink.cs | 1 + .../CompileModifiedProjectsOnly.cs | 10 ++- .../Services/ProjectService/ProjectService.cs | 31 +++++++ .../Commands/TextAreaContextmenuCommands.cs | 9 +- .../Gui/Editor/SharpDevelopTextAreaControl.cs | 12 +++ .../Gui/Editor/TextEditorDisplayBinding.cs | 18 ++-- .../Base/Project/Src/Util/ExtensionMethods.cs | 36 ++++++++ 10 files changed, 173 insertions(+), 30 deletions(-) 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. ///