Page MenuHomePhabricator

D9051.id21597.diff
No OneTemporary

D9051.id21597.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -36,6 +36,8 @@
'ArcanistChooseNoRevisionsException' => 'exception/ArcanistChooseNoRevisionsException.php',
'ArcanistCloseRevisionWorkflow' => 'workflow/ArcanistCloseRevisionWorkflow.php',
'ArcanistCloseWorkflow' => 'workflow/ArcanistCloseWorkflow.php',
+ 'ArcanistCoffeeLintLinter' => 'lint/linter/ArcanistCoffeeLintLinter.php',
+ 'ArcanistCoffeeLintLinterTestCase' => 'lint/linter/__tests__/ArcanistCoffeeLintLinterTestCase.php',
'ArcanistCommentRemover' => 'parser/ArcanistCommentRemover.php',
'ArcanistCommentRemoverTestCase' => 'parser/__tests__/ArcanistCommentRemoverTestCase.php',
'ArcanistCommitWorkflow' => 'workflow/ArcanistCommitWorkflow.php',
@@ -221,6 +223,8 @@
'ArcanistChooseNoRevisionsException' => 'Exception',
'ArcanistCloseRevisionWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistCloseWorkflow' => 'ArcanistBaseWorkflow',
+ 'ArcanistCoffeeLintLinter' => 'ArcanistExternalLinter',
+ 'ArcanistCoffeeLintLinterTestCase' => 'ArcanistArcanistLinterTestCase',
'ArcanistCommentRemoverTestCase' => 'ArcanistTestCase',
'ArcanistCommitWorkflow' => 'ArcanistBaseWorkflow',
'ArcanistConduitLinter' => 'ArcanistLinter',
diff --git a/src/lint/linter/ArcanistCoffeeLintLinter.php b/src/lint/linter/ArcanistCoffeeLintLinter.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/ArcanistCoffeeLintLinter.php
@@ -0,0 +1,173 @@
+<?php
+
+final class ArcanistCoffeeLintLinter extends ArcanistExternalLinter {
+
+ private $config;
+
+ /**
+ * Set the path to the custom configuration file that is to be used by
+ * `coffeelint`.
+ *
+ * @param string $path
+ */
+ public function setConfigFile($path) {
+ $working_copy = $this->getEngine()->getWorkingCopy();
+ $root = $working_copy->getProjectRoot();
+ $project_path = Filesystem::resolvePath($path, $root);
+
+ if (Filesystem::pathExists($project_path)) {
+ $this->config = $project_path;
+ } else {
+ throw new Exception(pht('Configuration file not found: %s', $path));
+ }
+ }
+
+ public function getInfoName() {
+ return 'CoffeeLint';
+ }
+
+ public function getInfoURI() {
+ return 'http://www.coffeelint.org';
+ }
+
+ public function getInfoDescription() {
+ return pht(
+ 'CoffeeLint is a style checker that helps keep CoffeeScript '.
+ 'code clean and consistent.');
+ }
+
+ public function getLinterName() {
+ return 'COFFEE';
+ }
+
+ public function getLinterConfigurationName() {
+ return 'coffeelint';
+ }
+
+ public function getDefaultBinary() {
+ return 'coffeelint';
+ }
+
+ public function getVersion() {
+ list($stdout) = execx('%C --version', $this->getExecutableCommand());
+
+ $matches = array();
+ if (preg_match('/^(?P<version>\d+\.\d+\.\d+)$/', $stdout, $matches)) {
+ return $matches['version'];
+ } else {
+ return false;
+ }
+ }
+
+ public function getInstallInstructions() {
+ return pht('Install CoffeeLint using `npm install -g coffeelint`.');
+ }
+
+ public function shouldExpectCommandErrors() {
+ return true;
+ }
+
+ public function supportsReadDataFromStdin() {
+ return true;
+ }
+
+ public function getReadDataFromStdinFilename() {
+ return '--stdin';
+ }
+
+ protected function getMandatoryFlags() {
+ $options = array(
+ '--checkstyle',
+ '--nocolor',
+ '--quiet',
+ );
+
+ if ($this->config) {
+ $options[] = csprintf('--file=%s', $this->config);
+ }
+
+ return $options;
+ }
+
+ public function getLinterConfigurationOptions() {
+ $options = array(
+ 'coffeelint.config' => array(
+ 'type' => 'optional string',
+ 'help' => pht('A custom configuration file.'),
+ ),
+ );
+
+ return $options + parent::getLinterConfigurationOptions();
+ }
+
+ public function setLinterConfigurationValue($key, $value) {
+ switch ($key) {
+ case 'coffeelint.config':
+ $this->setConfigFile($value);
+ return;
+ }
+
+ return parent::setLinterConfigurationValue($key, $value);
+ }
+
+ protected function parseLinterOutput($path, $err, $stdout, $stderr) {
+ $report_dom = new DOMDocument();
+ $ok = @$report_dom->loadXML($stdout);
+
+ if (!$ok) {
+ return false;
+ }
+
+ $files = $report_dom->getElementsByTagName('file');
+ $messages = array();
+ foreach ($files as $file) {
+ foreach ($file->getElementsByTagName('error') as $error) {
+ if (!($error instanceof DOMElement)) {
+ continue;
+ }
+
+ // Column number is not provided in the output.
+ // See https://github.com/clutchski/coffeelint/issues/87
+
+ $message = new ArcanistLintMessage();
+ $message->setPath($path);
+ $message->setLine($error->getAttribute('line'));
+ $message->setCode('CoffeeLint');
+ $message->setDescription(preg_replace(
+ '/; context: .*$/',
+ '.',
+ $error->getAttribute('message')));
+
+ switch ($error->getAttribute('severity')) {
+ case 'warning':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
+ break;
+
+ case 'error':
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
+ break;
+
+ default:
+ $message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
+ break;
+ }
+
+ $messages[] = $message;
+ }
+ }
+
+ return $messages;
+ }
+
+ protected function getLintCodeFromLinterConfigurationKey($code) {
+
+ // NOTE: We can't figure out which rule generated each message, so we
+ // can not customize severities.
+
+ throw new Exception(
+ pht(
+ "CoffeeLint does not currently support custom severity levels, ".
+ "because rules can't be identified from messages in output."));
+ }
+
+}
diff --git a/src/lint/linter/__tests__/ArcanistCoffeeLintLinterTestCase.php b/src/lint/linter/__tests__/ArcanistCoffeeLintLinterTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/ArcanistCoffeeLintLinterTestCase.php
@@ -0,0 +1,12 @@
+<?php
+
+final class ArcanistCoffeeLintLinterTestCase
+ extends ArcanistArcanistLinterTestCase {
+
+ public function testCSSLintLinter() {
+ $this->executeTestsInDirectory(
+ dirname(__FILE__).'/coffeelint/',
+ new ArcanistCoffeeLintLinter());
+ }
+
+}
diff --git a/src/lint/linter/__tests__/coffeelint/camel_case_classes.lint-test b/src/lint/linter/__tests__/coffeelint/camel_case_classes.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/coffeelint/camel_case_classes.lint-test
@@ -0,0 +1,3 @@
+class boaConstrictor
+~~~~~~~~~~
+error:1:
diff --git a/src/lint/linter/__tests__/coffeelint/duplicate_key.lint-test b/src/lint/linter/__tests__/coffeelint/duplicate_key.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/coffeelint/duplicate_key.lint-test
@@ -0,0 +1,18 @@
+class SomeThing
+ getConfig: ->
+ one = 1
+ one = 5
+ @config =
+ keyA: one
+ keyB: one
+ keyA: 2
+ getConfig: ->
+ @config =
+ foo: 1
+
+ @getConfig: ->
+ config =
+ foo: 1
+~~~~~~~~~~
+error:8:
+error:9:
diff --git a/src/lint/linter/__tests__/coffeelint/indentation.lint-test b/src/lint/linter/__tests__/coffeelint/indentation.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/coffeelint/indentation.lint-test
@@ -0,0 +1,7 @@
+twoSpaces = () ->
+ fourSpaces = () ->
+ eightSpaces = () ->
+ 'this is valid CoffeeScript'
+~~~~~~~~~~
+error:3:
+error:4:
diff --git a/src/lint/linter/__tests__/coffeelint/max_line_length.lint-test b/src/lint/linter/__tests__/coffeelint/max_line_length.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/coffeelint/max_line_length.lint-test
@@ -0,0 +1,3 @@
+#--------------------------------------------------------------------------------
+~~~~~~~~~~
+error:1:
diff --git a/src/lint/linter/__tests__/coffeelint/no_backticks.lint-test b/src/lint/linter/__tests__/coffeelint/no_backticks.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/coffeelint/no_backticks.lint-test
@@ -0,0 +1,3 @@
+`with(document) alert(height);`
+~~~~~~~~~~
+error:1:
diff --git a/src/lint/linter/__tests__/coffeelint/no_debugger.lint-test b/src/lint/linter/__tests__/coffeelint/no_debugger.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/coffeelint/no_debugger.lint-test
@@ -0,0 +1,3 @@
+debugger
+~~~~~~~~~~
+warning:1:
diff --git a/src/lint/linter/__tests__/coffeelint/no_tabs.lint-test b/src/lint/linter/__tests__/coffeelint/no_tabs.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/coffeelint/no_tabs.lint-test
@@ -0,0 +1,6 @@
+x = () ->
+ y = () ->
+ return 1234
+~~~~~~~~~~
+error:2:
+error:3:
diff --git a/src/lint/linter/__tests__/coffeelint/no_throwing_strings.lint-test b/src/lint/linter/__tests__/coffeelint/no_throwing_strings.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/coffeelint/no_throwing_strings.lint-test
@@ -0,0 +1,3 @@
+throw "i made a boo boo"
+~~~~~~~~~~
+error:1:
diff --git a/src/lint/linter/__tests__/coffeelint/no_trailing_semicolons.lint-test b/src/lint/linter/__tests__/coffeelint/no_trailing_semicolons.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/coffeelint/no_trailing_semicolons.lint-test
@@ -0,0 +1,3 @@
+alert('end of line');
+~~~~~~~~~~
+error:1:
diff --git a/src/lint/linter/__tests__/coffeelint/no_trailing_whitespace.lint-test b/src/lint/linter/__tests__/coffeelint/no_trailing_whitespace.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/coffeelint/no_trailing_whitespace.lint-test
@@ -0,0 +1,4 @@
+x = 1234
+y = 1
+~~~~~~~~~~
+error:1:

File Metadata

Mime Type
text/plain
Expires
Fri, Sep 20, 6:47 AM (5 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6622770
Default Alt Text
D9051.id21597.diff (9 KB)

Event Timeline