| // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:dwds/src/config/tool_configuration.dart'; |
| import 'package:dwds/src/debugging/location.dart'; |
| import 'package:dwds/src/debugging/metadata/provider.dart'; |
| import 'package:dwds/src/utilities/dart_uri.dart'; |
| |
| const maxValue = 2147483647; |
| |
| class SkipLists { |
| // Map of script ID to scriptList. |
| final _idToList = <String, List<Map<String, dynamic>>>{}; |
| // Map of url to script ID. |
| final _urlToId = <String, String>{}; |
| final String _root; |
| |
| SkipLists(this._root); |
| |
| /// Initialize any caches. |
| /// |
| /// If [modifiedModuleReport] is not null, only invalidates the caches for the |
| /// modified modules instead. |
| Future<void> initialize( |
| String entrypoint, { |
| ModifiedModuleReport? modifiedModuleReport, |
| }) async { |
| if (modifiedModuleReport != null) { |
| for (final url in _urlToId.keys) { |
| final dartUri = DartUri(url, _root); |
| final serverPath = dartUri.serverPath; |
| final module = await globalToolConfiguration.loadStrategy |
| .moduleForServerPath(entrypoint, serverPath); |
| if (modifiedModuleReport.modifiedModules.contains(module)) { |
| _idToList.remove(_urlToId[url]!); |
| _urlToId.remove(url); |
| } |
| } |
| return; |
| } |
| _idToList.clear(); |
| _urlToId.clear(); |
| } |
| |
| /// Returns a skipList as defined by the Chrome DevTools Protocol. |
| /// |
| /// A `skipList` is an array of `LocationRange`s see: |
| /// https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-stepInto |
| /// |
| /// Can return a cached value. |
| List<Map<String, dynamic>> compute( |
| String scriptId, |
| String url, |
| Set<Location> locations, |
| ) { |
| if (_idToList.containsKey(scriptId)) return _idToList[scriptId]!; |
| |
| final sortedLocations = locations.toList() |
| ..sort((a, b) => a.jsLocation.compareTo(b.jsLocation)); |
| |
| final ranges = <Map<String, dynamic>>[]; |
| var startLine = 0; |
| var startColumn = 0; |
| for (final location in sortedLocations) { |
| var endLine = location.jsLocation.line; |
| var endColumn = location.jsLocation.column; |
| // Stop before the known location. |
| endColumn -= 1; |
| if (endColumn < 0) { |
| endLine -= 1; |
| endColumn = maxValue; |
| } |
| if (endLine > startLine || endColumn > startColumn) { |
| if (endLine >= startLine) { |
| ranges.add( |
| _rangeFor(scriptId, startLine, startColumn, endLine, endColumn), |
| ); |
| } |
| startLine = location.jsLocation.line; |
| startColumn = location.jsLocation.column + 1; |
| } |
| } |
| ranges.add(_rangeFor(scriptId, startLine, startColumn, maxValue, maxValue)); |
| |
| if (url.isNotEmpty) { |
| _idToList[scriptId] = ranges; |
| _urlToId[url] = scriptId; |
| } |
| return ranges; |
| } |
| |
| Map<String, dynamic> _rangeFor( |
| String scriptId, |
| int startLine, |
| int startColumn, |
| int endLine, |
| int endColumn, |
| ) => { |
| 'scriptId': scriptId, |
| 'start': {'lineNumber': startLine, 'columnNumber': startColumn}, |
| 'end': {'lineNumber': endLine, 'columnNumber': endColumn}, |
| }; |
| } |