<commit_before>// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:linter/src/analyzer.dart';

const _desc = r'AVOID lines longer than 80 characters.';

const _details = r'''

**AVOID** lines longer than 80 characters

Readability studies show that long lines of text are harder to read because your
eye has to travel farther when moving to the beginning of the next line. This is
why newspapers and magazines use multiple columns of text.

If you really find yourself wanting lines longer than 80 characters, our
experience is that your code is likely too verbose and could be a little more
compact. The main offender is usually `VeryLongCamelCaseClassNames`. Ask
yourself, “Does each word in that type name tell me something critical or
prevent a name collision?” If not, consider omitting it.

Note that dartfmt does 99% of this for you, but the last 1% is you. It does not
split long string literals to fit in 80 columns, so you have to do that
manually.

We make an exception for URIs and file paths. When those occur in comments or
strings (usually in imports and exports), they may remain on a single line even
if they go over the line limit. This makes it easier to search source files for
a given path.
''';

class LinesLongerThan80Chars extends LintRule implements NodeLintRule {
  LinesLongerThan80Chars()
      : super(
            name: 'lines_longer_than_80_chars',
            description: _desc,
            details: _details,
            group: Group.style);

  @override
  void registerNodeProcessors(NodeLintRegistry registry) {
    final visitor = _Visitor(this);
    registry.addCompilationUnit(this, visitor);
  }
}

class _Visitor extends SimpleAstVisitor {
  final LintRule rule;

  _Visitor(this.rule);

  @override
  void visitCompilationUnit(CompilationUnit node) {
    final lineInfo = node.lineInfo;
    final lineCount = lineInfo.lineCount;
    final longLines = <_LineInfo>[];
    for (int i = 0; i < lineCount; i++) {
      final start = lineInfo.getOffsetOfLine(i);
      int end;
      if (i == lineCount - 1) {
        end = node.end;
      } else {
        end = lineInfo.getOffsetOfLineAfter(start) - 1;
        if (_charAt(node, end) == _lf && _charAt(node, end - 1) == _cr) {
          end--;
        }
      }
      final length = end - start;
      if (length > 80) {
        final line = _LineInfo(index: i, offset: start, end: end);
        longLines.add(line);
      }
    }

    if (longLines.isEmpty) return;

    final allowedLineVisitor = _AllowedLongLineVisitor(lineInfo);
    node.accept(allowedLineVisitor);
    final allowedCommentVisitor = _AllowedCommentVisitor(lineInfo);
    node.accept(allowedCommentVisitor);

    final allowedLines = []
      ..addAll(allowedLineVisitor.allowedLines)
      ..addAll(allowedCommentVisitor.allowedLines);

    for (final line in longLines) {
      if (allowedLines.contains(line.index + 1)) continue;
      rule.reporter
          .reportErrorForOffset(rule.lintCode, line.offset, line.length);
    }
  }
}

const _cr = '\r';
const _lf = '\n';

String _charAt(CompilationUnit unit, int offset) {
  final element = unit.element;
  final contents = element.context.getContents(element.source).data;
  return contents[offset];
}

class _LineInfo {
  _LineInfo({this.index, this.offset, this.end});
  final int index;
  final int offset;
  final int end;
  int get length => end - offset;
}

class _AllowedLongLineVisitor extends RecursiveAstVisitor {
  _AllowedLongLineVisitor(this.lineInfo);

  final LineInfo lineInfo;
  final allowedLines = <int>[];
<commit_msg>address review comments<commit_after>// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:linter/src/analyzer.dart';

const _desc = r'AVOID lines longer than 80 characters.';

const _details = r'''

**AVOID** lines longer than 80 characters

Readability studies show that long lines of text are harder to read because your
eye has to travel farther when moving to the beginning of the next line. This is
why newspapers and magazines use multiple columns of text.

If you really find yourself wanting lines longer than 80 characters, our
experience is that your code is likely too verbose and could be a little more
compact. The main offender is usually `VeryLongCamelCaseClassNames`. Ask
yourself, “Does each word in that type name tell me something critical or
prevent a name collision?” If not, consider omitting it.

Note that dartfmt does 99% of this for you, but the last 1% is you. It does not
split long string literals to fit in 80 columns, so you have to do that
manually.

We make an exception for URIs and file paths. When those occur in comments or
strings (usually in imports and exports), they may remain on a single line even
if they go over the line limit. This makes it easier to search source files for
a given path.
''';

class LinesLongerThan80Chars extends LintRule implements NodeLintRule {
  LinesLongerThan80Chars()
      : super(
            name: 'lines_longer_than_80_chars',
            description: _desc,
            details: _details,
            group: Group.style);

  @override
  void registerNodeProcessors(NodeLintRegistry registry) {
    final visitor = _Visitor(this);
    registry.addCompilationUnit(this, visitor);
  }
}

class _Visitor extends SimpleAstVisitor {
  final LintRule rule;

  _Visitor(this.rule);

  @override
  void visitCompilationUnit(CompilationUnit node) {
    final lineInfo = node.lineInfo;
    final lineCount = lineInfo.lineCount;
    final longLines = <_LineInfo>[];
    for (int i = 0; i < lineCount; i++) {
      final start = lineInfo.getOffsetOfLine(i);
      int end;
      if (i == lineCount - 1) {
        end = node.end;
      } else {
        end = lineInfo.getOffsetOfLine(i + 1) - 1;
        final length = end - start;
        if (length > 80) {
          final chars = _getChars(node);
          if (chars[end] == _lf && chars[end - 1] == _cr) {
            end--;
          }
        }
      }
      final length = end - start;
      if (length > 80) {
        final line = _LineInfo(index: i, offset: start, end: end);
        longLines.add(line);
      }
    }

    if (longLines.isEmpty) return;

    final allowedLineVisitor = _AllowedLongLineVisitor(lineInfo);
    node.accept(allowedLineVisitor);
    final allowedCommentVisitor = _AllowedCommentVisitor(lineInfo);
    node.accept(allowedCommentVisitor);

    final allowedLines = []
      ..addAll(allowedLineVisitor.allowedLines)
      ..addAll(allowedCommentVisitor.allowedLines);

    for (final line in longLines) {
      if (allowedLines.contains(line.index + 1)) continue;
      rule.reporter
          .reportErrorForOffset(rule.lintCode, line.offset, line.length);
    }
  }
}

const _cr = '\r';
const _lf = '\n';

String _getChars(CompilationUnit unit) {
  final element = unit.element;
  return element.context.getContents(element.source).data;
}

class _LineInfo {
  _LineInfo({this.index, this.offset, this.end});
  final int index;
  final int offset;
  final int end;
  int get length => end - offset;
}

class _AllowedLongLineVisitor extends RecursiveAstVisitor {
  _AllowedLongLineVisitor(this.lineInfo);

  final LineInfo lineInfo;
  final allowedLines = <int>[];

{'fraction_non_alphanumeric': 0.08706786171574904, 'fraction_numerical': 0.007554417413572343, 'mean_word_length': 3.363687150837989, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6806466', 'n_tokens_mistral': 2334, 'n_tokens_neox': 2230, 'n_words': 826}