diff --git a/php.editor/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParser.java b/php.editor/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParser.java --- a/php.editor/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParser.java +++ b/php.editor/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParser.java @@ -203,11 +203,14 @@ blockDescription = description + line; } else { description = description + line; + String tagDescription = description.length() > 0 && description.charAt(description.length() - 1) == '\n' + ? description.substring(0, description.length() - 1) + : description; PHPDocTag tag = createTag( startOffset + 3 + lastStartIndex, startOffset + 3 + lastEndIndex, lastTag, - description.substring(0, description.length() - 1), + tagDescription, comment, startOffset + 3); if (tag != null) { @@ -228,7 +231,9 @@ PHPDocNode varibaleNode = null; if (variable != null) { int startOfVariable = findStartOfDocNode(originalComment, originalCommentStart, variable, start); - varibaleNode = new PHPDocNode(startOfVariable, startOfVariable + variable.length(), variable); + if (startOfVariable != -1) { + varibaleNode = new PHPDocNode(startOfVariable, startOfVariable + variable.length(), variable); + } } else if (type.equals(PHPDocTag.Type.PARAM)) { varibaleNode = new PHPDocNode(start, start, ""); //NOI18N } @@ -240,9 +245,14 @@ String name = getMethodName(description); if (name != null) { int startOfVariable = findStartOfDocNode(originalComment, originalCommentStart, name, start); - PHPDocNode methodNode = new PHPDocNode(startOfVariable, startOfVariable + name.length(), name); - List params = findMethodParams(description, findStartOfDocNode(originalComment, originalCommentStart, description, start)); - return new PHPDocMethodTag(start, end, type, docTypes, methodNode, params, description); + if (startOfVariable != -1) { + PHPDocNode methodNode = new PHPDocNode(startOfVariable, startOfVariable + name.length(), name); + int startOfDescription = findStartOfDocNode(originalComment, originalCommentStart, description, start); + if (startOfDescription != -1) { + List params = findMethodParams(description, startOfDescription); + return new PHPDocMethodTag(start, end, type, docTypes, methodNode, params, description); + } + } } return null; } else if (type.equals(PHPDocTag.Type.RETURN) || type.equals(PHPDocTag.Type.VAR)) { @@ -272,6 +282,9 @@ for (String stype : getTypes(description, isReturnTag)) { stype = removeHTMLTags(stype); int startDocNode = findStartOfDocNode(originalComment, originalCommentStart, stype, startDescription); + if (startDocNode == -1) { + continue; + } int index = stype.indexOf("::"); //NOI18N boolean isArray = (stype.indexOf('[') > 0 && stype.indexOf(']') > 0); if (isArray) { @@ -354,11 +367,13 @@ paramName = getVaribleName(token.trim()); if (paramName != null) { int startOfParamName = findStartOfDocNode(description, startOfDescription, paramName, position); - PHPDocNode paramNameNode = new PHPDocNode(startOfParamName, startOfParamName + paramName.length(), paramName); - List types = token.trim().indexOf(' ') > -1 - ? findTypes(token, position, description, startOfDescription) - : Collections.EMPTY_LIST; - result.add(new PHPDocVarTypeTag(position, startOfParamName + paramName.length(), PHPDocTag.Type.PARAM, token, types, paramNameNode)); + if (startOfParamName != -1) { + PHPDocNode paramNameNode = new PHPDocNode(startOfParamName, startOfParamName + paramName.length(), paramName); + List types = token.trim().indexOf(' ') > -1 + ? findTypes(token, position, description, startOfDescription) + : Collections.EMPTY_LIST; + result.add(new PHPDocVarTypeTag(position, startOfParamName + paramName.length(), PHPDocTag.Type.PARAM, token, types, paramNameNode)); + } } position = position + token.length() + 1; } @@ -379,9 +394,19 @@ return value; } + /** + * Find the start position of the specified string in the comment. + * + * @param originalComment the comment + * @param originalStart the offset of the original comment + * @param what the target string + * @param from the start offset + * @return the start position of the specified string if it is found, + * otherwise -1. + */ private int findStartOfDocNode(String originalComment, int originalStart, String what, int from) { int pos = originalComment.indexOf(what, from - originalStart); - return originalStart + pos; + return pos == -1 ? pos : originalStart + pos; } private String removeStarAndTrim(String text) { diff --git a/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest.java b/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest.java --- a/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest.java +++ b/php.editor/test/unit/src/org/netbeans/modules/php/editor/parser/PHPDocCommentParserTest.java @@ -224,6 +224,36 @@ assertNotNull(block); } + public void testIssue257977() throws Exception { + /** + * @since 2012-02-14 + * @since 2012-02-15 + * + * Test + */ + String comment = + " * @since 2012-02-14\n" + + " * @since 2012-02-15\n" + + " *\n" + + " * Test"; + PHPDocCommentParser parser = new PHPDocCommentParser(); + PHPDocBlock block = parser.parse(-3, comment.length(), comment); + List tags = block.getTags(); + assertEquals(2, tags.size()); + + PHPDocTag firstTag = tags.get(0); + AnnotationParsedLine firstLine = firstTag.getKind(); + assertTrue(firstLine instanceof UnknownAnnotationLine); + assertEquals("2012-02-14", firstLine.getDescription()); + assertEquals(" 2012-02-14", firstTag.getDocumentation()); + + PHPDocTag secondTag = tags.get(1); + AnnotationParsedLine secondLine = secondTag.getKind(); + assertTrue(secondLine instanceof UnknownAnnotationLine); + assertEquals("2012-02-15", secondLine.getDescription()); + assertEquals(" 2012-02-15\n\nTest", secondTag.getDocumentation()); + } + public void testProperty01() throws Exception { String comment = " * PHP Template.\n" + " * @property string $name\n" + diff --git a/spellchecker.bindings.php/test/unit/src/org/netbeans/modules/spellchecker/bindings/php/PHPTokenListTest.java b/spellchecker.bindings.php/test/unit/src/org/netbeans/modules/spellchecker/bindings/php/PHPTokenListTest.java --- a/spellchecker.bindings.php/test/unit/src/org/netbeans/modules/spellchecker/bindings/php/PHPTokenListTest.java +++ b/spellchecker.bindings.php/test/unit/src/org/netbeans/modules/spellchecker/bindings/php/PHPTokenListTest.java @@ -171,6 +171,17 @@ ); } + public void testIssue257977() throws Exception { + tokenListTest( + " /**\n" + + " * @since 2016-02-12\n" + + " *\n" + + " * Test\n" + + " */", + "Test" + ); + } + public void testPositions() throws Exception { Document doc = new PlainDocument(); doc.putProperty(Language.class, PHPTokenId.language());