Skip to content

Commit

Permalink
Improve support for refactoring JSDoc annotations:
Browse files Browse the repository at this point in the history
* Handle stacked annotations such as @const @Private {Blah}
* Add support for @exports and @Package
* Correct bad replaces for annotations that are optionally followed by a type

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=149672270
  • Loading branch information
concavelenz authored and dimvar committed Mar 10, 2017
1 parent fab4a78 commit 49ea0b8
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 13 deletions.
31 changes: 21 additions & 10 deletions src/com/google/javascript/refactoring/SuggestedFix.java
Expand Up @@ -452,8 +452,6 @@ public Builder addOrReplaceJsDoc(Node n, String newJsDoc) {
* Changes the JS Doc Type of the given node. * Changes the JS Doc Type of the given node.
*/ */
public Builder changeJsDocType(Node n, AbstractCompiler compiler, String type) { public Builder changeJsDocType(Node n, AbstractCompiler compiler, String type) {
JSDocInfo info = NodeUtil.getBestJSDocInfo(n);
Preconditions.checkNotNull(info, "Node %s does not have JS Doc associated with it.", n);
Node typeNode = JsDocInfoParser.parseTypeString(type); Node typeNode = JsDocInfoParser.parseTypeString(type);
Preconditions.checkNotNull(typeNode, "Invalid type: %s", type); Preconditions.checkNotNull(typeNode, "Invalid type: %s", type);
JSTypeExpression typeExpr = new JSTypeExpression(typeNode, "jsflume"); JSTypeExpression typeExpr = new JSTypeExpression(typeNode, "jsflume");
Expand All @@ -462,15 +460,30 @@ public Builder changeJsDocType(Node n, AbstractCompiler compiler, String type) {
throw new RuntimeException("JS Compiler does not recognize type: " + type); throw new RuntimeException("JS Compiler does not recognize type: " + type);
} }


// TODO(mknichel): Use the JSDocInfoParser to find the end of the type declaration. This
// would also handle multiple lines, and record types (which contain '{')

// Only "@type" allows type names without "{}"
replaceTypePattern(n, type, Pattern.compile(
"@(type) *\\{?[^@\\s}]+\\}?"));

// Text following other annotations may be a comment, not a type.
replaceTypePattern(n, type, Pattern.compile(
"@(export|package|private|protected|public|const|return) *\\{[^}]+\\}"));

return this;
}

// The pattern supplied here should have one matching group, the annotation with
// associated the type expression, the entire pattern should match the annotation and
// the type expression to be replaced.
private void replaceTypePattern(Node n, String type, Pattern pattern) {
JSDocInfo info = NodeUtil.getBestJSDocInfo(n);
Preconditions.checkNotNull(info, "Node %s does not have JS Doc associated with it.", n);
String originalComment = info.getOriginalCommentString(); String originalComment = info.getOriginalCommentString();
int originalPosition = info.getOriginalCommentPosition(); int originalPosition = info.getOriginalCommentPosition();

// If there isn't an original comment, then it is generated and we can't make a change.
if (originalComment != null) { if (originalComment != null) {
// TODO(mknichel): Support multiline @type annotations. Matcher m = pattern.matcher(originalComment);
Pattern typeDocPattern = Pattern.compile(
"@(type|private|protected|public|const|return) *\\{?[^\\s}@]+\\}?");
Matcher m = typeDocPattern.matcher(originalComment);
while (m.find()) { while (m.find()) {
replacements.put( replacements.put(
n.getSourceFileName(), n.getSourceFileName(),
Expand All @@ -480,8 +493,6 @@ public Builder changeJsDocType(Node n, AbstractCompiler compiler, String type) {
"@" + m.group(1) + " {" + type + "}")); "@" + m.group(1) + " {" + type + "}"));
} }
} }

return this;
} }


/** /**
Expand Down
62 changes: 59 additions & 3 deletions test/com/google/javascript/refactoring/SuggestedFixTest.java
Expand Up @@ -382,16 +382,72 @@ public void testChangeJsDocType2() {
} }


@Test @Test
public void testChangeJsDocType_privateType() { public void testChangeJsDocType_packageType1() {
String before = "/** "; String before = "/** ";
String after = "@private Foo */\nvar foo = new Foo()"; String after = "@package {Foo} */\nvar foo = new Foo()";
Compiler compiler = getCompiler(before + after); Compiler compiler = getCompiler(before + after);
Node root = compileToScriptRoot(compiler); Node root = compileToScriptRoot(compiler);
SuggestedFix fix = new SuggestedFix.Builder() SuggestedFix fix = new SuggestedFix.Builder()
.changeJsDocType(root.getFirstChild(), compiler, "Object") .changeJsDocType(root.getFirstChild(), compiler, "Object")
.build(); .build();
CodeReplacement replacement = new CodeReplacement( CodeReplacement replacement = new CodeReplacement(
before.length(), "@private Foo".length(), "@private {Object}"); before.length(), "@package {Foo}".length(), "@package {Object}");
assertReplacement(fix, replacement);
}

@Test
public void testChangeJsDocType_privateType1() {
String before = "/** ";
String after = "@private {Foo} */\nvar foo = new Foo()";
Compiler compiler = getCompiler(before + after);
Node root = compileToScriptRoot(compiler);
SuggestedFix fix = new SuggestedFix.Builder()
.changeJsDocType(root.getFirstChild(), compiler, "Object")
.build();
CodeReplacement replacement = new CodeReplacement(
before.length(), "@private {Foo}".length(), "@private {Object}");
assertReplacement(fix, replacement);
}

@Test
public void testChangeJsDocType_privateType2() {
String before = "/** @private ";
String after = "@const {Foo} */\nvar foo = new Foo()";
Compiler compiler = getCompiler(before + after);
Node root = compileToScriptRoot(compiler);
SuggestedFix fix = new SuggestedFix.Builder()
.changeJsDocType(root.getFirstChild(), compiler, "Object")
.build();
CodeReplacement replacement = new CodeReplacement(
before.length(), "@const {Foo}".length(), "@const {Object}");
assertReplacement(fix, replacement);
}

@Test
public void testChangeJsDocType_privateType3() {
String before = "/** @private ";
String after = "@const {Foo} */\nvar foo = new Foo()";
Compiler compiler = getCompiler(before + after);
Node root = compileToScriptRoot(compiler);
SuggestedFix fix = new SuggestedFix.Builder()
.changeJsDocType(root.getFirstChild(), compiler, "Object")
.build();
CodeReplacement replacement = new CodeReplacement(
before.length(), "@const {Foo}".length(), "@const {Object}");
assertReplacement(fix, replacement);
}

@Test
public void testChangeJsDocType_privateType4() {
String before = "/** ";
String after = "@const {Foo} */\nvar foo = new Foo()";
Compiler compiler = getCompiler(before + after);
Node root = compileToScriptRoot(compiler);
SuggestedFix fix = new SuggestedFix.Builder()
.changeJsDocType(root.getFirstChild(), compiler, "Object")
.build();
CodeReplacement replacement = new CodeReplacement(
before.length(), "@const {Foo}".length(), "@const {Object}");
assertReplacement(fix, replacement); assertReplacement(fix, replacement);
} }


Expand Down

0 comments on commit 49ea0b8

Please sign in to comment.