Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorAaron Bockover <abock@microsoft.com>2019-07-03 23:10:19 +0300
committerGitHub <noreply@github.com>2019-07-03 23:10:19 +0300
commita5e6fa4c3fb65cebad4d0863dcf876913f3f6313 (patch)
tree28c8e7c25d9b2ecf6e224325721edf35e1655e47 /main
parent6fa0553335974757a356ef756094dd3502834dae (diff)
parent107025544efe812599ebcd5bcfdcc823825e7256 (diff)
Merge pull request #8123 from mono/backport-pr-7882-to-release-8.2
[release-8.2] Fixes VSTS 915954: "Rename File to {NewClassName}.cs" code action does not work
Diffstat (limited to 'main')
-rw-r--r--main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/TextViewContent.cs4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Documents/DocumentController.cs4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Documents/TextBufferFileModel.cs4
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Shell/DefaultWorkbench.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/RoslynDocumentExtension.cs2
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs121
-rw-r--r--main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DispatchService.cs30
7 files changed, 161 insertions, 6 deletions
diff --git a/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/TextViewContent.cs b/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/TextViewContent.cs
index ba0fc0e72e..24bd8b38eb 100644
--- a/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/TextViewContent.cs
+++ b/main/src/addins/MonoDevelop.TextEditor/MonoDevelop.TextEditor/TextViewContent.cs
@@ -236,8 +236,6 @@ namespace MonoDevelop.TextEditor
if (TextDocument == null)
return;
- UpdateTextBufferRegistration ();
-
warnOverwrite = false;
if (editorConfigContext != null) {
@@ -261,6 +259,8 @@ namespace MonoDevelop.TextEditor
//if (this.WorkbenchWindow?.Document != null)
// textEditor.InitializeExtensionChain (this.WorkbenchWindow.Document);
+ UpdateTextBufferRegistration ();
+
UpdateTextEditorOptions (null, null);
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Documents/DocumentController.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Documents/DocumentController.cs
index 759a537ed4..4299e121d5 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Documents/DocumentController.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Documents/DocumentController.cs
@@ -672,7 +672,9 @@ namespace MonoDevelop.Ide.Gui.Documents
// Get the list of nodes for which an extension has been created
- var allExtensions = extensionChain.GetAllExtensions ().OfType<DocumentControllerExtension> ().ToList ();
+ var allExtensions = (extensionChain.GetAllExtensions () ?? Array.Empty<DocumentControllerExtension>())
+ .OfType<DocumentControllerExtension> ().ToList ();
+
var loadedNodes = allExtensions.Where (ex => ex.SourceExtensionNode != null)
.Select (ex => ex.SourceExtensionNode.Data.NodeId).ToList ();
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Documents/TextBufferFileModel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Documents/TextBufferFileModel.cs
index 59f3193212..2941b70049 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Documents/TextBufferFileModel.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Documents/TextBufferFileModel.cs
@@ -107,6 +107,10 @@ namespace MonoDevelop.Ide.Gui.Documents
protected override void OnSetText (string text)
{
// OnLoad is always called before anything else, so the document should be ready
+ if (textDocument == null) {
+ return;
+ }
+
var edit = textDocument.TextBuffer.CreateEdit ();
edit.Replace (0, textDocument.TextBuffer.CurrentSnapshot.Length, text);
edit.Apply ();
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Shell/DefaultWorkbench.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Shell/DefaultWorkbench.cs
index 8ee60288a4..f7373dd586 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Shell/DefaultWorkbench.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Shell/DefaultWorkbench.cs
@@ -1065,7 +1065,7 @@ namespace MonoDevelop.Ide.Gui
bool IsInFullViewMode {
get {
- return dock.CurrentLayout != null && dock.CurrentLayout.EndsWith(fullViewModeTag, StringComparison.Ordinal);
+ return dock?.CurrentLayout?.EndsWith(fullViewModeTag, StringComparison.Ordinal) ?? false;
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/RoslynDocumentExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/RoslynDocumentExtension.cs
index c54d16a77a..d5153082dd 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/RoslynDocumentExtension.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/RoslynDocumentExtension.cs
@@ -136,6 +136,7 @@ namespace MonoDevelop.Ide.Gui
TryEditorInitialization ();
UpdateTextBufferRegistration ();
+ SubscribeControllerEvents ();
}
public void TryEditorInitialization ()
@@ -482,6 +483,7 @@ namespace MonoDevelop.Ide.Gui
if (doc != null)
return Task.CompletedTask;
}
+
if (Editor == null) {
UnsubscribeAnalysisDocument ();
UpdateTextBufferRegistration ();
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs
index aba2c748c8..7f59e41ec5 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs
@@ -995,6 +995,7 @@ namespace MonoDevelop.Ide.TypeSystem
// as a result, we can assume that the things it calls are _also_ main thread only
Runtime.CheckMainThread ();
lock (projectModifyLock) {
+ FileService.FreezeEvents ();
freezeProjectModify = true;
try {
var ret = base.TryApplyChanges (newSolution, progressTracker);
@@ -1024,6 +1025,7 @@ namespace MonoDevelop.Ide.TypeSystem
tryApplyState_documentTextChangedTasks.Clear ();
tryApplyState_changedProjects.Clear ();
freezeProjectModify = false;
+ FileService.ThawEvents ();
}
}
}
@@ -1040,6 +1042,7 @@ namespace MonoDevelop.Ide.TypeSystem
case ApplyChangesKind.RemoveMetadataReference:
case ApplyChangesKind.AddProjectReference:
case ApplyChangesKind.RemoveProjectReference:
+ case ApplyChangesKind.ChangeDocumentInfo:
return true;
default:
return false;
@@ -1295,8 +1298,124 @@ namespace MonoDevelop.Ide.TypeSystem
}
}
- #endregion
+ protected override void ApplyDocumentInfoChanged (DocumentId id, DocumentInfo info)
+ {
+ var currentSolution = CurrentSolution;
+ var document = currentSolution.GetDocument (id);
+ FailIfDocumentInfoChangesNotSupported (document, info);
+
+ // abort if the name is the same, as there's nothing to do
+ if (document.Name == info.Name
+ || string.IsNullOrEmpty (info.Name)
+ || string.IsNullOrEmpty (info.FilePath))
+ return;
+
+ MonoDevelop.Projects.Project mdProject = null;
+
+ if (id.ProjectId != null) {
+ var project = currentSolution.GetProject (id.ProjectId);
+ mdProject = GetMonoProject (project);
+ if (mdProject == null) {
+ LoggingService.LogWarning ("Couldn't find project for newly file file {0} (Project {1}).",
+ info.Name, info.Id.ProjectId);
+
+ return;
+ }
+ }
+
+ var guiDoc = documentManager
+ .Documents
+ .FirstOrDefault (d => d.IsFile
+ && document.FilePath.Equals (d.FilePath, FilePath.PathComparison));
+
+ DispatchService.PumpingWait (() => guiDoc.IsDirty ? guiDoc.Save () : Task.CompletedTask);
+
+ // TODO: the Visual Studio Windows implementation of this also adds an undo unit to the
+ // global undo manager which we don't currently support.
+
+ var newName = NameGenerator.GenerateUniqueName (
+ Path.GetFileNameWithoutExtension (info.Name),
+ Path.GetExtension (info.Name),
+ nx => !mdProject.Files.Any (p => string.Equals (p.Name, nx, FilePath.PathComparison)));
+
+ var mdFile = mdProject.GetProjectFile (document.FilePath);
+ if (mdFile == null) {
+ LoggingService.LogWarning ($"{document.FilePath} was not found in project in ApplyDocumentInfo");
+ return;
+ }
+
+ DispatchService.PumpingWait (() => {
+ return Runtime.RunInMainThread (() => {
+
+ FileService.RenameFile (document.FilePath, newName);
+
+ var childrenToRename = GetDependentFilesToRename (mdFile, newName);
+ if (childrenToRename != null) {
+ // we also need to rename children!
+ foreach (var child in childrenToRename) {
+ FileService.RenameFile (child.File.FilePath, child.NewName);
+ }
+ }
+ });
+ });
+
+ tryApplyState_changedProjects.Add (mdProject);
+ }
+
+ static List<(MonoDevelop.Projects.ProjectFile File, string NewName)> GetDependentFilesToRename (
+ MonoDevelop.Projects.ProjectFile file,
+ string newName)
+ {
+ if (!file.HasChildren)
+ return null;
+
+ List<(MonoDevelop.Projects.ProjectFile File, string NewName)> files = null;
+
+ string oldName = file.FilePath.FileName;
+ foreach (MonoDevelop.Projects.ProjectFile child in file.DependentChildren) {
+ string oldChildName = child.FilePath.FileName;
+ if (oldChildName.StartsWith (oldName, StringComparison.CurrentCultureIgnoreCase)) {
+ string childNewName = newName + oldChildName.Substring (oldName.Length);
+
+ if (files == null)
+ files = new List<(MonoDevelop.Projects.ProjectFile projectFile, string name)> ();
+ files.Add ((child, childNewName));
+ }
+ }
+ return files;
+ }
+
+ static void FailIfDocumentInfoChangesNotSupported (Document document, DocumentInfo updatedInfo)
+ {
+ if (document.SourceCodeKind != updatedInfo.SourceCodeKind) {
+ throw new InvalidOperationException (
+ $"This Workspace does not support changing a document's {nameof (document.SourceCodeKind)}.");
+ }
+
+ if (document.FilePath != updatedInfo.FilePath) {
+ throw new InvalidOperationException (
+ $"This Workspace does not support changing a document's {nameof (document.FilePath)}.");
+ }
+
+ if (document.Id != updatedInfo.Id) {
+ throw new InvalidOperationException (
+ $"This Workspace does not support changing a document's {nameof (document.Id)}.");
+ }
+
+ if (document.Folders != updatedInfo.Folders) {
+ throw new InvalidOperationException (
+ $"This Workspace does not support changing a document's {nameof (document.Folders)}.");
+ }
+
+ if (document.State.Attributes.IsGenerated != updatedInfo.IsGenerated) {
+ throw new InvalidOperationException (
+ $"This Workspace does not support changing a document's {nameof (document.State.Attributes.IsGenerated)} state.");
+ }
+ }
+
+ #endregion
+
internal Document GetDocument (DocumentId documentId, CancellationToken cancellationToken = default (CancellationToken))
{
var project = CurrentSolution.GetProject (documentId.ProjectId);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DispatchService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DispatchService.cs
index 17cc0257b7..8cbdec1fef 100644
--- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DispatchService.cs
+++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DispatchService.cs
@@ -196,7 +196,35 @@ namespace MonoDevelop.Ide
Gdk.Threads.Leave();
}
-
+
+ /// <summary>
+ /// This method executes an async method on the main thread, but does not
+ /// technically block the calling thread. It does however "loop" and pumps
+ /// events in the mean time. This is a workaround for times when some of our
+ /// model (which is aync) is called by an external party (i.e. Roslyn) which
+ /// does not support async methods and potentially causes a deadlock.
+ /// </summary>
+ /// <param name="asyncMethod"></param>
+ /// <remarks>See: https://github.com/mono/monodevelop/pull/7823</remarks>
+ internal static void PumpingWait (Func<Task> asyncMethod)
+ {
+ // This method can be called by Roslyn or the editor in a context which is not the GTK UI context
+ // that MonoDevelop uses. In that case, before starting async an operation that may queue
+ // task continuations into the current context, we switch to the GTK context, so that
+ // whatever is queued will be dispatched when we run RunPendingEvents.
+ var oldContext = SynchronizationContext.Current;
+ try {
+ SynchronizationContext.SetSynchronizationContext (Runtime.MainSynchronizationContext);
+ Task task = asyncMethod ();
+ // Can't wait for the task to finish synchronously since doing so would deadlock the UI thread.
+ while (task != null && !task.IsCompleted) {
+ DispatchService.RunPendingEvents (30);
+ }
+ } finally {
+ SynchronizationContext.SetSynchronizationContext (oldContext);
+ }
+ }
+
#region Animations
/// <summary>