public LinterTest(IServiceContainer services) { _services = services; _validator = new ValidatorAggregator(_services); _options = _services.GetService <IREditorSettings>().LintOptions; _options.Enabled = true; }
private static IValidationError SemicolonCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { if (options.Semicolons && node is TokenNode t && t.Token.TokenType == RTokenType.Semicolon) { return(new ValidationWarning(node, Resources.Lint_Semicolons, ErrorLocation.Token)); } return(null); }
private static IValidationError SpacesInsideParenthesisCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { // There should be no space after (, [ or [[ and no space before ), ] or ]] // unless ] or ]] is preceded by a comma as in x[1, ] if (!options.SpacesInsideParenthesis || !(node is TokenNode t)) { return(null); } var tp = node.Root.TextProvider; switch (t.Token.TokenType) { case RTokenType.OpenBrace: case RTokenType.OpenSquareBracket: case RTokenType.OpenDoubleSquareBracket: // x[1, OK x( 2) is not if (!tp.IsNewLineAfterPosition(node.End) && tp.IsWhitespaceAfterPosition(node.End - 1)) { var lineEnd = tp.IndexOf('\n', node.End); lineEnd = lineEnd >= 0 ? lineEnd : tp.Length; var text = tp.GetText(TextRange.FromBounds(node.End, lineEnd)); var wsEnd = text.IndexWhere(ch => !char.IsWhiteSpace(ch)).FirstOrDefault(); wsEnd = wsEnd > 0 ? wsEnd + node.End : tp.Length; return(new ValidationWarning(TextRange.FromBounds(node.End, wsEnd), Resources.Lint_SpaceAfterLeftParenthesis, ErrorLocation.Token)); } break; case RTokenType.CloseBrace: // () is OK, (,,,) is OK, x( ) is not OK. But we do allow line break before ) if (tp.IsWhitespaceBeforePosition(node.Start) && !tp.IsNewLineBeforePosition(node.Start)) { var i = node.Start - 1; for (; i >= 0; i--) { if (!char.IsWhiteSpace(tp[i]) || tp[i] == '\r' || tp[i] == '\n') { i++; break; } } i = Math.Max(i, 0); return(new ValidationWarning(TextRange.FromBounds(i, node.Start), Resources.Lint_SpaceBeforeClosingBrace, ErrorLocation.Token)); } break; case RTokenType.CloseSquareBracket: case RTokenType.CloseDoubleSquareBracket: // x[1] is OK, x[1,] is not OK, should be x[1, ] var prevChar = node.Start > 0 ? tp[node.Start - 1] : '\0'; if (!tp.IsWhitespaceAfterPosition(node.End - 1) && prevChar == ',') { return(new ValidationWarning(node, Resources.Lint_NoSpaceBetweenCommaAndClosingBracket, ErrorLocation.Token)); } break; } return(null); }
private static IValidationError TabCheck(CharacterStream cs, ILintOptions options) { if (options.NoTabs && cs.CurrentChar == '\t' && cs.Position < cs.Length) { // // no_tab_linter: check that only spaces are used, never tabs return(new ValidationWarning(new TextRange(cs.Position, 1), Resources.Lint_Tabs, ErrorLocation.Token)); } return(null); }
private static IValidationError TrailingWhitespaceCheck(CharacterStream cs, ILintOptions options) { if (options.TrailingWhitespace) { if (cs.IsWhiteSpace() && !cs.CurrentChar.IsLineBreak() && (cs.NextChar.IsLineBreak() || cs.Position == cs.Length - 1)) { // trailing_whitespace_linter: check there are no trailing whitespace characters. return(new ValidationWarning(new TextRange(cs.Position, 1), Resources.Lint_TrailingWhitespace, ErrorLocation.Token)); } } return(null); }
private static IValidationError SpaceAfterFunctionNameCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { if (!options.NoSpaceAfterFunctionName || !(node is FunctionCall fc)) { return(null); } var tp = node.Root.TextProvider; if (fc.RightOperand is Variable v && tp.IsWhitespaceAfterPosition(v.End - 1)) { return(new ValidationWarning(TextRange.FromBounds(v.End, fc.OpenBrace.Start), Resources.Lint_SpaceAfterFunctionName, ErrorLocation.Token)); } return(null); }
private static IValidationError AssignmentCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { if (options.AssignmentType) { // assignment_linter: checks that ’<-’ is always used for assignment if (node is IOperator op && op.OperatorType == OperatorType.Equals) { if (!(op.LeftOperand is NamedArgument)) { return(new ValidationWarning(((TokenOperator)op).OperatorToken, Resources.Lint_Assignment, ErrorLocation.Token)); } } } return(null); }
private static IValidationError TrueFalseNamesCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { // Use TRUE and FALSE instead of T and F if (options.TrueFalseNames) { if (node is TokenNode t && t.Token.TokenType == RTokenType.Logical) { if (t.Token.Length == 1) { return(new ValidationWarning(t.Token, Resources.Lint_TrueFalseNames, ErrorLocation.Token)); } } } return(null); }
private static IValidationError DoubleQuotesCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { // open_curly_linter: check that opening curly braces are never on their own line and are // always followed by a newline if (options.DoubleQuotes) { if (node is TokenNode t && t.Token.TokenType == RTokenType.String) { if (node.Root.TextProvider[node.Start] != '\"') { return(new ValidationWarning(node, Resources.Lint_DoubleQuotes, ErrorLocation.Token)); } } } return(null); }
private static IValidationError CloseCurlySeparateLineCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { // closed_curly_linter: check that closed curly braces should always be // on their own line unless they follow an else if (!options.CloseCurlySeparateLine) { return(null); } if (!(node is TokenNode t) || t.Token.TokenType != RTokenType.CloseCurlyBrace) { return(null); } // Special case {r in R Markdown if (projectedBuffer && IsRMarkdownBlock(node)) { return(null); } // Check 'after' case first to we allow '{x})' or '{x},' var text = GetLineTextAfterPosition(node.Root.TextProvider, node.Start).TrimStart(); // Handle '},', '})' and '} else' if (text.StartsWithOrdinal(",") || text.StartsWithOrdinal(")")) { return(null); } if (!string.IsNullOrWhiteSpace(text) && !text.StartsWithOrdinal("else")) { return(new ValidationWarning(node, Resources.Lint_CloseCurlySeparateLine, ErrorLocation.Token)); } text = GetLineTextBeforePosition(node.Root.TextProvider, node.Start); // allow '{ }' if (string.IsNullOrWhiteSpace(text) || text.TrimEnd().EndsWithOrdinal("{")) { return(null); } return(new ValidationWarning(node, Resources.Lint_CloseCurlySeparateLine, ErrorLocation.Token)); }
private static IEnumerable <IValidationError> NameCheck(IAstNode node, ILintOptions options) { // camel_case_linter: check that objects are not in camelCase. // snake_case_linter: check that objects are not in snake_case. // multiple_dots_linter: check that objects do not have.multiple.dots. // object_length_linter: check that objects do are not very long.not have.multiple.dots. if (node is TokenNode t && t.Token.TokenType == RTokenType.Identifier) { var list = new List <IValidationError>(); var text = node.Root.TextProvider.GetText(node); if (options.UpperCase && IsUpperCase(text)) { list.Add(new ValidationWarning(node, Resources.Lint_Uppercase, ErrorLocation.Token)); } if (options.CamelCase && IsCamelCase(text)) { list.Add(new ValidationWarning(node, Resources.Lint_CamelCase, ErrorLocation.Token)); } if (options.SnakeCase && IsSnakeCase(text)) { list.Add(new ValidationWarning(node, Resources.Lint_SnakeCase, ErrorLocation.Token)); } if (options.PascalCase && IsPascalCase(text)) { list.Add(new ValidationWarning(node, Resources.Lint_PascalCase, ErrorLocation.Token)); } if (options.MultipleDots && HasMultipleDots(text)) { list.Add(new ValidationWarning(node, Resources.Lint_MultileDots, ErrorLocation.Token)); } if (options.NameLength && text.Length > options.MaxNameLength) { list.Add(new ValidationWarning(node, Resources.Lint_NameTooLong.FormatInvariant(text.Length, options.MaxNameLength), ErrorLocation.Token)); } return(list); } return(Enumerable.Empty <IValidationError>()); }
private static IValidationError MultipleStatementsCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { if (options.MultipleStatements && node is TokenNode t && t.Token.TokenType == RTokenType.Semicolon) { var tp = node.Root.TextProvider; if (!tp.IsNewLineAfterPosition(node.End)) { // # comment is OK but comments are not part of the AST. var lineBreakIndex = tp.IndexOf('\n', node.End); var trailingTextEnd = lineBreakIndex >= 0 ? lineBreakIndex : tp.Length; var trailingText = tp.GetText(TextRange.FromBounds(node.End, trailingTextEnd)); var tokens = new RTokenizer().Tokenize(trailingText); var offendingTokens = tokens.Where(x => x.TokenType != RTokenType.Comment); if (offendingTokens.Any()) { var squiggle = TextRange.FromBounds(node.End + offendingTokens.First().Start, node.End + offendingTokens.Last().End); return(new ValidationWarning(squiggle, Resources.Lint_MultipleStatementsInLine, ErrorLocation.Token)); } } } return(null); }
private static IValidationError SpaceBeforeOpenBraceCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { // spaces_left_parentheses_linter: check that all left parentheses have a space // before them unless they are in a function call. if (!options.SpaceBeforeOpenBrace) { return(null); } if (!(node is TokenNode t) || t.Token.TokenType != RTokenType.OpenBrace) { return(null); } if (node.Parent is IKeywordExpression) { var tp = node.Root.TextProvider; if (!tp.IsWhitespaceBeforePosition(node.Start)) { return(new ValidationWarning(t.Token, Resources.Lint_SpaceBeforeOpenBrace, ErrorLocation.Token)); } } return(null); }
private static IValidationError CommaSpacesCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { // commas_linter: check that all commas are followed by spaces, // but do not have spaces before them unless followed by a closing brace var warning = false; if (!options.SpacesAroundComma) { return(null); } if (node is TokenNode t && t.Token.TokenType == RTokenType.Comma) { var tp = node.Root.TextProvider; warning = tp.IsWhitespaceBeforePosition(t.Start); if (!warning && t.End < tp.Length) { var nextChar = tp[t.End]; warning = !char.IsWhiteSpace(nextChar) && nextChar != ')' && nextChar != ','; } } return(warning ? new ValidationWarning(node, Resources.Lint_CommaSpaces, ErrorLocation.Token) : null); }
private static IValidationError InfixOperatorsSpacesCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { // infix_spaces_linter: check that all infix operators have spaces around them. if (!options.SpacesAroundOperators) { return(null); } if (node is IOperator op && !op.IsUnary && op is TokenOperator to) { var tp = node.Root.TextProvider; var t = to.OperatorToken; var text = tp.GetText(t); if (IsOperatorWithoutSpaces(text)) // Special case @, $, :, :: and ::: { return(null); } if (!tp.IsWhitespaceBeforePosition(t.Start) || !tp.IsWhitespaceAfterPosition(t.End - 1)) { return(new ValidationWarning(t, Resources.Lint_OperatorSpaces, ErrorLocation.Token)); } } return(null); }
private static IValidationError OpenCurlyPositionCheck(IAstNode node, ILintOptions options, bool projectedBuffer) { // open_curly_linter: check that opening curly braces are never on their own line // and are always followed by a newline if (!options.OpenCurlyPosition) { return(null); } if (!(node is TokenNode t) || t.Token.TokenType != RTokenType.OpenCurlyBrace) { return(null); } // Special case {r in R Markdown if (projectedBuffer && IsRMarkdownBlock(node)) { return(null); } var tp = node.Root.TextProvider; var text = GetLineTextBeforePosition(tp, node.Start); if (string.IsNullOrWhiteSpace(text)) { return(new ValidationWarning(node, Resources.Lint_OpenCurlyPosition, ErrorLocation.Token)); } // allow '{ }' text = GetLineTextAfterPosition(tp, node.Start); if (string.IsNullOrWhiteSpace(text) || text.TrimStart().StartsWithOrdinal("}")) { return(null); } return(new ValidationWarning(node, Resources.Lint_OpenCurlyPosition, ErrorLocation.Token)); }
private static IEnumerable <IValidationError> LineLengthCheck(ITextProvider tp, ILintOptions options, bool projectedBuffer) { if (!options.LineLength || tp.Length <= 1) { return(Enumerable.Empty <IValidationError>()); } // line_length_linter: check the line length of both comments and code is less than length. var list = new List <IValidationError>(); var start = 0; for (var i = 0; i < tp.Length + 1; i++) { var ch = i < tp.Length ? tp[i] : '\0'; if (ch.IsLineBreak() || ch == '\0') { var length = i - start; if (length > options.MaxLineLength) { list.Add(new ValidationWarning(new TextRange(start, length), Resources.Lint_LineTooLong.FormatInvariant(length, options.MaxLineLength), ErrorLocation.Token)); } if (i < tp.Length && ((ch == '\r' && tp[i + 1] == '\n') || (ch == '\n' && tp[i + 1] == '\r'))) { i++; } start = i + 1; } } return(list); }
private static IEnumerable <IValidationError> TrailingBlankLinesCheck(ITextProvider tp, ILintOptions options, bool projectedBuffer) { if (options.TrailingBlankLines && !projectedBuffer && tp.Length > 1) { // trailing_blank_lines_linter: check there are no trailing blank lines var trailingWhitespace = string.Empty; int i; for (i = tp.Length - 1; i >= 0; i--) { if (!char.IsWhiteSpace(tp[i]) && i < tp.Length - 1) { i++; trailingWhitespace = tp.GetText(new TextRange(i, tp.Length - i)); break; } } // On Windows, we get \r\n and need to squiggle \r. // On other platforms we may get standalone \n and should then report it var warnings = GetEolWarnings(trailingWhitespace, i, '\r'); if (!warnings.Any()) { warnings = GetEolWarnings(trailingWhitespace, i, '\n'); } return(warnings); } return(Enumerable.Empty <IValidationError>()); }
public RLintOptionsDialog() { SettingsRegistryPath = @"UserSettings\R_Lint"; _options = VsAppShell.Current.GetService <IWritableREditorSettings>().LintOptions; }