diff options
author | Mike Krüger <mkrueger@xamarin.com> | 2015-04-17 09:20:49 +0300 |
---|---|---|
committer | Mike Krüger <mkrueger@xamarin.com> | 2015-04-17 09:20:49 +0300 |
commit | 427b0a002f7e797a368b196de802f69f45e99ad5 (patch) | |
tree | 0976e0d88dadb237c4803d66750c89b52224b1bb | |
parent | 16f319d4067a2146af0bf10df44efbbe915beb2b (diff) |
[Ide] Projections now track editor updates.
Code behind editor gets now updated when a projection changes and
orignal projection segments get updated on text changes as well.
6 files changed, 164 insertions, 17 deletions
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs index f4ee56c67b..4571fefeb8 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs @@ -62,16 +62,22 @@ namespace MonoDevelop.Ide.Editor.Projection this.projectedEditor = projectedEditor; this.originalContext = originalContext; - var originalProjectId = TypeSystemService.GetProjectId (originalContext.Project); - var originalProject = TypeSystemService.Workspace.CurrentSolution.GetProject (originalProjectId); - - projectedDocument = originalProject.AddDocument ( - projectedEditor.FileName, - projectedEditor - ); + if (originalContext.Project != null) { + var originalProjectId = TypeSystemService.GetProjectId (originalContext.Project); + if (originalProjectId != null) { + var originalProject = TypeSystemService.Workspace.CurrentSolution.GetProject (originalProjectId); + if (originalProject != null) { + projectedDocument = originalProject.AddDocument ( + projectedEditor.FileName, + projectedEditor + ); + } + } + } projectedEditor.TextChanged += delegate(object sender, TextChangeEventArgs e) { - projectedDocument = projectedDocument.WithText (projectedEditor); + if (projectedDocument != null) + projectedDocument = projectedDocument.WithText (projectedEditor); ReparseDocument (); }; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/Projection.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/Projection.cs index 392de94cd7..432e88edd4 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/Projection.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/Projection.cs @@ -37,20 +37,38 @@ namespace MonoDevelop.Ide.Editor.Projection { public ITextDocument Document { get; private set; } - public IReadOnlyList<ProjectedSegment> ProjectedSegments { - get; - private set; + SegmentTree<ProjectedTreeSegment> originalProjections = new SegmentTree<ProjectedTreeSegment> (); + SegmentTree<ProjectedTreeSegment> projectedProjections = new SegmentTree<ProjectedTreeSegment> (); + + class ProjectedTreeSegment : TreeSegment + { + public ProjectedTreeSegment LinkedTo { get; set; } + + public ProjectedTreeSegment (int offset, int length) : base (offset, length) + { + } + } + + public IEnumerable<ProjectedSegment> ProjectedSegments { + get { + foreach (var treeSeg in originalProjections) { + yield return new ProjectedSegment (treeSeg.Offset, treeSeg.LinkedTo.Offset, treeSeg.Length); + } + } } TextEditor projectedEditor; - internal TextEditor ProjectedEditor { - get { + internal TextEditor ProjectedEditor + { + get + { return projectedEditor; } } ProjectedDocumentContext projectedDocumentContext; + TextEditor attachedEditor; internal DocumentContext ProjectedContext { get { @@ -59,11 +77,12 @@ namespace MonoDevelop.Ide.Editor.Projection } public TextEditor CreateProjectedEditor (DocumentContext originalContext) - { + { if (projectedEditor == null) { projectedEditor = TextEditorFactory.CreateNewEditor (Document); projectedDocumentContext = new ProjectedDocumentContext (projectedEditor, originalContext); projectedEditor.InitializeExtensionChain (projectedDocumentContext); + projectedProjections.InstallListener (projectedEditor); } return projectedEditor; } @@ -71,9 +90,41 @@ namespace MonoDevelop.Ide.Editor.Projection public Projection (ITextDocument document, IReadOnlyList<ProjectedSegment> projectedSegments) { if (document == null) - throw new ArgumentNullException ("document"); + throw new ArgumentNullException (nameof (document)); this.Document = document; - this.ProjectedSegments = projectedSegments; + + for (int i = 0; i < projectedSegments.Count; i++) { + var p = projectedSegments [i]; + var original = new ProjectedTreeSegment (p.Offset, p.Length); + var projected = new ProjectedTreeSegment (p.ProjectedOffset, p.Length); + original.LinkedTo = projected; + projected.LinkedTo = original; + originalProjections.Add (original); + projectedProjections.Add (projected); + } + } + + internal void Dettach () + { + attachedEditor.TextChanged += HandleTextChanged; + } + + internal void Attach (TextEditor textEditor) + { + attachedEditor = textEditor; + attachedEditor.TextChanged += HandleTextChanged; + } + + void HandleTextChanged (object sender, TextChangeEventArgs e) + { + foreach (var segment in originalProjections) { + if (segment.Contains (e.Offset)) { + var projectedOffset = e.Offset - segment.Offset + segment.LinkedTo.Offset; + projectedEditor.ReplaceText (projectedOffset, e.RemovalLength, e.InsertedText); + } + } + + originalProjections.UpdateOnTextReplace (sender, e); } } }
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SegmentTree.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SegmentTree.cs index 10222c059b..e22632c0b9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SegmentTree.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/SegmentTree.cs @@ -167,7 +167,7 @@ namespace MonoDevelop.Ide.Editor ownerDocument = null; } - void UpdateOnTextReplace (object sender, TextChangeEventArgs e) + internal void UpdateOnTextReplace (object sender, TextChangeEventArgs e) { if (e.RemovalLength == 0) { var length = e.InsertionLength; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs index 7f5ea69ace..f143276e14 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs @@ -1170,7 +1170,18 @@ namespace MonoDevelop.Ide.Editor { if (ctx == null) throw new ArgumentNullException ("ctx"); + if (this.projections != null) { + foreach (var projection in this.projections) { + projection.Dettach (); + } + } this.projections = projections; + if (projections != null) { + foreach (var projection in projections) { + projection.Attach (this); + } + } + if ((disabledFeatures & DisabledProjectionFeatures.SemanticHighlighting) != DisabledProjectionFeatures.SemanticHighlighting) { if (SemanticHighlighting is ProjectedSemanticHighlighting) { ((ProjectedSemanticHighlighting)SemanticHighlighting).UpdateProjection (projections); diff --git a/main/tests/UnitTests/MonoDevelop.Ide.Editor/TextEditorProjectionTests.cs b/main/tests/UnitTests/MonoDevelop.Ide.Editor/TextEditorProjectionTests.cs new file mode 100644 index 0000000000..5cc9c56ce9 --- /dev/null +++ b/main/tests/UnitTests/MonoDevelop.Ide.Editor/TextEditorProjectionTests.cs @@ -0,0 +1,78 @@ +// +// TextEditorProjectionTests.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2015 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using NUnit.Framework; +using System.Collections.Generic; +using MonoDevelop.Ide.Editor.Projection; +using MonoDevelop.Core.Text; +using MonoDevelop.Ide.Gui; +using MonoDevelop.CSharpBinding; +using UnitTests; +using MonoDevelop.CSharpBinding.Tests; +using System.Linq; + +namespace MonoDevelop.Ide.Editor +{ + [TestFixture] + public class TextEditorProjectionTests : TestBase + { + [Test] + public void TestProjectionUpdate () + { + var editor = TextEditorFactory.CreateNewEditor (); + editor.Text = "1234567890"; + + var projectedDocument = TextEditorFactory.CreateNewDocument ( + new StringTextSource ("__12__34__56__78__90"), + "a" + ); + + var segments = new List<ProjectedSegment> (); + for (int i = 0; i < 5; i++) { + segments.Add (new ProjectedSegment (i * 2, 2 + i * 4, 2)); + } + var projection = new Projection.Projection (projectedDocument, segments); + var tww = new TestWorkbenchWindow (); + var content = new TestViewContent (); + tww.ViewContent = content; + + var originalContext = new Document (tww); + var projectedEditor = projection.CreateProjectedEditor (originalContext); + editor.SetOrUpdateProjections (originalContext, new [] { projection }, TypeSystem.DisabledProjectionFeatures.All); + editor.InsertText (1, "foo"); + Assert.AreEqual ("__1foo2__34__56__78__90", projectedEditor.Text); + + Assert.AreEqual (2 , projection.ProjectedSegments.ElementAt (0).ProjectedOffset); + Assert.AreEqual (2 + "foo".Length, projection.ProjectedSegments.ElementAt (0).Length); + for (int i = 1; i < 5; i++) { + Assert.AreEqual (2 + i * 4 + "foo".Length, projection.ProjectedSegments.ElementAt (i).ProjectedOffset); + Assert.AreEqual (2, projection.ProjectedSegments.ElementAt (i).Length); + } + + } + } +} + diff --git a/main/tests/UnitTests/UnitTests.csproj b/main/tests/UnitTests/UnitTests.csproj index 95c9eeb799..37e796a3fe 100644 --- a/main/tests/UnitTests/UnitTests.csproj +++ b/main/tests/UnitTests/UnitTests.csproj @@ -281,6 +281,7 @@ <Compile Include="MonoDevelop.Ide.Editor\Tests\SimpleReadonlyDocument_Tests.cs" /> <Compile Include="MonoDevelop.Ide.Editor\Tests\MonoTextEditorImplementationTests.cs" /> <Compile Include="MonoDevelop.Ide.Editor\Commands\CodeCommentTests.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\TextEditorProjectionTests.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="..\..\md.targets" /> |