Skip to content

Commit

Permalink
Merge branch 'next' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
retailcoder committed Mar 19, 2017
2 parents 117e74e + d23fe3b commit 886aff1
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 381 deletions.
Expand Up @@ -105,13 +105,15 @@ private void ImplementMissingMembers()
AddItems(nonImplementedMembers);
}

private void AddItems(List<Declaration> members)
private void AddItems(List<Declaration> missingMembers)
{
var module = _targetClass.QualifiedSelection.QualifiedName.Component.CodeModule;
{
var missingMembersText = members.Aggregate(string.Empty, (current, member) => current + Environment.NewLine + GetInterfaceMember(member));
module.InsertLines(module.CountOfDeclarationLines + 1, missingMembersText);
}
var missingMembersText = missingMembers.Aggregate(string.Empty,
(current, member) => current + Environment.NewLine + GetInterfaceMember(member));

var rewriter = _state.GetRewriter(_targetClass);
rewriter.InsertAfter(rewriter.TokenStream.Size, Environment.NewLine + missingMembersText);

rewriter.Rewrite();
}

private string GetInterfaceMember(Declaration member)
Expand Down
@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Antlr4.Runtime;
using Rubberduck.Common;
using Rubberduck.Parsing;
using Rubberduck.Parsing.Grammar;
using Rubberduck.Parsing.PostProcessing;
using Rubberduck.Parsing.Symbols;
using Rubberduck.Parsing.VBA;
using Rubberduck.UI;
Expand All @@ -21,6 +21,8 @@ public class MoveCloserToUsageRefactoring : IRefactoring
private readonly RubberduckParserState _state;
private readonly IMessageBox _messageBox;
private Declaration _target;

private readonly HashSet<IModuleRewriter> _rewriters = new HashSet<IModuleRewriter>();

public MoveCloserToUsageRefactoring(IVBE vbe, RubberduckParserState state, IMessageBox messageBox)
{
Expand Down Expand Up @@ -56,8 +58,6 @@ public void Refactor(QualifiedSelection selection)
}

MoveCloserToUsage();
//var rewriter = _state.GetRewriter(_target);
//rewriter.Rewrite();
}

public void Refactor(Declaration target)
Expand All @@ -71,8 +71,6 @@ public void Refactor(Declaration target)

_target = target;
MoveCloserToUsage();
var rewriter = _state.GetRewriter(_target);
rewriter.Rewrite();
}

private bool TargetIsReferencedFromMultipleMethods(Declaration target)
Expand All @@ -96,7 +94,8 @@ private void MoveCloserToUsage()

if (TargetIsReferencedFromMultipleMethods(_target))
{
var message = string.Format(RubberduckUI.MoveCloserToUsage_TargetIsUsedInMultipleMethods, _target.IdentifierName);
var message = string.Format(RubberduckUI.MoveCloserToUsage_TargetIsUsedInMultipleMethods,
_target.IdentifierName);
_messageBox.Show(message, RubberduckUI.MoveCloserToUsage_Caption, MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);

Expand All @@ -106,29 +105,28 @@ private void MoveCloserToUsage()
QualifiedSelection? oldSelection = null;
var pane = _vbe.ActiveCodePane;
var module = pane.CodeModule;
if (!module.IsWrappingNullReference)
{
if (!module.IsWrappingNullReference)
{
oldSelection = module.GetQualifiedSelection();
}

// it doesn't make sense to do it backwards, but we need to work from the bottom up so our selections are accurate
InsertDeclaration();
oldSelection = module.GetQualifiedSelection();
}

InsertNewDeclaration();
RemoveOldDeclaration();
UpdateOtherModules();

if (oldSelection.HasValue)
{
pane.Selection = oldSelection.Value.Selection;
}
if (oldSelection.HasValue)
{
pane.Selection = oldSelection.Value.Selection;
}

_state.StateChanged += _state_StateChanged;
_state.OnParseRequested(this);
foreach (var rewriter in _rewriters)
{
rewriter.Rewrite();
}
}

private void _state_StateChanged(object sender, ParserStateEventArgs e)
private void UpdateOtherModules()
{
if (e.State != ParserState.Ready) { return; }

QualifiedSelection? oldSelection = null;
var pane = _vbe.ActiveCodePane;
var module = pane.CodeModule;
Expand All @@ -137,8 +135,6 @@ private void _state_StateChanged(object sender, ParserStateEventArgs e)
oldSelection = module.GetQualifiedSelection();
}

var rewriter = _state.GetRewriter(module.Parent); // todo: determine if we still need to reparse or if we can just pass the rewriter around

var newTarget = _state.AllUserDeclarations.FirstOrDefault(
item => item.ComponentName == _target.ComponentName &&
item.IdentifierName == _target.IdentifierName &&
Expand All @@ -149,131 +145,61 @@ private void _state_StateChanged(object sender, ParserStateEventArgs e)
if (newTarget != null)
{
UpdateCallsToOtherModule(newTarget.References.ToList());
rewriter.Remove(newTarget);
var content = rewriter.GetText();
rewriter.Rewrite();
}

if (oldSelection.HasValue)
{
pane.Selection = oldSelection.Value.Selection;
}

_state.StateChanged -= _state_StateChanged;
_state.OnParseRequested(this);
}

private void InsertDeclaration()
private void InsertNewDeclaration()
{
_state.GetRewriter(_target);
var module = _target.References.First().QualifiedModuleName.Component.CodeModule;
var firstReference = _target.References.OrderBy(r => r.Selection.StartLine).First();
var beginningOfInstructionSelection = GetBeginningOfInstructionSelection(firstReference);

var oldLines = module.GetLines(beginningOfInstructionSelection.StartLine,
beginningOfInstructionSelection.LineCount);
var newLines = oldLines.Insert(beginningOfInstructionSelection.StartColumn - 1, GetDeclarationString());
var newVariable = $"Dim {_target.IdentifierName} As {_target.AsTypeName}{Environment.NewLine}";

var newLinesWithoutStringLiterals = newLines.StripStringLiterals();
var firstReference = _target.References.OrderBy(r => r.Selection.StartLine).First();

var lastIndexOfColon = GetIndexOfLastStatementSeparator(newLinesWithoutStringLiterals);
// ReSharper disable once StringLastIndexOfIsCultureSpecific.1
while (lastIndexOfColon != -1)
RuleContext expression = firstReference.Context;
while (!(expression is VBAParser.BlockStmtContext))
{
var numberOfCharsToRemove = lastIndexOfColon == newLines.Length - 1 ||
newLines[lastIndexOfColon + 1] != ' '
? 1
: 2;

newLinesWithoutStringLiterals = newLinesWithoutStringLiterals
.Remove(lastIndexOfColon, numberOfCharsToRemove)
.Insert(lastIndexOfColon, Environment.NewLine);

newLines = newLines
.Remove(lastIndexOfColon, numberOfCharsToRemove)
.Insert(lastIndexOfColon, Environment.NewLine);

lastIndexOfColon = GetIndexOfLastStatementSeparator(newLinesWithoutStringLiterals);
expression = expression.Parent;
}

module.DeleteLines(beginningOfInstructionSelection.StartLine, beginningOfInstructionSelection.LineCount);
module.InsertLines(beginningOfInstructionSelection.StartLine, newLines);
}
var insertionIndex = (expression as ParserRuleContext).Start.TokenIndex;

private static readonly Regex StatementSeparatorRegex = new Regex(":[^=]", RegexOptions.RightToLeft);
private static int GetIndexOfLastStatementSeparator(string input)
{
var matches = StatementSeparatorRegex.Matches(input);
return matches.Count == 0 ? -1 : matches[0].Index;
}
var rewriter = _state.GetRewriter(firstReference.QualifiedModuleName);
rewriter.InsertBefore(insertionIndex, newVariable);

private Selection GetBeginningOfInstructionSelection(IdentifierReference reference)
{
var referenceSelection = reference.Selection;
var module = reference.QualifiedModuleName.Component.CodeModule;
{
var currentLine = referenceSelection.StartLine;

var codeLine = module.GetLines(currentLine, 1).StripStringLiterals();
while (GetIndexOfLastStatementSeparator(codeLine.Remove(referenceSelection.StartColumn)) == -1)
{
codeLine = module.GetLines(--currentLine, 1).StripStringLiterals();
if (!codeLine.EndsWith(" _"))
{
return new Selection(currentLine + 1, 1, currentLine + 1, 1);
}
}

var index = GetIndexOfLastStatementSeparator(codeLine.Remove(referenceSelection.StartColumn)) + 1;
return new Selection(currentLine, index, currentLine, index);
}
_rewriters.Add(rewriter);
}

private string GetDeclarationString()
private void RemoveOldDeclaration()
{
return Environment.NewLine + " Dim " + _target.IdentifierName + " As " + _target.AsTypeName + Environment.NewLine;
var rewriter = _state.GetRewriter(_target);
rewriter.Remove(_target);

_rewriters.Add(rewriter);
}

private void UpdateCallsToOtherModule(List<IdentifierReference> references)
{
foreach (var reference in references.OrderByDescending(o => o.Selection.StartLine).ThenByDescending(t => t.Selection.StartColumn))
{
var module = reference.QualifiedModuleName.Component.CodeModule;
var parent = reference.Context.Parent;
while (!(parent is VBAParser.MemberAccessExprContext) && parent.Parent != null)
{
var parent = reference.Context.Parent;
while (!(parent is VBAParser.MemberAccessExprContext) && parent.Parent != null)
{
parent = parent.Parent;
}

if (!(parent is VBAParser.MemberAccessExprContext))
{
continue;
}

var parentSelection = ((VBAParser.MemberAccessExprContext)parent).GetSelection();

var oldText = module.GetLines(parentSelection.StartLine, parentSelection.LineCount);
string newText;

if (parentSelection.LineCount == 1)
{
newText = oldText.Remove(parentSelection.StartColumn - 1,
parentSelection.EndColumn - parentSelection.StartColumn);
}
else
{
var lines = oldText.Split(new[] { " _" + Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

newText = lines.First().Remove(parentSelection.StartColumn - 1);
newText += lines.Last().Remove(0, parentSelection.EndColumn - 1);
}

newText = newText.Insert(parentSelection.StartColumn - 1, reference.IdentifierName);

module.DeleteLines(parentSelection.StartLine, parentSelection.LineCount);
module.InsertLines(parentSelection.StartLine, newText);
parent = parent.Parent;
}

if (!(parent is VBAParser.MemberAccessExprContext))
{
continue;
}

var rewriter = _state.GetRewriter(reference.QualifiedModuleName);
rewriter.Replace(parent as ParserRuleContext, reference.IdentifierName);

_rewriters.Add(rewriter);
}
}
}
Expand Down
Expand Up @@ -220,9 +220,8 @@ public void MoveFieldCloserToUsage_QuickFixWorks()

const string expectedCode =
@"Public Sub Foo()
Dim bar As String
bar = ""test""
bar = ""test""
End Sub";

IVBComponent component;
Expand Down

0 comments on commit 886aff1

Please sign in to comment.