Skip to content

Commit

Permalink
Support for @TypeDoc. Separate parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
arthepsy committed Jul 8, 2015
1 parent 1b689c2 commit 3f48031
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,6 @@ public class ElixirMeasureSensor implements Sensor {
private final FileSystem fileSystem;
private final FilePredicate mainFilePredicate;

private final Pattern defPattern = Pattern.compile("^\\s*def(|p|module|struct)\\s(.*)$");
private final Matcher defMatcher = defPattern.matcher("");
private final Pattern heredocPattern = Pattern.compile("^.*\"\"\"\\s*$");
private final Matcher heredocMatcher = heredocPattern.matcher("");
private final Pattern docPattern = Pattern.compile("^\\s*@(moduledoc|doc)([\"\\s].*)$");
private final Matcher docMatcher = docPattern.matcher("");

public ElixirMeasureSensor(FileSystem fileSystem) {
this.fileSystem = fileSystem;
Expand All @@ -79,105 +73,32 @@ public boolean shouldExecuteOnProject(Project project) {
}

private void processMainFile(InputFile inputFile, SensorContext context) {
int lineCount = 0;
int emptyLineCount = 0;
int commentLineCount = 0;
int classCount = 0;
int publicFunctionCount = 0;
int privateFunctionCount = 0;
int documentedClassCount = 0;
int documentedPrivateFunctionCount = 0;
int documentedPublicFunctionCount = 0;
boolean hasDoc = false;
boolean inClass = false;

List<String> lines = null;
List<String> lines;
try {
lines = Files.readAllLines(Paths.get(inputFile.absolutePath()), fileSystem.encoding());
lineCount = lines.size();
for (int i = 0; i < lineCount; i++) {
String line = lines.get(i);
if (StringUtils.isBlank(line)) {
emptyLineCount++;
}
docMatcher.reset(line);
boolean inDoc = docMatcher.find();
if (inDoc) {
commentLineCount++;
String docText = docMatcher.group(2).trim();
if (! (StringUtils.equalsIgnoreCase(docText, "false") || StringUtils.equalsIgnoreCase(docText, "nil"))) {
switch (docMatcher.group(1)) {
case "doc":
hasDoc = true;
break;
case "moduledoc":
if (inClass) {
documentedClassCount++;
}
break;
}
}
}
heredocMatcher.reset(line);
if (heredocMatcher.find()) {
while (i < lineCount - 1) {
if (inDoc) {
commentLineCount++;
}
i++;
line = lines.get(i);
if (line.matches("^\\s*\"\"\"\\s*$")) {
break;
}
}
continue;
}

defMatcher.reset(line);
if (defMatcher.find()) {
switch (defMatcher.group(1)) {
case "module":
classCount++;
inClass = true;
break;
case "":
publicFunctionCount++;
if (hasDoc) {
documentedPublicFunctionCount++;
}
break;
case "p":
privateFunctionCount++;
if (hasDoc) {
documentedPrivateFunctionCount++;
}
break;
}
hasDoc = false;
}
if (line.matches("^\\s*#.*$")) {
commentLineCount++;
}
}
} catch (IOException e) {
LOG.warn(LOG_PREFIX + "could not process file: " + inputFile.toString());
return;
}
if (lines != null) {
LOG.debug(LOG_PREFIX + "processing file: " + inputFile.toString());
context.saveMeasure(inputFile, CoreMetrics.LINES, (double) lineCount);
context.saveMeasure(inputFile, CoreMetrics.NCLOC, (double)(lineCount - emptyLineCount - commentLineCount));
context.saveMeasure(inputFile, CoreMetrics.COMMENT_LINES, (double)commentLineCount);
ElixirParser parser = new ElixirParser();
parser.parse(lines);

double publicApi = publicFunctionCount + classCount;
double documentedApi = documentedPublicFunctionCount + documentedClassCount;
double undocumentedApi = publicApi - documentedApi;
double documentedApiDensity = (publicApi == 0 ? 100.0 : ParsingUtils.scaleValue(documentedApi / publicApi * 100, 2));
context.saveMeasure(inputFile, CoreMetrics.PUBLIC_API, publicApi);
context.saveMeasure(inputFile, CoreMetrics.PUBLIC_UNDOCUMENTED_API, undocumentedApi);
context.saveMeasure(inputFile, CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY, documentedApiDensity);
LOG.debug(LOG_PREFIX + "processing file: " + inputFile.toString());
double linesOfCode = parser.getLineCount() - parser.getEmptyLineCount() - parser.getCommentLineCount();
context.saveMeasure(inputFile, CoreMetrics.LINES, (double)parser.getLineCount());
context.saveMeasure(inputFile, CoreMetrics.NCLOC, (double)linesOfCode);
context.saveMeasure(inputFile, CoreMetrics.COMMENT_LINES, (double)parser.getCommentLineCount());

context.saveMeasure(inputFile, CoreMetrics.CLASSES, (double)classCount);
context.saveMeasure(inputFile, CoreMetrics.FUNCTIONS, (double)(publicFunctionCount + privateFunctionCount));
}
double publicApi = parser.getPublicFunctionCount() + parser.getClassCount();
double documentedApi = parser.getDocumentedPublicFunctionCount() + parser.getDocumentedClassCount();
double undocumentedApi = publicApi - documentedApi;
double documentedApiDensity = (publicApi == 0 ? 100.0 : ParsingUtils.scaleValue(documentedApi / publicApi * 100, 2));
context.saveMeasure(inputFile, CoreMetrics.PUBLIC_API, publicApi);
context.saveMeasure(inputFile, CoreMetrics.PUBLIC_UNDOCUMENTED_API, undocumentedApi);
context.saveMeasure(inputFile, CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY, documentedApiDensity);

double functionCount = parser.getPublicFunctionCount() + parser.getPrivateFunctionCount();
context.saveMeasure(inputFile, CoreMetrics.CLASSES, (double)parser.getClassCount());
context.saveMeasure(inputFile, CoreMetrics.FUNCTIONS, (double)(functionCount));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* SonarQube Elixir plugin
* Copyright (C) 2015 Andris Raugulis
* [email protected]
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package eu.arthepsy.sonar.plugins.elixir.language;

import org.apache.commons.lang.StringUtils;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ElixirParser {
private int lineCount = 0;
private int emptyLineCount = 0;
private int commentLineCount = 0;
private int classCount = 0;
private int publicFunctionCount = 0;
private int privateFunctionCount = 0;
private int documentedClassCount = 0;
private int documentedPrivateFunctionCount = 0;
private int documentedPublicFunctionCount = 0;

private final Pattern defPattern = Pattern.compile("^\\s*def(|p|module|struct)\\s(.*)$");
private final Matcher defMatcher = defPattern.matcher("");
private final Pattern heredocPattern = Pattern.compile("^.*\"\"\"\\s*$");
private final Matcher heredocMatcher = heredocPattern.matcher("");
private final Pattern docPattern = Pattern.compile("^\\s*@(doc|moduledoc|typedoc)([\"\\s].*)$");
private final Matcher docMatcher = docPattern.matcher("");

private boolean hasDoc = false;
private boolean inClass = false;

public ElixirParser() { }

public int getLineCount() { return lineCount; }
public int getEmptyLineCount() { return emptyLineCount; }
public int getCommentLineCount() { return commentLineCount; }
public int getClassCount() { return classCount; }
public int getPublicFunctionCount() { return publicFunctionCount; }
public int getPrivateFunctionCount() { return privateFunctionCount; }
public int getDocumentedClassCount() { return documentedClassCount; }
public int getDocumentedPublicFunctionCount() { return documentedPublicFunctionCount; }
public int getDocumentedPrivateFunctionCount() { return documentedPrivateFunctionCount; }

public void parse(List<String> lines) {
this.parseLines(lines);
}

private void parseLines(List<String> lines) {
hasDoc = false;
inClass = false;

lineCount = lines.size();
for (int i = 0; i < lineCount; i++) {
String line = lines.get(i);
if (StringUtils.isBlank(line)) {
emptyLineCount++;
}
docMatcher.reset(line);
boolean inDoc = docMatcher.find();
if (inDoc) {
commentLineCount++;
String docHead = docMatcher.group(2).trim();
if (! (StringUtils.equalsIgnoreCase(docHead, "false") || StringUtils.equalsIgnoreCase(docHead, "nil"))) {
switch (docMatcher.group(1)) {
case "doc":
hasDoc = true;
break;
case "moduledoc":
if (inClass) {
documentedClassCount++;
}
break;
case "typedoc":
break;
}
}
}
heredocMatcher.reset(line);
if (heredocMatcher.find()) {
while (i < lineCount - 1) {
if (inDoc) {
commentLineCount++;
}
i++;
line = lines.get(i);
if (line.matches("^\\s*\"\"\"\\s*$")) {
break;
}
}
continue;
}

defMatcher.reset(line);
if (defMatcher.find()) {
switch (defMatcher.group(1)) {
case "module":
classCount++;
inClass = true;
break;
case "":
publicFunctionCount++;
if (hasDoc) {
documentedPublicFunctionCount++;
}
break;
case "p":
privateFunctionCount++;
if (hasDoc) {
documentedPrivateFunctionCount++;
}
break;
}
hasDoc = false;
}
if (line.matches("^\\s*#.*$")) {
commentLineCount++;
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ public void testDocAnnotation() throws IOException {

sensor.analyse(project, context);

verify(context).saveMeasure(any(InputFile.class), eq(CoreMetrics.LINES), eq(33.0));
verify(context).saveMeasure(any(InputFile.class), eq(CoreMetrics.NCLOC), eq(14.0));
verify(context).saveMeasure(any(InputFile.class), eq(CoreMetrics.COMMENT_LINES), eq(13.0));
verify(context).saveMeasure(any(InputFile.class), eq(CoreMetrics.LINES), eq(37.0));
verify(context).saveMeasure(any(InputFile.class), eq(CoreMetrics.NCLOC), eq(15.0));
verify(context).saveMeasure(any(InputFile.class), eq(CoreMetrics.COMMENT_LINES), eq(14.0));
verify(context).saveMeasure(any(InputFile.class), eq(CoreMetrics.CLASSES), eq(1.0));
verify(context).saveMeasure(any(InputFile.class), eq(CoreMetrics.FUNCTIONS), eq(6.0));
verify(context).saveMeasure(any(InputFile.class), eq(CoreMetrics.PUBLIC_API), eq(5.0));
Expand Down
4 changes: 4 additions & 0 deletions sonar-elixir-plugin/src/test/resources/test_doc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ defmodule Docs do
# private function doc6
defp doc6() do
end

@typedoc "type doc example"
@type doc7 :: any

end

0 comments on commit 3f48031

Please sign in to comment.