Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 14683x 14683x 14683x 14683x 14683x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 43916x 14683x 14683x 14120x 14120x 14683x 14683x 264x 264x 14683x 14683x 307x 307x 307x 307x 1757x 1757x 1757x 1757x 1734x 1734x 1734x 1757x 24x 24x 1757x 307x 307x 307x 307x 14683x 14683x 2041x 2041x 2020x 2020x 21x 21x 21x 21x 2041x 13x 13x 5x 5x 8x 8x 8x 8x 21x 21x 21x 14683x 14683x 13x 13x 14683x 14683x | import {Issue} from "../issue";
import {ABAPRule} from "./_abap_rule";
import {BasicRuleConfig} from "./_basic_rule_config";
import {IRuleMetadata, RuleTag} from "./_irule";
import {ABAPFile} from "../abap/abap_file";
import {Comment} from "../abap/2_statements/statements/_statement";
import {AbstractToken} from "../abap/1_lexer/tokens/abstract_token";
export class AbapdocShorttextLengthConf extends BasicRuleConfig {
}
const MAX_LENGTH = 60;
export class AbapdocShorttextLength extends ABAPRule {
private conf = new AbapdocShorttextLengthConf();
public getMetadata(): IRuleMetadata {
return {
key: "abapdoc_shorttext_length",
title: "ABAP Doc shorttext too long",
shortDescription: `Reports ABAP Doc shorttexts longer than 60 characters.`,
extendedInformation: `A shorttext is written as <p class="shorttext">...</p> or
<p class="shorttext synchronized">...</p>, the system warns "Shorttext must not exceed 60 characters."
if the text is longer.
The text may span several "! lines, line breaks count as one blank. Leading and trailing blanks are not counted,
an entity like < counts as one character.`,
tags: [RuleTag.SingleFile],
badExample: `INTERFACE zif_foo PUBLIC.
"! <p class="shorttext synchronized">Returns the name of the selected row in the table of the view</p>
METHODS name RETURNING VALUE(result) TYPE string.
ENDINTERFACE.`,
goodExample: `INTERFACE zif_foo PUBLIC.
"! <p class="shorttext synchronized">Returns the name of the selected row</p>
METHODS name RETURNING VALUE(result) TYPE string.
ENDINTERFACE.`,
};
}
public getConfig() {
return this.conf;
}
public setConfig(conf: AbapdocShorttextLengthConf) {
this.conf = conf;
}
public runParsed(file: ABAPFile): Issue[] {
const issues: Issue[] = [];
let block: AbstractToken[] = [];
for (const statement of file.getStatements()) {
const token = statement.getFirstToken();
const doc = statement.get() instanceof Comment && token.getStr().startsWith(`"!`);
const previous = block[block.length - 1];
if (doc === false || (previous !== undefined && token.getStart().getRow() !== previous.getStart().getRow() + 1)) {
issues.push(...this.checkBlock(file, block));
block = [];
}
if (doc === true) {
block.push(token);
}
}
issues.push(...this.checkBlock(file, block));
return issues;
}
private checkBlock(file: ABAPFile, block: AbstractToken[]): Issue[] {
const issues: Issue[] = [];
if (block.length === 0) {
return issues;
}
const text = block.map(t => t.getStr().substring(2)).join("\n");
const regex = /<p\s[^>]*class\s*=\s*"shorttext(?:\s+synchronized)?"[^>]*>([\s\S]*?)<\/p>/gi;
let match: RegExpExecArray | null;
while ((match = regex.exec(text)) !== null) {
const length = this.decode(match[1].replace(/\s+/g, " ").trim()).length;
if (length <= MAX_LENGTH) {
continue;
}
const token = block[text.substring(0, match.index).split("\n").length - 1];
const message = `Shorttext must not exceed ${MAX_LENGTH} characters, found ${length}`;
issues.push(Issue.atToken(file, token, message, this.getMetadata().key, this.conf.severity));
}
return issues;
}
private decode(text: string): string {
return text.replace(/&(#\d+|#x[0-9a-fA-F]+|[a-zA-Z]+);/g, "_");
}
}
|