@@ -32,26 +32,35 @@ More info: https://sass-lang.com/d/slash-div""";
32
32
..addFlag ('pessimistic' ,
33
33
abbr: 'p' ,
34
34
help: "Only migrate / expressions that are unambiguously division." ,
35
- negatable: false );
35
+ negatable: false )
36
+ ..addFlag ('multiplication' ,
37
+ help: 'Migrate / expressions with certain constant divisors to use '
38
+ 'multiplication instead.' ,
39
+ defaultsTo: true );
36
40
37
41
bool get isPessimistic => argResults! ['pessimistic' ] as bool ;
42
+ bool get useMultiplication => argResults! ['multiplication' ] as bool ;
38
43
39
44
@override
40
45
Map <Uri , String > migrateFile (
41
46
ImportCache importCache, Stylesheet stylesheet, Importer importer) {
42
47
var visitor = _DivisionMigrationVisitor (
43
- importCache, this . isPessimistic, migrateDependencies);
48
+ importCache, isPessimistic, useMultiplication , migrateDependencies);
44
49
var result = visitor.run (stylesheet, importer);
45
50
missingDependencies.addAll (visitor.missingDependencies);
46
51
return result;
47
52
}
48
53
}
49
54
55
+ /// The set of constant divisors that should be migrated to multiplication.
56
+ const _allowedDivisors = {2 , 4 , 5 , 8 , 10 , 20 , 40 , 50 , 80 , 100 , 1000 };
57
+
50
58
class _DivisionMigrationVisitor extends MigrationVisitor {
51
59
final bool isPessimistic;
60
+ final bool useMultiplication;
52
61
53
- _DivisionMigrationVisitor (
54
- ImportCache importCache, this .isPessimistic , bool migrateDependencies)
62
+ _DivisionMigrationVisitor (ImportCache importCache, this .isPessimistic,
63
+ this .useMultiplication , bool migrateDependencies)
55
64
: super (importCache, migrateDependencies);
56
65
57
66
/// True when division is allowed by the context the current node is in.
@@ -260,9 +269,8 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
260
269
/// Visits a `/` operation [node] and migrates it to either the `division`
261
270
/// function or the `slash-list` function.
262
271
///
263
- /// Returns true the `/` was migrated to either function call, and false if
264
- /// the `/` is ambiguous and a warning was emitted instead (pessimistic mode
265
- /// only).
272
+ /// Returns true the `/` was migrated to either function call (indicating that
273
+ /// parentheses surrounding this operation should be removed).
266
274
bool _visitSlashOperation (BinaryOperationExpression node) {
267
275
if ((! _isDivisionAllowed && _onlySlash (node)) ||
268
276
_isDefinitelyNotNumber (node)) {
@@ -276,17 +284,17 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
276
284
_visitSlashListArguments (node);
277
285
}
278
286
return true ;
279
- } else if (_expectsNumericResult ||
280
- _isDefinitelyNumber (node) ||
281
- ! isPessimistic) {
287
+ }
288
+ if (_expectsNumericResult || _isDefinitelyNumber (node) || ! isPessimistic) {
282
289
// Definitely division
290
+ _withContext (() => super .visitBinaryOperationExpression (node),
291
+ expectsNumericResult: true );
292
+ if (_tryMultiplication (node)) return false ;
283
293
addPatch (patchBefore (node, "${_builtInPrefix ('math' )}div(" ));
284
294
addPatch (patchAfter (node, ")" ));
285
295
_patchParensIfAny (node.left);
286
296
_patchOperatorToComma (node);
287
297
_patchParensIfAny (node.right);
288
- _withContext (() => super .visitBinaryOperationExpression (node),
289
- expectsNumericResult: true );
290
298
return true ;
291
299
} else {
292
300
emitWarning ("Could not determine whether this is division" , node.span);
@@ -295,6 +303,27 @@ class _DivisionMigrationVisitor extends MigrationVisitor {
295
303
}
296
304
}
297
305
306
+ /// Given a division operation [node] , patches it to use multiplication
307
+ /// instead if the reciprocal of the divisor can be accurately represented as
308
+ /// a decimal.
309
+ ///
310
+ /// Returns true if patched and false otherwise.
311
+ bool _tryMultiplication (BinaryOperationExpression node) {
312
+ if (! useMultiplication) return false ;
313
+ var divisor = node.right;
314
+ if (divisor is ! NumberExpression ) return false ;
315
+ if (divisor.unit != null ) return false ;
316
+ if (! _allowedDivisors.contains (divisor.value)) return false ;
317
+ var operatorSpan = node.left.span
318
+ .extendThroughWhitespace ()
319
+ .end
320
+ .pointSpan ()
321
+ .extendIfMatches ('/' );
322
+ addPatch (Patch (operatorSpan, '*' ));
323
+ addPatch (Patch (node.right.span, '${1 / divisor .value }' ));
324
+ return true ;
325
+ }
326
+
298
327
/// Visits the arguments of a `/` operation that is being converted into a
299
328
/// call to `slash-list` , converting slashes to commas and removing
300
329
/// unnecessary interpolation.
0 commit comments