Skip to content
Browse files

Break EditingSession down into individual editing action classes.

  • Loading branch information...
1 parent 21027f3 commit bc9285ce108a7b80d1de507e329e385a958a45b0 @tomphp tomphp committed Dec 27, 2013
Showing with 1,905 additions and 258 deletions.
  1. +28 −34 src/main/QafooLabs/Refactoring/Application/ConvertLocalToInstanceVariable.php
  2. +58 −27 src/main/QafooLabs/Refactoring/Application/ExtractMethod.php
  3. +30 −35 src/main/QafooLabs/Refactoring/Application/RenameLocalVariable.php
  4. +85 −0 src/main/QafooLabs/Refactoring/Application/SingleFileRefactoring.php
  5. +8 −0 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction.php
  6. +150 −0 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction/AddMethod.php
  7. +37 −0 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction/AddProperty.php
  8. +38 −0 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction/LocalVariableToInstance.php
  9. +62 −0 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction/RenameVariable.php
  10. +79 −0 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction/ReplaceWithMethodCall.php
  11. +10 −111 src/main/QafooLabs/Refactoring/Domain/Model/EditingSession.php
  12. +56 −0 src/main/QafooLabs/Refactoring/Domain/Model/IndentationDetector.php
  13. +36 −0 src/main/QafooLabs/Refactoring/Domain/Model/IndentingLineCollection.php
  14. +43 −0 src/main/QafooLabs/Refactoring/Domain/Model/Line.php
  15. +82 −0 src/main/QafooLabs/Refactoring/Domain/Model/LineCollection.php
  16. +52 −0 src/main/QafooLabs/Refactoring/Utils/ToStringIterator.php
  17. +0 −51 src/test/QafooLabs/Refactoring/Application/RenameLocalVariableTest.php.orig
  18. +229 −0 src/test/QafooLabs/Refactoring/Domain/Model/EditingAction/AddMethodTest.php
  19. +50 −0 src/test/QafooLabs/Refactoring/Domain/Model/EditingAction/AddPropertyTest.php
  20. +114 −0 src/test/QafooLabs/Refactoring/Domain/Model/EditingAction/LocalVariableToInstanceTest.php
  21. +117 −0 src/test/QafooLabs/Refactoring/Domain/Model/EditingAction/RenameVariableTest.php
  22. +122 −0 src/test/QafooLabs/Refactoring/Domain/Model/EditingAction/ReplaceWithMethodCallTest.php
  23. +36 −0 src/test/QafooLabs/Refactoring/Domain/Model/EditingSessionTest.php
  24. +68 −0 src/test/QafooLabs/Refactoring/Domain/Model/IndentationDetectorTest.php
  25. +109 −0 src/test/QafooLabs/Refactoring/Domain/Model/IndentingLineCollectionTest.php
  26. +114 −0 src/test/QafooLabs/Refactoring/Domain/Model/LineCollectionTest.php
  27. +43 −0 src/test/QafooLabs/Refactoring/Domain/Model/LineTest.php
  28. +49 −0 src/test/QafooLabs/Refactoring/Utils/ToStringIteratorTest.php
View
62 src/main/QafooLabs/Refactoring/Application/ConvertLocalToInstanceVariable.php
@@ -13,56 +13,50 @@
use QafooLabs\Refactoring\Domain\Services\VariableScanner;
use QafooLabs\Refactoring\Domain\Services\CodeAnalysis;
use QafooLabs\Refactoring\Domain\Services\Editor;
+use QafooLabs\Refactoring\Domain\Model\EditingAction\AddProperty;
+use QafooLabs\Refactoring\Domain\Model\EditingAction\LocalVariableToInstance;
-class ConvertLocalToInstanceVariable
+class ConvertLocalToInstanceVariable extends SingleFileRefactoring
{
/**
- * @var \QafooLabs\Refactoring\Domain\Services\VariableScanner
+ * @var Variable
*/
- private $variableScanner;
+ private $convertVariable;
/**
- * @var \QafooLabs\Refactoring\Domain\Services\CodeAnalysis
+ * @param int $line
*/
- private $codeAnalysis;
+ public function refactor(File $file, $line, Variable $convertVariable)
+ {
+ $this->file = $file;
+ $this->line = $line;
+ $this->convertVariable = $convertVariable;
- /**
- * @var \QafooLabs\Refactoring\Domain\Services\Editor
- */
- private $editor;
+ $this->assertIsInsideMethod();
- public function __construct(VariableScanner $variableScanner, CodeAnalysis $codeAnalysis, Editor $editor)
- {
- $this->variableScanner = $variableScanner;
- $this->codeAnalysis = $codeAnalysis;
- $this->editor = $editor;
+ $this->startEditingSession();
+ $this->addProperty();
+ $this->convertVariablesToInstanceVariables();
+ $this->completeEditingSession();
}
- public function refactor(File $file, $line, Variable $convertVariable)
+ private function addProperty()
{
- if ( ! $this->codeAnalysis->isInsideMethod($file, LineRange::fromSingleLine($line))) {
- throw RefactoringException::rangeIsNotInsideMethod(LineRange::fromSingleLine($line));
- }
+ $line = $this->codeAnalysis->getLineOfLastPropertyDefinedInScope($this->file, $this->line);
- $instanceVariable = $convertVariable->convertToInstance();
- $lastPropertyLine = $this->codeAnalysis->getLineOfLastPropertyDefinedInScope($file, $line);
-
- $selectedMethodLineRange = $this->codeAnalysis->findMethodRange($file, LineRange::fromSingleLine($line));
- $definedVariables = $this->variableScanner->scanForVariables(
- $file, $selectedMethodLineRange
+ $this->session->addEdit(
+ new AddProperty($line, $this->convertVariable->getName())
);
+ }
- if ( ! $definedVariables->contains($convertVariable)) {
- throw RefactoringException::variableNotInRange($convertVariable, $selectedMethodLineRange);
- }
-
- $buffer = $this->editor->openBuffer($file);
+ private function convertVariablesToInstanceVariables()
+ {
+ $definedVariables = $this->getDefinedVariables();
- $session = new EditingSession($buffer);
- $session->addProperty($lastPropertyLine, $convertVariable->getName());
- $session->replaceString($definedVariables, $convertVariable, $instanceVariable);
+ if ( ! $definedVariables->contains($this->convertVariable)) {
+ throw RefactoringException::variableNotInRange($this->convertVariable, $selectedMethodLineRange);
+ }
- $this->editor->save();
+ $this->session->addEdit(new LocalVariableToInstance($definedVariables, $this->convertVariable));
}
}
-
View
85 src/main/QafooLabs/Refactoring/Application/ExtractMethod.php
@@ -11,60 +11,91 @@
use QafooLabs\Refactoring\Domain\Services\VariableScanner;
use QafooLabs\Refactoring\Domain\Services\CodeAnalysis;
use QafooLabs\Refactoring\Domain\Services\Editor;
+use QafooLabs\Refactoring\Domain\Model\LineCollection;
+use QafooLabs\Refactoring\Domain\Model\EditingAction\AddMethod;
+use QafooLabs\Refactoring\Domain\Model\EditingAction\ReplaceWithMethodCall;
/**
* Extract Method Refactoring
*/
-class ExtractMethod
+class ExtractMethod extends SingleFileRefactoring
{
/**
- * @var \QafooLabs\Refactoring\Domain\Services\VariableScanner
+ * @var LineRange
*/
- private $variableScanner;
+ private $extractRange;
/**
- * @var \QafooLabs\Refactoring\Domain\Services\CodeAnalysis
+ * @var MethodSignature
*/
- private $codeAnalysis;
+ private $newMethod;
/**
- * @var \QafooLabs\Refactoring\Domain\Services\Editor
+ * @param string $newMethodName
*/
- private $editor;
-
- public function __construct(VariableScanner $variableScanner, CodeAnalysis $codeAnalysis, Editor $editor)
+ public function refactor(File $file, LineRange $extractRange, $newMethodName)
{
- $this->variableScanner = $variableScanner;
- $this->codeAnalysis = $codeAnalysis;
- $this->editor = $editor;
+ $this->file = $file;
+ $this->extractRange = $extractRange;
+
+ $this->assertIsInsideMethod();
+
+ $this->createNewMethodSignature($newMethodName);
+
+ $this->startEditingSession();
+ $this->replaceCodeWithMethodCall();
+ $this->addNewMethod();
+ $this->completeEditingSession();
}
- public function refactor(File $file, LineRange $extractRange, $newMethodName)
+ protected function assertIsInsideMethod()
{
- if ( ! $this->codeAnalysis->isInsideMethod($file, $extractRange)) {
- throw RefactoringException::rangeIsNotInsideMethod($extractRange);
+ if ( ! $this->codeAnalysis->isInsideMethod($this->file, $this->extractRange)) {
+ throw RefactoringException::rangeIsNotInsideMethod($this->extractRange);
}
+ }
- $isStatic = $this->codeAnalysis->isMethodStatic($file, $extractRange);
- $methodRange = $this->codeAnalysis->findMethodRange($file, $extractRange);
- $selectedCode = $extractRange->sliceCode($file->getCode());
-
- $extractVariables = $this->variableScanner->scanForVariables($file, $extractRange);
- $methodVariables = $this->variableScanner->scanForVariables($file, $methodRange);
+ private function createNewMethodSignature($newMethodName)
+ {
+ $extractVariables = $this->variableScanner->scanForVariables($this->file, $this->extractRange);
+ $methodVariables = $this->variableScanner->scanForVariables($this->file, $this->findMethodRange());
- $buffer = $this->editor->openBuffer($file);
+ $isStatic = $this->codeAnalysis->isMethodStatic($this->file, $this->extractRange);
- $newMethod = new MethodSignature(
+ $this->newMethod = new MethodSignature(
$newMethodName,
$isStatic ? MethodSignature::IS_STATIC : 0,
$methodVariables->variablesFromSelectionUsedBefore($extractVariables),
$methodVariables->variablesFromSelectionUsedAfter($extractVariables)
);
+ }
- $session = new EditingSession($buffer);
- $session->replaceRangeWithMethodCall($extractRange, $newMethod);
- $session->addMethod($methodRange->getEnd(), $newMethod, $selectedCode);
+ private function addNewMethod()
+ {
+ $this->session->addEdit(new AddMethod(
+ $this->findMethodRange()->getEnd(),
+ $this->newMethod,
+ $this->getSelectedCode()
+ ));
+ }
+
+ private function replaceCodeWithMethodCall()
+ {
+ $this->session->addEdit(new ReplaceWithMethodCall(
+ $this->extractRange,
+ $this->newMethod
+ ));
+ }
- $this->editor->save();
+ private function findMethodRange()
+ {
+ return $this->codeAnalysis->findMethodRange($this->file, $this->extractRange);
+ }
+
+ private function getSelectedCode()
+ {
+ return LineCollection::createFromArray(
+ $this->extractRange->sliceCode($this->file->getCode())
+ );
}
}
View
65 src/main/QafooLabs/Refactoring/Application/RenameLocalVariable.php
@@ -12,63 +12,58 @@
use QafooLabs\Refactoring\Domain\Services\VariableScanner;
use QafooLabs\Refactoring\Domain\Services\CodeAnalysis;
use QafooLabs\Refactoring\Domain\Services\Editor;
+use QafooLabs\Refactoring\Domain\Model\EditingAction\RenameVariable;
/**
* Rename Local Variable Refactoring
*/
-class RenameLocalVariable
+class RenameLocalVariable extends SingleFileRefactoring
{
/**
- * @var \QafooLabs\Refactoring\Domain\Services\VariableScanner
+ * @var Variable
*/
- private $variableScanner;
+ private $oldName;
/**
- * @var \QafooLabs\Refactoring\Domain\Services\CodeAnalysis
+ * @var Variable
*/
- private $codeAnalysis;
+ private $newName;
/**
- * @var \QafooLabs\Refactoring\Domain\Services\Editor
+ * @param int $line
*/
- private $editor;
-
- public function __construct(VariableScanner $variableScanner, CodeAnalysis $codeAnalysis, Editor $editor)
- {
- $this->variableScanner = $variableScanner;
- $this->codeAnalysis = $codeAnalysis;
- $this->editor = $editor;
- }
-
public function refactor(File $file, $line, Variable $oldName, Variable $newName)
{
- if ( ! $this->codeAnalysis->isInsideMethod($file, LineRange::fromSingleLine($line))) {
- throw RefactoringException::rangeIsNotInsideMethod(LineRange::fromSingleLine($line));
- }
+ $this->file = $file;
+ $this->line = $line;
+ $this->newName = $newName;
+ $this->oldName = $oldName;
- if ( ! $oldName->isLocal()) {
- throw RefactoringException::variableNotLocal($oldName);
- }
+ $this->assertIsInsideMethod();
- if ( ! $newName->isLocal()) {
- throw RefactoringException::variableNotLocal($newName);
- }
+ $this->assertVariableIsLocal($this->oldName);
+ $this->assertVariableIsLocal($this->newName);
- $selectedMethodLineRange = $this->codeAnalysis->findMethodRange($file, LineRange::fromSingleLine($line));
- $definedVariables = $this->variableScanner->scanForVariables(
- $file, $selectedMethodLineRange
- );
+ $this->startEditingSession();
+ $this->renameLocalVariable();
+ $this->completeEditingSession();
+ }
- if ( ! $definedVariables->contains($oldName)) {
- throw RefactoringException::variableNotInRange($oldName, $selectedMethodLineRange);
+ private function assertVariableIsLocal(Variable $variable)
+ {
+ if ( ! $variable->isLocal()) {
+ throw RefactoringException::variableNotLocal($variable);
}
+ }
- $buffer = $this->editor->openBuffer($file);
+ private function renameLocalVariable()
+ {
+ $definedVariables = $this->getDefinedVariables();
- $session = new EditingSession($buffer);
- $session->replaceString($definedVariables, $oldName, $newName);
+ if ( ! $definedVariables->contains($this->oldName)) {
+ throw RefactoringException::variableNotInRange($this->oldName, $selectedMethodLineRange);
+ }
- $this->editor->save();
+ $this->session->addEdit(new RenameVariable($definedVariables, $this->oldName, $this->newName));
}
}
-
View
85 src/main/QafooLabs/Refactoring/Application/SingleFileRefactoring.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace QafooLabs\Refactoring\Application;
+
+use QafooLabs\Refactoring\Domain\Services\VariableScanner;
+use QafooLabs\Refactoring\Domain\Services\CodeAnalysis;
+use QafooLabs\Refactoring\Domain\Services\Editor;
+use QafooLabs\Refactoring\Domain\Model\EditingSession;
+use QafooLabs\Refactoring\Domain\Model\File;
+use QafooLabs\Refactoring\Domain\Model\LineRange;
+
+abstract class SingleFileRefactoring
+{
+ /**
+ * @var VariableScanner
+ */
+ protected $variableScanner;
+
+ /**
+ * @var CodeAnalysis
+ */
+ protected $codeAnalysis;
+
+ /**
+ * @var Editor
+ */
+ protected $editor;
+
+ /**
+ * @var EditingSession
+ */
+ protected $session;
+
+ /**
+ * @var File
+ */
+ protected $file;
+
+ /**
+ * @var int
+ */
+ protected $line;
+
+ public function __construct(
+ VariableScanner $variableScanner,
+ CodeAnalysis $codeAnalysis,
+ Editor $editor
+ ) {
+ $this->variableScanner = $variableScanner;
+ $this->codeAnalysis = $codeAnalysis;
+ $this->editor = $editor;
+ }
+
+ protected function assertIsInsideMethod()
+ {
+ if ( ! $this->codeAnalysis->isInsideMethod($this->file, LineRange::fromSingleLine($this->line))) {
+ throw RefactoringException::rangeIsNotInsideMethod(LineRange::fromSingleLine($this->line));
+ }
+ }
+
+ protected function startEditingSession()
+ {
+ $buffer = $this->editor->openBuffer($this->file);
+
+ $this->session = new EditingSession($buffer);
+ }
+
+ protected function completeEditingSession()
+ {
+ $this->session->performEdits();
+
+ $this->editor->save();
+ }
+
+ protected function getDefinedVariables()
+ {
+ $selectedMethodLineRange = $this->codeAnalysis->findMethodRange($this->file, LineRange::fromSingleLine($this->line));
+
+ $definedVariables = $this->variableScanner->scanForVariables(
+ $this->file, $selectedMethodLineRange
+ );
+
+ return $definedVariables;
+ }
+}
View
8 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model;
+
+interface EditingAction
+{
+ public function performEdit(EditorBuffer $buffer);
+}
View
150 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction/AddMethod.php
@@ -0,0 +1,150 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
+
+use QafooLabs\Refactoring\Domain\Model\EditingAction;
+use QafooLabs\Refactoring\Domain\Model\EditorBuffer;
+use QafooLabs\Refactoring\Domain\Model\IndentationDetector;
+use QafooLabs\Refactoring\Domain\Model\IndentingLineCollection;
+use QafooLabs\Refactoring\Domain\Model\Line;
+use QafooLabs\Refactoring\Domain\Model\LineCollection;
+use QafooLabs\Refactoring\Domain\Model\MethodSignature;
+use QafooLabs\Refactoring\Utils\ToStringIterator;
+
+class AddMethod implements EditingAction
+{
+ /**
+ * @var int
+ */
+ private $lineNumber;
+
+ /**
+ * @var MethodSignature
+ */
+ private $newMethod;
+
+ /**
+ * @var LineCollection
+ */
+ private $selectedCode;
+
+ /**
+ * @var IndentingLineCollection
+ */
+ private $newCode;
+
+ /**
+ * @param int $lineNumber
+ */
+ public function __construct(
+ $lineNumber,
+ MethodSignature $newMethod,
+ LineCollection $selectedCode
+ ) {
+ $this->lineNumber = $lineNumber;
+ $this->newMethod = $newMethod;
+ $this->selectedCode = $selectedCode;
+ }
+
+ public function performEdit(EditorBuffer $buffer)
+ {
+ $this->newCode = new IndentingLineCollection();
+
+ $this->newCode->addIndentation();
+
+ $this->addMethodOpening();
+ $this->addMethodBody();
+ $this->addReturnStatement();
+ $this->addMethodClosing();
+
+ $buffer->append($this->lineNumber, $this->getNewCodeAsStringArray());
+ }
+
+ private function addMethodOpening()
+ {
+ $this->newCode->appendBlankLine();
+
+ $this->newCode->appendString($this->getNewMethodSignatureString());
+ $this->newCode->appendString('{');
+
+ $this->newCode->addIndentation();
+ }
+
+ /**
+ * @return string
+ */
+ private function getNewMethodSignatureString()
+ {
+ return sprintf(
+ 'private %sfunction %s(%s)',
+ ($this->newMethod->isStatic() ? 'static ' : ''),
+ $this->newMethod->getName(),
+ $this->createVariableList($this->newMethod->arguments())
+ );
+ }
+
+ /**
+ * @param string[] $variables
+ *
+ * @return string
+ */
+ private function createVariableList(array $variables)
+ {
+ return implode(', ', array_map(function ($variableName) {
+ return '$' . $variableName;
+ }, $variables));
+ }
+
+ private function addMethodBody()
+ {
+ $this->newCode->appendLines($this->getUnindentedSelectedCode());
+ }
+
+ private function getUnindentedSelectedCode()
+ {
+ $detector = new IndentationDetector($this->selectedCode);
+
+ $lines = array_map(function ($line) use ($detector) {
+ return substr($line, $detector->getMinIndentation());
+ }, iterator_to_array(new ToStringIterator($this->selectedCode->getIterator())));
+
+ return LineCollection::createFromArray($lines);
+ }
+
+ private function addReturnStatement()
+ {
+ $returnVars = $this->newMethod->returnVariables();
+
+ $numVariables = count($returnVars);
+
+ if ($numVariables === 0) {
+ return;
+ }
+
+ $returnVariable = '$' . reset($returnVars);
+
+ if ($numVariables > 1) {
+ $returnVariable = 'array(' . $this->createVariableList($returnVars) . ')';
+ }
+
+ $this->newCode->appendBlankLine();
+ $this->newCode->appendString('return ' . $returnVariable . ';');
+ }
+
+ private function addMethodClosing()
+ {
+ $this->newCode->removeIndentation();
+ $this->newCode->appendString('}');
+ }
+
+
+ /**
+ * @return string[]
+ */
+ private function getNewCodeAsStringArray()
+ {
+ $toString = new ToStringIterator($this->newCode->getIterator());
+
+ return iterator_to_array($toString);
+ }
+}
View
37 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction/AddProperty.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
+
+use QafooLabs\Refactoring\Domain\Model\EditingAction;
+use QafooLabs\Refactoring\Domain\Model\EditorBuffer;
+
+class AddProperty implements EditingAction
+{
+ /**
+ * @var int
+ */
+ private $line;
+
+ /**
+ * @var string
+ */
+ private $propertyName;
+
+ /**
+ * @param int $line
+ * @param string $propertyName
+ */
+ public function __construct($line, $propertyName)
+ {
+ $this->line = $line;
+ $this->propertyName = $propertyName;
+ }
+
+ public function performEdit(EditorBuffer $buffer)
+ {
+ $buffer->append($this->line, array(
+ ' private $' . $this->propertyName . ';',
+ ''
+ ));
+ }
+}
View
38 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction/LocalVariableToInstance.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
+
+use QafooLabs\Refactoring\Domain\Model\EditingAction;
+use QafooLabs\Refactoring\Domain\Model\EditorBuffer;
+use QafooLabs\Refactoring\Domain\Model\DefinedVariables;
+use QafooLabs\Refactoring\Domain\Model\Variable;
+
+class LocalVariableToInstance implements EditingAction
+{
+ /**
+ * @var DefinedVariables
+ */
+ private $definedVars;
+
+ /**
+ * @var Variable
+ */
+ private $variable;
+
+ public function __construct(DefinedVariables $definedVars, Variable $variable)
+ {
+ $this->definedVars = $definedVars;
+ $this->variable = $variable;
+ }
+
+ public function performEdit(EditorBuffer $buffer)
+ {
+ $renamer = new RenameVariable(
+ $this->definedVars,
+ $this->variable,
+ $this->variable->convertToInstance()
+ );
+
+ $renamer->performEdit($buffer);
+ }
+}
View
62 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction/RenameVariable.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
+
+use QafooLabs\Refactoring\Domain\Model\EditingAction;
+use QafooLabs\Refactoring\Domain\Model\EditorBuffer;
+use QafooLabs\Refactoring\Domain\Model\DefinedVariables;
+use QafooLabs\Refactoring\Domain\Model\Variable;
+
+class RenameVariable implements EditingAction
+{
+ /**
+ * @var DefinedVariables
+ */
+ private $definedVars;
+
+ /**
+ * @var Variable
+ */
+ private $oldName;
+
+ /**
+ * @var Variable
+ */
+ private $newName;
+
+ public function __construct(DefinedVariables $definedVars, Variable $oldName, Variable $newName)
+ {
+ $this->definedVars = $definedVars;
+ $this->oldName = $oldName;
+ $this->newName = $newName;
+ }
+
+ public function performEdit(EditorBuffer $buffer)
+ {
+ foreach ($this->getLinesVariableIsUsedOn() as $line) {
+ $buffer->replaceString(
+ $line,
+ $this->oldName->getToken(),
+ $this->newName->getToken()
+ );
+ }
+ }
+
+ /**
+ * @return int[]
+ */
+ private function getLinesVariableIsUsedOn()
+ {
+ $variables = $this->definedVars->all();
+ $variableName = $this->oldName->getName();
+
+ $lines = array();
+
+ if (isset($variables[$variableName])) {
+ $lines = $variables[$variableName];
+ }
+
+ return $lines;
+ }
+}
+
View
79 src/main/QafooLabs/Refactoring/Domain/Model/EditingAction/ReplaceWithMethodCall.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
+
+use QafooLabs\Refactoring\Domain\Model\EditingAction;
+use QafooLabs\Refactoring\Domain\Model\EditorBuffer;
+use QafooLabs\Refactoring\Domain\Model\MethodSignature;
+use QafooLabs\Refactoring\Domain\Model\LineRange;
+
+class ReplaceWithMethodCall implements EditingAction
+{
+ /**
+ * @var LineRange
+ */
+ private $range;
+
+ /**
+ * @var MethodSignature
+ */
+ private $newMethod;
+
+ public function __construct(LineRange $range, MethodSignature $newMethod)
+ {
+ $this->range = $range;
+ $this->newMethod = $newMethod;
+ }
+
+ public function performEdit(EditorBuffer $buffer)
+ {
+ $buffer->replace($this->range, array($this->getIndent() . $this->getMethodCall()));
+ }
+
+ private function getIndent()
+ {
+ return ' ';
+ }
+
+ private function getMethodCall()
+ {
+ return sprintf(
+ '%s%s%s(%s);',
+ $this->getReturnVariables(),
+ ($this->newMethod->isStatic() ? 'self::' : '$this->'),
+ $this->newMethod->getName(),
+ $this->createVariableList($this->newMethod->arguments())
+ );
+ }
+
+ private function getReturnVariables()
+ {
+ $returnVars = $this->newMethod->returnVariables();
+
+ $numVariables = count($returnVars);
+
+ if ($numVariables === 0) {
+ return;
+ }
+
+ $returnVariable = '$' . reset($returnVars);
+
+ if ($numVariables > 1) {
+ $returnVariable = 'list(' . $this->createVariableList($returnVars) . ')';
+ }
+
+ return $returnVariable . ' = ';
+ }
+
+ /**
+ * @param string[] $variables
+ *
+ * @return string
+ */
+ private function createVariableList(array $variables)
+ {
+ return implode(', ', array_map(function ($variableName) {
+ return '$' . $variableName;
+ }, $variables));
+ }
+}
View
121 src/main/QafooLabs/Refactoring/Domain/Model/EditingSession.php
@@ -21,126 +21,25 @@ class EditingSession
*/
private $buffer;
+ /**
+ * @var EditingAction[]
+ */
+ private $actions = array();
+
public function __construct(EditorBuffer $buffer)
{
$this->buffer = $buffer;
}
- public function replaceString(DefinedVariables $definedVariables, Variable $oldName, Variable $newName)
- {
- $this->replaceStringInArray($definedVariables->all(), $oldName, $newName);
- }
-
- private function replaceStringInArray(array $variables, Variable $oldName, Variable $newName)
- {
- if (isset($variables[$oldName->getName()])) {
- foreach ($variables[$oldName->getName()] as $line) {
- $this->buffer->replaceString($line, $oldName->getToken(), $newName->getToken());
- }
- }
- }
-
- public function replaceRangeWithMethodCall(LineRange $range, MethodSignature $newMethod)
- {
- $argumentLine = $this->implodeVariables($newMethod->arguments());
-
- $code = $newMethod->isStatic() ? 'self::%s(%s);' : '$this->%s(%s);';
- $call = sprintf($code, $newMethod->getName(), $argumentLine);
-
- if (count($newMethod->returnVariables()) == 1) {
- $call = '$' . $newMethod->returnVariables()[0] . ' = ' . $call;
- } else if (count($newMethod->returnVariables()) > 1) {
- $call = 'list(' . $this->implodeVariables($newMethod->returnVariables()) . ') = ' . $call;
- }
-
- $this->buffer->replace($range, array($this->whitespace(8) . $call));
- }
-
- public function addMethod($line, MethodSignature $newMethod, $selectedCode)
- {
- if (count($newMethod->returnVariables()) == 1) {
- $selectedCode[] = '';
- $selectedCode[] = $this->whitespace(8) . 'return $' . $newMethod->returnVariables()[0] . ';';
- } else if (count($newMethod->returnVariables()) > 1) {
- $selectedCode[] = '';
- $selectedCode[] = $this->whitespace(8) . 'return array(' . $this->implodeVariables($newMethod->returnVariables()) . ');';
- }
-
- $methodCode = array_merge(
- array(
- '',
- $this->whitespace(4) . $this->renderMethodSignature($newMethod),
- $this->whitespace(4) . '{'
- ),
- $this->realign($selectedCode, 8),
- array($this->whitespace(4) . '}')
- );
-
- $this->buffer->append($line, $methodCode);
- }
-
- private function alignedAtWhitespaces(array $lines)
+ public function addEdit(EditingAction $action)
{
- return array_reduce($lines, function ($minWhitespace, $line) {
- if ($this->isEmptyLine($line)) {
- return $minWhitespace;
- }
-
- return min($minWhitespace, $this->leftWhitespacesOf($line));
- }, 100);
+ $this->actions[] = $action;
}
- private function realign(array $lines, $atWhitespaces)
+ public function performEdits()
{
- $minWhitespaces = $this->alignedAtWhitespaces($lines);
- $whitespaceCorrection = $atWhitespaces - $minWhitespaces;
-
- if ($whitespaceCorrection === 0) {
- return $lines;
+ foreach ($this->actions as $action) {
+ $action->performEdit($this->buffer);
}
-
- return array_map(function ($line) use($whitespaceCorrection) {
- if ($whitespaceCorrection > 0) {
- return $this->whitespace($whitespaceCorrection) . $line;
- }
-
- return substr($line, $whitespaceCorrection - 2); // Why -2?
- }, $lines);
- }
-
- private function isEmptyLine($line)
- {
- return trim($line) === "";
- }
-
- private function leftWhitespacesOf($line)
- {
- return strlen($line) - strlen(ltrim($line));
- }
-
- private function renderMethodSignature(MethodSignature $method)
- {
- $paramLine = $this->implodeVariables($method->arguments());
-
- return sprintf('private%sfunction %s(%s)', $method->isStatic() ? ' static ' : ' ', $method->getName(), $paramLine);
- }
-
- private function implodeVariables($variableNames)
- {
- return implode(', ', array_map(function ($variableName) {
- return '$' . $variableName;
- }, $variableNames));
- }
-
- public function addProperty($line, $propertyName)
- {
- $this->buffer->append($line, array(
- $this->whitespace(4) . 'private $' . $propertyName . ';', ''
- ));
- }
-
- private function whitespace($number)
- {
- return str_repeat(' ', $number);
}
}
View
56 src/main/QafooLabs/Refactoring/Domain/Model/IndentationDetector.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model;
+
+class IndentationDetector
+{
+ /**
+ * @var LineCollection
+ */
+ private $lines;
+
+ public function __construct(LineCollection $lines)
+ {
+ $this->lines = $lines;
+ }
+
+ /**
+ * @return int
+ */
+ public function getMinIndentation()
+ {
+ return array_reduce(
+ iterator_to_array($this->lines),
+ function ($minIndentation, $line) {
+ $indentation = $line->getIndentation();
+
+ if ($line->isEmpty()) {
+ return $minIndentation;
+ }
+
+ if ($minIndentation === null) {
+ return $indentation;
+ }
+
+ return min($minIndentation, $indentation);
+ }
+ );
+ }
+
+ /**
+ * @return int
+ */
+ public function getFirstLineIndentation()
+ {
+ $indentation = null;
+
+ foreach ($this->lines as $line) {
+ if (!$line->isEmpty()) {
+ $indentation = $line->getIndentation();
+ break;
+ }
+ }
+
+ return $indentation;
+ }
+}
View
36 src/main/QafooLabs/Refactoring/Domain/Model/IndentingLineCollection.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model;
+
+class IndentingLineCollection extends LineCollection
+{
+ const INDENTATION_SIZE = 4;
+
+ /**
+ * @var int
+ */
+ private $indentation = 0;
+
+ public function addIndentation()
+ {
+ $this->indentation++;
+ }
+
+ public function removeIndentation()
+ {
+ $this->indentation--;
+ }
+
+ public function append(Line $line)
+ {
+ parent::append(new Line($this->createIndentationString() . (string) $line));
+ }
+
+ /**
+ * @return string
+ */
+ private function createIndentationString()
+ {
+ return str_repeat(' ', $this->indentation * self::INDENTATION_SIZE);
+ }
+}
View
43 src/main/QafooLabs/Refactoring/Domain/Model/Line.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model;
+
+class Line
+{
+ /**
+ * @var string
+ */
+ private $line;
+
+ /**
+ * @param string $line
+ */
+ public function __construct($line)
+ {
+ $this->line = (string) $line;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->line;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return trim($this->line) === '';
+ }
+
+ /**
+ * @return int
+ */
+ public function getIndentation()
+ {
+ return strlen($this->line) - strlen(ltrim($this->line));
+ }
+}
View
82 src/main/QafooLabs/Refactoring/Domain/Model/LineCollection.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model;
+
+use ArrayIterator;
+use IteratorAggregate;
+
+class LineCollection implements IteratorAggregate
+{
+ /**
+ * @var Line[]
+ */
+ private $lines;
+
+ /**
+ * @param Line[] $lines
+ */
+ public function __construct(array $lines = array())
+ {
+ $this->lines = $lines;
+ }
+
+ public function getIterator()
+ {
+ return new ArrayIterator($this->lines);
+ }
+
+ /**
+ * @return Line[]
+ */
+ public function getLines()
+ {
+ return $this->lines;
+ }
+
+ public function append(Line $line)
+ {
+ $this->lines[] = $line;
+ }
+
+ /**
+ * @param string $line
+ */
+ public function appendString($line)
+ {
+ $this->append(new Line($line));
+ }
+
+ public function appendLines(LineCollection $lines)
+ {
+ foreach ($lines as $line) {
+ $this->append($line);
+ }
+ }
+
+ public function appendBlankLine()
+ {
+ $this->lines[] = new Line('');
+ }
+
+ /**
+ * @param string[] $lines
+ *
+ * @return LineCollection
+ */
+ public static function createFromArray(array $lines)
+ {
+ return new self(array_map(function ($line) {
+ return new Line($line);
+ }, $lines));
+ }
+
+ /**
+ * @param string $code
+ *
+ * @return LineCollection
+ */
+ public static function createFromString($code)
+ {
+ return self::createFromArray(explode("\n", $code));
+ }
+}
View
52 src/main/QafooLabs/Refactoring/Utils/ToStringIterator.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace QafooLabs\Refactoring\Utils;
+
+use Iterator;
+
+class ToStringIterator implements Iterator
+{
+ /**
+ * @var Iterator
+ */
+ private $iterator;
+
+ public function __construct(Iterator $it)
+ {
+ $this->iterator = $it;
+ }
+
+ /**
+ * @return string
+ */
+ public function current()
+ {
+ return (string) $this->iterator->current();
+ }
+
+ /**
+ * @return scalar
+ */
+ public function key()
+ {
+ return $this->iterator->key();
+ }
+
+ public function next()
+ {
+ $this->iterator->next();
+ }
+
+ public function rewind()
+ {
+ $this->iterator->rewind();
+ }
+
+ /**
+ * @return bool
+ */
+ public function valid()
+ {
+ return $this->iterator->valid();
+ }
+}
View
51 src/test/QafooLabs/Refactoring/Application/RenameLocalVariableTest.php.orig
@@ -1,51 +0,0 @@
-<?php
-
-namespace QafooLabs\Refactoring\Application;
-
-use QafooLabs\Refactoring\Domain\Model\File;
-use QafooLabs\Refactoring\Domain\Model\Variable;
-use QafooLabs\Refactoring\Domain\Model\DefinedVariables;
-
-use QafooLabs\Refactoring\Adapters\PHPParser\ParserVariableScanner;
-use QafooLabs\Refactoring\Adapters\TokenReflection\StaticCodeAnalysis;
-use QafooLabs\Refactoring\Adapters\Patches\PatchEditor;
-
-class RenameLocalVariableTest extends \PHPUnit_Framework_TestCase
-{
- public function testRenameLocalVariable()
- {
- $scanner = \Phake::mock('QafooLabs\Refactoring\Domain\Services\VariableScanner');
- $codeAnalysis = \Phake::mock('QafooLabs\Refactoring\Domain\Services\CodeAnalysis');
- $editor = \Phake::mock('QafooLabs\Refactoring\Domain\Services\Editor');
- $buffer = \Phake::mock('QafooLabs\Refactoring\Domain\Model\EditorBuffer');
-
- \Phake::when($scanner)->scanForVariables(\Phake::anyParameters())->thenReturn(
- new DefinedVariables(array('helloWorld' => array(6)))
- );
- \Phake::when($editor)->openBuffer(\Phake::anyParameters())->thenReturn($buffer);
-
- $refactoring = new RenameLocalVariable($scanner, $codeAnalysis, $editor);
-
- $patch = $refactoring->refactor(new File("foo.php", <<<'PHP'
-<?php
-class Foo
-{
- public function main()
- {
- $helloWorld = 'bar';
- }
-}
-PHP
- ), 6, new Variable('$helloWorld'), new Variable('$var'));
-
- \Phake::verify($buffer)->replaceString(6, '$helloWorld', '$var');
- }
-
- public function testRenameNonLocalVariable_ThrowsException()
- {
- }
-
- public function testRenameIntoNonLocalVariable_ThrowsException()
- {
- }
-}
View
229 src/test/QafooLabs/Refactoring/Domain/Model/EditingAction/AddMethodTest.php
@@ -0,0 +1,229 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
+
+use QafooLabs\Refactoring\Domain\Model\MethodSignature;
+use QafooLabs\Refactoring\Domain\Model\Line;
+use QafooLabs\Refactoring\Domain\Model\LineCollection;
+
+class AddMethodTest extends \PHPUnit_Framework_TestCase
+{
+ private $action;
+
+ private $buffer;
+
+ protected function setUp()
+ {
+ $this->buffer = $this->getMock('QafooLabs\Refactoring\Domain\Model\EditorBuffer');
+ }
+
+ public function testItIsAnEditingAction()
+ {
+ $this->assertInstanceOf(
+ 'QafooLabs\Refactoring\Domain\Model\EditingAction',
+ new AddMethod(0, new MethodSignature('test'), new LineCollection())
+ );
+ }
+
+ public function testBufferAppendIsPerformedAtTheGivenLineNumber()
+ {
+ $lineNumber = 27;
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('append')
+ ->with($this->equalTo($lineNumber), $this->anything());
+
+ $action = new AddMethod($lineNumber, new MethodSignature('test'), new LineCollection());
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testAppendsMethod()
+ {
+ $action = new AddMethod(0, new MethodSignature('testMethod'), new LineCollection());
+
+ $this->assertGeneratedCodeMatches(array(
+ '',
+ ' private function testMethod()',
+ ' {',
+ ' }'
+ ), $action);
+ }
+
+ public function testReturnStatementForSingleVariable()
+ {
+ $action = new AddMethod(
+ 0,
+ $this->createMethodSignatureWithReturnVars(array('returnVar')),
+ new LineCollection()
+ );
+
+ $this->assertGeneratedCodeMatches(array(
+ '',
+ ' private function testMethod()',
+ ' {',
+ '',
+ ' return $returnVar;',
+ ' }'
+ ), $action);
+ }
+
+ public function testReturnStatementForSingleVariableHasCorrectName()
+ {
+ $action = new AddMethod(
+ 0,
+ $this->createMethodSignatureWithReturnVars(array('specialVar')),
+ new LineCollection()
+ );
+
+ $this->assertGeneratedCodeMatches(array(
+ '',
+ ' private function testMethod()',
+ ' {',
+ '',
+ ' return $specialVar;',
+ ' }'
+ ), $action);
+ }
+
+ public function testReturnStatementForMultipleVariables()
+ {
+ $action = new AddMethod(
+ 0,
+ $this->createMethodSignatureWithReturnVars(array('ret1', 'ret2')),
+ new LineCollection()
+ );
+
+ $this->assertGeneratedCodeMatches(array(
+ '',
+ ' private function testMethod()',
+ ' {',
+ '',
+ ' return array($ret1, $ret2);',
+ ' }'
+ ), $action);
+ }
+
+ public function testMethodNameIsUsed()
+ {
+ $action = new AddMethod(
+ 0,
+ new MethodSignature('realMethodName'),
+ new LineCollection()
+ );
+
+ $this->assertGeneratedCodeMatches(array(
+ '',
+ ' private function realMethodName()',
+ ' {',
+ ' }'
+ ), $action);
+ }
+
+ public function testStaticMethodsAreDefinedCorrectly()
+ {
+ $action = new AddMethod(
+ 0,
+ new MethodSignature(
+ 'realMethodName',
+ MethodSignature::IS_PRIVATE | MethodSignature::IS_STATIC
+ ),
+ new LineCollection()
+ );
+
+ $this->assertGeneratedCodeMatches(array(
+ '',
+ ' private static function realMethodName()',
+ ' {',
+ ' }'
+ ), $action);
+ }
+
+ public function testMethodArgumentsAreDefinedCorrectly()
+ {
+ $action = new AddMethod(
+ 0,
+ new MethodSignature(
+ 'testMethod',
+ MethodSignature::IS_PRIVATE,
+ array('param1', 'param2')
+ ),
+ new LineCollection()
+ );
+
+ $this->assertGeneratedCodeMatches(array(
+ '',
+ ' private function testMethod($param1, $param2)',
+ ' {',
+ ' }'
+ ), $action);
+ }
+
+ public function testSelectedCodeIsAdded()
+ {
+ $action = new AddMethod(
+ 0,
+ $this->createMethodSignatureWithReturnVars(array()),
+ LineCollection::createFromArray(array(
+ 'echo "Hello World!";'
+ ))
+ );
+
+ $this->assertGeneratedCodeMatches(array(
+ '',
+ ' private function testMethod()',
+ ' {',
+ ' echo "Hello World!";',
+ ' }'
+ ), $action);
+ }
+
+ public function testSelectedCodeIsAddedWithCorrectIndetations()
+ {
+ $action = new AddMethod(
+ 0,
+ $this->createMethodSignatureWithReturnVars(array()),
+ LineCollection::createFromArray(array(
+ ' if ($something) {',
+ ' echo "Hello World!";',
+ ' }'
+ ))
+ );
+
+ $this->assertGeneratedCodeMatches(array(
+ '',
+ ' private function testMethod()',
+ ' {',
+ ' if ($something) {',
+ ' echo "Hello World!";',
+ ' }',
+ ' }'
+ ), $action);
+ }
+
+ private function assertGeneratedCodeMatches(array $expected, AddMethod $action)
+ {
+ $this->makeBufferAppendExpectCode($expected);
+
+ $action->performEdit($this->buffer);
+ }
+
+ private function createMethodSignatureWithReturnVars(array $returnVars)
+ {
+ return new MethodSignature(
+ 'testMethod',
+ MethodSignature::IS_PRIVATE,
+ array(),
+ $returnVars
+ );
+ }
+
+ private function makeBufferAppendExpectCode(array $codeLines)
+ {
+ $this->buffer
+ ->expects($this->once())
+ ->method('append')
+ ->with($this->anything(), $this->equalTo($codeLines));
+ }
+}
View
50 src/test/QafooLabs/Refactoring/Domain/Model/EditingAction/AddPropertyTest.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
+
+class AddPropertyTest extends \PHPUnit_Framework_TestCase
+{
+ private $buffer;
+
+ protected function setUp()
+ {
+ $this->buffer = $this->getMock('QafooLabs\Refactoring\Domain\Model\EditorBuffer');
+ }
+
+ public function testItIsAnEditingAction()
+ {
+ $this->assertInstanceOf(
+ 'QafooLabs\Refactoring\Domain\Model\EditingAction',
+ new AddProperty(5, 'testProperty')
+ );
+ }
+
+ public function testPropertyIsAppenedAtGivenLine()
+ {
+ $line = 27;
+
+ $action = new AddProperty($line, '');
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('append')
+ ->with($line, $this->anything());
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testPropertyCodeIsCorrect()
+ {
+ $action = new AddProperty(5, 'testProperty');
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('append')
+ ->with($this->anything(), $this->equalTo(array(
+ ' private $testProperty;',
+ ''
+ )));
+
+ $action->performEdit($this->buffer);
+ }
+}
View
114 src/test/QafooLabs/Refactoring/Domain/Model/EditingAction/LocalVariableToInstanceTest.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
+
+use QafooLabs\Refactoring\Domain\Model\Variable;
+use QafooLabs\Refactoring\Domain\Model\DefinedVariables;
+
+class LocalVariableToInstanceTest extends \PHPUnit_Framework_TestCase
+{
+ private $buffer;
+
+ protected function setUp()
+ {
+ $this->buffer = $this->getMock('QafooLabs\Refactoring\Domain\Model\EditorBuffer');
+ }
+
+ public function testItIsAnEditingAction()
+ {
+ $this->assertInstanceOf(
+ 'QafooLabs\Refactoring\Domain\Model\EditingAction',
+ new LocalVariableToInstance(
+ new DefinedVariables(array(), array()),
+ new Variable('testVar')
+ )
+ );
+ }
+
+ public function testItReplacesVariableWithInstanceVariableVersion()
+ {
+ $variable = new Variable('varName');
+
+ $action = new LocalVariableToInstance(
+ new DefinedVariables(array('varName' => array(1)), array()),
+ $variable
+ );
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('replaceString')
+ ->with($this->anything(), $this->equalTo('$varName'), $this->equalTo('$this->varName'));
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testItReplacesOnLineForReadOnlyVariable()
+ {
+ $definedVars = new DefinedVariables(array('theVar' => array(12)), array());
+ $variable = new Variable('theVar');
+
+ $action = new LocalVariableToInstance($definedVars, $variable);
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('replaceString')
+ ->with($this->equalTo(12), $this->anything(), $this->anything());
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testItReplacesOn2LinesForReadOnlyVariable()
+ {
+ $definedVars = new DefinedVariables(array('theVar' => array(12, 15)), array());
+ $variable = new Variable('theVar');
+
+ $action = new LocalVariableToInstance($definedVars, $variable);
+
+ $this->buffer
+ ->expects($this->at(0))
+ ->method('replaceString')
+ ->with($this->equalTo(12), $this->anything(), $this->anything());
+
+ $this->buffer
+ ->expects($this->at(1))
+ ->method('replaceString')
+ ->with($this->equalTo(15), $this->anything(), $this->anything());
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testItReplacesOnLineForChangedVariable()
+ {
+ $definedVars = new DefinedVariables(array(), array('theVar' => array(12)));
+ $variable = new Variable('theVar');
+
+ $action = new LocalVariableToInstance($definedVars, $variable);
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('replaceString')
+ ->with($this->equalTo(12), $this->anything(), $this->anything());
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testItReplacesOn2LinesForChangedVariable()
+ {
+ $definedVars = new DefinedVariables(array(), array('theVar' => array(12, 15)));
+ $variable = new Variable('theVar');
+
+ $action = new LocalVariableToInstance($definedVars, $variable);
+
+ $this->buffer
+ ->expects($this->at(0))
+ ->method('replaceString')
+ ->with($this->equalTo(12), $this->anything(), $this->anything());
+
+ $this->buffer
+ ->expects($this->at(1))
+ ->method('replaceString')
+ ->with($this->equalTo(15), $this->anything(), $this->anything());
+
+ $action->performEdit($this->buffer);
+ }
+}
View
117 src/test/QafooLabs/Refactoring/Domain/Model/EditingAction/RenameVariableTest.php
@@ -0,0 +1,117 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
+
+use QafooLabs\Refactoring\Domain\Model\Variable;
+use QafooLabs\Refactoring\Domain\Model\DefinedVariables;
+
+class RenameVariableTest extends \PHPUnit_Framework_TestCase
+{
+ private $buffer;
+
+ protected function setUp()
+ {
+ $this->buffer = $this->getMock('QafooLabs\Refactoring\Domain\Model\EditorBuffer');
+ }
+
+ public function testItIsAnEditingAction()
+ {
+ $this->assertInstanceOf(
+ 'QafooLabs\Refactoring\Domain\Model\EditingAction',
+ new RenameVariable(
+ new DefinedVariables(array(), array()),
+ new Variable('testVar'),
+ new Variable('newVar')
+ )
+ );
+ }
+
+ public function testItReplacesVariableWithInstanceVariableVersion()
+ {
+ $oldName = new Variable('varName');
+ $newName = new Variable('newName');
+
+ $action = new RenameVariable(
+ new DefinedVariables(array('varName' => array(1)), array()),
+ $oldName,
+ $newName
+ );
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('replaceString')
+ ->with($this->anything(), $this->equalTo('$varName'), $this->equalTo('$newName'));
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testItReplacesOnLineForReadOnlyVariable()
+ {
+ $definedVars = new DefinedVariables(array('theVar' => array(12)), array());
+ $variable = new Variable('theVar');
+
+ $action = new RenameVariable($definedVars, $variable, $variable);
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('replaceString')
+ ->with($this->equalTo(12), $this->anything(), $this->anything());
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testItReplacesOn2LinesForReadOnlyVariable()
+ {
+ $definedVars = new DefinedVariables(array('theVar' => array(12, 15)), array());
+ $variable = new Variable('theVar');
+
+ $action = new RenameVariable($definedVars, $variable, $variable);
+
+ $this->buffer
+ ->expects($this->at(0))
+ ->method('replaceString')
+ ->with($this->equalTo(12), $this->anything(), $this->anything());
+
+ $this->buffer
+ ->expects($this->at(1))
+ ->method('replaceString')
+ ->with($this->equalTo(15), $this->anything(), $this->anything());
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testItReplacesOnLineForChangedVariable()
+ {
+ $definedVars = new DefinedVariables(array(), array('theVar' => array(12)));
+ $variable = new Variable('theVar');
+
+ $action = new RenameVariable($definedVars, $variable, $variable);
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('replaceString')
+ ->with($this->equalTo(12), $this->anything(), $this->anything());
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testItReplacesOn2LinesForChangedVariable()
+ {
+ $definedVars = new DefinedVariables(array(), array('theVar' => array(12, 15)));
+ $variable = new Variable('theVar');
+
+ $action = new RenameVariable($definedVars, $variable, $variable);
+
+ $this->buffer
+ ->expects($this->at(0))
+ ->method('replaceString')
+ ->with($this->equalTo(12), $this->anything(), $this->anything());
+
+ $this->buffer
+ ->expects($this->at(1))
+ ->method('replaceString')
+ ->with($this->equalTo(15), $this->anything(), $this->anything());
+
+ $action->performEdit($this->buffer);
+ }
+}
View
122 src/test/QafooLabs/Refactoring/Domain/Model/EditingAction/ReplaceWithMethodCallTest.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model\EditingAction;
+
+use QafooLabs\Refactoring\Domain\Model\LineRange;
+use QafooLabs\Refactoring\Domain\Model\MethodSignature;
+
+class ReplaceWithMethodCallTest extends \PHPUnit_Framework_TestCase
+{
+ private $buffer;
+
+ protected function setUp()
+ {
+ $this->buffer = $this->getMock('QafooLabs\Refactoring\Domain\Model\EditorBuffer');
+ }
+
+ public function testItIsAnEditingAction()
+ {
+ $this->assertInstanceOf(
+ 'QafooLabs\Refactoring\Domain\Model\EditingAction',
+ new ReplaceWithMethodCall(
+ LineRange::fromLines(1, 2),
+ new MethodSignature('testMethod')
+ )
+ );
+ }
+
+ public function testBufferReplacesAtGivenRange()
+ {
+ $range = LineRange::fromLines(1, 2);
+
+ $action = new ReplaceWithMethodCall(
+ $range,
+ new MethodSignature('testMethod')
+ );
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('replace')
+ ->with($this->equalTo($range), $this->anything());
+
+ $action->performEdit($this->buffer);
+ }
+
+ public function testMethodCallIsCorrectForSimpleMethod()
+ {
+ $action = new ReplaceWithMethodCall(
+ LineRange::fromLines(1, 2),
+ new MethodSignature('testMethod')
+ );
+
+ $this->assertGeneratedMethodCallMatches('$this->testMethod();', $action);
+ }
+
+ public function testMethodCallUsesGivenMethodName()
+ {
+ $action = new ReplaceWithMethodCall(
+ LineRange::fromLines(1, 2),
+ new MethodSignature('realMethod')
+ );
+
+ $this->assertGeneratedMethodCallMatches('$this->realMethod();', $action);
+ }
+
+ public function testStaticMethodCall()
+ {
+ $action = new ReplaceWithMethodCall(
+ LineRange::fromLines(1, 2),
+ new MethodSignature('testMethod', MethodSignature::IS_STATIC)
+ );
+
+ $this->assertGeneratedMethodCallMatches('self::testMethod();', $action);
+ }
+
+ public function testMethodCallWithSingleReturnVariable()
+ {
+ $action = new ReplaceWithMethodCall(
+ LineRange::fromLines(1, 2),
+ new MethodSignature('testMethod', 0, array(), array('result'))
+ );
+
+ $this->assertGeneratedMethodCallMatches('$result = $this->testMethod();', $action);
+ }
+
+ public function testMethodCallWithMultipleReturnVariables()
+ {
+ $action = new ReplaceWithMethodCall(
+ LineRange::fromLines(1, 2),
+ new MethodSignature('testMethod', 0, array(), array('result1', 'result2'))
+ );
+
+ $this->assertGeneratedMethodCallMatches(
+ 'list($result1, $result2) = $this->testMethod();',
+ $action
+ );
+ }
+
+ public function testMethodCallWithArguments()
+ {
+ $action = new ReplaceWithMethodCall(
+ LineRange::fromLines(1, 2),
+ new MethodSignature('testMethod', 0, array('arg1', 'arg2'))
+ );
+
+ $this->assertGeneratedMethodCallMatches(
+ '$this->testMethod($arg1, $arg2);',
+ $action
+ );
+ }
+
+ private function assertGeneratedMethodCallMatches($expected, $action)
+ {
+ $expected = ' ' . $expected;
+
+ $this->buffer
+ ->expects($this->once())
+ ->method('replace')
+ ->with($this->anything(), $this->equalTo(array($expected)));
+
+ $action->performEdit($this->buffer);
+ }
+}
View
36 src/test/QafooLabs/Refactoring/Domain/Model/EditingSessionTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model;
+
+class EditingSessionTest extends \PHPUnit_Framework_TestCase
+{
+ private $session;
+
+ private $buffer;
+
+ protected function setUp()
+ {
+ $this->buffer = $this->getMock('QafooLabs\Refactoring\Domain\Model\EditorBuffer');
+
+ $this->session = new EditingSession($this->buffer);
+ }
+
+ public function testEditActionsArePerformed()
+ {
+ $action1 = $this->getMock('QafooLabs\Refactoring\Domain\Model\EditingAction');
+ $action2 = $this->getMock('QafooLabs\Refactoring\Domain\Model\EditingAction');
+
+ $action1->expects($this->once())
+ ->method('performEdit')
+ ->with($this->equalTo($this->buffer));
+
+ $action2->expects($this->once())
+ ->method('performEdit')
+ ->with($this->equalTo($this->buffer));
+
+ $this->session->addEdit($action1);
+ $this->session->addEdit($action2);
+
+ $this->session->performEdits();
+ }
+}
View
68 src/test/QafooLabs/Refactoring/Domain/Model/IndentationDetectorTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model;
+
+class IndentationDetectorTest extends \PHPUnit_Framework_TestCase
+{
+ public function testGetMinIndentationForOneLine()
+ {
+ $detector = $this->createDetector(array(' echo "test";'));
+
+ $this->assertEquals(4, $detector->getMinIndentation());
+ }
+
+ public function testGetMinIndentationForFirstLine()
+ {
+ $detector = $this->createDetector(array(
+ ' echo "Line 1";',
+ ' echo "Line 2";',
+ ));
+
+ $this->assertEquals(2, $detector->getMinIndentation());
+ }
+
+ public function testGetMinIntentationForLaterLine()
+ {
+ $detector = $this->createDetector(array(
+ ' echo "Line 1";',
+ ' echo "Line 2";',
+ ));
+
+ $this->assertEquals(2, $detector->getMinIndentation());
+ }
+
+ public function testGetMinIndentationWithBlankLines()
+ {
+ $detector = $this->createDetector(array(
+ '',
+ ' echo "test";',
+ ));
+
+ $this->assertEquals(4, $detector->getMinIndentation());
+ }
+
+ public function testGetFirstLineIndentation()
+ {
+ $detector = $this->createDetector(array(
+ ' echo "line 1";',
+ ' echo "line 2";',
+ ));
+
+ $this->assertEquals(4, $detector->getFirstLineIndentation());
+ }
+
+ public function testGetFirstLineIndentationWithBlankLines()
+ {
+ $detector = $this->createDetector(array(
+ '',
+ ' echo "test";',
+ ));
+
+ $this->assertEquals(2, $detector->getFirstLineIndentation());
+ }
+
+ private function createDetector(array $lines)
+ {
+ return new IndentationDetector(LineCollection::createFromArray($lines));
+ }
+}
View
109 src/test/QafooLabs/Refactoring/Domain/Model/IndentingLineCollectionTest.php
@@ -0,0 +1,109 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model;
+
+use QafooLabs\Refactoring\Utils\ToStringIterator;
+
+class IndentingLineCollectionTest extends \PHPUnit_Framework_TestCase
+{
+ private $lines;
+
+ protected function setUp()
+ {
+ $this->lines = new IndentingLineCollection();
+ }
+
+ public function testIsALineCollection()
+ {
+ $this->assertInstanceOf(
+ 'QafooLabs\Refactoring\Domain\Model\LineCollection',
+ $this->lines
+ );
+ }
+
+ public function testAppendAddsIndentation()
+ {
+ $this->lines->addIndentation();
+
+ $this->lines->append(new Line('echo "test";'));
+
+ $this->assertLinesMatch(array(
+ ' echo "test";'
+ ));
+ }
+
+ public function testAppendAddsMulitpleIndentation()
+ {
+ $this->lines->append(new Line('echo "line1";'));
+ $this->lines->addIndentation();
+ $this->lines->append(new Line('echo "line2";'));
+ $this->lines->addIndentation();
+ $this->lines->append(new Line('echo "line3";'));
+
+ $this->assertLinesMatch(array(
+ 'echo "line1";',
+ ' echo "line2";',
+ ' echo "line3";'
+ ));
+ }
+
+ public function testAppendRemovesIndentation()
+ {
+ $this->lines->append(new Line('echo "line1";'));
+ $this->lines->addIndentation();
+ $this->lines->append(new Line('echo "line2";'));
+ $this->lines->removeIndentation();
+ $this->lines->append(new Line('echo "line3";'));
+
+ $this->assertLinesMatch(array(
+ 'echo "line1";',
+ ' echo "line2";',
+ 'echo "line3";'
+ ));
+ }
+
+ public function testAppendStringObeysIndentation()
+ {
+ $this->lines->appendString('echo "line1";');
+ $this->lines->addIndentation();
+ $this->lines->appendString('echo "line2";');
+ $this->lines->removeIndentation();
+ $this->lines->appendString('echo "line3";');
+
+ $this->assertLinesMatch(array(
+ 'echo "line1";',
+ ' echo "line2";',
+ 'echo "line3";'
+ ));
+ }
+
+ public function testAppendLinesObeysIndentation()
+ {
+ $this->lines->addIndentation();
+
+ $this->lines->appendLines(LineCollection::createFromArray(array(
+ 'echo "line1";',
+ 'echo "line2";'
+ )));
+
+ $this->assertLinesMatch(array(
+ ' echo "line1";',
+ ' echo "line2";',
+ ));
+ }
+
+ public function testAddBlankLineContainsNoIndentation()
+ {
+ $this->lines->appendBlankLine();
+
+ $this->assertLinesMatch(array(''));
+ }
+
+ private function assertLinesMatch(array $expected)
+ {
+ $this->assertEquals(
+ $expected,
+ iterator_to_array(new ToStringIterator($this->lines->getIterator()))
+ );
+ }
+}
View
114 src/test/QafooLabs/Refactoring/Domain/Model/LineCollectionTest.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace QafooLabs\Refactoring\Domain\Model;
+
+use QafooLabs\Refactoring\Utils\ToStringIterator;
+
+class LineCollectionTest extends \PHPUnit_Framework_TestCase
+{
+ public function testItStoresLines()
+ {
+ $lineObjects = array(
+ new Line('line 1'),
+ new Line('line 2')
+ );
+
+ $lines = new LineCollection($lineObjects);
+
+ $this->assertSame($lineObjects, $lines->getLines());
+ }
+
+ public function testAppendAddsALine()
+ {
+ $line1 = new Line('line 1');
+ $line2 = new Line('line 2');
+
+ $lines = new LineCollection(array($line1));
+
+ $lines->append($line2);
+
+ $this->assertSame(array($line1, $line2), $lines->getLines());
+ }
+
+ public function testAppendStringAddsALine()
+ {
+ $line1 = 'line 1';
+ $line2 = 'line 2';
+
+ $lines = new LineCollection(array(new Line($line1)));
+
+ $lines->appendString($line2);
+
+ $this->assertEquals(
+ array(new Line($line1), new Line($line2)),
+ $lines->getLines()
+ );