@@ -8,6 +8,7 @@ import 'dart:io';
88
99import 'package:meta/meta.dart' ;
1010import 'package:path/path.dart' as p;
11+ import 'package:source_span/source_span.dart' ;
1112
1213import 'license_detection/license_detector.dart' hide License, Range;
1314import 'model.dart' ;
@@ -41,6 +42,11 @@ Future<List<License>> detectLicenseInFile(
4142 return licenses;
4243}
4344
45+ /// Characters and expression that are accepted as non-relevant range gaps,
46+ /// and consecutive [Range] values can be merged if they have only these
47+ /// between.
48+ final _rangeMergeRegexp = RegExp (r'^[\s\.\-\(\)\*]+$' );
49+
4450/// Returns the license(s) detected from the [SPDX-corpus][1] .
4551///
4652/// [1] : https://spdx.org/licenses/
@@ -55,22 +61,52 @@ Future<List<License>> detectLicenseInContent(
5561 return < License > [];
5662 }
5763
64+ List <int > buildCoverages (LicenseMatch match) {
65+ final ranges = < ({int start, int end})> [];
66+ // ignore: invalid_use_of_visible_for_testing_member
67+ for (final token in match.tokens) {
68+ // check to merge into last range
69+ final last = ranges.lastOrNull;
70+ if (last != null ) {
71+ var mergeWithLast = false ;
72+ if (last.end == token.span.start.offset) {
73+ mergeWithLast = true ;
74+ } else {
75+ final textBetween = content.substring (
76+ last.end,
77+ token.span.start.offset,
78+ );
79+ if (_rangeMergeRegexp.matchAsPrefix (textBetween) != null ) {
80+ mergeWithLast = true ;
81+ }
82+ }
83+ if (mergeWithLast) {
84+ ranges[ranges.length - 1 ] = (
85+ start: last.start,
86+ end: token.span.end.offset,
87+ );
88+ continue ;
89+ }
90+ }
91+ // fallback: start a new range
92+ ranges.add ((start: token.span.start.offset, end: token.span.end.offset));
93+ }
94+ return ranges.expand ((e) => [e.start, e.end]).toList ();
95+ }
96+
5897 return licenseResult.matches.map ((e) {
5998 return License (
6099 path: relativePath,
61100 spdxIdentifier: e.identifier,
62101 range: Range (
63- start: Position (
64- offset: e.start.offset,
65- line: e.start.line,
66- column: e.start.column,
67- ),
68- end: Position (
69- offset: e.end.offset,
70- line: e.end.line,
71- column: e.end.column,
72- ),
102+ start: e.start.toPosition (),
103+ end: e.end.toPosition (),
104+ coverages: buildCoverages (e),
73105 ),
74106 );
75107 }).toList ();
76108}
109+
110+ extension on SourceLocation {
111+ Position toPosition () => Position (offset: offset, line: line, column: column);
112+ }
0 commit comments