array( 'type' => 'optional bool', 'help' => pht( 'Enable strict math, which only processes mathematical expressions '. 'inside extraneous parentheses.'), ), 'lessc.strict-units' => array( 'type' => 'optional bool', 'help' => pht('Enable strict handling of units in expressions.'), ), ); } public function setLinterConfigurationValue($key, $value) { switch ($key) { case 'lessc.strict-math': $this->strictMath = $value; return; case 'lessc.strict-units': $this->strictUnits = $value; return; } return parent::setLinterConfigurationValue($key, $value); } public function getLintNameMap() { return array( self::LINT_RUNTIME_ERROR => pht('Runtime Error'), self::LINT_ARGUMENT_ERROR => pht('Argument Error'), self::LINT_FILE_ERROR => pht('File Error'), self::LINT_NAME_ERROR => pht('Name Error'), self::LINT_OPERATION_ERROR => pht('Operation Error'), self::LINT_PARSE_ERROR => pht('Parse Error'), self::LINT_SYNTAX_ERROR => pht('Syntax Error'), ); } public function getDefaultBinary() { return 'lessc'; } public function getVersion() { list($stdout) = execx('%C --version', $this->getExecutableCommand()); $matches = array(); $regex = '/^lessc (?P\d+\.\d+\.\d+)\b/'; if (preg_match($regex, $stdout, $matches)) { $version = $matches['version']; } else { return false; } } public function getInstallInstructions() { return pht('Install lessc using `npm install -g less`.'); } public function supportsReadDataFromStdin() { // Technically `lessc` can read data from standard input however, when doing // so, relative imports cannot be resolved. Therefore, this functionality is // disabled. return false; } public function getReadDataFromStdinFilename() { return '-'; } protected function getMandatoryFlags() { return array( '--lint', '--no-color', '--strict-math='.($this->strictMath ? 'on' : 'off'), '--strict-units='.($this->strictUnits ? 'on' : 'off'), ); } protected function parseLinterOutput($path, $err, $stdout, $stderr) { $lines = phutil_split_lines($stderr, false); $messages = array(); foreach ($lines as $line) { $matches = null; $match = preg_match( '/^(?P\w+): (?P.+) '. 'in (?P.+|-) '. 'on line (?P\d+), column (?P\d+):$/', $line, $matches); if ($match) { switch ($matches['name']) { case 'RuntimeError': $code = self::LINT_RUNTIME_ERROR; break; case 'ArgumentError': $code = self::LINT_ARGUMENT_ERROR; break; case 'FileError': $code = self::LINT_FILE_ERROR; break; case 'NameError': $code = self::LINT_NAME_ERROR; break; case 'OperationError': $code = self::LINT_OPERATION_ERROR; break; case 'ParseError': $code = self::LINT_PARSE_ERROR; break; case 'SyntaxError': $code = self::LINT_SYNTAX_ERROR; break; default: throw new RuntimeException(pht( 'Unrecognized lint message code "%s".', $code)); } $code = $this->getLintCodeFromLinterConfigurationKey($matches['name']); $message = new ArcanistLintMessage(); $message->setPath($path); $message->setLine($matches['line']); $message->setChar($matches['column']); $message->setCode($this->getLintMessageFullCode($code)); $message->setSeverity($this->getLintMessageSeverity($code)); $message->setName($this->getLintMessageName($code)); $message->setDescription(ucfirst($matches['description'])); $messages[] = $message; } } if ($err && !$messages) { return false; } return $messages; } }