aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-css-plugin
diff options
context:
space:
mode:
authorAmaury Levé2018-06-13 18:24:11 +0200
committerGitHub2018-06-13 18:24:11 +0200
commitb3887099d35a9d8909c00f43ca194cc02ba0b16a (patch)
tree5e5c1e1f99f205d0627743d4734303b7d560b14a /sonar-css-plugin
parentdf153ba45fffa47f1bff7a4201d5fd16fc7b3445 (diff)
downloadsonar-css-b3887099d35a9d8909c00f43ca194cc02ba0b16a.tar.bz2
Add syntax highlighting for strings and comments (#43)
Diffstat (limited to 'sonar-css-plugin')
-rw-r--r--sonar-css-plugin/pom.xml7
-rw-r--r--sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java1
-rw-r--r--sonar-css-plugin/src/main/java/org/sonar/css/plugin/MetricSensor.java86
-rw-r--r--sonar-css-plugin/src/main/java/org/sonar/css/plugin/Tokenizer.java9
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java5
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/MetricSensorTest.java112
6 files changed, 211 insertions, 9 deletions
diff --git a/sonar-css-plugin/pom.xml b/sonar-css-plugin/pom.xml
index 0ceb4b0..a1b3ecc 100644
--- a/sonar-css-plugin/pom.xml
+++ b/sonar-css-plugin/pom.xml
@@ -56,6 +56,11 @@
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
</dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.6</version>
+ </dependency>
</dependencies>
<build>
@@ -111,7 +116,7 @@
<rules>
<requireFilesSize>
<minsize>10000</minsize>
- <maxsize>50000</maxsize>
+ <maxsize>200000</maxsize>
<files>
<file>${project.build.directory}/${project.build.finalName}.jar</file>
</files>
diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java
index 07b4feb..65e6453 100644
--- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java
+++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java
@@ -34,6 +34,7 @@ public class CssPlugin implements Plugin {
@Override
public void define(Context context) {
context.addExtensions(
+ MetricSensor.class,
CssLanguage.class,
SonarWayProfile.class,
diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/MetricSensor.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/MetricSensor.java
new file mode 100644
index 0000000..fb9c522
--- /dev/null
+++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/MetricSensor.java
@@ -0,0 +1,86 @@
+/*
+ * SonarCSS
+ * Copyright (C) 2018-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.css.plugin;
+
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
+import org.sonar.api.batch.sensor.highlighting.TypeOfText;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+
+import javax.script.ScriptException;
+
+import java.io.IOException;
+import java.util.Optional;
+
+public class MetricSensor implements Sensor {
+
+ private static final Logger LOG = Loggers.get(MetricSensor.class);
+
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor
+ .onlyOnLanguage(CssLanguage.KEY);
+ }
+
+ @Override
+ public void execute(SensorContext context) {
+ FileSystem fileSystem = context.fileSystem();
+ Iterable<InputFile> inputFiles = fileSystem.inputFiles(fileSystem.predicates().hasLanguage(CssLanguage.KEY));
+
+ Tokenizer tokenizer = new Tokenizer();
+
+ for (InputFile input : inputFiles) {
+ saveHighlights(context, input, tokenizer);
+ }
+ }
+
+ private static void saveHighlights(SensorContext sensorContext, InputFile input, Tokenizer tokenizer) {
+ try {
+ NewHighlighting highlighting = sensorContext.newHighlighting().onFile(input);
+ tokenizer.tokenize(input.contents())
+ .forEach(token -> getHighlightingType(token).ifPresent(type ->
+ highlighting.highlight(token.startLine, token.startColumn, token.endLine, token.endColumn, type)));
+ highlighting.save();
+
+ } catch (ScriptException e) {
+ LOG.error(String.format("Failed to tokenize file '%s'", input.toString()), e);
+ } catch (IOException e) {
+ LOG.error(String.format("Failed to read file '%s'", input.toString()), e);
+ }
+ }
+
+ private static Optional<TypeOfText> getHighlightingType(Token token) {
+ switch (token.type) {
+ case COMMENT:
+ return Optional.of(TypeOfText.COMMENT);
+
+ case STRING:
+ return Optional.of(TypeOfText.STRING);
+
+ default:
+ return Optional.empty();
+ }
+ }
+}
diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/Tokenizer.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/Tokenizer.java
index 8f03492..220bfaa 100644
--- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/Tokenizer.java
+++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/Tokenizer.java
@@ -26,16 +26,15 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
-import org.sonar.api.internal.apachecommons.lang.StringEscapeUtils;
+import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+import org.apache.commons.lang.StringEscapeUtils;
import org.sonar.css.plugin.Token.Type;
public class Tokenizer {
public List<Token> tokenize(String css) throws ScriptException {
- ScriptEngineManager factory = new ScriptEngineManager();
- ScriptEngine engine = factory.getEngineByName("JavaScript");
+ ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine();
InputStream tokenizeScript = Tokenizer.class.getClassLoader().getResourceAsStream("tokenize.js");
engine.eval(new InputStreamReader(tokenizeScript, StandardCharsets.UTF_8));
String cssInput = "tokenize('" + StringEscapeUtils.escapeJavaScript(css) + "')";
@@ -105,7 +104,7 @@ public class Tokenizer {
if (value instanceof Double) {
return ((Double) value).intValue();
} else if (value instanceof Integer) {
- return (Integer) value;
+ return (Integer) value;
} else {
throw new IllegalStateException("Failed to convert to number: " + value);
}
diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java
index 3da86ca..33bd4dd 100644
--- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java
+++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java
@@ -28,15 +28,14 @@ import org.sonar.api.utils.Version;
import static org.assertj.core.api.Assertions.assertThat;
-
public class CssPluginTest {
@Test
- public void count_extensions() throws Exception {
+ public void count_extensions() {
SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.create(6, 7), SonarQubeSide.SCANNER);
Plugin.Context context = new Plugin.Context(runtime);
Plugin underTest = new CssPlugin();
underTest.define(context);
- assertThat(context.getExtensions()).hasSize(3);
+ assertThat(context.getExtensions()).hasSize(4);
}
}
diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/MetricSensorTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/MetricSensorTest.java
new file mode 100644
index 0000000..c93c16a
--- /dev/null
+++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/MetricSensorTest.java
@@ -0,0 +1,112 @@
+/*
+ * SonarCSS
+ * Copyright (C) 2018-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.css.plugin;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+import org.sonar.api.batch.sensor.highlighting.TypeOfText;
+import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MetricSensorTest {
+
+ private DefaultInputFile inputFile;
+ private SensorContextTester sensorContext;
+
+ @Rule
+ public TemporaryFolder tempFolder = new TemporaryFolder();
+
+ @Before
+ public void setUp() {
+ sensorContext = SensorContextTester.create(tempFolder.getRoot());
+ }
+
+ @Test
+ public void should_describe() {
+ DefaultSensorDescriptor desc = new DefaultSensorDescriptor();
+ new MetricSensor().describe(desc);
+
+ assertThat(desc.languages()).containsOnly("css");
+ }
+
+ @Test
+ public void empty_input() throws Exception {
+ highlight("foo");
+
+ assertThat(sensorContext.highlightingTypeAt(inputFile.key(), 1, 0)).isEmpty();
+ assertThat(sensorContext.highlightingTypeAt(inputFile.key(), 1, 1)).isEmpty();
+ }
+
+ @Test
+ public void singleline_multiline_comment() throws IOException {
+ highlight("/* some comment */");
+
+ assertHighlighting(1, 1, 17, TypeOfText.COMMENT);
+ }
+
+ @Test
+ public void multiline_comment() throws IOException {
+ String content = "/* some comment\nmultiline */";
+ highlight(content);
+
+ assertHighlighting(1, 1, 15, TypeOfText.COMMENT);
+ assertHighlighting(2, 1, 11, TypeOfText.COMMENT);
+ }
+
+ @Test
+ public void string() throws IOException {
+ String content = "\"foo\"";
+ highlight(content);
+
+ assertHighlighting(1, 1, content.length() - 1, TypeOfText.STRING);
+ }
+
+ private void highlight(String content) throws IOException {
+ File file = tempFolder.newFile();
+ inputFile = new TestInputFileBuilder("moduleKey", file.getName())
+ .setLanguage("css")
+ .setContents(content)
+ .build();
+
+ sensorContext.fileSystem().add(inputFile);
+
+ new MetricSensor().execute(sensorContext);
+ }
+
+ private void assertHighlighting(int line, int column, int length, TypeOfText type) {
+ if (column < 1) {
+ throw new IllegalStateException("Column should be greater than or equal to 1");
+ }
+
+ for (int i = column; i < column + length; i++) {
+ List<TypeOfText> typeOfTexts = sensorContext.highlightingTypeAt(inputFile.key(), line, i);
+ assertThat(typeOfTexts).containsOnly(type);
+ }
+ }
+}