Module: Sloplint::Engine
- Defined in:
- lib/sloplint/engine.rb
Class Method Summary collapse
-
.blank_markdown(text) ⇒ Object
Replace fenced code, inline code, and URLs with same-length whitespace so line/column stay correct.
-
.line_col(text, offset, line_starts: nil) ⇒ Object
1-indexed line and column for a char offset into text.
-
.line_starts_for(text) ⇒ Object
Char offset where each line begins, index 0 = line 1.
-
.scan(text, rules: RULES, markdown: false, path: "-") ⇒ Object
text: the source.
Class Method Details
.blank_markdown(text) ⇒ Object
Replace fenced code, inline code, and URLs with same-length whitespace so line/column stay correct. Newlines are preserved.
70 71 72 73 74 75 76 |
# File 'lib/sloplint/engine.rb', line 70 def blank_markdown(text) blank = lambda { |s| s.gsub(/[^\n]/, " ") } text .gsub(/```.*?```/m) { |s| blank.call(s) } # fenced code .gsub(/`[^`\n]*`/) { |s| blank.call(s) } # inline code .gsub(%r{https?://\S+}) { |s| blank.call(s) } # bare URLs end |
.line_col(text, offset, line_starts: nil) ⇒ Object
1-indexed line and column for a char offset into text. Binary-searches a precomputed line_starts table (see line_starts_for) so a scan with many notes doesn't re-walk the prefix from offset 0 for every single one -- the previous version did text[0, offset] per note, which is O(n) per call and O(n * notes) overall, quadratic on a large file with many hits. line_starts is optional so this stays callable standalone.
47 48 49 50 51 |
# File 'lib/sloplint/engine.rb', line 47 def line_col(text, offset, line_starts: nil) line_starts ||= line_starts_for(text) idx = (line_starts.bsearch_index { |s| s > offset } || line_starts.length) - 1 [idx + 1, offset - line_starts[idx] + 1] end |
.line_starts_for(text) ⇒ Object
Char offset where each line begins, index 0 = line 1. Computed once per scan and shared across every note instead of recomputed per note.
Built via each_line + line.length, not repeated String#index(pat, pos) calls -- on non-ASCII-only text (e.g. curly quotes, em dashes), index with a start position is not O(1)-amortized per call in CRuby, and a few thousand newlines turned this quadratic: 7.4s on a 1.2MB UTF-8 file that each_line does in 0.004s.
61 62 63 64 65 66 |
# File 'lib/sloplint/engine.rb', line 61 def line_starts_for(text) starts = [0] text.each_line { |line| starts << starts.last + line.length } starts.pop unless text.empty? || text.end_with?("\n") starts end |
.scan(text, rules: RULES, markdown: false, path: "-") ⇒ Object
text: the source. rules: which Rule objects to run. markdown: blank code/URLs first. path: label carried into each Note (e.g. filename or "-" for stdin).
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/sloplint/engine.rb', line 17 def scan(text, rules: RULES, markdown: false, path: "-") text = blank_markdown(text) if markdown line_starts = line_starts_for(text) notes = [] rules.each do |rule| text.to_enum(:scan, rule.pattern).each do m = Regexp.last_match matched = m[0] next if rule.skip.any? { |re| matched.match?(re) } count = rule.count_group ? matched.scan(rule.count_group).size : nil line, column = line_col(text, m.begin(0), line_starts:) = count ? rule. % { count: count } : rule. notes << Note.new( path: path, line: line, column: column, severity: rule.severity, rule: rule.id, category: rule.category, message: , excerpt: matched.gsub(/\s+/, " ").strip, count: count, suggestion: rule.suggestion ) end end notes.sort_by { |n| [n.line, n.column] } end |