From 3ce7c07e759b74482e7eb1d92654bd6d53034d24 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 11 Mar 2012 01:22:28 +0100 Subject: [PATCH] More work on context actions. --- .../Project/Src/Refactoring/SDScript.cs | 55 +- .../Src/ContextActionsRenderer.cs | 36 +- .../ICSharpCode.AvalonEdit/Editing/Caret.cs | 9 +- .../Editing/SimpleSelection.cs | 12 +- .../Ast/AstNode.cs | 22 +- .../Formatter/AstFormattingVisitor.cs | 520 ++++++++++-------- .../OutputVisitor/CSharpOutputVisitor.cs | 2 +- .../ContextAction/AddAnotherAccessor.cs | 2 +- .../ContextAction/CreateBackingStore.cs | 16 +- .../Refactoring/Script.cs | 22 +- .../Editor/ReadOnlyDocument.cs | 5 +- .../ContextActionsBulbViewModel.cs | 10 + 12 files changed, 419 insertions(+), 292 deletions(-) diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDScript.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDScript.cs index db855b390c..28e4eec6bd 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDScript.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/SDScript.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using CSharpBinding.Parser; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.NRefactory; using ICSharpCode.NRefactory.CSharp; @@ -10,6 +11,7 @@ using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Parser; namespace CSharpBinding.Refactoring { @@ -20,33 +22,32 @@ namespace CSharpBinding.Refactoring { int indentationSize = 4; readonly ITextEditor editor; - readonly TextSegmentCollection textSegmentCollection = new TextSegmentCollection(); - readonly OffsetChangeMap offsetChangeMap = new OffsetChangeMap(); - readonly List actions = new List(); + readonly IDocument originalDocument; + readonly TextSegmentCollection textSegmentCollection; + readonly IDisposable undoGroup; public SDScript(ITextEditor editor, string eolMarker) : base(eolMarker, new CSharpFormattingOptions()) { this.editor = editor; + this.textSegmentCollection = new TextSegmentCollection((TextDocument)editor.Document); + this.originalDocument = editor.Document.CreateDocumentSnapshot(); + undoGroup = editor.Document.OpenUndoGroup(); } public override int GetCurrentOffset(TextLocation originalDocumentLocation) { - int offset = editor.Document.GetOffset(originalDocumentLocation); - return offsetChangeMap.GetNewOffset(offset, AnchorMovementType.Default); + int offset = originalDocument.GetOffset(originalDocumentLocation); + return GetCurrentOffset(offset); } public override int GetCurrentOffset(int originalDocumentOffset) { - return offsetChangeMap.GetNewOffset(originalDocumentOffset, AnchorMovementType.Default); + return originalDocument.Version.MoveOffsetTo(editor.Document.Version, originalDocumentOffset, AnchorMovementType.Default); } public override void Replace(int offset, int length, string newText) { - var changeMapEntry = new OffsetChangeMapEntry(offset, length, newText.Length); - textSegmentCollection.UpdateOffsets(changeMapEntry); - offsetChangeMap.Add(changeMapEntry); - - actions.Add(delegate { editor.Document.Replace(offset, length, newText); }); + editor.Document.Replace(offset, length, newText); } protected override ISegment CreateTrackedSegment(int offset, int length) @@ -60,7 +61,7 @@ namespace CSharpBinding.Refactoring protected override int GetIndentLevelAt(int offset) { - int oldOffset = offsetChangeMap.Invert().GetNewOffset(offset, AnchorMovementType.Default); + int oldOffset = editor.Document.Version.MoveOffsetTo(originalDocument.Version, offset, AnchorMovementType.Default); var line = editor.Document.GetLineByOffset(oldOffset); int spaces = 0; int indentationLevel = 0; @@ -84,29 +85,53 @@ namespace CSharpBinding.Refactoring public override void Rename(IEntity entity, string name) { + // TODO } public override void InsertWithCursor(string operation, AstNode node, InsertPosition defaultPosition) { + // TODO } public override void FormatText(int offset, int length) { + var parseInfo = ParserService.Parse(editor.FileName, editor.Document) as CSharpFullParseInformation; + if (parseInfo != null) { + //var startLocation = editor.Document.GetLocation(offset); + //var endLocation = editor.Document.GetLocation(offset + length); + //var node = parseInfo.CompilationUnit.GetNodeContaining(startLocation, endLocation); + var formatter = new AstFormattingVisitor(new CSharpFormattingOptions(), editor.Document, false, 4); + parseInfo.CompilationUnit.AcceptVisitor(formatter); + formatter.ApplyChanges(offset, length); + } + } + + public override void Select(AstNode node) + { + var segment = GetSegment(node); + int startOffset = segment.Offset; + int endOffset = segment.EndOffset; + // If the area to select includes a newline (e.g. node is a statement), + // exclude that newline from the selection. + if (endOffset > startOffset && editor.Document.GetLineByOffset(endOffset).Offset == endOffset) { + endOffset = editor.Document.GetLineByOffset(endOffset).PreviousLine.EndOffset; + } + editor.Select(startOffset, endOffset - startOffset); } public override void Select(int offset, int length) { - actions.Add(delegate { editor.Select(offset, length); }); + editor.Select(offset, length); } public override void Link(params AstNode[] nodes) { + // TODO } public override void Dispose() { - foreach (var action in actions) - action(); + undoGroup.Dispose(); } } } diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ContextActionsRenderer.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ContextActionsRenderer.cs index f061aadd54..e57397a2a7 100644 --- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ContextActionsRenderer.cs +++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ContextActionsRenderer.cs @@ -67,7 +67,6 @@ namespace ICSharpCode.AvalonEdit.AddIn public void Dispose() { WorkbenchSingleton.Workbench.ActiveViewContentChanged -= WorkbenchSingleton_Workbench_ActiveViewContentChanged; - delayMoveTimer.Stop(); ClosePopup(); } @@ -75,8 +74,7 @@ namespace ICSharpCode.AvalonEdit.AddIn { if (this.popup == null) return; - if (e.Key == Key.T && Keyboard.Modifiers == ModifierKeys.Control) - { + if (e.Key == Key.T && Keyboard.Modifiers == ModifierKeys.Control) { if (popup.ViewModel != null && popup.ViewModel.Actions != null && popup.ViewModel.Actions.Count > 0) { popup.IsDropdownOpen = true; popup.Focus(); @@ -87,18 +85,20 @@ namespace ICSharpCode.AvalonEdit.AddIn this.cancellationTokenSourceForPopupBeingOpened = new CancellationTokenSource(); var cancellationToken = cancellationTokenSourceForPopupBeingOpened.Token; try { - await Task.WhenAll(popupVM.LoadActionsAsync(cancellationToken), popupVM.LoadHiddenActionsAsync(cancellationToken)); + await popupVM.LoadActionsAsync(cancellationToken); + if (popupVM.Actions.Count == 0) + await popupVM.LoadHiddenActionsAsync(cancellationToken); } catch (OperationCanceledException) { return; } if (cancellationToken.IsCancellationRequested) return; this.cancellationTokenSourceForPopupBeingOpened = null; - if (popupVM.HiddenActions.Count == 0) + if (popupVM.Actions.Count == 0 && popupVM.HiddenActions.Count == 0) return; this.popup.ViewModel = popupVM; this.popup.IsDropdownOpen = true; - this.popup.IsHiddenActionsExpanded = true; + this.popup.IsHiddenActionsExpanded = popupVM.Actions.Count == 0; this.popup.OpenAtLineStart(this.Editor); this.popup.Focus(); } @@ -107,17 +107,18 @@ namespace ICSharpCode.AvalonEdit.AddIn void ScrollChanged(object sender, EventArgs e) { - ClosePopup(); + StartTimer(); } CancellationTokenSource cancellationTokenSourceForPopupBeingOpened; async void TimerMoveTick(object sender, EventArgs e) { - this.delayMoveTimer.Stop(); - if (!IsEnabled) + if (!delayMoveTimer.IsEnabled) return; ClosePopup(); + if (!IsEnabled) + return; ContextActionsBulbViewModel popupVM = BuildPopupViewModel(this.Editor); this.cancellationTokenSourceForPopupBeingOpened = new CancellationTokenSource(); @@ -144,10 +145,16 @@ namespace ICSharpCode.AvalonEdit.AddIn } void CaretPositionChanged(object sender, EventArgs e) + { + StartTimer(); + } + + void StartTimer() { ClosePopup(); - this.delayMoveTimer.Stop(); - this.delayMoveTimer.Start(); + IViewContent activeViewContent = WorkbenchSingleton.Workbench.ActiveViewContent; + if (activeViewContent != null && activeViewContent.PrimaryFileName == this.Editor.FileName) + delayMoveTimer.Start(); } void ClosePopup() @@ -158,6 +165,7 @@ namespace ICSharpCode.AvalonEdit.AddIn cancellationTokenSourceForPopupBeingOpened = null; } + this.delayMoveTimer.Stop(); this.popup.Close(); this.popup.IsDropdownOpen = false; this.popup.IsHiddenActionsExpanded = false; @@ -166,11 +174,7 @@ namespace ICSharpCode.AvalonEdit.AddIn void WorkbenchSingleton_Workbench_ActiveViewContentChanged(object sender, EventArgs e) { // open the popup again if in current file - IViewContent activeViewContent = WorkbenchSingleton.Workbench.ActiveViewContent; - if (activeViewContent != null && activeViewContent.PrimaryFileName == this.Editor.FileName) - CaretPositionChanged(this, EventArgs.Empty); - else // otherwise close popup - ClosePopup(); + StartTimer(); } } } diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs index 17fa18fee3..de309b7b4b 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/Caret.cs @@ -167,7 +167,14 @@ namespace ICSharpCode.AvalonEdit.Editing { InvalidateVisualColumn(); if (storedCaretOffset >= 0) { - int newCaretOffset = e.GetNewOffset(storedCaretOffset, AnchorMovementType.Default); + // If the caret is at the end of a selection, we don't expand the selection if something + // is inserted at the end. Thus we also need to keep the caret in front of the insertion. + AnchorMovementType caretMovementType; + if (!textArea.Selection.IsEmpty && storedCaretOffset == textArea.Selection.SurroundingSegment.EndOffset) + caretMovementType = AnchorMovementType.BeforeInsertion; + else + caretMovementType = AnchorMovementType.Default; + int newCaretOffset = e.GetNewOffset(storedCaretOffset, caretMovementType); TextDocument document = textArea.Document; if (document != null) { // keep visual column diff --git a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs index bdddcaa182..6659bfd8d4 100644 --- a/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs +++ b/src/Libraries/AvalonEdit/ICSharpCode.AvalonEdit/Editing/SimpleSelection.cs @@ -89,10 +89,18 @@ namespace ICSharpCode.AvalonEdit.Editing { if (e == null) throw new ArgumentNullException("e"); + int newStartOffset, newEndOffset; + if (startOffset <= endOffset) { + newStartOffset = e.GetNewOffset(startOffset, AnchorMovementType.Default); + newEndOffset = Math.Max(newStartOffset, e.GetNewOffset(endOffset, AnchorMovementType.BeforeInsertion)); + } else { + newEndOffset = e.GetNewOffset(endOffset, AnchorMovementType.Default); + newStartOffset = Math.Max(newEndOffset, e.GetNewOffset(startOffset, AnchorMovementType.BeforeInsertion)); + } return Selection.Create( textArea, - new TextViewPosition(textArea.Document.GetLocation(e.GetNewOffset(startOffset, AnchorMovementType.Default)), start.VisualColumn), - new TextViewPosition(textArea.Document.GetLocation(e.GetNewOffset(endOffset, AnchorMovementType.Default)), end.VisualColumn) + new TextViewPosition(textArea.Document.GetLocation(newStartOffset), start.VisualColumn), + new TextViewPosition(textArea.Document.GetLocation(newEndOffset), end.VisualColumn) ); } diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs index 8d5d30441e..c9f4c7fd70 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs @@ -169,6 +169,13 @@ namespace ICSharpCode.NRefactory.CSharp public Role Role { get { return role; } + set { + if (value == null) + throw new ArgumentNullException("value"); + if (!value.IsValid(this)) + throw new ArgumentException("This node is not valid in the new role."); + role = value; + } } public AstNode NextSibling { @@ -350,7 +357,6 @@ namespace ICSharpCode.NRefactory.CSharp parent.lastChild = prevSibling; } parent = null; - role = Roles.Root; prevSibling = null; nextSibling = null; } @@ -408,7 +414,6 @@ namespace ICSharpCode.NRefactory.CSharp parent = null; prevSibling = null; nextSibling = null; - role = Roles.Root; } } @@ -450,7 +455,6 @@ namespace ICSharpCode.NRefactory.CSharp AstNode copy = (AstNode)MemberwiseClone (); // First, reset the shallow pointer copies copy.parent = null; - copy.role = Roles.Root; copy.firstChild = null; copy.lastChild = null; copy.prevSibling = null; @@ -590,6 +594,18 @@ namespace ICSharpCode.NRefactory.CSharp return result; } + /// + /// Gets the node that fully contains the range from startLocation to endLocation. + /// + public AstNode GetNodeContaining(TextLocation startLocation, TextLocation endLocation) + { + for (AstNode child = firstChild; child != null; child = child.nextSibling) { + if (child.StartLocation <= startLocation && endLocation <= child.EndLocation) + return child.GetNodeContaining(startLocation, endLocation); + } + return this; + } + public IEnumerable GetNodesBetween (int startLine, int startColumn, int endLine, int endColumn) { return GetNodesBetween (new TextLocation (startLine, startColumn), new TextLocation (endLine, endColumn)); diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs index a73bc2aef5..74450bcbdd 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs @@ -1,6 +1,6 @@ // // AstFormattingVisitor.cs -// +// // Author: // Mike Krüger // @@ -24,6 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; +using System.Diagnostics; using System.Text; using System.Collections.Generic; using System.Linq; @@ -35,10 +36,23 @@ namespace ICSharpCode.NRefactory.CSharp { public class AstFormattingVisitor : DepthFirstAstVisitor { + struct TextReplaceAction + { + internal readonly int Offset; + internal readonly int RemovalLength; + internal readonly string NewText; + + public TextReplaceAction(int offset, int removalLength, string newText) + { + this.Offset = offset; + this.RemovalLength = removalLength; + this.NewText = newText; + } + } + CSharpFormattingOptions policy; IDocument document; - Script script; - //List changes = new List (); + List changes = new List (); Indent curIndent = new Indent (); public int IndentLevel { @@ -67,22 +81,70 @@ namespace ICSharpCode.NRefactory.CSharp public string EolMarker { get; set; } - public AstFormattingVisitor (CSharpFormattingOptions policy, IDocument document, Script script, bool tabsToSpaces = false, int indentationSize = 4) + public AstFormattingVisitor (CSharpFormattingOptions policy, IDocument document, bool tabsToSpaces = false, int indentationSize = 4) { if (policy == null) throw new ArgumentNullException("policy"); if (document == null) throw new ArgumentNullException("document"); - if (script == null) - throw new ArgumentNullException("script"); this.policy = policy; this.document = document; - this.script = script; this.curIndent.TabsToSpaces = tabsToSpaces; this.curIndent.TabSize = indentationSize; this.EolMarker = Environment.NewLine; CorrectBlankLines = true; } + + /// + /// Applies the changes to the input document. + /// + public void ApplyChanges() + { + ApplyChanges(0, document.TextLength, document.Replace); + } + + public void ApplyChanges(int startOffset, int length) + { + ApplyChanges(startOffset, length, document.Replace); + } + + /// + /// Applies the changes to the given Script instance. + /// + public void ApplyChanges(Script script) + { + ApplyChanges(0, document.TextLength, script.Replace); + } + + public void ApplyChanges(int startOffset, int length, Script script) + { + ApplyChanges(startOffset, length, script.Replace); + } + + void ApplyChanges(int startOffset, int length, Action documentReplace) + { + int endOffset = startOffset + length; + int lastChangeEnd = 0; + int delta = 0; + foreach (var change in changes.OrderBy(c => c.Offset)) { + if (change.Offset < lastChangeEnd) { + Debug.Fail("Detected overlapping change"); + continue; + } + lastChangeEnd = change.Offset + change.RemovalLength; + if (change.Offset < startOffset) { + // skip all changes in front of the begin offset + continue; + } else if (change.Offset > endOffset) { + // skip this change unless it depends on one that we already applied + continue; + } + + documentReplace(change.Offset + delta, change.RemovalLength, change.NewText); + delta += change.NewText.Length - change.RemovalLength; + } + changes.Clear(); + } public override object VisitCompilationUnit (CompilationUnit unit, object data) { @@ -137,9 +199,9 @@ namespace ICSharpCode.NRefactory.CSharp public override object VisitUsingDeclaration (UsingDeclaration usingDeclaration, object data) { - if (!(usingDeclaration.PrevSibling is UsingDeclaration || usingDeclaration.PrevSibling is UsingAliasDeclaration)) + if (!(usingDeclaration.PrevSibling is UsingDeclaration || usingDeclaration.PrevSibling is UsingAliasDeclaration)) EnsureBlankLinesBefore (usingDeclaration, policy.BlankLinesBeforeUsings); - if (!(usingDeclaration.NextSibling is UsingDeclaration || usingDeclaration.NextSibling is UsingAliasDeclaration)) + if (!(usingDeclaration.NextSibling is UsingDeclaration || usingDeclaration.NextSibling is UsingAliasDeclaration)) EnsureBlankLinesAfter (usingDeclaration, policy.BlankLinesAfterUsings); return null; @@ -147,9 +209,9 @@ namespace ICSharpCode.NRefactory.CSharp public override object VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration, object data) { - if (!(usingDeclaration.PrevSibling is UsingDeclaration || usingDeclaration.PrevSibling is UsingAliasDeclaration)) + if (!(usingDeclaration.PrevSibling is UsingDeclaration || usingDeclaration.PrevSibling is UsingAliasDeclaration)) EnsureBlankLinesBefore (usingDeclaration, policy.BlankLinesBeforeUsings); - if (!(usingDeclaration.NextSibling is UsingDeclaration || usingDeclaration.NextSibling is UsingAliasDeclaration)) + if (!(usingDeclaration.NextSibling is UsingDeclaration || usingDeclaration.NextSibling is UsingAliasDeclaration)) EnsureBlankLinesAfter (usingDeclaration, policy.BlankLinesAfterUsings); return null; } @@ -176,24 +238,24 @@ namespace ICSharpCode.NRefactory.CSharp BraceStyle braceStyle; bool indentBody = false; switch (typeDeclaration.ClassType) { - case ClassType.Class: - braceStyle = policy.ClassBraceStyle; - indentBody = policy.IndentClassBody; - break; - case ClassType.Struct: - braceStyle = policy.StructBraceStyle; - indentBody = policy.IndentStructBody; - break; - case ClassType.Interface: - braceStyle = policy.InterfaceBraceStyle; - indentBody = policy.IndentInterfaceBody; - break; - case ClassType.Enum: - braceStyle = policy.EnumBraceStyle; - indentBody = policy.IndentEnumBody; - break; - default: - throw new InvalidOperationException ("unsupported class type : " + typeDeclaration.ClassType); + case ClassType.Class: + braceStyle = policy.ClassBraceStyle; + indentBody = policy.IndentClassBody; + break; + case ClassType.Struct: + braceStyle = policy.StructBraceStyle; + indentBody = policy.IndentStructBody; + break; + case ClassType.Interface: + braceStyle = policy.InterfaceBraceStyle; + indentBody = policy.IndentInterfaceBody; + break; + case ClassType.Enum: + braceStyle = policy.EnumBraceStyle; + indentBody = policy.IndentEnumBody; + break; + default: + throw new InvalidOperationException ("unsupported class type : " + typeDeclaration.ClassType); } EnforceBraceStyle (braceStyle, typeDeclaration.LBraceToken, typeDeclaration.RBraceToken); @@ -302,10 +364,10 @@ namespace ICSharpCode.NRefactory.CSharp // if (n == null || n.IsNull) // return 0; // AstLocation location = n.StartLocation; -// +// // int offset = data.LocationToOffset (location.Line, location.Column); // int i = offset - 1; -// +// // while (i >= 0 && IsSpacing (data.GetCharAt (i))) { // i--; // } @@ -321,7 +383,7 @@ namespace ICSharpCode.NRefactory.CSharp // respect manual line breaks. if (location.Column <= 1 || GetIndentation (location.Line).Length == location.Column - 1) return 0; - + int offset = document.GetOffset (location); int i = offset - 1; while (i >= 0 && IsSpacing (document.GetCharAt (i))) { @@ -336,39 +398,39 @@ namespace ICSharpCode.NRefactory.CSharp FormatAttributedNode (propertyDeclaration); bool oneLine = false; switch (policy.PropertyFormatting) { - case PropertyFormatting.AllowOneLine: - bool isSimple = IsSimpleAccessor (propertyDeclaration.Getter) && IsSimpleAccessor (propertyDeclaration.Setter); - if (!isSimple || propertyDeclaration.LBraceToken.StartLocation.Line != propertyDeclaration.RBraceToken.StartLocation.Line) { - EnforceBraceStyle (policy.PropertyBraceStyle, propertyDeclaration.LBraceToken, propertyDeclaration.RBraceToken); - } else { - ForceSpacesBefore (propertyDeclaration.Getter, true); - ForceSpacesBefore (propertyDeclaration.Setter, true); - ForceSpacesBefore (propertyDeclaration.RBraceToken, true); - oneLine = true; - } - break; - case PropertyFormatting.ForceNewLine: - EnforceBraceStyle (policy.PropertyBraceStyle, propertyDeclaration.LBraceToken, propertyDeclaration.RBraceToken); - break; - case PropertyFormatting.ForceOneLine: - isSimple = IsSimpleAccessor (propertyDeclaration.Getter) && IsSimpleAccessor (propertyDeclaration.Setter); - if (isSimple) { - int offset = this.document.GetOffset (propertyDeclaration.LBraceToken.StartLocation); - - int start = SearchWhitespaceStart (offset); - int end = SearchWhitespaceEnd (offset); - AddChange (start, offset - start, " "); - AddChange (offset + 1, end - offset - 2, " "); - - offset = this.document.GetOffset (propertyDeclaration.RBraceToken.StartLocation); - start = SearchWhitespaceStart (offset); - AddChange (start, offset - start, " "); - oneLine = true; - - } else { + case PropertyFormatting.AllowOneLine: + bool isSimple = IsSimpleAccessor (propertyDeclaration.Getter) && IsSimpleAccessor (propertyDeclaration.Setter); + if (!isSimple || propertyDeclaration.LBraceToken.StartLocation.Line != propertyDeclaration.RBraceToken.StartLocation.Line) { + EnforceBraceStyle (policy.PropertyBraceStyle, propertyDeclaration.LBraceToken, propertyDeclaration.RBraceToken); + } else { + ForceSpacesBefore (propertyDeclaration.Getter, true); + ForceSpacesBefore (propertyDeclaration.Setter, true); + ForceSpacesBefore (propertyDeclaration.RBraceToken, true); + oneLine = true; + } + break; + case PropertyFormatting.ForceNewLine: EnforceBraceStyle (policy.PropertyBraceStyle, propertyDeclaration.LBraceToken, propertyDeclaration.RBraceToken); - } - break; + break; + case PropertyFormatting.ForceOneLine: + isSimple = IsSimpleAccessor (propertyDeclaration.Getter) && IsSimpleAccessor (propertyDeclaration.Setter); + if (isSimple) { + int offset = this.document.GetOffset (propertyDeclaration.LBraceToken.StartLocation); + + int start = SearchWhitespaceStart (offset); + int end = SearchWhitespaceEnd (offset); + AddChange (start, offset - start, " "); + AddChange (offset + 1, end - offset - 2, " "); + + offset = this.document.GetOffset (propertyDeclaration.RBraceToken.StartLocation); + start = SearchWhitespaceStart (offset); + AddChange (start, offset - start, " "); + oneLine = true; + + } else { + EnforceBraceStyle (policy.PropertyBraceStyle, propertyDeclaration.LBraceToken, propertyDeclaration.RBraceToken); + } + break; } if (policy.IndentPropertyBody) IndentLevel++; @@ -686,7 +748,7 @@ namespace ICSharpCode.NRefactory.CSharp ForceSpacesBefore (constructorDeclaration.RParToken, policy.SpaceBetweenEmptyConstructorDeclarationParentheses); } FormatCommas (constructorDeclaration, policy.SpaceBeforeConstructorDeclarationParameterComma, policy.SpaceAfterConstructorDeclarationParameterComma); - + object result = null; if (!constructorDeclaration.Body.IsNull) { EnforceBraceStyle (policy.ConstructorBraceStyle, constructorDeclaration.Body.LBraceToken, constructorDeclaration.Body.RBraceToken); @@ -809,53 +871,53 @@ namespace ICSharpCode.NRefactory.CSharp int originalLevel = curIndent.Level; bool isBlock = node is BlockStatement; switch (braceForcement) { - case BraceForcement.DoNotChange: - //nothing - break; - case BraceForcement.AddBraces: - if (!isBlock) { - AstNode n = node.Parent.GetCSharpNodeBefore (node); - int start = document.GetOffset (n.EndLocation); - var next = n.GetNextNode (); - int offset = document.GetOffset (next.StartLocation); - string startBrace = ""; - switch (braceStyle) { - case BraceStyle.EndOfLineWithoutSpace: - startBrace = "{"; - break; - case BraceStyle.EndOfLine: - startBrace = " {"; - break; - case BraceStyle.NextLine: - startBrace = this.EolMarker + curIndent.IndentString + "{"; - break; - case BraceStyle.NextLineShifted2: - case BraceStyle.NextLineShifted: - startBrace = this.EolMarker + curIndent.IndentString + curIndent.SingleIndent + "{"; - break; + case BraceForcement.DoNotChange: + //nothing + break; + case BraceForcement.AddBraces: + if (!isBlock) { + AstNode n = node.Parent.GetCSharpNodeBefore (node); + int start = document.GetOffset (n.EndLocation); + var next = n.GetNextNode (); + int offset = document.GetOffset (next.StartLocation); + string startBrace = ""; + switch (braceStyle) { + case BraceStyle.EndOfLineWithoutSpace: + startBrace = "{"; + break; + case BraceStyle.EndOfLine: + startBrace = " {"; + break; + case BraceStyle.NextLine: + startBrace = this.EolMarker + curIndent.IndentString + "{"; + break; + case BraceStyle.NextLineShifted2: + case BraceStyle.NextLineShifted: + startBrace = this.EolMarker + curIndent.IndentString + curIndent.SingleIndent + "{"; + break; + } + if (IsLineIsEmptyUpToEol (document.GetOffset (node.StartLocation))) + startBrace += this.EolMarker + GetIndentation (node.StartLocation.Line); + AddChange (start, offset - start, startBrace); } - if (IsLineIsEmptyUpToEol (document.GetOffset (node.StartLocation))) - startBrace += this.EolMarker + GetIndentation (node.StartLocation.Line); - AddChange (start, offset - start, startBrace); - } - break; - case BraceForcement.RemoveBraces: - if (isBlock) { - BlockStatement block = node as BlockStatement; - if (block.Statements.Count () == 1) { - int offset1 = document.GetOffset (node.StartLocation); - int start = SearchWhitespaceStart (offset1); - - int offset2 = document.GetOffset (node.EndLocation); - int end = SearchWhitespaceStart (offset2 - 1); - - AddChange (start, offset1 - start + 1, null); - AddChange (end + 1, offset2 - end, null); - node = block.FirstChild; - isBlock = false; + break; + case BraceForcement.RemoveBraces: + if (isBlock) { + BlockStatement block = node as BlockStatement; + if (block.Statements.Count () == 1) { + int offset1 = document.GetOffset (node.StartLocation); + int start = SearchWhitespaceStart (offset1); + + int offset2 = document.GetOffset (node.EndLocation); + int end = SearchWhitespaceStart (offset2 - 1); + + AddChange (start, offset1 - start + 1, null); + AddChange (end + 1, offset2 - end, null); + node = block.FirstChild; + isBlock = false; + } } - } - break; + break; } if (isBlock) { BlockStatement block = node as BlockStatement; @@ -873,42 +935,42 @@ namespace ICSharpCode.NRefactory.CSharp } } if (policy.IndentBlocks && - !(policy.AlignEmbeddedIfStatements && node is IfElseStatement && node.Parent is IfElseStatement || - policy.AlignEmbeddedUsingStatements && node is UsingStatement && node.Parent is UsingStatement)) + !(policy.AlignEmbeddedIfStatements && node is IfElseStatement && node.Parent is IfElseStatement || + policy.AlignEmbeddedUsingStatements && node is UsingStatement && node.Parent is UsingStatement)) curIndent.Level++; object result = isBlock ? base.VisitBlockStatement ((BlockStatement)node, null) : node.AcceptVisitor (this, null); curIndent.Level = originalLevel; switch (braceForcement) { - case BraceForcement.DoNotChange: - break; - case BraceForcement.AddBraces: - if (!isBlock) { - int offset = document.GetOffset (node.EndLocation); - if (!char.IsWhiteSpace (document.GetCharAt (offset))) - offset++; - string startBrace = ""; - switch (braceStyle) { - case BraceStyle.DoNotChange: - startBrace = null; - break; - case BraceStyle.EndOfLineWithoutSpace: - startBrace = this.EolMarker + curIndent.IndentString + "}"; - break; - case BraceStyle.EndOfLine: - startBrace = this.EolMarker + curIndent.IndentString + "}"; - break; - case BraceStyle.NextLine: - startBrace = this.EolMarker + curIndent.IndentString + "}"; - break; - case BraceStyle.NextLineShifted2: - case BraceStyle.NextLineShifted: - startBrace = this.EolMarker + curIndent.IndentString + curIndent.SingleIndent + "}"; - break; + case BraceForcement.DoNotChange: + break; + case BraceForcement.AddBraces: + if (!isBlock) { + int offset = document.GetOffset (node.EndLocation); + if (!char.IsWhiteSpace (document.GetCharAt (offset))) + offset++; + string startBrace = ""; + switch (braceStyle) { + case BraceStyle.DoNotChange: + startBrace = null; + break; + case BraceStyle.EndOfLineWithoutSpace: + startBrace = this.EolMarker + curIndent.IndentString + "}"; + break; + case BraceStyle.EndOfLine: + startBrace = this.EolMarker + curIndent.IndentString + "}"; + break; + case BraceStyle.NextLine: + startBrace = this.EolMarker + curIndent.IndentString + "}"; + break; + case BraceStyle.NextLineShifted2: + case BraceStyle.NextLineShifted: + startBrace = this.EolMarker + curIndent.IndentString + curIndent.SingleIndent + "}"; + break; + } + if (startBrace != null) + AddChange (offset, 0, startBrace); } - if (startBrace != null) - AddChange (offset, 0, startBrace); - } - break; + break; } return result; } @@ -928,39 +990,39 @@ namespace ICSharpCode.NRefactory.CSharp string startIndent = ""; string endIndent = ""; switch (braceStyle) { - case BraceStyle.DoNotChange: - startIndent = endIndent = null; - break; - case BraceStyle.EndOfLineWithoutSpace: - startIndent = ""; - endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString : this.EolMarker + curIndent.IndentString; - break; - case BraceStyle.EndOfLine: - var prevNode = lbrace.GetPrevNode (); - if (prevNode is Comment) { - // delete old bracket - AddChange (whitespaceStart, lbraceOffset - whitespaceStart + 1, ""); - - while (prevNode is Comment) { - prevNode = prevNode.GetPrevNode (); + case BraceStyle.DoNotChange: + startIndent = endIndent = null; + break; + case BraceStyle.EndOfLineWithoutSpace: + startIndent = ""; + endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString : this.EolMarker + curIndent.IndentString; + break; + case BraceStyle.EndOfLine: + var prevNode = lbrace.GetPrevNode (); + if (prevNode is Comment) { + // delete old bracket + AddChange (whitespaceStart, lbraceOffset - whitespaceStart + 1, ""); + + while (prevNode is Comment) { + prevNode = prevNode.GetPrevNode (); + } + whitespaceStart = document.GetOffset (prevNode.EndLocation); + lbraceOffset = whitespaceStart; + startIndent = " {"; + } else { + startIndent = " "; } - whitespaceStart = document.GetOffset (prevNode.EndLocation); - lbraceOffset = whitespaceStart; - startIndent = " {"; - } else { - startIndent = " "; - } - endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString : this.EolMarker + curIndent.IndentString; - break; - case BraceStyle.NextLine: - startIndent = this.EolMarker + curIndent.IndentString; - endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString : this.EolMarker + curIndent.IndentString; - break; - case BraceStyle.NextLineShifted2: - case BraceStyle.NextLineShifted: - startIndent = this.EolMarker + curIndent.IndentString + curIndent.SingleIndent; - endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString + curIndent.SingleIndent : this.EolMarker + curIndent.IndentString + curIndent.SingleIndent; - break; + endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString : this.EolMarker + curIndent.IndentString; + break; + case BraceStyle.NextLine: + startIndent = this.EolMarker + curIndent.IndentString; + endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString : this.EolMarker + curIndent.IndentString; + break; + case BraceStyle.NextLineShifted2: + case BraceStyle.NextLineShifted: + startIndent = this.EolMarker + curIndent.IndentString + curIndent.SingleIndent; + endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString + curIndent.SingleIndent : this.EolMarker + curIndent.IndentString + curIndent.SingleIndent; + break; } if (lbraceOffset > 0 && startIndent != null) @@ -971,35 +1033,7 @@ namespace ICSharpCode.NRefactory.CSharp void AddChange (int offset, int removedChars, string insertedText) { - script.Replace(offset, removedChars, insertedText); -// if (changes.Any (c => c.Offset == offset && c.RemovedChars == removedChars -// && c.InsertedText == insertedText)) -// return; -// string currentText = document.GetText (offset, removedChars); -// if (currentText == insertedText) -// return; -// if (currentText.Any (c => !(char.IsWhiteSpace (c) || c == '\r' || c == '\t' || c == '{' || c == '}'))) -// throw new InvalidOperationException ("Tried to remove non ws chars: '" + currentText + "'"); -// foreach (var change in changes) { -// if (change.Offset == offset) { -// if (removedChars > 0 && insertedText == change.InsertedText) { -// change.RemovedChars = removedChars; -//// change.InsertedText = insertedText; -// return; -// } -// if (!string.IsNullOrEmpty (change.InsertedText)) { -// change.InsertedText += insertedText; -// } else { -// change.InsertedText = insertedText; -// } -// change.RemovedChars = System.Math.Max (removedChars, change.RemovedChars); -// return; -// } -// } -// //Console.WriteLine ("offset={0}, removedChars={1}, insertedText={2}", offset, removedChars, insertedText == null ? "" : insertedText.Replace ("\n", "\\n").Replace ("\r", "\\r").Replace ("\t", "\\t").Replace (" ", ".")); -// //Console.WriteLine (Environment.StackTrace); -// -// changes.Add (factory.CreateTextReplaceAction (offset, removedChars, insertedText)); + changes.Add(new TextReplaceAction(offset, removedChars, insertedText)); } public bool IsLineIsEmptyUpToEol (TextLocation startLocation) @@ -1169,7 +1203,7 @@ namespace ICSharpCode.NRefactory.CSharp } if (policy.IndentCaseBody) curIndent.Level--; - + if (policy.IndentSwitchBody) curIndent.Level--; return null; @@ -1322,41 +1356,41 @@ namespace ICSharpCode.NRefactory.CSharp { bool forceSpaces = false; switch (binaryOperatorExpression.Operator) { - case BinaryOperatorType.Equality: - case BinaryOperatorType.InEquality: - forceSpaces = policy.SpaceAroundEqualityOperator; - break; - case BinaryOperatorType.GreaterThan: - case BinaryOperatorType.GreaterThanOrEqual: - case BinaryOperatorType.LessThan: - case BinaryOperatorType.LessThanOrEqual: - forceSpaces = policy.SpaceAroundRelationalOperator; - break; - case BinaryOperatorType.ConditionalAnd: - case BinaryOperatorType.ConditionalOr: - forceSpaces = policy.SpaceAroundLogicalOperator; - break; - case BinaryOperatorType.BitwiseAnd: - case BinaryOperatorType.BitwiseOr: - case BinaryOperatorType.ExclusiveOr: - forceSpaces = policy.SpaceAroundBitwiseOperator; - break; - case BinaryOperatorType.Add: - case BinaryOperatorType.Subtract: - forceSpaces = policy.SpaceAroundAdditiveOperator; - break; - case BinaryOperatorType.Multiply: - case BinaryOperatorType.Divide: - case BinaryOperatorType.Modulus: - forceSpaces = policy.SpaceAroundMultiplicativeOperator; - break; - case BinaryOperatorType.ShiftLeft: - case BinaryOperatorType.ShiftRight: - forceSpaces = policy.SpaceAroundShiftOperator; - break; - case BinaryOperatorType.NullCoalescing: - forceSpaces = policy.SpaceAroundNullCoalescingOperator; - break; + case BinaryOperatorType.Equality: + case BinaryOperatorType.InEquality: + forceSpaces = policy.SpaceAroundEqualityOperator; + break; + case BinaryOperatorType.GreaterThan: + case BinaryOperatorType.GreaterThanOrEqual: + case BinaryOperatorType.LessThan: + case BinaryOperatorType.LessThanOrEqual: + forceSpaces = policy.SpaceAroundRelationalOperator; + break; + case BinaryOperatorType.ConditionalAnd: + case BinaryOperatorType.ConditionalOr: + forceSpaces = policy.SpaceAroundLogicalOperator; + break; + case BinaryOperatorType.BitwiseAnd: + case BinaryOperatorType.BitwiseOr: + case BinaryOperatorType.ExclusiveOr: + forceSpaces = policy.SpaceAroundBitwiseOperator; + break; + case BinaryOperatorType.Add: + case BinaryOperatorType.Subtract: + forceSpaces = policy.SpaceAroundAdditiveOperator; + break; + case BinaryOperatorType.Multiply: + case BinaryOperatorType.Divide: + case BinaryOperatorType.Modulus: + forceSpaces = policy.SpaceAroundMultiplicativeOperator; + break; + case BinaryOperatorType.ShiftLeft: + case BinaryOperatorType.ShiftRight: + forceSpaces = policy.SpaceAroundShiftOperator; + break; + case BinaryOperatorType.NullCoalescing: + forceSpaces = policy.SpaceAroundNullCoalescingOperator; + break; } ForceSpacesAround (binaryOperatorExpression.OperatorToken, forceSpaces); @@ -1554,7 +1588,7 @@ namespace ICSharpCode.NRefactory.CSharp } return result; } - */ + */ public void FixSemicolon (CSharpTokenNode semicolon) @@ -1569,7 +1603,7 @@ namespace ICSharpCode.NRefactory.CSharp if (offset < endOffset) { AddChange (offset, endOffset - offset, null); } - } + } void PlaceOnNewLine (bool newLine, AstNode keywordNode) { @@ -1611,7 +1645,7 @@ namespace ICSharpCode.NRefactory.CSharp Console.WriteLine (Environment.StackTrace); return; } - + string lineIndent = GetIndentation (location.Line); string indentString = this.curIndent.IndentString; if (indentString != lineIndent && location.Column - 1 + relOffset == lineIndent.Length) { @@ -1625,10 +1659,10 @@ namespace ICSharpCode.NRefactory.CSharp string indentString = this.curIndent.IndentString; if (location.Column - 1 == lineIndent.Length) { AddChange (document.GetOffset (location.Line, 1), lineIndent.Length, indentString); - } else { + } else { int offset = document.GetOffset (location); int start = SearchWhitespaceLineStart (offset); - if (start > 0) { + if (start > 0) { char ch = document.GetCharAt (start - 1); if (ch == '\n') { start--; diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs index 7e47a861cf..70cb7198b4 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -1510,7 +1510,7 @@ namespace ICSharpCode.NRefactory.CSharp else if (blockStatement.Parent.Role == CustomEventDeclaration.RemoveAccessorRole) style = policy.EventRemoveBraceStyle; else - throw new NotSupportedException ("Unknown type of accessor"); + style = policy.StatementBraceStyle; } else { style = policy.StatementBraceStyle; } diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/AddAnotherAccessor.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/AddAnotherAccessor.cs index f35f8ad068..aa56f276f7 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/AddAnotherAccessor.cs +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/AddAnotherAccessor.cs @@ -52,7 +52,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring Body = new BlockStatement { accessorStatement } }; - pdecl.AddChild (accessor, pdecl.Setter.IsNull ? PropertyDeclaration.SetterRole : PropertyDeclaration.GetterRole); + accessor.Role = pdecl.Setter.IsNull ? PropertyDeclaration.SetterRole : PropertyDeclaration.GetterRole; using (var script = context.StartScript ()) { script.InsertBefore (pdecl.RBraceToken, accessor); diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/CreateBackingStore.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/CreateBackingStore.cs index db6598a877..50eef37ca3 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/CreateBackingStore.cs +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/ContextAction/CreateBackingStore.cs @@ -1,6 +1,6 @@ -// +// // CreateBackingStore.cs -// +// // Author: // Mike Krüger // @@ -32,7 +32,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring public bool IsValid (RefactoringContext context) { var propertyDeclaration = context.GetNode (); - return propertyDeclaration != null && + return propertyDeclaration != null && !propertyDeclaration.Getter.IsNull && !propertyDeclaration.Setter.IsNull && // automatic properties always need getter & setter propertyDeclaration.Getter.Body.IsNull && propertyDeclaration.Setter.Body.IsNull; @@ -53,13 +53,17 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring // create new property & implement the get/set bodies var newProperty = (PropertyDeclaration)property.Clone (); - var id1 = new IdentifierExpression (backingStoreName); - var id2 = new IdentifierExpression (backingStoreName); + Expression id1; + if (backingStoreName == "value") + id1 = new ThisReferenceExpression().Member("value"); + else + id1 = new IdentifierExpression (backingStoreName); + Expression id2 = id1.Clone(); newProperty.Getter.Body = new BlockStatement () { new ReturnStatement (id1) }; newProperty.Setter.Body = new BlockStatement () { - new ExpressionStatement (new AssignmentExpression (id2, AssignmentOperatorType.Assign, new IdentifierExpression ("value"))) + new AssignmentExpression (id2, AssignmentOperatorType.Assign, new IdentifierExpression ("value")) }; using (var script = context.StartScript ()) { diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs index 6db950abee..378eb4d278 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs @@ -178,15 +178,33 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring output.RegisterTrackedSegments(this, startOffset); } - public void FormatText (AstNode node) + public virtual void FormatText (AstNode node) { var segment = GetSegment(node); FormatText(segment.Offset, segment.Length); } + /// + /// Format the specified node and surrounding whitespace. + /// + public virtual void FormatTextAround (AstNode node) + { + int startOffset; + if (node.PrevSibling != null) + startOffset = GetSegment(node.PrevSibling).EndOffset; + else + startOffset = GetSegment(node).Offset; + int endOffset; + if (node.NextSibling != null) + endOffset = GetSegment(node.NextSibling).Offset; + else + endOffset = GetSegment(node).EndOffset; + FormatText(startOffset, endOffset - startOffset); + } + public abstract void FormatText (int offset, int length); - public void Select (AstNode node) + public virtual void Select (AstNode node) { var segment = GetSegment(node); Select(segment.Offset, segment.Length); diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs index 4912ccd2d9..813f30818b 100644 --- a/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs +++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs @@ -220,8 +220,9 @@ namespace ICSharpCode.NRefactory.Editor get { return lines.Length; } } - ITextSourceVersion ITextSource.Version { - get { return null; } + /// + public ITextSourceVersion Version { + get { return textSource.Version; } } /// diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsBulbViewModel.cs b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsBulbViewModel.cs index 68732fea46..70352afe33 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsBulbViewModel.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/ContextActions/ContextActionsBulbViewModel.cs @@ -39,16 +39,26 @@ namespace ICSharpCode.SharpDevelop.Refactoring this.ActionVisibleChangedCommand = new ActionVisibleChangedCommand(model); } + bool actionsLoaded; + public async Task LoadActionsAsync(CancellationToken cancellationToken) { + if (actionsLoaded) + return; + actionsLoaded = true; this.Actions.Clear(); foreach (var action in await Model.GetVisibleActionsAsync(cancellationToken)) { this.Actions.Add(new ContextActionViewModel(action, this.Model.EditorContext) { IsVisible = true }); } } + bool hiddenActionsLoaded; + public async Task LoadHiddenActionsAsync(CancellationToken cancellationToken) { + if (hiddenActionsLoaded) + return; + hiddenActionsLoaded = true; this.HiddenActions.Clear(); foreach (var action in await Model.GetHiddenActionsAsync(cancellationToken)) { this.HiddenActions.Add(new ContextActionViewModel(action, this.Model.EditorContext) { IsVisible = false });