Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14719509
D11458.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
D11458.diff
View Options
diff --git a/src/lint/linter/ArcanistTextLinter.php b/src/lint/linter/ArcanistTextLinter.php
--- a/src/lint/linter/ArcanistTextLinter.php
+++ b/src/lint/linter/ArcanistTextLinter.php
@@ -5,15 +5,16 @@
*/
final class ArcanistTextLinter extends ArcanistLinter {
- const LINT_DOS_NEWLINE = 1;
- const LINT_TAB_LITERAL = 2;
+ const LINT_END_OF_LINE = 1;
+ const LINT_INDENT_STYLE = 2;
const LINT_LINE_WRAP = 3;
const LINT_EOF_NEWLINE = 4;
- const LINT_BAD_CHARSET = 5;
+ const LINT_CHARSET = 5;
const LINT_TRAILING_WHITESPACE = 6;
const LINT_BOF_WHITESPACE = 8;
const LINT_EOF_WHITESPACE = 9;
+ private $editorconfig;
private $maxLineLength = 80;
public function getInfoName() {
@@ -23,7 +24,7 @@
public function getInfoDescription() {
return pht(
'Enforces basic text rules like line length, character encoding, '.
- 'and trailing whitespace.');
+ 'and trailing whitespace. Supports EditorConfig configuration.');
}
public function getLinterPriority() {
@@ -36,7 +37,9 @@
'type' => 'optional int',
'help' => pht(
'Adjust the maximum line length before a warning is raised. By '.
- 'default, a warning is raised on lines exceeding 80 characters.'),
+ 'default, a warning is raised on lines exceeding 80 characters. '.
+ 'This is deprecated and you should use an EditorConfig file to '.
+ 'configure this value.'),
),
);
@@ -51,6 +54,9 @@
public function setLinterConfigurationValue($key, $value) {
switch ($key) {
case 'text.max-line-length':
+ phutil_deprecated(
+ $key,
+ 'You should specify this configuration in an EditorConfig file.');
$this->setMaxLineLength($value);
return;
}
@@ -69,25 +75,30 @@
public function getLintSeverityMap() {
return array(
self::LINT_LINE_WRAP => ArcanistLintSeverity::SEVERITY_WARNING,
- self::LINT_TRAILING_WHITESPACE => ArcanistLintSeverity::SEVERITY_AUTOFIX,
- self::LINT_BOF_WHITESPACE => ArcanistLintSeverity::SEVERITY_AUTOFIX,
- self::LINT_EOF_WHITESPACE => ArcanistLintSeverity::SEVERITY_AUTOFIX,
+ self::LINT_TRAILING_WHITESPACE => ArcanistLintSeverity::SEVERITY_ADVICE,
+ self::LINT_BOF_WHITESPACE => ArcanistLintSeverity::SEVERITY_ADVICE,
+ self::LINT_EOF_WHITESPACE => ArcanistLintSeverity::SEVERITY_ADVICE,
);
}
public function getLintNameMap() {
return array(
- self::LINT_DOS_NEWLINE => pht('DOS Newlines'),
- self::LINT_TAB_LITERAL => pht('Tab Literal'),
+ self::LINT_END_OF_LINE => pht('End Of Line'),
+ self::LINT_INDENT_STYLE => pht('Indent Style'),
self::LINT_LINE_WRAP => pht('Line Too Long'),
self::LINT_EOF_NEWLINE => pht('File Does Not End in Newline'),
- self::LINT_BAD_CHARSET => pht('Bad Charset'),
+ self::LINT_CHARSET => pht('Bad Charset'),
self::LINT_TRAILING_WHITESPACE => pht('Trailing Whitespace'),
self::LINT_BOF_WHITESPACE => pht('Leading Whitespace at BOF'),
self::LINT_EOF_WHITESPACE => pht('Trailing Whitespace at EOF'),
);
}
+ public function willLintPaths(array $paths) {
+ $root = $this->getEngine()->getWorkingCopy()->getProjectRoot();
+ $this->editorconfig = new PhutilEditorConfig($root);
+ }
+
public function lintPath($path) {
if (!strlen($this->getData($path))) {
// If the file is empty, don't bother; particularly, don't require
@@ -95,8 +106,8 @@
return;
}
- $this->lintNewlines($path);
- $this->lintTabs($path);
+ $this->lintEndOfLine($path);
+ $this->lintIndentStyle($path);
if ($this->didStopAllLinters()) {
return;
@@ -116,39 +127,123 @@
$this->lintEOFWhitespace($path);
}
- protected function lintNewlines($path) {
+ private function lintEndOfLine($path) {
+ switch ($this->getEditorConfig($path, PhutilEditorConfig::END_OF_LINE)) {
+ case 'lf':
+ $search = "\r";
+ $replace = array(
+ "\r\n" => "\n",
+ );
+ break;
+
+ case 'cr':
+ $search = "\n";
+ $replace = array(
+ "\r\n" => "\r",
+ );
+ break;
+
+ case 'lfcr':
+ $search = "\n";
+ $replace = array(
+ "\r\n" => "\n\r",
+ );
+ break;
+
+ default:
+ return;
+ }
+
$data = $this->getData($path);
- $pos = strpos($this->getData($path), "\r");
+ $pos = strpos($this->getData($path), $search);
if ($pos !== false) {
$this->raiseLintAtOffset(
0,
- self::LINT_DOS_NEWLINE,
- pht('You must use ONLY Unix linebreaks ("%s") in source code.', '\n'),
+ self::LINT_END_OF_LINE,
+ pht('Invalid end-of-line character(s).'),
$data,
- str_replace("\r\n", "\n", $data));
+ str_replace(array_keys($replace), array_values($replace), $data));
- if ($this->isMessageEnabled(self::LINT_DOS_NEWLINE)) {
+ if ($this->isMessageEnabled(self::LINT_END_OF_LINE)) {
$this->stopAllLinters();
}
}
}
- protected function lintTabs($path) {
+ private function lintIndentStyle($path) {
+ switch ($this->getEditorConfig($path, PhutilEditorConfig::INDENT_STYLE)) {
+ case 'space':
+ break;
+
+ case 'tab':
+ // TODO: Can we do anything here?
+ return;
+
+ default:
+ return;
+ }
+
$pos = strpos($this->getData($path), "\t");
if ($pos !== false) {
$this->raiseLintAtOffset(
$pos,
- self::LINT_TAB_LITERAL,
+ self::LINT_INDENT_STYLE,
pht('Configure your editor to use spaces for indentation.'),
"\t");
}
}
- protected function lintLineLength($path) {
- $lines = explode("\n", $this->getData($path));
+ private function lintCharset($path) {
+ switch ($this->getEditorConfig($path, PhutilEditorConfig::CHARSET)) {
+ case 'latin1':
+ $charset = 'ISO-8859-1';
+ break;
+
+ case 'utf-8':
+ case 'utf-8-bom':
+ // TODO: Do we really care about the BOM?
+ $charset = 'UTF-8';
+ break;
+
+ case 'utf-16be':
+ $charset = 'UTF-16BE';
+ break;
+
+ case 'utf-16le':
+ $charset = 'UTF-16LE';
+ break;
+
+ default:
+ return;
+ }
+
+ $data = $this->getData($path);
+
+ if (mb_check_encoding($data, $charset)) {
+ return;
+ }
+
+ $this->raiseLintAtPath(
+ self::LINT_CHARSET,
+ pht('Invalid characters in charset.'));
+
+ if ($this->isMessageEnabled(self::LINT_BAD_CHARSET)) {
+ $this->stopAllLinters();
+ }
+ }
+
+ private function lintLineLength($path) {
+ $lines = phutil_split_lines($this->getData($path), false);
+ $width = $this->getEditorConfig(
+ $path,
+ PhutilEditorConfig::LINE_LENGTH,
+ $this->maxLineLength);
+
+ if (!$width) {
+ return;
+ }
- $width = $this->maxLineLength;
foreach ($lines as $line_idx => $line) {
if (strlen($line) > $width) {
$this->raiseLintAtLine(
@@ -165,53 +260,47 @@
}
}
- protected function lintEOFNewline($path) {
- $data = $this->getData($path);
- if (!strlen($data) || $data[strlen($data) - 1] != "\n") {
- $this->raiseLintAtOffset(
- strlen($data),
- self::LINT_EOF_NEWLINE,
- pht('Files must end in a newline.'),
- '',
- "\n");
+ private function lintEOFNewline($path) {
+ $config = $this->getEditorConfig(
+ $path,
+ PhutilEditorConfig::FINAL_NEWLINE,
+ false);
+
+ if (!$config) {
+ return;
}
- }
- protected function lintCharset($path) {
$data = $this->getData($path);
-
$matches = null;
- $bad = '[^\x09\x0A\x20-\x7E]';
- $preg = preg_match_all(
- "/{$bad}(.*{$bad})?/",
+ $preg = preg_match(
+ '/(\n|\r|\r\n)$/',
$data,
$matches,
PREG_OFFSET_CAPTURE);
- if (!$preg) {
+ if ($preg) {
return;
}
- foreach ($matches[0] as $match) {
- list($string, $offset) = $match;
- $this->raiseLintAtOffset(
- $offset,
- self::LINT_BAD_CHARSET,
- pht(
- 'Source code should contain only ASCII bytes with ordinal '.
- 'decimal values between 32 and 126 inclusive, plus linefeed. '.
- 'Do not use UTF-8 or other multibyte charsets.'),
- $string);
- }
+ $this->raiseLintAtOffset(
+ strlen($data),
+ self::LINT_EOF_NEWLINE,
+ pht('Files must end in a newline.'),
+ '',
+ $this->getEndOfLineCharacters($path));
+ }
- if ($this->isMessageEnabled(self::LINT_BAD_CHARSET)) {
- $this->stopAllLinters();
+ private function lintTrailingWhitespace($path) {
+ $config = $this->getEditorConfig(
+ $path,
+ PhutilEditorConfig::TRAILING_WHITESPACE,
+ false);
+
+ if (!$config) {
+ return;
}
- }
- protected function lintTrailingWhitespace($path) {
$data = $this->getData($path);
-
$matches = null;
$preg = preg_match_all(
'/ +$/m',
@@ -237,9 +326,17 @@
}
}
- protected function lintBOFWhitespace($path) {
- $data = $this->getData($path);
+ private function lintBOFWhitespace($path) {
+ $config = $this->getEditorConfig(
+ $path,
+ PhutilEditorConfig::TRAILING_WHITESPACE,
+ false);
+ if (!$config) {
+ return;
+ }
+
+ $data = $this->getData($path);
$matches = null;
$preg = preg_match(
'/^\s*\n/',
@@ -262,9 +359,17 @@
'');
}
- protected function lintEOFWhitespace($path) {
- $data = $this->getData($path);
+ private function lintEOFWhitespace($path) {
+ $config = $this->getEditorConfig(
+ $path,
+ PhutilEditorConfig::TRAILING_WHITESPACE,
+ false);
+
+ if (!$config) {
+ return;
+ }
+ $data = $this->getData($path);
$matches = null;
$preg = preg_match(
'/(?<=\n)\s+$/',
@@ -287,4 +392,45 @@
'');
}
+ /**
+ * Retrieve a value from the EditorConfig files.
+ *
+ * This function delegates handling of the EditorConfig file(s) to
+ * @{class:PhutilEditorConfigParser}.
+ *
+ * @param string The path of the file.
+ * @param string The name of the EditorConfig property.
+ * @param mixed Default value.
+ * @return mixed
+ */
+ protected function getEditorConfig($path, $property, $default = null) {
+ if (!$this->editorconfig) {
+ throw new PhutilInvalidStateException('willLintPaths');
+ }
+
+ $path = $this->getEngine()->getFilePathOnDisk($path);
+ $value = $this->editorconfig->getProperty($path, $property);
+
+ return coalesce($value, $default);
+ }
+
+ /**
+ * Return the end-of-line character(s) for a given path.
+ *
+ * @param string The path to the file.
+ * @return string|null
+ */
+ protected function getEndOfLineCharacters($path) {
+ switch ($this->getEditorConfig($path, PhutilEditorConfig::END_OF_LINE)) {
+ case 'lf':
+ return "\n";
+
+ case 'cr':
+ return "\r";
+
+ case 'lfcr':
+ return "\r\n";
+ }
+ }
+
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jan 18, 9:11 PM (20 h, 35 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7010748
Default Alt Text
D11458.diff (11 KB)
Attached To
Mode
D11458: Modify `ArcanistTextLinter` to respect `.editorconfig`
Attached
Detach File
Event Timeline
Log In to Comment