~ubuntu-branches/ubuntu/wily/phabricator/wily

« back to all changes in this revision

Viewing changes to src/lint/linter/ArcanistTextLinter.php

  • Committer: Package Import Robot
  • Author(s): Richard Sellam
  • Date: 2014-11-01 23:20:06 UTC
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: package-import@ubuntu.com-20141101232006-mvlnp0cil67tsboe
Tags: upstream-0~git20141101/arcanist
Import upstream version 0~git20141101, component arcanist

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
 
 
3
/**
 
4
 * Enforces basic text file rules.
 
5
 */
 
6
final class ArcanistTextLinter extends ArcanistLinter {
 
7
 
 
8
  const LINT_DOS_NEWLINE          = 1;
 
9
  const LINT_TAB_LITERAL          = 2;
 
10
  const LINT_LINE_WRAP            = 3;
 
11
  const LINT_EOF_NEWLINE          = 4;
 
12
  const LINT_BAD_CHARSET          = 5;
 
13
  const LINT_TRAILING_WHITESPACE  = 6;
 
14
  const LINT_NO_COMMIT            = 7;
 
15
  const LINT_BOF_WHITESPACE       = 8;
 
16
  const LINT_EOF_WHITESPACE       = 9;
 
17
 
 
18
  private $maxLineLength = 80;
 
19
 
 
20
  public function getInfoName() {
 
21
    return pht('Basic Text Linter');
 
22
  }
 
23
 
 
24
  public function getInfoDescription() {
 
25
    return pht(
 
26
      'Enforces basic text rules like line length, character encoding, '.
 
27
      'and trailing whitespace.');
 
28
  }
 
29
 
 
30
  public function getLinterPriority() {
 
31
    return 0.5;
 
32
  }
 
33
 
 
34
  public function getLinterConfigurationOptions() {
 
35
    $options = array(
 
36
      'text.max-line-length' => array(
 
37
        'type' => 'optional int',
 
38
        'help' => pht(
 
39
          'Adjust the maximum line length before a warning is raised. By '.
 
40
          'default, a warning is raised on lines exceeding 80 characters.'),
 
41
      ),
 
42
    );
 
43
 
 
44
    return $options + parent::getLinterConfigurationOptions();
 
45
  }
 
46
 
 
47
  public function setMaxLineLength($new_length) {
 
48
    $this->maxLineLength = $new_length;
 
49
    return $this;
 
50
  }
 
51
 
 
52
  public function setLinterConfigurationValue($key, $value) {
 
53
    switch ($key) {
 
54
      case 'text.max-line-length':
 
55
        $this->setMaxLineLength($value);
 
56
        return;
 
57
    }
 
58
 
 
59
    return parent::setLinterConfigurationValue($key, $value);
 
60
  }
 
61
 
 
62
  public function getLinterName() {
 
63
    return 'TXT';
 
64
  }
 
65
 
 
66
  public function getLinterConfigurationName() {
 
67
    return 'text';
 
68
  }
 
69
 
 
70
  public function getLintSeverityMap() {
 
71
    return array(
 
72
      self::LINT_LINE_WRAP           => ArcanistLintSeverity::SEVERITY_WARNING,
 
73
      self::LINT_TRAILING_WHITESPACE => ArcanistLintSeverity::SEVERITY_AUTOFIX,
 
74
      self::LINT_BOF_WHITESPACE      => ArcanistLintSeverity::SEVERITY_AUTOFIX,
 
75
      self::LINT_EOF_WHITESPACE      => ArcanistLintSeverity::SEVERITY_AUTOFIX,
 
76
    );
 
77
  }
 
78
 
 
79
  public function getLintNameMap() {
 
80
    return array(
 
81
      self::LINT_DOS_NEWLINE         => pht('DOS Newlines'),
 
82
      self::LINT_TAB_LITERAL         => pht('Tab Literal'),
 
83
      self::LINT_LINE_WRAP           => pht('Line Too Long'),
 
84
      self::LINT_EOF_NEWLINE         => pht('File Does Not End in Newline'),
 
85
      self::LINT_BAD_CHARSET         => pht('Bad Charset'),
 
86
      self::LINT_TRAILING_WHITESPACE => pht('Trailing Whitespace'),
 
87
      self::LINT_NO_COMMIT           => pht('Explicit %s', '@no'.'commit'),
 
88
      self::LINT_BOF_WHITESPACE      => pht('Leading Whitespace at BOF'),
 
89
      self::LINT_EOF_WHITESPACE      => pht('Trailing Whitespace at EOF'),
 
90
    );
 
91
  }
 
92
 
 
93
  public function lintPath($path) {
 
94
    if (!strlen($this->getData($path))) {
 
95
      // If the file is empty, don't bother; particularly, don't require
 
96
      // the user to add a newline.
 
97
      return;
 
98
    }
 
99
 
 
100
    $this->lintNewlines($path);
 
101
    $this->lintTabs($path);
 
102
 
 
103
    if ($this->didStopAllLinters()) {
 
104
      return;
 
105
    }
 
106
 
 
107
    $this->lintCharset($path);
 
108
 
 
109
    if ($this->didStopAllLinters()) {
 
110
      return;
 
111
    }
 
112
 
 
113
    $this->lintLineLength($path);
 
114
    $this->lintEOFNewline($path);
 
115
    $this->lintTrailingWhitespace($path);
 
116
 
 
117
    $this->lintBOFWhitespace($path);
 
118
    $this->lintEOFWhitespace($path);
 
119
 
 
120
    if ($this->getEngine()->getCommitHookMode()) {
 
121
      $this->lintNoCommit($path);
 
122
    }
 
123
  }
 
124
 
 
125
  protected function lintNewlines($path) {
 
126
    $data = $this->getData($path);
 
127
    $pos  = strpos($this->getData($path), "\r");
 
128
 
 
129
    if ($pos !== false) {
 
130
      $this->raiseLintAtOffset(
 
131
        0,
 
132
        self::LINT_DOS_NEWLINE,
 
133
        pht('You must use ONLY Unix linebreaks ("%s") in source code.', '\n'),
 
134
        $data,
 
135
        str_replace("\r\n", "\n", $data));
 
136
 
 
137
      if ($this->isMessageEnabled(self::LINT_DOS_NEWLINE)) {
 
138
        $this->stopAllLinters();
 
139
      }
 
140
    }
 
141
  }
 
142
 
 
143
  protected function lintTabs($path) {
 
144
    $pos = strpos($this->getData($path), "\t");
 
145
    if ($pos !== false) {
 
146
      $this->raiseLintAtOffset(
 
147
        $pos,
 
148
        self::LINT_TAB_LITERAL,
 
149
        pht('Configure your editor to use spaces for indentation.'),
 
150
        "\t");
 
151
    }
 
152
  }
 
153
 
 
154
  protected function lintLineLength($path) {
 
155
    $lines = explode("\n", $this->getData($path));
 
156
 
 
157
    $width = $this->maxLineLength;
 
158
    foreach ($lines as $line_idx => $line) {
 
159
      if (strlen($line) > $width) {
 
160
        $this->raiseLintAtLine(
 
161
          $line_idx + 1,
 
162
          1,
 
163
          self::LINT_LINE_WRAP,
 
164
          pht(
 
165
            'This line is %s characters long, but the '.
 
166
            'convention is %s characters.',
 
167
            new PhutilNumber(strlen($line)),
 
168
            $width),
 
169
          $line);
 
170
      }
 
171
    }
 
172
  }
 
173
 
 
174
  protected function lintEOFNewline($path) {
 
175
    $data = $this->getData($path);
 
176
    if (!strlen($data) || $data[strlen($data) - 1] != "\n") {
 
177
      $this->raiseLintAtOffset(
 
178
        strlen($data),
 
179
        self::LINT_EOF_NEWLINE,
 
180
        pht('Files must end in a newline.'),
 
181
        '',
 
182
        "\n");
 
183
    }
 
184
  }
 
185
 
 
186
  protected function lintCharset($path) {
 
187
    $data = $this->getData($path);
 
188
 
 
189
    $matches = null;
 
190
    $bad = '[^\x09\x0A\x20-\x7E]';
 
191
    $preg = preg_match_all(
 
192
      "/{$bad}(.*{$bad})?/",
 
193
      $data,
 
194
      $matches,
 
195
      PREG_OFFSET_CAPTURE);
 
196
 
 
197
    if (!$preg) {
 
198
      return;
 
199
    }
 
200
 
 
201
    foreach ($matches[0] as $match) {
 
202
      list($string, $offset) = $match;
 
203
      $this->raiseLintAtOffset(
 
204
        $offset,
 
205
        self::LINT_BAD_CHARSET,
 
206
        pht(
 
207
          'Source code should contain only ASCII bytes with ordinal '.
 
208
          'decimal values between 32 and 126 inclusive, plus linefeed. '.
 
209
          'Do not use UTF-8 or other multibyte charsets.'),
 
210
        $string);
 
211
    }
 
212
 
 
213
    if ($this->isMessageEnabled(self::LINT_BAD_CHARSET)) {
 
214
      $this->stopAllLinters();
 
215
    }
 
216
  }
 
217
 
 
218
  protected function lintTrailingWhitespace($path) {
 
219
    $data = $this->getData($path);
 
220
 
 
221
    $matches = null;
 
222
    $preg = preg_match_all(
 
223
      '/ +$/m',
 
224
      $data,
 
225
      $matches,
 
226
      PREG_OFFSET_CAPTURE);
 
227
 
 
228
    if (!$preg) {
 
229
      return;
 
230
    }
 
231
 
 
232
    foreach ($matches[0] as $match) {
 
233
      list($string, $offset) = $match;
 
234
      $this->raiseLintAtOffset(
 
235
        $offset,
 
236
        self::LINT_TRAILING_WHITESPACE,
 
237
        pht(
 
238
          'This line contains trailing whitespace. Consider setting '.
 
239
          'up your editor to automatically remove trailing whitespace, '.
 
240
          'you will save time.'),
 
241
        $string,
 
242
        '');
 
243
    }
 
244
  }
 
245
 
 
246
  protected function lintBOFWhitespace($path) {
 
247
    $data = $this->getData($path);
 
248
 
 
249
    $matches = null;
 
250
    $preg = preg_match(
 
251
      '/^\s*\n/',
 
252
      $data,
 
253
      $matches,
 
254
      PREG_OFFSET_CAPTURE);
 
255
 
 
256
    if (!$preg) {
 
257
      return;
 
258
    }
 
259
 
 
260
    list($string, $offset) = $matches[0];
 
261
    $this->raiseLintAtOffset(
 
262
      $offset,
 
263
      self::LINT_BOF_WHITESPACE,
 
264
      pht(
 
265
        'This file contains leading whitespace at the beginning of the file. '.
 
266
        'This is unnecessary and should be avoided when possible.'),
 
267
      $string,
 
268
      '');
 
269
  }
 
270
 
 
271
  protected function lintEOFWhitespace($path) {
 
272
    $data = $this->getData($path);
 
273
 
 
274
    $matches = null;
 
275
    $preg = preg_match(
 
276
      '/(?<=\n)\s+$/',
 
277
      $data,
 
278
      $matches,
 
279
      PREG_OFFSET_CAPTURE);
 
280
 
 
281
    if (!$preg) {
 
282
      return;
 
283
    }
 
284
 
 
285
    list($string, $offset) = $matches[0];
 
286
    $this->raiseLintAtOffset(
 
287
      $offset,
 
288
      self::LINT_EOF_WHITESPACE,
 
289
      pht(
 
290
        'This file contains trailing whitespace at the end of the file. '.
 
291
        'This is unnecessary and should be avoided when possible.'),
 
292
      $string,
 
293
      '');
 
294
  }
 
295
 
 
296
  private function lintNoCommit($path) {
 
297
    $data = $this->getData($path);
 
298
 
 
299
    $deadly = '@no'.'commit';
 
300
 
 
301
    $offset = strpos($data, $deadly);
 
302
    if ($offset !== false) {
 
303
      $this->raiseLintAtOffset(
 
304
        $offset,
 
305
        self::LINT_NO_COMMIT,
 
306
        pht(
 
307
          'This file is explicitly marked as "%s", which blocks commits.',
 
308
          $deadly),
 
309
        $deadly);
 
310
    }
 
311
  }
 
312
 
 
313
}