Skip to content

Commit

Permalink
added canRename API call
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexHaxe committed May 3, 2022
1 parent eada3d5 commit 23924e8
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 59 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## dev branch / next version (2.x.x)

## 2.2.0 (2022-05-03)

- added canRename API call

## 2.1.4 (2022-05-02)

- fixed handling of star imports
Expand Down
4 changes: 2 additions & 2 deletions haxelib.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"refactor"
],
"description": "A code renaming tool for Haxe",
"version": "2.1.4",
"releasenote": "fixed handling of star imports",
"version": "2.2.0",
"releasenote": "added canRename API call - see CHANGELOG",
"contributors": [
"AlexHaxe"
],
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@haxecheckstyle/haxe-rename",
"version": "2.1.4",
"version": "2.2.0",
"description": "Renaming tool for Haxe",
"repository": {
"type": "git",
Expand Down
16 changes: 16 additions & 0 deletions src/refactor/CanRefactorContext.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package refactor;

import refactor.ITyper;
import refactor.RefactorContext;
import refactor.discover.FileList;
import refactor.discover.NameMap;
import refactor.discover.TypeList;

typedef CanRefactorContext = {
var nameMap:NameMap;
var fileList:FileList;
var typeList:TypeList;
var what:RefactorWhat;
var verboseLog:VerboseLogger;
var typer:Null<ITyper>;
}
8 changes: 8 additions & 0 deletions src/refactor/CanRefactorResult.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package refactor;

import refactor.discover.IdentifierPos;

typedef CanRefactorResult = {
var name:String;
var pos:IdentifierPos;
}
62 changes: 53 additions & 9 deletions src/refactor/Refactor.hx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package refactor;

import refactor.discover.File;
import refactor.discover.Identifier;
import refactor.discover.IdentifierPos;
import refactor.rename.RenameAnonStructField;
import refactor.rename.RenameEnumField;
import refactor.rename.RenameField;
Expand All @@ -12,6 +13,48 @@ import refactor.rename.RenameScopedLocal;
import refactor.rename.RenameTypeName;

class Refactor {
public static function canRename(context:CanRefactorContext):Promise<CanRefactorResult> {
var file:Null<File> = context.fileList.getFile(context.what.fileName);
if (file == null) {
return Promise.reject(RefactorResult.NotFound.printRefactorResult());
}
var identifier:Identifier = file.getIdentifier(context.what.pos);
if (identifier == null) {
return Promise.reject(RefactorResult.NotFound.printRefactorResult());
}
return switch (identifier.type) {
case PackageName | ImportAlias | Abstract | Class | Enum | Interface | Typedef | ModuleLevelStaticVar | ModuleLevelStaticMethod | Property |
FieldVar(_) | Method(_) | TypedefField(_) | StructureField(_) | InterfaceProperty | InterfaceVar | InterfaceMethod | EnumField(_) |
ScopedLocal(_, _):
Promise.resolve({name: identifier.name, pos: identifier.pos});
case ImportModul | UsingModul | Extends | Implements | AbstractOver | AbstractFrom | AbstractTo | TypeHint | StringConst | TypedParameter |
TypedefBase | Call(true) | CaseLabel(_):
Promise.reject(RefactorResult.Unsupported(identifier.toString()).printRefactorResult());
case Call(false) | Access | ArrayAccess(_) | ForIterator:
var candidate:Null<Identifier> = findActualWhat(context, file, identifier);
if (candidate == null) {
return Promise.reject(RefactorResult.Unsupported(identifier.toString()).printRefactorResult());
}
if (identifier.name.startsWith(candidate.name)) {
var pos:IdentifierPos = {
fileName: identifier.pos.fileName,
start: identifier.pos.start,
end: identifier.pos.start + context.what.toName.length
}
return Promise.resolve({name: candidate.name, pos: pos});
}
if (identifier.name.endsWith(candidate.name)) {
var pos:IdentifierPos = {
fileName: identifier.pos.fileName,
start: identifier.pos.end - context.what.toName.length,
end: identifier.pos.end
}
return Promise.resolve({name: candidate.name, pos: pos});
}
Promise.reject(RefactorResult.Unsupported(identifier.toString()).printRefactorResult());
}
}

public static function rename(context:RefactorContext):Promise<RefactorResult> {
var file:Null<File> = context.fileList.getFile(context.what.fileName);
if (file == null) {
Expand Down Expand Up @@ -68,7 +111,12 @@ class Refactor {
Promise.reject(RefactorResult.Unsupported(identifier.toString()).printRefactorResult());
case Call(false) | Access | ArrayAccess(_) | ForIterator:
context.verboseLog('rename "${identifier.name}" at call/access location - trying to find definition');
findActualWhat(context, file, identifier);
var candidate:Null<Identifier> = findActualWhat(context, file, identifier);
if (candidate == null) {
return Promise.reject(RefactorResult.Unsupported(identifier.toString()).printRefactorResult());
}
context.what.pos = candidate.pos.start;
rename(context);
case CaseLabel(_):
Promise.reject(RefactorResult.Unsupported(identifier.toString()).printRefactorResult());
case ScopedLocal(scopeEnd, type):
Expand All @@ -77,10 +125,10 @@ class Refactor {
}
}

static function findActualWhat(context:RefactorContext, file:File, identifier:Identifier):Promise<RefactorResult> {
static function findActualWhat(context:CanRefactorContext, file:File, identifier:Identifier):Null<Identifier> {
var parts:Array<String> = identifier.name.split(".");
if (parts.length <= 0) {
return Promise.reject(RefactorResult.Unsupported(identifier.toString()).printRefactorResult());
return null;
}
var firstPart:String = parts.shift();
var onlyFields:Bool = false;
Expand All @@ -92,7 +140,7 @@ class Refactor {
}
if (context.what.pos > identifier.pos.start + firstPart.length + offset) {
// rename position is not in first part of dotted identifiier
return Promise.reject(RefactorResult.Unsupported(identifier.toString()).printRefactorResult());
return null;
}
var allUses:Array<Identifier> = file.findAllIdentifiers((i) -> i.name == firstPart);
var candidate:Null<Identifier> = null;
Expand All @@ -116,10 +164,6 @@ class Refactor {
default:
}
}
if (candidate != null) {
context.what.pos = candidate.pos.start;
return rename(context);
}
return Promise.reject(RefactorResult.Unsupported(identifier.toString()).printRefactorResult());
return candidate;
}
}
11 changes: 1 addition & 10 deletions src/refactor/RefactorContext.hx
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
package refactor;

import haxe.PosInfos;
import refactor.ITyper;
import refactor.discover.FileList;
import refactor.discover.NameMap;
import refactor.discover.TypeList;
import refactor.edits.IEditableDocument;

typedef RefactorContext = {
var nameMap:NameMap;
var fileList:FileList;
var typeList:TypeList;
typedef RefactorContext = CanRefactorContext & {
var what:RefactorWhat;
var forRealExecute:Bool;
var docFactory:(fileName:String) -> IEditableDocument;
var verboseLog:VerboseLogger;
var typer:Null<ITyper>;
}

typedef VerboseLogger = (text:String, ?pos:PosInfos) -> Void;
45 changes: 32 additions & 13 deletions src/refactor/discover/UsageCollector.hx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,8 @@ class UsageCollector {
public function new() {}

public function parseFile(content:ByteData, context:UsageContext) {
if (context.cache != null) {
var file:Null<File> = context.cache.getFile(context.fileName, context.nameMap);
if (file != null) {
context.fileList.addFile(file);
for (type in file.typeList) {
context.typeList.addType(type);
}
return;
}
if (isCached(context)) {
return;
}
var root:Null<TokenTree> = null;
try {
Expand All @@ -35,6 +28,21 @@ class UsageCollector {
t = lexer.token(haxeparser.HaxeLexer.tok);
}
root = TokenTreeBuilder.buildTokenTree(tokens, content, TypeLevel);
parseFileWithTokens(root, context);
} catch (e:ParserError) {
throw 'failed to parse ${context.fileName} - ParserError: $e (${e.pos})';
} catch (e:LexerError) {
throw 'failed to parse ${context.fileName} - LexerError: ${e.msg} (${e.pos})';
} catch (e:Exception) {
throw 'failed to parse ${context.fileName} - ${e.details()}';
}
}

public function parseFileWithTokens(root:TokenTree, context:UsageContext) {
if (isCached(context)) {
return;
}
try {
var file:File = new File(context.fileName);
context.file = file;
context.type = null;
Expand All @@ -45,15 +53,26 @@ class UsageCollector {
if (context.cache != null) {
context.cache.storeFile(file);
}
} catch (e:ParserError) {
throw 'failed to parse ${context.fileName} - ParserError: $e (${e.pos})';
} catch (e:LexerError) {
throw 'failed to parse ${context.fileName} - LexerError: ${e.msg} (${e.pos})';
} catch (e:Exception) {
throw 'failed to parse ${context.fileName} - ${e.details()}';
}
}

function isCached(context:UsageContext):Bool {
if (context.cache != null) {
var file:Null<File> = context.cache.getFile(context.fileName, context.nameMap);
if (file == null) {
return false;
}
context.fileList.addFile(file);
for (type in file.typeList) {
context.typeList.addType(type);
}
return true;
}
return false;
}

public function updateImportHx(context:UsageContext) {
for (importHxFile in context.fileList.files) {
var importHxPath:Path = new Path(importHxFile.name);
Expand Down
54 changes: 32 additions & 22 deletions test/refactor/TestBase.hx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package refactor;

import haxe.CallStack;
import haxe.Exception;
import haxe.PosInfos;
import js.lib.Promise;
Expand Down Expand Up @@ -75,36 +74,47 @@ class TestBase implements ITest {

function doRefactor(what:RefactorWhat, edits:Array<TestEdit>, pos:PosInfos):Promise<RefactorResult> {
var editList:TestEditList = new TestEditList();
return Refactor.rename({
return Refactor.canRename({
nameMap: usageContext.nameMap,
fileList: usageContext.fileList,
typeList: usageContext.typeList,
what: what,
forRealExecute: true,
docFactory: (fileName) -> editList.newDoc(fileName),
verboseLog: function(text:String, ?pos:PosInfos) {
Sys.println('${pos.fileName}:${pos.lineNumber}: $text');
},
typer: null
}).then(function(success:RefactorResult) {
editList.sortEdits();
Assert.equals(Done, success, pos);
Assert.equals(editList.docCounter, editList.docFinishedCounter, pos);
Assert.equals(edits.length, editList.edits.length, pos);
if (edits.length == editList.edits.length) {
for (index in 0...edits.length) {
var expected:TestEdit = edits[index];
var actual:TestEdit = editList.edits[index];
Assert.equals(expected.fileName, actual.fileName, expected.pos);
Assert.equals(fileEditToString(expected.edit), fileEditToString(actual.edit), expected.pos);
}).then(function(success:CanRefactorResult) {
return Refactor.rename({
nameMap: usageContext.nameMap,
fileList: usageContext.fileList,
typeList: usageContext.typeList,
what: what,
forRealExecute: true,
docFactory: (fileName) -> editList.newDoc(fileName),
verboseLog: function(text:String, ?pos:PosInfos) {
Sys.println('${pos.fileName}:${pos.lineNumber}: $text');
},
typer: null
}).then(function(success:RefactorResult) {
editList.sortEdits();
Assert.equals(Done, success, pos);
Assert.equals(editList.docCounter, editList.docFinishedCounter, pos);
Assert.equals(edits.length, editList.edits.length, pos);
if (edits.length == editList.edits.length) {
for (index in 0...edits.length) {
var expected:TestEdit = edits[index];
var actual:TestEdit = editList.edits[index];
Assert.equals(expected.fileName, actual.fileName, expected.pos);
Assert.equals(fileEditToString(expected.edit), fileEditToString(actual.edit), expected.pos);
}
} else {
for (edit in editList.edits) {
Sys.println(fileEditToString(edit.edit));
}
Assert.fail("length mismatch - edits were not checked", pos);
}
} else {
for (edit in editList.edits) {
Sys.println(fileEditToString(edit.edit));
}
Assert.fail("length mismatch - edits were not checked", pos);
}
return Promise.resolve(success);
return Promise.resolve(success);
});
});
}

Expand Down

0 comments on commit 23924e8

Please sign in to comment.