diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 12e172c74..dd4d9d280 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,7 +4,7 @@ { "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04", - "updateContentCommand": "tool/gh_codespaces/run_setup.sh", + "postCreateCommand": "tool/gh_codespaces/run_setup.sh", "customizations": { "codespaces": { diff --git a/.github/workflows/build_devtool.yml b/.github/workflows/build_devtool.yml index 6263bb692..12e761153 100644 --- a/.github/workflows/build_devtool.yml +++ b/.github/workflows/build_devtool.yml @@ -18,11 +18,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup Flutter SDK - uses: flutter-actions/setup-flutter@v2 - with: - channel: stable - version: 3.29.0 + - name: Setup Flutter and Dart + run: tool/gh_codespaces/install_flutter.sh + + - name: Install project dependencies + run: tool/gh_actions/install_dependencies.sh - name: Run Flutter Test run: tool/gh_actions/devtool/run_devtool_test.sh diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index ef6dbffd0..0f070a93e 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -20,8 +20,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Setup Dart - uses: dart-lang/setup-dart@v1 + - name: Setup Flutter and Dart + run: tool/gh_codespaces/install_flutter.sh - name: Install dependencies run: tool/gh_actions/install_dependencies.sh diff --git a/.github/workflows/general.yml b/.github/workflows/general.yml index f2ed2cf65..0739eea6b 100644 --- a/.github/workflows/general.yml +++ b/.github/workflows/general.yml @@ -32,8 +32,8 @@ jobs: use-quiet-mode: yes # output is too noisy, see https://github.com/gaurav-nelson/github-action-markdown-link-check/issues/121 config-file: .github/configs/mlc_config.json - - name: Setup Dart - uses: dart-lang/setup-dart@v1 + - name: Setup Flutter and Dart + run: tool/gh_codespaces/install_flutter.sh - name: Setup Node uses: actions/setup-node@v4 @@ -85,8 +85,8 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Setup Dart - uses: dart-lang/setup-dart@v1 + - name: Setup Flutter and Dart + run: tool/gh_codespaces/install_flutter.sh - name: Install project dependencies run: tool/gh_actions/install_dependencies.sh @@ -107,11 +107,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup Flutter SDK - uses: flutter-actions/setup-flutter@v2 - with: - channel: stable - version: 3.29.0 + - name: Setup Flutter and Dart + run: tool/gh_codespaces/install_flutter.sh + + - name: Install project dependencies + run: tool/gh_actions/install_dependencies.sh - name: Run Flutter Test run: tool/gh_actions/devtool/run_devtool_test.sh @@ -121,4 +121,3 @@ jobs: - name: Test DevTools Installation run: tool/gh_actions/devtool/test_devtools_install.sh extension/devtools - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6bb9116ce..17940eed8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ Anyone interested in participating in ROHD is more than welcome to help! ## Code of Conduct -ROHD adopts the [Contributor Covenant](https://www.contributor-covenant.org/) v2.1 for the code of conduct. It can be accessed [here](CODE_OF_CONDUCT.md). +ROHD adopts the [Contributor Covenant](https://www.contributor-covenant.org/) v2.1 for the [Code of Conduct](CODE_OF_CONDUCT.md). ## Getting Help @@ -40,7 +40,14 @@ The [ROHD Forum](https://intel.github.io/rohd-website/forum/rohd-forum/) is a pe ### Requirements -You must have [Dart](https://dart.dev/) installed on your system to use ROHD. You can find detailed instructions for how to install Dart here: +ROHD uses a [pub workspace](https://dart.dev/tools/pub/workspaces) for its +active packages. Pub resolves every workspace member together, and some +members declare `sdk: flutter`, so +[Flutter](https://docs.flutter.dev/get-started/install), which includes a +compatible Dart SDK, is required to resolve dependencies anywhere in the +workspace, even for core-ROHD-only development. Once dependencies are +resolved, day-to-day analysis and testing of the core ROHD package uses plain +[Dart](https://dart.dev/get-dart) tooling (`dart analyze`, `dart test`). To run the complete ROHD test suite for development, you need to install [Icarus Verilog](https://steveicarus.github.io/iverilog/). It is used to compare SystemVerilog functionality with the ROHD simulator functionality. Installation instructions are available here: @@ -64,17 +71,87 @@ The below button will allow you to create a GitHub Codespace with ROHD already c [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=409325108) -### Cloning and Running the Tests +### Core ROHD Setup and Validation -Once requirements are installed, you can clone and run the test suite: +The root [`pubspec.yaml`](pubspec.yaml) `workspace:` list defines the active +packages. + +For core ROHD development, clone the repository and resolve the workspace with +Flutter, then use Dart for everyday analysis and testing: ```shell git clone https://github.com/intel/rohd.git cd rohd -dart pub get + +# Resolve the workspace dependencies. Flutter is required because pub +# resolves every workspace member together and some members declare +# `sdk: flutter`. +flutter pub get + +# Open Folder: open only the root ROHD package in VSCode. +code . + +# Analyze and test the core package. +dart analyze dart test ``` +As a local convenience, `tool/gh_actions/install_dependencies.sh` performs the +same dependency-resolution step and requires Flutter to be installed. + +In VSCode, this is equivalent to **File > Open Folder...** and selecting the +repository root. Use this mode when working only on the core ROHD package. + +### Full Workspace Setup, VSCode, and Validation + +For DevTools development or repository-wide validation, use Flutter to resolve +the mixed Dart/Flutter workspace. The checked-in +[`rohd-multipackage.code-workspace`](rohd-multipackage.code-workspace) is +generated from the root `workspace:` list, so it can be opened immediately +after cloning: + +```shell +# Open Workspace from File: open every active member in its own package context. +code rohd-multipackage.code-workspace + +# Resolve every workspace member from a terminal at the repository root. +flutter pub get +``` + +In VSCode, this is equivalent to **File > Open Workspace from File...** and +selecting `rohd-multipackage.code-workspace`. Do not use **Open Folder** for +all-package work: the generated multi-root workspace gives each active package +its own Dart or Flutter package context. + +After opening the generated workspace, select the Flutter-bundled Dart SDK if +the Dart extension does not select it automatically. The generated workspace +includes every active member, so Dart and Flutter analysis can resolve package +boundaries correctly. + +Run repository-wide validation from the repository root: + +```shell +# Analyze every active workspace member. +dart run tool/workspace.dart analyze + +# Run native non-benchmark tests, using flutter test for Flutter packages. +dart run tool/workspace.dart test + +# Run non-benchmark Node.js tests for each non-Flutter workspace member. +dart run tool/workspace.dart test-node +``` + +The Node.js pass can take substantially longer than native tests because Dart +compiles browser test bundles. Run an individual package or test file directly +while iterating, then use the workspace commands before submitting a change. +The workspace test commands exclude the long-running `benchmark` tag; run the +[benchmark suite](benchmark/README.md) separately when measuring performance. + +When adding or removing an active package, update the root `workspace:` list, +regenerate `rohd-multipackage.code-workspace` with +`dart run tool/workspace.dart vscode`, and commit the generated workspace file +alongside the `pubspec.yaml` change. Then run `flutter pub get`. + ## How to Contribute ### Reporting Vulnerabilities @@ -109,7 +186,13 @@ Take a look around the issues on the repo and see if there's any you'd like to t If you have a change that you have implemented and would like to contribute, you can open a pull request. Please try to make sure you have implemented tests covering the changes, if applicable. Smaller, simpler pull requests are easier to review. -Be sure to run the test suite (`dart test`) before asking for your code to be merged. You may also locally generate API documentation (`dart doc`) to make sure it looks right and doesn't have any errors. You should use the dart formatter on all code (`dart format .`), and may prefer to have it automatically format on every file save. If you are using VSCode with the Dart extension, then consider using the recommended settings: +Be sure to run the applicable workspace validation commands described above +before asking for your code to be merged. You may also locally generate API +documentation (`dart doc`) to make sure it looks right and doesn't have any +errors. You should use the dart formatter on all code (`dart format .`), and +may prefer to have it automatically format on every file save. If you are using +VSCode with the Dart extension, then consider using the recommended settings: + **Tests must pass, documentation must generate, and the formatter must be run on every pull request or the automated GitHub Actions flow will fail.** @@ -124,7 +207,7 @@ Please include the SPDX tag near the top of any new files you create: Here is an example of a recommended file header template: ```dart -// Copyright (C) 2021-2023 Intel Corporation +// Copyright (C) 2021-2026 Intel Corporation // SPDX-License-Identifier: BSD-3-Clause // // example.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index b4e2c5480..37e6cd6c3 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -8,10 +8,13 @@ analyzer: strict-raw-types: true exclude: - doc/tutorials/chapter_9/rohd_vf_example - - packages/rohd_hierarchy - - packages/rohd_waveform - - rohd_devtools_extension - - rohd_extension + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** # keep up to date, matching https://dart.dev/tools/linter-rules/all # some lints are not yet available, so disabled and marked with [not currently recognized] diff --git a/lib/src/collections/iterable_removable_queue.dart b/lib/src/collections/iterable_removable_queue.dart index 06b7981e7..c6042da5d 100644 --- a/lib/src/collections/iterable_removable_queue.dart +++ b/lib/src/collections/iterable_removable_queue.dart @@ -58,7 +58,7 @@ class IterableRemovableQueue { return; } - if (_first == _last && _removeWhere!(_first!.item)) { + if (_first == _last && _removeWhere(_first!.item)) { // if size is 1 and its removable, then we can just clear the queue and be // done with it clear(); @@ -77,7 +77,7 @@ class IterableRemovableQueue { } while (_patrol != null) { - if (_removeWhere!(_patrol!.item)) { + if (_removeWhere(_patrol!.item)) { assert(size > 0, 'Should not be removing if size is already 0.'); if (_patrol == _first && _first == _last) { @@ -118,7 +118,7 @@ class IterableRemovableQueue { /// Also may remove items from the queue if they are indicated by /// [_removeWhere]. void add(T item) { - if (_removeWhere != null && _removeWhere!(item)) { + if (_removeWhere != null && _removeWhere(item)) { // If the item should be removed, we don't add it. return; } @@ -184,7 +184,7 @@ class IterableRemovableQueue { var element = _first; _IterableRemovableElement? previous; while (element != null) { - if (_removeWhere != null && _removeWhere!(element.item)) { + if (_removeWhere != null && _removeWhere(element.item)) { assert(size > 0, 'Should not be removing if size is already 0.'); previous?.next = element.next; diff --git a/lib/src/modules/conditionals/flop.dart b/lib/src/modules/conditionals/flop.dart index cd9aa8750..c647479c5 100644 --- a/lib/src/modules/conditionals/flop.dart +++ b/lib/src/modules/conditionals/flop.dart @@ -146,7 +146,7 @@ class FlipFlop extends Module with SystemVerilog { var contents = [q < _d]; if (_en != null) { - contents = [If(_en!, then: contents)]; + contents = [If(_en, then: contents)]; } Sequential( diff --git a/packages/rohd_hierarchy/analysis_options.yaml b/packages/rohd_hierarchy/analysis_options.yaml deleted file mode 100644 index f04c6cf0f..000000000 --- a/packages/rohd_hierarchy/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options.yaml diff --git a/packages/rohd_hierarchy/pubspec.yaml b/packages/rohd_hierarchy/pubspec.yaml index c75cd3d34..65b6de369 100644 --- a/packages/rohd_hierarchy/pubspec.yaml +++ b/packages/rohd_hierarchy/pubspec.yaml @@ -7,8 +7,10 @@ issue_tracker: https://github.com/intel/rohd/issues publish_to: none +resolution: workspace + environment: - sdk: '>=3.0.0 <4.0.0' + sdk: ^3.6.0 dependencies: collection: ^1.15.0 diff --git a/packages/rohd_hierarchy/test/filter_bank_integration_test.dart b/packages/rohd_hierarchy/test/filter_bank_integration_test.dart index 6a8fe173a..b7778fd54 100644 --- a/packages/rohd_hierarchy/test/filter_bank_integration_test.dart +++ b/packages/rohd_hierarchy/test/filter_bank_integration_test.dart @@ -8,6 +8,9 @@ // 2026 April // Author: Desmond Kirkpatrick +@TestOn('vm') +library; + import 'dart:io'; import 'package:rohd_hierarchy/rohd_hierarchy.dart'; @@ -172,19 +175,6 @@ void main() { expect(outputs.map((s) => s.name), contains('done')); }); - test(r'isPrimitiveType is true for $-prefixed types', () { - expect(HierarchyOccurrence.isPrimitiveType(r'$mux'), isTrue); - expect(HierarchyOccurrence.isPrimitiveType(r'$and'), isTrue); - }); - - test(r'isPrimitiveType is false for non-$-prefixed types', () { - expect(HierarchyOccurrence.isPrimitiveType('FilterBank'), isFalse); - }); - - test('isPrimitiveType is false for empty string', () { - expect(HierarchyOccurrence.isPrimitiveType(''), isFalse); - }); - test('isPrimitiveCell reflects isPrimitive field and type', () { // A node marked isPrimitive=true final primCell = service.root.children.firstWhere((c) => c.isPrimitive); @@ -293,56 +283,6 @@ void main() { // At least ch0_1 and ch1_1 have children expect(withSlash, isNotEmpty); }); - - test('hasRegexChars is false for plain text', () { - expect(HierarchyService.hasRegexChars('clk'), isFalse); - }); - - test('hasRegexChars detects * glob', () { - expect(HierarchyService.hasRegexChars('c*'), isTrue); - }); - - test('hasRegexChars detects ? glob', () { - expect(HierarchyService.hasRegexChars('cl?'), isTrue); - }); - - test('hasRegexChars detects character class', () { - expect(HierarchyService.hasRegexChars('[a-z]'), isTrue); - }); - - test('hasRegexChars detects group alternation', () { - expect(HierarchyService.hasRegexChars('(a|b)'), isTrue); - }); - - test('hasRegexChars detects + quantifier', () { - expect(HierarchyService.hasRegexChars('a+'), isTrue); - }); - - test('longestCommonPrefix finds shared prefix', () { - expect( - HierarchyService.longestCommonPrefix([ - 'FilterBank/ch0', - 'FilterBank/ch1', - ]), - 'FilterBank/ch', - ); - }); - - test('longestCommonPrefix returns null for empty list', () { - expect(HierarchyService.longestCommonPrefix([]), isNull); - }); - - test('longestCommonPrefix returns null for no common prefix', () { - expect(HierarchyService.longestCommonPrefix(['abc', 'xyz']), isNull); - }); - - test('longestCommonPrefix is case-sensitive', () { - final prefix = HierarchyService.longestCommonPrefix([ - 'Filter/abc', - 'Filter/abd', - ]); - expect(prefix, 'Filter/ab'); - }); }); // ─────────────── HierarchySearchController ─────────────── @@ -456,18 +396,6 @@ void main() { }); }); - // ─────────────── BaseHierarchyAdapter edge case ─────────────── - // The real uninitialized-root StateError test lives in - // coverage_gaps_test.dart. Here we just verify fromTree works. - - group('BaseHierarchyAdapter — fromTree produces usable root', () { - test('fromTree immediately sets root', () { - final tree = HierarchyOccurrence(name: 'r'); - final svc = BaseHierarchyAdapter.fromTree(tree); - expect(svc.root.name, 'r'); - }); - }); - // ─────────────── Multiple instantiation (dedup) ─────────────── group('Multiple instantiation — FilterChannel dedup', () { diff --git a/packages/rohd_hierarchy/test/hierarchy_model_test.dart b/packages/rohd_hierarchy/test/hierarchy_model_test.dart new file mode 100644 index 000000000..9ef3485c6 --- /dev/null +++ b/packages/rohd_hierarchy/test/hierarchy_model_test.dart @@ -0,0 +1,71 @@ +// Copyright (C) 2026 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// hierarchy_model_test.dart +// Cross-platform hierarchy model and utility tests. +// +// 2026 August +// Author: Desmond Kirkpatrick + +import 'package:rohd_hierarchy/rohd_hierarchy.dart'; +import 'package:test/test.dart'; + +void main() { + group('HierarchyOccurrence primitive detection', () { + test(r'isPrimitiveType is true for $-prefixed types', () { + expect(HierarchyOccurrence.isPrimitiveType(r'$mux'), isTrue); + expect(HierarchyOccurrence.isPrimitiveType(r'$and'), isTrue); + }); + + test(r'isPrimitiveType is false for non-$-prefixed types', () { + expect(HierarchyOccurrence.isPrimitiveType('FilterBank'), isFalse); + }); + + test('isPrimitiveType is false for empty string', () { + expect(HierarchyOccurrence.isPrimitiveType(''), isFalse); + }); + }); + + group('HierarchyService search utilities', () { + test('hasRegexChars is false for plain text', () { + expect(HierarchyService.hasRegexChars('clk'), isFalse); + }); + + test('hasRegexChars detects glob and regex syntax', () { + for (final query in ['c*', 'cl?', '[a-z]', '(a|b)', 'a+']) { + expect(HierarchyService.hasRegexChars(query), isTrue); + } + }); + + test('longestCommonPrefix finds shared prefix', () { + expect( + HierarchyService.longestCommonPrefix([ + 'FilterBank/ch0', + 'FilterBank/ch1', + ]), + 'FilterBank/ch', + ); + }); + + test('longestCommonPrefix returns null without a shared prefix', () { + expect(HierarchyService.longestCommonPrefix([]), isNull); + expect(HierarchyService.longestCommonPrefix(['abc', 'xyz']), isNull); + }); + + test('longestCommonPrefix is case-sensitive', () { + expect( + HierarchyService.longestCommonPrefix(['Filter/abc', 'Filter/abd']), + 'Filter/ab', + ); + }); + }); + + group('BaseHierarchyAdapter', () { + test('fromTree immediately sets root', () { + final service = + BaseHierarchyAdapter.fromTree(HierarchyOccurrence(name: 'r')); + + expect(service.root.name, 'r'); + }); + }); +} diff --git a/packages/rohd_waveform/analysis_options.yaml b/packages/rohd_waveform/analysis_options.yaml deleted file mode 100644 index f04c6cf0f..000000000 --- a/packages/rohd_waveform/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options.yaml diff --git a/packages/rohd_waveform/lib/src/waveform_api.dart b/packages/rohd_waveform/lib/src/waveform_api.dart index 8daa6ffa7..65962c6a3 100644 --- a/packages/rohd_waveform/lib/src/waveform_api.dart +++ b/packages/rohd_waveform/lib/src/waveform_api.dart @@ -33,14 +33,13 @@ abstract class SignalWaveformApi { required List signalIds, int? startTime, int? endTime, - }) async { - // Base implementation: must be overridden by concrete implementations - // Port no longer contains data - implementations must fetch from - // their source. - throw UnimplementedError( - 'getWaveformData must be implemented by subclasses', - ); - } + }) => + // Base implementation: must be overridden by concrete implementations + // Port no longer contains data - implementations must fetch from + // their source. + Future.error(UnimplementedError( + 'getWaveformData must be implemented by subclasses', + )); /// Streams waveform data incrementally for specific signals. /// @@ -71,12 +70,11 @@ abstract class SignalWaveformApi { /// /// Returns a [Future] that completes with the current time as an integer, /// or null if the time cannot be determined. - Future getCurrentTime() async { - // Default implementation: must be overridden by concrete implementations - throw UnimplementedError( - 'getCurrentTime must be implemented by subclasses', - ); - } + Future getCurrentTime() => + // Default implementation: must be overridden by concrete implementations + Future.error(UnimplementedError( + 'getCurrentTime must be implemented by subclasses', + )); /// Retrieves a snapshot of all signal values at the given [time]. /// @@ -87,9 +85,9 @@ abstract class SignalWaveformApi { /// - `direction`: signal direction (if port) /// /// Returns null if the snapshot could not be retrieved. - Future>?> getSnapshot(int time) async { - throw UnimplementedError('getSnapshot must be implemented by subclasses'); - } + Future>?> getSnapshot(int time) => + Future.error( + UnimplementedError('getSnapshot must be implemented by subclasses')); /// Proactively expand all slim module definitions so the client-side /// evaluator can compute internal signals immediately. @@ -97,5 +95,5 @@ abstract class SignalWaveformApi { /// Called when the user enables "internal signals" in the wave viewer. /// Default implementation is a no-op; overridden by implementations /// that support client-side synthesis. - Future expandAllSlimModules() async {} + Future expandAllSlimModules() => Future.value(); } diff --git a/packages/rohd_waveform/lib/src/waveform_repository.dart b/packages/rohd_waveform/lib/src/waveform_repository.dart index ba24cf12d..27d04f006 100644 --- a/packages/rohd_waveform/lib/src/waveform_repository.dart +++ b/packages/rohd_waveform/lib/src/waveform_repository.dart @@ -152,10 +152,8 @@ class SignalWaveformRepository { } /// Get the current simulation time from the waveform API. - Future getCurrentTime() async { - await _ensureReady(); - return _signalWaveformApi.getCurrentTime(); - } + Future getCurrentTime() => + _ensureReady().then((_) => _signalWaveformApi.getCurrentTime()); /// Retrieves waveform data for specific signals. /// diff --git a/packages/rohd_waveform/pubspec.yaml b/packages/rohd_waveform/pubspec.yaml index 9ec84a97e..70069d0eb 100644 --- a/packages/rohd_waveform/pubspec.yaml +++ b/packages/rohd_waveform/pubspec.yaml @@ -7,8 +7,10 @@ issue_tracker: https://github.com/intel/rohd/issues publish_to: none +resolution: workspace + environment: - sdk: '>=3.0.0 <4.0.0' + sdk: ^3.6.0 dependencies: collection: ^1.15.0 @@ -16,9 +18,5 @@ dependencies: rohd_hierarchy: ^0.1.0 dev_dependencies: - lints: ^3.0.0 + lints: ^5.0.0 test: ^1.17.3 - -dependency_overrides: - rohd_hierarchy: - path: ../rohd_hierarchy diff --git a/pubspec.yaml b/pubspec.yaml index 42949a933..d4acd9649 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,14 @@ issue_tracker: https://github.com/intel/rohd/issues documentation: https://intel.github.io/rohd-website/docs/sample-example/ environment: - sdk: '>=3.0.0 <4.0.0' + sdk: ^3.6.0 + +workspace: + - packages/rohd_hierarchy + - packages/rohd_waveform + - rohd_devtools_extension + - rohd_devtools_extension/packages/rohd_devtools_widgets + - rohd_extension/vscode dependencies: collection: ^1.15.0 diff --git a/rohd-multipackage.code-workspace b/rohd-multipackage.code-workspace index 3c0ed09fc..3b6e3aaa8 100644 --- a/rohd-multipackage.code-workspace +++ b/rohd-multipackage.code-workspace @@ -1,6 +1,3 @@ -// Opens each nested Dart package as a workspace root so the Dart analyzer uses -// its own package context and reports fewer cross-package Problems. Open this -// file in VS Code with File > Open Workspace from File... . { "folders": [ { @@ -22,6 +19,10 @@ { "name": "rohd_devtools_widgets", "path": "rohd_devtools_extension/packages/rohd_devtools_widgets" + }, + { + "name": "vscode", + "path": "rohd_extension/vscode" } ], "settings": { diff --git a/rohd_devtools_extension/analysis_options.yaml b/rohd_devtools_extension/analysis_options.yaml index f82d6cc51..d770e9ca6 100644 --- a/rohd_devtools_extension/analysis_options.yaml +++ b/rohd_devtools_extension/analysis_options.yaml @@ -1,250 +1,10 @@ -# Lints set up with some guidance from here: -# https://rydmike.com/blog_flutter_linting.html - analyzer: - language: - strict-casts: true - strict-inference: true - strict-raw-types: true - -# keep up to date, matching https://dart.dev/tools/linter-rules/all -# some lints are not yet available, so disabled and marked with [not currently recognized] -linter: - rules: - - always_declare_return_types - - always_put_control_body_on_new_line - - always_put_required_named_parameters_first - # - always_specify_types - - always_use_package_imports - - annotate_overrides - - annotate_redeclares - # - avoid_annotating_with_dynamic - - avoid_bool_literals_in_conditional_expressions - - avoid_catches_without_on_clauses - - avoid_catching_errors - # - avoid_classes_with_only_static_members - - avoid_double_and_int_checks - - avoid_dynamic_calls - - avoid_empty_else - - avoid_equals_and_hash_code_on_mutable_classes - - avoid_escaping_inner_quotes - - avoid_field_initializers_in_const_classes - - avoid_final_parameters - - avoid_function_literals_in_foreach_calls - - avoid_futureor_void - - avoid_implementing_value_types - - avoid_init_to_null - - avoid_js_rounded_ints - - avoid_multiple_declarations_per_line - - avoid_null_checks_in_equality_operators - - avoid_positional_boolean_parameters - - avoid_print - - avoid_private_typedef_functions - - avoid_redundant_argument_values - - avoid_relative_lib_imports - - avoid_renaming_method_parameters - - avoid_return_types_on_setters - - avoid_returning_null_for_void - - avoid_returning_this - - avoid_setters_without_getters - - avoid_shadowing_type_parameters - - avoid_single_cascade_in_expression_statements - - avoid_slow_async_io - - avoid_type_to_string - - avoid_types_as_parameter_names - - avoid_types_on_closure_parameters - - avoid_unnecessary_containers - - avoid_unused_constructor_parameters - - avoid_void_async - - avoid_web_libraries_in_flutter - - await_only_futures - - camel_case_extensions - - camel_case_types - - cancel_subscriptions - - cascade_invocations - - cast_nullable_to_non_nullable - - close_sinks - - collection_methods_unrelated_type - - combinators_ordering - - comment_references - - conditional_uri_does_not_exist - - constant_identifier_names - - control_flow_in_finally - - curly_braces_in_flow_control_structures - - dangling_library_doc_comments - - depend_on_referenced_packages - - deprecated_consistency - - deprecated_member_use_from_same_package - - diagnostic_describe_all_properties - - directives_ordering - - discarded_futures - - do_not_use_environment - # - document_ignores - - empty_catches - - empty_constructor_bodies - - empty_statements - - eol_at_end_of_file - - exhaustive_cases - - file_names - - flutter_style_todos - - hash_and_equals - - implementation_imports - - implicit_call_tearoffs - - implicit_reopen - - invalid_case_patterns - - invalid_runtime_check_with_js_interop_types - - join_return_with_assignment - - leading_newlines_in_multiline_strings - - library_annotations - - library_names - - library_prefixes - - library_private_types_in_public_api - - lines_longer_than_80_chars - - literal_only_boolean_expressions - - matching_super_parameters - - missing_code_block_language_in_doc_comment - - missing_whitespace_between_adjacent_strings - - no_adjacent_strings_in_list - - no_default_cases - - no_duplicate_case_values - - no_leading_underscores_for_library_prefixes - - no_leading_underscores_for_local_identifiers - - no_literal_bool_comparisons - - no_logic_in_create_state - - no_runtimeType_toString - - no_self_assignments - - no_wildcard_variable_uses - - non_constant_identifier_names - - noop_primitive_operations - - null_check_on_nullable_type_parameter - - null_closures - - omit_local_variable_types - - omit_obvious_local_variable_types - # - omit_obvious_property_types - - one_member_abstracts - - only_throw_errors - - overridden_fields - - package_names - - package_prefixed_library_names - # parameter_assignments - disabled; ROHD idiomatically reassigns - # constructor parameters via addInput/addOutput. - # - parameter_assignments - - prefer_adjacent_string_concatenation - - prefer_asserts_in_initializer_lists - - prefer_asserts_with_message - - prefer_collection_literals - - prefer_conditional_assignment - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_const_literals_to_create_immutables - - prefer_constructors_over_static_methods - - prefer_contains - # - prefer_double_quotes - - prefer_expression_function_bodies - - prefer_final_fields - - prefer_final_in_for_each - - prefer_final_locals - # - prefer_final_parameters - - prefer_for_elements_to_map_fromIterable - - prefer_foreach - - prefer_function_declarations_over_variables - - prefer_generic_function_type_aliases - - prefer_if_elements_to_conditional_expressions - - prefer_if_null_operators - - prefer_initializing_formals - - prefer_inlined_adds - - prefer_int_literals - - prefer_interpolation_to_compose_strings - - prefer_is_empty - - prefer_is_not_empty - - prefer_is_not_operator - - prefer_iterable_whereType - - prefer_mixin - - prefer_null_aware_method_calls - - prefer_null_aware_operators - # - prefer_relative_imports - - prefer_single_quotes - - prefer_spread_collections - - prefer_typing_uninitialized_variables - - prefer_void_to_null - - provide_deprecation_message - - public_member_api_docs - - recursive_getters - # - require_trailing_commas - - secure_pubspec_urls - - sized_box_for_whitespace - - sized_box_shrink_expand - - slash_for_doc_comments - - sort_child_properties_last - # - sort_constructors_first - - sort_pub_dependencies - - sort_unnamed_constructors_first - # conflicts with omit_obvious_local_variable_types - # - specify_nonobvious_local_variable_types - # - specify_nonobvious_property_types - - strict_top_level_inference - # - switch_on_type - - test_types_in_equals - - throw_in_finally - - tighten_type_of_initializing_formals - - type_annotate_public_apis - - type_init_formals - - type_literal_in_constant_pattern - - unawaited_futures - - unintended_html_in_doc_comment - # - unnecessary_async - - unnecessary_await_in_return - - unnecessary_brace_in_string_interps - - unnecessary_breaks - - unnecessary_const - - unnecessary_constructor_name - # - unnecessary_final - - unnecessary_getters_setters - - unnecessary_ignore - - unnecessary_lambdas - - unnecessary_late - - unnecessary_library_directive - - unnecessary_library_name - - unnecessary_new - - unnecessary_null_aware_assignments - - unnecessary_null_aware_operator_on_extension_on_nullable - - unnecessary_null_checks - - unnecessary_null_in_if_null_operators - - unnecessary_nullable_for_final_variable_declarations - - unnecessary_overrides - - unnecessary_parenthesis - - unnecessary_raw_strings - # - unnecessary_statements - - unnecessary_string_escapes - - unnecessary_string_interpolations - - unnecessary_this - - unnecessary_to_list_in_spreads - # - unnecessary_unawaited - - unnecessary_underscores - # - unreachable_from_main - - unrelated_type_equality_checks - # - unsafe_variance - - use_build_context_synchronously - - use_colored_box - - use_decorated_box - - use_enums - - use_full_hex_values_for_flutter_colors - - use_function_type_syntax_for_parameters - - use_if_null_to_convert_nulls_to_bools - - use_is_even_rather_than_modulo - - use_key_in_widget_constructors - - use_late_for_private_fields_and_variables - - use_named_constants - - use_null_aware_elements - - use_raw_strings - - use_rethrow_when_possible - - use_setters_to_change_properties - - use_string_buffers - - use_string_in_part_of_directives - - use_super_parameters - - use_test_throws_matchers - - use_to_and_as_if_applicable - - use_truncating_division - - valid_regexps - - void_checks \ No newline at end of file + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** +include: ../analysis_options.yaml diff --git a/rohd_devtools_extension/lib/main.dart b/rohd_devtools_extension/lib/main.dart index 278c04212..98336fa23 100644 --- a/rohd_devtools_extension/lib/main.dart +++ b/rohd_devtools_extension/lib/main.dart @@ -14,8 +14,8 @@ import 'package:devtools_app_shared/utils.dart'; import 'package:devtools_extensions/api.dart'; import 'package:devtools_extensions/devtools_extensions.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/const/app_theme.dart'; import 'package:rohd_devtools_extension/rohd_devtools/view/rohd_devtools_page.dart'; import 'package:rohd_devtools_extension/rohd_devtools_observer.dart'; diff --git a/rohd_devtools_extension/lib/main_standalone.dart b/rohd_devtools_extension/lib/main_standalone.dart index f42e8c4f6..565811e66 100644 --- a/rohd_devtools_extension/lib/main_standalone.dart +++ b/rohd_devtools_extension/lib/main_standalone.dart @@ -14,8 +14,8 @@ // Author: Desmond Kirkpatrick import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/services/services.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/standalone_app_shell.dart'; diff --git a/rohd_devtools_extension/lib/rohd_devtools/const/app_theme.dart b/rohd_devtools_extension/lib/rohd_devtools/const/app_theme.dart index 2c6df9e89..fac67eab0 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/const/app_theme.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/const/app_theme.dart @@ -7,7 +7,7 @@ // 2026 January // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; const _fontFallback = ['Noto Color Emoji']; diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/details_help_button.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/details_help_button.dart index 4d5278be6..3d9753ef6 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/details_help_button.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/details_help_button.dart @@ -11,7 +11,7 @@ // Author: Desmond Kirkpatrick import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/devtool_appbar.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/devtool_appbar.dart index 53fc54e6a..83898f5e0 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/devtool_appbar.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/devtool_appbar.dart @@ -8,8 +8,8 @@ // Author: Yao Jing Quek import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/cubits.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/devtools_help_button.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/platform_icon.dart'; diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/devtools_connection_host.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/devtools_connection_host.dart index de4b5d010..259b355c5 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/devtools_connection_host.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/devtools_connection_host.dart @@ -12,7 +12,7 @@ import 'dart:async'; import 'package:dtd/dtd.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/dtd_vm_service_info.dart'; import 'package:rohd_devtools_extension/rohd_devtools/services/services.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/ui.dart'; @@ -1084,7 +1084,7 @@ abstract class DevToolsConnectionHostState /// /// Connects to DTD, calls getVmServices(), returns the list. /// Also probes for registered services (new DTD 4.0 API). - Future> discoverVmServices(String dtdUri) async => + Future> discoverVmServices(String dtdUri) => discoverVmServicesViaDtd( dtdUri, onRegisteredServices: (serviceNames) { diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/devtools_help_button.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/devtools_help_button.dart index 2e7ff4c16..ca698bc0a 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/devtools_help_button.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/devtools_help_button.dart @@ -11,7 +11,7 @@ // Author: Desmond Kirkpatrick import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/module_tree_card.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/module_tree_card.dart index 9cf9e0741..ec9d0a4a4 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/module_tree_card.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/module_tree_card.dart @@ -8,10 +8,9 @@ // Author: Yao Jing Quek import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_simple_treeview/flutter_simple_treeview.dart'; - +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/cubits.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/tree_model.dart'; import 'package:rohd_devtools_extension/rohd_devtools/services/services.dart'; diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/module_tree_details_navbar.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/module_tree_details_navbar.dart index 183684ce2..8883b2c8f 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/module_tree_details_navbar.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/module_tree_details_navbar.dart @@ -8,8 +8,8 @@ // Author: Yao Jing Quek import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/cubits.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/details_help_button.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/platform_icon.dart'; diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/platform_icon.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/platform_icon.dart index 821c515d9..bbf908e9e 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/platform_icon.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/platform_icon.dart @@ -10,7 +10,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; /// A widget that renders either a Material Icon or emoji text based on /// platform emoji font availability. diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/schematic_icon.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/schematic_icon.dart index b8a5c69df..d71f7b14c 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/schematic_icon.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/schematic_icon.dart @@ -9,7 +9,7 @@ // Author: Desmond Kirkpatrick import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; /// A custom-painted icon showing three colored rectangles connected /// by orthogonal (right-angle) wires — a miniature schematic diagram. diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/signal_details_card.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/signal_details_card.dart index c3972ff40..d1eaac830 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/signal_details_card.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/signal_details_card.dart @@ -10,7 +10,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/snapshot_cubit.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/tree_model.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/details_help_button.dart'; diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/signal_table.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/signal_table.dart index 140da744b..308d8a121 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/signal_table.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/signal_table.dart @@ -8,7 +8,7 @@ // Author: Yao Jing Quek import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/snapshot_cubit.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/signal_model.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/tree_model.dart'; diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/signal_table_text_field.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/signal_table_text_field.dart index 23a4e480b..1f23f2175 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/signal_table_text_field.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/signal_table_text_field.dart @@ -8,7 +8,7 @@ // Author: Yao Jing Quek import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; /// A text field widget for filtering signals in the signal table. /// diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/standalone_app_shell.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/standalone_app_shell.dart index 62cbe2ed2..2c1a04581 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/standalone_app_shell.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/standalone_app_shell.dart @@ -10,8 +10,8 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/const/app_theme.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/cubits.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/dtd_vm_service_info.dart'; diff --git a/rohd_devtools_extension/lib/rohd_devtools/ui/vm_connection_form.dart b/rohd_devtools_extension/lib/rohd_devtools/ui/vm_connection_form.dart index 421bf5433..6d51e52e4 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/ui/vm_connection_form.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/ui/vm_connection_form.dart @@ -10,7 +10,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/platform_icon.dart'; /// Describes a single VM service discovered via DTD. diff --git a/rohd_devtools_extension/lib/rohd_devtools/view/rohd_devtools_page.dart b/rohd_devtools_extension/lib/rohd_devtools/view/rohd_devtools_page.dart index c999cb61b..a17923cd2 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/view/rohd_devtools_page.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/view/rohd_devtools_page.dart @@ -8,8 +8,8 @@ // Author: Roberto Torres import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/const/app_theme.dart'; import 'package:rohd_devtools_extension/rohd_devtools/rohd_devtools.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/ui.dart'; diff --git a/rohd_devtools_extension/lib/rohd_devtools/view/tree_structure_page.dart b/rohd_devtools_extension/lib/rohd_devtools/view/tree_structure_page.dart index 83dca6df7..8369557ff 100644 --- a/rohd_devtools_extension/lib/rohd_devtools/view/tree_structure_page.dart +++ b/rohd_devtools_extension/lib/rohd_devtools/view/tree_structure_page.dart @@ -8,8 +8,8 @@ // Author: Yao Jing Quek import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/cubits.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/analysis_options.yaml b/rohd_devtools_extension/packages/rohd_devtools_widgets/analysis_options.yaml index 572dd239d..0d5eaab35 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/analysis_options.yaml +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/analysis_options.yaml @@ -1 +1,10 @@ -include: package:lints/recommended.yaml +analyzer: + exclude: + - build/** + - android/** + - ios/** + - web/** + - windows/** + - macos/** + - linux/** +include: ../../../analysis_options.yaml diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/rohd_devtools_widgets.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/rohd_devtools_widgets.dart index 452567fae..799f4e945 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/rohd_devtools_widgets.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/rohd_devtools_widgets.dart @@ -8,34 +8,27 @@ // 2026 April // Author: Desmond Kirkpatrick -// Help -export 'src/markdown_help_button.dart'; - // Overlay export 'src/app_bar_overlay.dart'; - +// Shared "Expand Bits" / "Define Bit Fields" popup-menu helpers +export 'src/bit_expansion_menu.dart'; +// Bit-field parsing, formatting, and dialog utilities +export 'src/bit_field_utils.dart'; // PNG export export 'src/capture_boundary.dart'; -export 'src/export_button.dart'; -export 'src/export_toast.dart'; -export 'src/save_png_stub.dart' - if (dart.library.io) 'src/save_png_native.dart' - if (dart.library.js_interop) 'src/save_png_web.dart'; - -// Cross-probing -export 'src/cross_probe_service.dart'; export 'src/cross_probe_button.dart'; export 'src/cross_probe_menu.dart'; - +// Cross-probing +export 'src/cross_probe_service.dart'; +export 'src/export_button.dart'; +export 'src/export_toast.dart'; // Logic type utilities export 'src/logic_type_utils.dart'; - -// Bit-field parsing, formatting, and dialog utilities -export 'src/bit_field_utils.dart'; - -// Shared "Expand Bits" / "Define Bit Fields" popup-menu helpers -export 'src/bit_expansion_menu.dart'; - +// Help +export 'src/markdown_help_button.dart'; +export 'src/rohd_extension_client.dart'; // ROHD extension client export 'src/rohd_extension_status.dart'; -export 'src/rohd_extension_client.dart'; +export 'src/save_png_stub.dart' + if (dart.library.io) 'src/save_png_native.dart' + if (dart.library.js_interop) 'src/save_png_web.dart'; diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/app_bar_overlay.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/app_bar_overlay.dart index ba210d5b2..ce2c8b992 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/app_bar_overlay.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/app_bar_overlay.dart @@ -14,7 +14,8 @@ // 2026 April // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; +import 'package:material_ui/material_ui.dart'; /// Wraps a [body] widget and an [appBar] widget, where the AppBar /// auto-hides by sliding up when [autoHide] is true. @@ -41,10 +42,11 @@ class AppBarOverlay extends StatefulWidget { /// Duration of the slide animation. final Duration animationDuration; + /// Creates an [AppBarOverlay]. const AppBarOverlay({ - super.key, required this.appBar, required this.body, + super.key, this.autoHide = false, this.triggerHeight = 12, this.panelOpacity = 0.92, @@ -53,6 +55,18 @@ class AppBarOverlay extends StatefulWidget { @override State createState() => _AppBarOverlayState(); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('autoHide', autoHide)) + ..add(DoubleProperty('triggerHeight', triggerHeight)) + ..add(DoubleProperty('panelOpacity', panelOpacity)) + ..add( + DiagnosticsProperty('animationDuration', animationDuration), + ); + } } class _AppBarOverlayState extends State @@ -105,7 +119,9 @@ class _AppBarOverlayState extends State } void _hide() { - if (!widget.autoHide) return; + if (!widget.autoHide) { + return; + } _controller.reverse(); } diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/bit_expansion_menu.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/bit_expansion_menu.dart index c01c55e31..7c707870d 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/bit_expansion_menu.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/bit_expansion_menu.dart @@ -10,20 +10,20 @@ // 2026 January // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; -import 'bit_field_utils.dart'; +import 'package:rohd_devtools_widgets/src/bit_field_utils.dart'; /// Popup-menu values used by the bit-expansion items. /// /// Callers may match against these string constants when handling a /// `showMenu` result that includes bit-expansion items. abstract final class BitExpansionMenuValues { - /// Menu item: "Expand Bits [N]" — expand each bit (or a chosen range) + /// Menu item: "Expand Bits \[N\]" — expand each bit (or a chosen range) /// as a synthesized 1-bit waveform. static const String expandBits = 'expand_bits'; - /// Menu item: "Define Bit Fields [N]..." — open a dialog that lets the + /// Menu item: "Define Bit Fields \[N\]..." — open a dialog that lets the /// user name arbitrary bit ranges. static const String defineFields = 'define_fields'; } @@ -44,6 +44,7 @@ class BitExpandRangeAction extends BitExpansionAction { /// High bit (inclusive) of the range to expand. final int bitEnd; + /// Creates an action that expands the inclusive bit range. const BitExpandRangeAction(this.bitStart, this.bitEnd); } @@ -53,6 +54,7 @@ class BitDefineFieldsAction extends BitExpansionAction { /// The user-defined bit fields. final List fields; + /// Creates an action that defines [fields]. const BitDefineFieldsAction(this.fields); } @@ -73,24 +75,24 @@ List> buildBitExpansionMenuItems({ double fontSize = 13, double itemHeight = 32, bool includeDivider = false, -}) { - return >[ - if (includeDivider) const PopupMenuDivider(height: 8), - PopupMenuItem( - height: itemHeight, - value: BitExpansionMenuValues.expandBits, - child: Text('Expand Bits [$width]', style: TextStyle(fontSize: fontSize)), - ), - PopupMenuItem( - height: itemHeight, - value: BitExpansionMenuValues.defineFields, - child: Text( - 'Define Bit Fields [$width]...', - style: TextStyle(fontSize: fontSize), +}) => + >[ + if (includeDivider) const PopupMenuDivider(height: 8), + PopupMenuItem( + height: itemHeight, + value: BitExpansionMenuValues.expandBits, + child: + Text('Expand Bits [$width]', style: TextStyle(fontSize: fontSize)), ), - ), - ]; -} + PopupMenuItem( + height: itemHeight, + value: BitExpansionMenuValues.defineFields, + child: Text( + 'Define Bit Fields [$width]...', + style: TextStyle(fontSize: fontSize), + ), + ), + ]; /// Translate a popup-menu [value] returned by `showMenu` into a /// [BitExpansionAction], showing any follow-up dialog as needed. @@ -122,7 +124,9 @@ Future resolveBitExpansionMenuValue( signalName: signalName, width: width, ); - if (parsed == null) return null; + if (parsed == null) { + return null; + } final (high, low) = parsed; return BitExpandRangeAction(low, high); } @@ -132,7 +136,9 @@ Future resolveBitExpansionMenuValue( signalName: signalName, width: width, ); - if (fields == null || fields.isEmpty) return null; + if (fields == null || fields.isEmpty) { + return null; + } return BitDefineFieldsAction(fields); } return null; diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/bit_field_utils.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/bit_field_utils.dart index efb61eb93..bb27b841b 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/bit_field_utils.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/bit_field_utils.dart @@ -7,7 +7,7 @@ // 2025 May // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; /// A named bit-field definition within a bitvector signal. class BitFieldDef { @@ -20,6 +20,7 @@ class BitFieldDef { /// Low bit (inclusive, LSB of the field). final int low; + /// Creates a bit-field definition. const BitFieldDef({ required this.name, required this.high, @@ -50,16 +51,22 @@ abstract final class BitFieldUtils { static (int, int)? parseBitRange(String input, int maxBit) { if (input.contains(':')) { final parts = input.split(':'); - if (parts.length != 2) return null; + if (parts.length != 2) { + return null; + } final high = int.tryParse(parts[0].trim()); final low = int.tryParse(parts[1].trim()); - if (high == null || low == null) return null; + if (high == null || low == null) { + return null; + } final h = high.clamp(0, maxBit); final l = low.clamp(0, maxBit); return h >= l ? (h, l) : (l, h); } final bit = int.tryParse(input); - if (bit == null) return null; + if (bit == null) { + return null; + } final clamped = bit.clamp(0, maxBit); return (clamped, clamped); } @@ -76,7 +83,9 @@ abstract final class BitFieldUtils { final fields = []; for (final rawLine in lines) { final line = rawLine.trim(); - if (line.isEmpty) continue; + if (line.isEmpty) { + continue; + } final tokens = _splitAsciiWhitespace(line); // Try: name high:low @@ -158,7 +167,9 @@ abstract final class BitFieldUtils { } static bool _isWord(String value) { - if (value.isEmpty) return false; + if (value.isEmpty) { + return false; + } for (var i = 0; i < value.length; i++) { final char = value.codeUnitAt(i); final isUppercase = char >= 65 && char <= 90; @@ -173,10 +184,14 @@ abstract final class BitFieldUtils { } static bool _isUnsignedDecimal(String value) { - if (value.isEmpty) return false; + if (value.isEmpty) { + return false; + } for (var i = 0; i < value.length; i++) { final char = value.codeUnitAt(i); - if (char < 48 || char > 57) return false; + if (char < 48 || char > 57) { + return false; + } } return true; } @@ -208,37 +223,37 @@ Future<(int, int)?> showBitRangeDialog( final result = await showDialog( context: context, barrierColor: Colors.black26, - builder: (ctx) { - return AlertDialog( - title: Text( - '$signalName [$width bits]', - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + builder: (ctx) => AlertDialog( + title: Text( + '$signalName [$width bits]', + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + content: TextField( + controller: controller, + autofocus: true, + decoration: InputDecoration( + labelText: 'Bit range (high:low) or single bit', + hintText: '$maxBit:0', + isDense: true, ), - content: TextField( - controller: controller, - autofocus: true, - decoration: InputDecoration( - labelText: 'Bit range (high:low) or single bit', - hintText: '$maxBit:0', - isDense: true, - ), - onSubmitted: (value) => Navigator.of(ctx).pop(value), + onSubmitted: (value) => Navigator.of(ctx).pop(value), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(ctx).pop(), + child: const Text('Cancel'), ), - actions: [ - TextButton( - onPressed: () => Navigator.of(ctx).pop(), - child: const Text('Cancel'), - ), - TextButton( - onPressed: () => Navigator.of(ctx).pop(controller.text), - child: const Text('OK'), - ), - ], - ); - }, + TextButton( + onPressed: () => Navigator.of(ctx).pop(controller.text), + child: const Text('OK'), + ), + ], + ), ); - if (result == null || result.trim().isEmpty) return null; + if (result == null || result.trim().isEmpty) { + return null; + } return BitFieldUtils.parseBitRange(result.trim(), maxBit); } @@ -258,11 +273,7 @@ Future?> showDefineBitFieldsDialog( // type additional fields without accidentally replacing existing ones. final hasExisting = existingDefs != null && existingDefs.isNotEmpty; final initialText = hasExisting - ? '${existingDefs.map((f) { - return f.high == f.low - ? '${f.name} ${f.high}' - : '${f.name} ${f.high}:${f.low}'; - }).join('\n')}\n' + ? '${_formatBitFieldDefs(existingDefs)}\n' : 'field0 $maxBit:0'; final controller = TextEditingController(text: initialText); @@ -282,44 +293,52 @@ Future?> showDefineBitFieldsDialog( final result = await showDialog( context: context, barrierColor: Colors.black26, - builder: (ctx) { - return AlertDialog( - title: Text( - '$signalName [$width bits] — Define Fields', - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), - ), - content: SizedBox( - width: 320, - child: TextField( - controller: controller, - autofocus: true, - maxLines: 8, - minLines: 3, - style: const TextStyle(fontFamily: 'monospace', fontSize: 13), - decoration: InputDecoration( - labelText: 'One field per line: name high:low', - hintText: 'exponent $maxBit:${maxBit - 10}\n' - 'mantissa ${maxBit - 11}:0', - isDense: true, - border: const OutlineInputBorder(), - ), + builder: (ctx) => AlertDialog( + title: Text( + '$signalName [$width bits] — Define Fields', + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + content: SizedBox( + width: 320, + child: TextField( + controller: controller, + autofocus: true, + maxLines: 8, + minLines: 3, + style: const TextStyle(fontFamily: 'monospace', fontSize: 13), + decoration: InputDecoration( + labelText: 'One field per line: name high:low', + hintText: 'exponent $maxBit:${maxBit - 10}\n' + 'mantissa ${maxBit - 11}:0', + isDense: true, + border: const OutlineInputBorder(), ), ), - actions: [ - TextButton( - onPressed: () => Navigator.of(ctx).pop(), - child: const Text('Cancel'), - ), - TextButton( - onPressed: () => Navigator.of(ctx).pop(controller.text), - child: const Text('OK'), - ), - ], - ); - }, + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(ctx).pop(), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () => Navigator.of(ctx).pop(controller.text), + child: const Text('OK'), + ), + ], + ), ); - if (result == null || result.trim().isEmpty) return null; + if (result == null || result.trim().isEmpty) { + return null; + } final fields = BitFieldUtils.parseBitFieldDefs(result, maxBit); return fields.isEmpty ? null : fields; } + +String _formatBitFieldDefs(List fields) => fields + .map( + (field) => field.high == field.low + ? '${field.name} ${field.high}' + : '${field.name} ${field.high}:${field.low}', + ) + .join('\n'); diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/capture_boundary.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/capture_boundary.dart index 9c3e05ab8..fd14c3848 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/capture_boundary.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/capture_boundary.dart @@ -10,9 +10,8 @@ import 'dart:typed_data' show Uint8List; import 'dart:ui' as ui; -import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart' show RenderRepaintBoundary; - +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart' as export_png; /// Capture a [RepaintBoundary] identified by [boundaryKey], encode to PNG, diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_button.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_button.dart index ea6148492..857ed07c9 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_button.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_button.dart @@ -7,8 +7,9 @@ // 2026 June // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; -import 'cross_probe_service.dart'; +import 'package:flutter/foundation.dart'; +import 'package:material_ui/material_ui.dart'; +import 'package:rohd_devtools_widgets/src/cross_probe_service.dart'; /// A toolbar icon button for cross-probing signal selections between viewers. /// @@ -26,23 +27,27 @@ class CrossProbeButton extends StatelessWidget { const CrossProbeButton({required this.service, super.key}); @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: service.isActive, - builder: (context, active, _) { - final color = active - ? Theme.of(context).colorScheme.primary - : Theme.of(context).disabledColor; - return Tooltip( - message: active - ? 'Cross-probing active — tap to disable' - : 'Cross-probing disabled — tap to enable', - child: IconButton( - icon: Icon(Icons.compare_arrows, color: color), - onPressed: () => service.isActive.value = !service.isActive.value, - ), - ); - }, - ); + Widget build(BuildContext context) => ValueListenableBuilder( + valueListenable: service.isActive, + builder: (context, active, _) { + final color = active + ? Theme.of(context).colorScheme.primary + : Theme.of(context).disabledColor; + return Tooltip( + message: active + ? 'Cross-probing active — tap to disable' + : 'Cross-probing disabled — tap to enable', + child: IconButton( + icon: Icon(Icons.compare_arrows, color: color), + onPressed: () => service.isActive.value = !service.isActive.value, + ), + ); + }, + ); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DiagnosticsProperty('service', service)); } } diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_menu.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_menu.dart index b8134e4ad..8aafe3bef 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_menu.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_menu.dart @@ -15,9 +15,9 @@ // 2026 June // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; -import 'rohd_extension_status.dart'; +import 'package:rohd_devtools_widgets/src/rohd_extension_status.dart'; /// Returns the source formats currently navigable for the active module. /// @@ -139,7 +139,9 @@ Widget _sourceFormatAssetIcon( ), ); - if (!isDark) return image; + if (!isDark) { + return image; + } return Container( width: size + 4, diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_service.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_service.dart index 694950740..7c6b5350a 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_service.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/cross_probe_service.dart @@ -64,7 +64,9 @@ class LocalCrossProbeChannel extends ChangeNotifier { /// /// Does nothing when [signalPaths] is empty. void broadcast(List signalPaths, String source) { - if (signalPaths.isEmpty) return; + if (signalPaths.isEmpty) { + return; + } _lastSource = source; _lastPaths = List.unmodifiable(signalPaths); notifyListeners(); @@ -75,7 +77,7 @@ class LocalCrossProbeChannel extends ChangeNotifier { /// /// Create one [LocalCrossProbeService] per viewer, all sharing the same /// [LocalCrossProbeChannel]. Each service filters out its own broadcasts -/// (matched by [source]) so viewers do not receive their own selections. +/// (matched by the source tag) so viewers do not receive their own selections. /// /// ```dart /// final channel = LocalCrossProbeChannel(); @@ -109,15 +111,21 @@ class LocalCrossProbeService implements CrossProbeService { } void _onChannelMessage() { - if (!isActive.value) return; + if (!isActive.value) { + return; + } final src = _channel.lastSource; - if (src == null || src == _source) return; // ignore own broadcasts + if (src == null || src == _source) { + return; + } incomingSignals.value = _channel.lastPaths; } @override void send(List signalPaths, {required String source}) { - if (!isActive.value || signalPaths.isEmpty) return; + if (!isActive.value || signalPaths.isEmpty) { + return; + } _channel.broadcast(signalPaths, source); } diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/export_button.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/export_button.dart index 4c0dd1327..c7f8e2bcb 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/export_button.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/export_button.dart @@ -7,7 +7,8 @@ // 2026 April // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; +import 'package:material_ui/material_ui.dart'; /// Small camera-icon button for triggering PNG export. /// @@ -20,9 +21,10 @@ class ExportPngButton extends StatelessWidget { /// Tooltip text shown on hover. final String tooltip; + /// Creates an [ExportPngButton]. const ExportPngButton({ - super.key, required this.onPressed, + super.key, this.tooltip = 'Export as PNG', }); @@ -50,4 +52,12 @@ class ExportPngButton extends StatelessWidget { ), ); } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(ObjectFlagProperty.has('onPressed', onPressed)) + ..add(StringProperty('tooltip', tooltip)); + } } diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/export_toast.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/export_toast.dart index e962a6dd0..967e99177 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/export_toast.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/export_toast.dart @@ -9,7 +9,7 @@ import 'dart:async'; -import 'package:flutter/material.dart'; +import 'package:material_ui/material_ui.dart'; /// Show a brief floating toast at the bottom of the screen. /// diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/markdown_help_button.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/markdown_help_button.dart index ddc373afd..9734a5c9a 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/markdown_help_button.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/markdown_help_button.dart @@ -20,7 +20,10 @@ // 2026 March // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:material_ui/material_ui.dart'; /// A help button that loads its content from a markdown asset file. /// @@ -109,6 +112,22 @@ class MarkdownHelpButton extends StatefulWidget { @override State createState() => _MarkdownHelpButtonState(); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(StringProperty('assetPath', assetPath)) + ..add(DiagnosticsProperty('isDark', isDark)) + ..add(StringProperty('label', label)) + ..add(StringProperty('package', package)) + ..add( + DiagnosticsProperty?>( + 'substitutions', + substitutions, + ), + ); + } } class _MarkdownHelpButtonState extends State { @@ -118,7 +137,7 @@ class _MarkdownHelpButtonState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - _loadContent(); + unawaited(_loadContent()); } @override @@ -126,7 +145,7 @@ class _MarkdownHelpButtonState extends State { super.didUpdateWidget(oldWidget); if (oldWidget.assetPath != widget.assetPath || oldWidget.package != widget.package) { - _loadContent(); + unawaited(_loadContent()); } } @@ -139,13 +158,10 @@ class _MarkdownHelpButtonState extends State { // dependency in a host app), then fall back to the bare asset path // (standalone mode). This order avoids a spurious 404 on the web // when the bare path doesn't exist. - // Use catch-all because AssetBundle.loadString throws FlutterError - // (an Error, not Exception) when the asset is missing. try { raw = await assetBundle .loadString('packages/${widget.package}/${widget.assetPath}'); - // ignore: avoid_catches_without_on_clauses - } catch (_) { + } on Object { raw = await assetBundle.loadString(widget.assetPath); } } else { @@ -163,8 +179,7 @@ class _MarkdownHelpButtonState extends State { _content = _HelpContent.parse(raw); }); } - // ignore: avoid_catches_without_on_clauses - } catch (e) { + } on Object catch (e) { debugPrint('Failed to load help asset: $e'); if (mounted) { setState(() { @@ -281,7 +296,7 @@ class _MarkdownHelpButtonState extends State { } } - showDialog( + unawaited(showDialog( context: context, builder: (ctx) => Dialog( backgroundColor: bgColor, @@ -330,7 +345,7 @@ class _MarkdownHelpButtonState extends State { ), ), ), - ); + )); } } @@ -367,7 +382,7 @@ class _HelpContent { final title = _extractTitle(raw) ?? 'Help'; // Extract tooltip text. - String tooltip = ''; + var tooltip = ''; if (tooltipIdx >= 0 && detailsIdx > tooltipIdx) { tooltip = raw.substring(tooltipIdx + tooltipMarker.length, detailsIdx).trim(); @@ -392,7 +407,7 @@ class _HelpContent { final blocks = <_DetailBlock>[]; final lines = raw.split('\n'); - for (int i = 0; i < lines.length; i++) { + for (var i = 0; i < lines.length; i++) { final line = lines[i]; final trimmed = line.trim(); diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/rohd_extension_client.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/rohd_extension_client.dart index 6c1c59d69..155197562 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/rohd_extension_client.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/rohd_extension_client.dart @@ -13,8 +13,9 @@ // Author: Desmond Kirkpatrick import 'package:flutter/foundation.dart'; +import 'package:rohd_devtools_widgets/src/rohd_extension_status.dart'; + export 'rohd_extension_status.dart'; -import 'rohd_extension_status.dart'; /// Abstract client for the ROHD VS Code extension handshake. /// diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/rohd_extension_status.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/rohd_extension_status.dart index f87428254..9cfe3c74d 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/rohd_extension_status.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/rohd_extension_status.dart @@ -39,6 +39,7 @@ class RohdFormatInfo { /// Resolved absolute path to the file, if known. final String? path; + /// Creates information for one source format. const RohdFormatInfo({ required this.available, this.fileFound = false, @@ -92,6 +93,7 @@ class RohdModuleInfo { /// `true` while the extension is still loading an FST file asynchronously. final bool fstLoading; + /// Creates information for one ROHD module. const RohdModuleInfo({ required this.extensionAvailable, this.module, @@ -165,7 +167,9 @@ class RohdModuleInfo { final formats = {}; for (final entry in rawFormats.entries) { final fmt = _parseFormat(entry.key); - if (fmt == null) continue; + if (fmt == null) { + continue; + } final fmtMap = entry.value as Map? ?? const {}; formats[fmt] = RohdFormatInfo( available: fmtMap['available'] as bool? ?? false, diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/save_png_web.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/save_png_web.dart index 5b0b9221e..555d3cc0e 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/save_png_web.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/lib/src/save_png_web.dart @@ -25,8 +25,9 @@ Future savePngBytes(Uint8List pngBytes, String fileName) async { ..download = fileName ..style.display = 'none'; web.document.body!.append(anchor); - anchor.click(); - anchor.remove(); + anchor + ..click() + ..remove(); web.URL.revokeObjectURL(url); return null; } diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/pubspec.yaml b/rohd_devtools_extension/packages/rohd_devtools_widgets/pubspec.yaml index dcb1b7a95..220459bc0 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/pubspec.yaml +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/pubspec.yaml @@ -2,17 +2,16 @@ name: rohd_devtools_widgets description: "Shared widgets for ROHD DevTools sub-packages: help button, PNG export, auto-hiding overlay." version: 0.1.0 publish_to: none +resolution: workspace + environment: - sdk: '>=3.4.0 <4.0.0' + sdk: '>=3.6.0 <4.0.0' flutter: '>=3.0.0' dependencies: flutter: {sdk: flutter} + material_ui: ^1.0.1 rohd: ^0.6.9 web: ^1.0.0 dev_dependencies: flutter_test: {sdk: flutter} lints: ^5.0.0 - -dependency_overrides: - rohd: - path: ../../.. diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/app_bar_overlay_test.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/app_bar_overlay_test.dart index 080f41c11..acf9926b9 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/app_bar_overlay_test.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/app_bar_overlay_test.dart @@ -9,8 +9,8 @@ import 'dart:ui' show PointerDeviceKind; -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; void main() { diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/bit_expansion_menu_test.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/bit_expansion_menu_test.dart index 921ebe684..2575d494c 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/bit_expansion_menu_test.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/bit_expansion_menu_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; void main() { @@ -112,7 +112,7 @@ void main() { await tester.tap(find.text('OK')); await tester.pumpAndSettle(); - final range = await rangeFuture as BitExpandRangeAction; + final range = (await rangeFuture)! as BitExpandRangeAction; expect(range.bitStart, 3); expect(range.bitEnd, 6); @@ -127,7 +127,7 @@ void main() { await tester.tap(find.text('OK')); await tester.pumpAndSettle(); - final fields = await fieldsFuture as BitDefineFieldsAction; + final fields = (await fieldsFuture)! as BitDefineFieldsAction; expect(fields.fields.map((field) => field.name), ['upper', 'lower']); }); } diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/bit_field_utils_test.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/bit_field_utils_test.dart index 3999dfe58..2702a5048 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/bit_field_utils_test.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/bit_field_utils_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; void main() { diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/capture_boundary_test.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/capture_boundary_test.dart index d1574c192..a56d3b800 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/capture_boundary_test.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/capture_boundary_test.dart @@ -9,9 +9,9 @@ import 'dart:typed_data'; -import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart' show RenderRepaintBoundary; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; void main() { @@ -100,7 +100,7 @@ void main() { find.byWidgetPredicate( (widget) => widget is Text && - widget.data?.startsWith('Saved: /tmp/wave_') == true, + (widget.data?.startsWith('Saved: /tmp/wave_') ?? false), ), findsOneWidget, ); @@ -132,7 +132,7 @@ void main() { find.byWidgetPredicate( (widget) => widget is Text && - widget.data?.startsWith('Downloaded capture_') == true, + (widget.data?.startsWith('Downloaded capture_') ?? false), ), findsOneWidget, ); diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/cross_probe_button_test.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/cross_probe_button_test.dart index 22df3efc5..22e6064c9 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/cross_probe_button_test.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/cross_probe_button_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; void main() { diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/cross_probe_menu_test.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/cross_probe_menu_test.dart index 4da66a7af..454e2fa79 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/cross_probe_menu_test.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/cross_probe_menu_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; void main() { @@ -39,7 +39,7 @@ void main() { test('resolves default and exact navigable formats', () { expect(resolveNavigableFormats(null), kDefaultNavigableFormats); expect( - resolveNavigableFormats(const RohdModuleInfo(extensionAvailable: false)), + resolveNavigableFormats(RohdModuleInfo.unavailable), kDefaultNavigableFormats, ); expect( @@ -55,7 +55,6 @@ void main() { formats: { RohdSourceFormat.rohd: RohdFormatInfo( available: true, - fileFound: false, ), RohdSourceFormat.sv: RohdFormatInfo( available: false, @@ -67,18 +66,18 @@ void main() { isEmpty, ); - final info = RohdModuleInfo( + const info = RohdModuleInfo( extensionAvailable: true, formats: { - RohdSourceFormat.rohd: const RohdFormatInfo( + RohdSourceFormat.rohd: RohdFormatInfo( available: true, fileFound: true, ), - RohdSourceFormat.sc: const RohdFormatInfo( + RohdSourceFormat.sc: RohdFormatInfo( available: true, fileFound: true, ), - RohdSourceFormat.fst: const RohdFormatInfo( + RohdSourceFormat.fst: RohdFormatInfo( available: true, fileFound: true, ), @@ -96,8 +95,7 @@ void main() { final items = buildGotoSourceMenuItems( formats: [RohdSourceFormat.rohd, RohdSourceFormat.sv], count: 2, - showIcons: true, - iconBuilder: (format, {double size = 18}) => Icon( + iconBuilder: (format, {size = 18}) => Icon( Icons.code, key: ValueKey(format), size: size, @@ -185,7 +183,7 @@ void main() { ], size: 13, gap: 5, - iconBuilder: (format, {double size = 16}) => Icon( + iconBuilder: (format, {size = 16}) => Icon( Icons.code, key: ValueKey('strip-${format.name}'), size: size, diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/export_button_test.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/export_button_test.dart index bdabebc52..0fc8bc907 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/export_button_test.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/export_button_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; void main() { diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/export_toast_test.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/export_toast_test.dart index b2fa49cf9..51be8f170 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/export_toast_test.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/export_toast_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; void main() { diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/logic_type_utils_test.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/logic_type_utils_test.dart index 86c9b6e68..23675737a 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/logic_type_utils_test.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/logic_type_utils_test.dart @@ -151,7 +151,8 @@ void main() { expect( tooltip, - '''packet (Packet) + ''' +packet (Packet) payload: 4'b1010 nibble: 4'b1010 ...''', diff --git a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/markdown_help_button_test.dart b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/markdown_help_button_test.dart index c1731db48..ca5aae295 100644 --- a/rohd_devtools_extension/packages/rohd_devtools_widgets/test/markdown_help_button_test.dart +++ b/rohd_devtools_extension/packages/rohd_devtools_widgets/test/markdown_help_button_test.dart @@ -9,9 +9,9 @@ import 'dart:convert'; -import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_widgets/rohd_devtools_widgets.dart'; void main() { diff --git a/rohd_devtools_extension/pubspec.yaml b/rohd_devtools_extension/pubspec.yaml index c6d89e98b..94f880b23 100644 --- a/rohd_devtools_extension/pubspec.yaml +++ b/rohd_devtools_extension/pubspec.yaml @@ -6,6 +6,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 +resolution: workspace + environment: sdk: ^3.6.0 @@ -19,8 +21,10 @@ dependencies: flutter_bloc: ^9.0.0 flutter_simple_treeview: ^3.0.2 logging: ^1.2.0 + material_ui: ^1.0.1 + rohd: ^0.6.10 rohd_devtools_widgets: - path: packages/rohd_devtools_widgets + ^0.1.0 vm_service: ^14.3.1 web: ^1.0.0 web_socket_channel: ^3.0.0 @@ -37,7 +41,3 @@ flutter: assets: - assets/help/ - assets/icons/ - -dependency_overrides: - rohd: - path: ../ diff --git a/rohd_devtools_extension/test/const/app_theme_test.dart b/rohd_devtools_extension/test/const/app_theme_test.dart index b7510c70b..99b1199f9 100644 --- a/rohd_devtools_extension/test/const/app_theme_test.dart +++ b/rohd_devtools_extension/test/const/app_theme_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/const/app_theme.dart'; void main() { diff --git a/rohd_devtools_extension/test/cubit/basic_cubits_test.dart b/rohd_devtools_extension/test/cubit/basic_cubits_test.dart index d8a7d5d3c..9dd1847e3 100644 --- a/rohd_devtools_extension/test/cubit/basic_cubits_test.dart +++ b/rohd_devtools_extension/test/cubit/basic_cubits_test.dart @@ -17,7 +17,7 @@ import 'package:rohd_devtools_extension/rohd_devtools/models/tree_model.dart'; void main() { group('search term cubits', () { - test('store the latest signal and tree search terms', () async { + test('store the latest signal and tree search terms', () { final signalCubit = SignalSearchTermCubit(); final treeCubit = TreeSearchTermCubit(); addTearDown(signalCubit.close); @@ -34,7 +34,7 @@ void main() { }); }); - test('DetailsTabCubit selects each available details view', () async { + test('DetailsTabCubit selects each available details view', () { final cubit = DetailsTabCubit(); addTearDown(cubit.close); @@ -47,7 +47,7 @@ void main() { expect(cubit.state, DetailsTab.schematic); }); - test('SelectedModuleCubit exposes the selected module', () async { + test('SelectedModuleCubit exposes the selected module', () { final cubit = SelectedModuleCubit(); addTearDown(cubit.close); final module = TreeModel( @@ -66,7 +66,7 @@ void main() { }); group('DevToolsThemeCubit', () { - test('starts dark and toggles between the supported modes', () async { + test('starts dark and toggles between the supported modes', () { final cubit = DevToolsThemeCubit(); addTearDown(cubit.close); @@ -81,7 +81,7 @@ void main() { expect(cubit.state, DevToolsThemeMode.dark); }); - test('sets an explicit theme mode', () async { + test('sets an explicit theme mode', () { final cubit = DevToolsThemeCubit(); addTearDown(cubit.close); diff --git a/rohd_devtools_extension/test/cubit/snapshot_cubit_test.dart b/rohd_devtools_extension/test/cubit/snapshot_cubit_test.dart index 58f7d79d4..0e0fbbc08 100644 --- a/rohd_devtools_extension/test/cubit/snapshot_cubit_test.dart +++ b/rohd_devtools_extension/test/cubit/snapshot_cubit_test.dart @@ -156,7 +156,7 @@ void main() { expect(state.getSignal('top.counter')!.width, 8); }); - test('clear returns to camera mode and its initial state', () async { + test('clear returns to camera mode and its initial state', () { final cubit = SnapshotCubit(); addTearDown(cubit.close); diff --git a/rohd_devtools_extension/test/modules/tree_structure/model_tree_card_test.dart b/rohd_devtools_extension/test/modules/tree_structure/model_tree_card_test.dart index 85aa22124..9761386a1 100644 --- a/rohd_devtools_extension/test/modules/tree_structure/model_tree_card_test.dart +++ b/rohd_devtools_extension/test/modules/tree_structure/model_tree_card_test.dart @@ -7,9 +7,9 @@ // 2024 January 9 // Author: Yao Jing Quek -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/rohd_devtools.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/module_tree_card.dart'; diff --git a/rohd_devtools_extension/test/modules/tree_structure/tree_structure_page_test.dart b/rohd_devtools_extension/test/modules/tree_structure/tree_structure_page_test.dart index df8936c46..09cb13d5b 100644 --- a/rohd_devtools_extension/test/modules/tree_structure/tree_structure_page_test.dart +++ b/rohd_devtools_extension/test/modules/tree_structure/tree_structure_page_test.dart @@ -7,9 +7,9 @@ // 2024 January 9 // Author: Yao Jing Quek -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:mocktail/mocktail.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/tree_model.dart'; import 'package:rohd_devtools_extension/rohd_devtools/rohd_devtools.dart'; diff --git a/rohd_devtools_extension/test/ui/devtool_appbar_test.dart b/rohd_devtools_extension/test/ui/devtool_appbar_test.dart index e73fe2f61..771bd8b79 100644 --- a/rohd_devtools_extension/test/ui/devtool_appbar_test.dart +++ b/rohd_devtools_extension/test/ui/devtool_appbar_test.dart @@ -7,9 +7,9 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/theme_cubit.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/devtool_appbar.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/devtools_help_button.dart'; diff --git a/rohd_devtools_extension/test/ui/devtools_connection_host_lifecycle_test.dart b/rohd_devtools_extension/test/ui/devtools_connection_host_lifecycle_test.dart index 066efd90b..a14c4a632 100644 --- a/rohd_devtools_extension/test/ui/devtools_connection_host_lifecycle_test.dart +++ b/rohd_devtools_extension/test/ui/devtools_connection_host_lifecycle_test.dart @@ -10,8 +10,8 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:mocktail/mocktail.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/dtd_vm_service_info.dart'; import 'package:rohd_devtools_extension/rohd_devtools/services/connection_state_machine.dart'; @@ -80,7 +80,7 @@ class _TestConnectionHostState VmConnectionStrategy? get connectionStrategy => widget.strategy; @override - Future> discoverVmServices(String dtdUri) async => + Future> discoverVmServices(String dtdUri) => widget.discoverVmServices?.call(dtdUri) ?? super.discoverVmServices(dtdUri); diff --git a/rohd_devtools_extension/test/ui/diagnostic_properties_test.dart b/rohd_devtools_extension/test/ui/diagnostic_properties_test.dart index d6a6bdc2e..127968ce9 100644 --- a/rohd_devtools_extension/test/ui/diagnostic_properties_test.dart +++ b/rohd_devtools_extension/test/ui/diagnostic_properties_test.dart @@ -8,8 +8,8 @@ // Author: Desmond Kirkpatrick import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/snapshot_cubit.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/tree_model.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/ui.dart'; diff --git a/rohd_devtools_extension/test/ui/module_tree_details_navbar_test.dart b/rohd_devtools_extension/test/ui/module_tree_details_navbar_test.dart index 41c38b4d4..9895a4c2d 100644 --- a/rohd_devtools_extension/test/ui/module_tree_details_navbar_test.dart +++ b/rohd_devtools_extension/test/ui/module_tree_details_navbar_test.dart @@ -7,9 +7,9 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/details_tab_cubit.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/module_tree_details_navbar.dart'; diff --git a/rohd_devtools_extension/test/ui/presentation_widgets_test.dart b/rohd_devtools_extension/test/ui/presentation_widgets_test.dart index 6ed702aa3..0886bbe89 100644 --- a/rohd_devtools_extension/test/ui/presentation_widgets_test.dart +++ b/rohd_devtools_extension/test/ui/presentation_widgets_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/platform_icon.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/simulation_time_display.dart'; diff --git a/rohd_devtools_extension/test/ui/signal_details_card_test.dart b/rohd_devtools_extension/test/ui/signal_details_card_test.dart index ed9753a17..866af8cd1 100644 --- a/rohd_devtools_extension/test/ui/signal_details_card_test.dart +++ b/rohd_devtools_extension/test/ui/signal_details_card_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/snapshot_cubit.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/signal_model.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/tree_model.dart'; diff --git a/rohd_devtools_extension/test/ui/signal_table_test.dart b/rohd_devtools_extension/test/ui/signal_table_test.dart index 830db6c37..ec54e2d5b 100644 --- a/rohd_devtools_extension/test/ui/signal_table_test.dart +++ b/rohd_devtools_extension/test/ui/signal_table_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/cubit/snapshot_cubit.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/signal_model.dart'; import 'package:rohd_devtools_extension/rohd_devtools/models/tree_model.dart'; diff --git a/rohd_devtools_extension/test/ui/signal_table_text_field_test.dart b/rohd_devtools_extension/test/ui/signal_table_text_field_test.dart index 21848dbba..9a4cdae4c 100644 --- a/rohd_devtools_extension/test/ui/signal_table_text_field_test.dart +++ b/rohd_devtools_extension/test/ui/signal_table_text_field_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/signal_table_text_field.dart'; void main() { diff --git a/rohd_devtools_extension/test/ui/vm_connection_form_test.dart b/rohd_devtools_extension/test/ui/vm_connection_form_test.dart index 7d8cc2464..75eba4158 100644 --- a/rohd_devtools_extension/test/ui/vm_connection_form_test.dart +++ b/rohd_devtools_extension/test/ui/vm_connection_form_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/vm_connection_form.dart'; void main() { diff --git a/rohd_devtools_extension/test/view/rohd_devtools_page_test.dart b/rohd_devtools_extension/test/view/rohd_devtools_page_test.dart index 3b3d2c94d..70e393e96 100644 --- a/rohd_devtools_extension/test/view/rohd_devtools_page_test.dart +++ b/rohd_devtools_extension/test/view/rohd_devtools_page_test.dart @@ -7,8 +7,8 @@ // 2026 July // Author: Desmond Kirkpatrick -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:material_ui/material_ui.dart'; import 'package:rohd_devtools_extension/rohd_devtools/rohd_devtools.dart'; import 'package:rohd_devtools_extension/rohd_devtools/ui/ui.dart'; diff --git a/rohd_devtools_extension/tool/test_devtools_install.dart b/rohd_devtools_extension/tool/test_devtools_install.dart index 01e6a1c04..2162c3ca1 100644 --- a/rohd_devtools_extension/tool/test_devtools_install.dart +++ b/rohd_devtools_extension/tool/test_devtools_install.dart @@ -61,7 +61,8 @@ Future main(List args) async { _requireFile(extensionAssetsPath, 'flutter.js'); _requireFile(extensionAssetsPath, 'main.dart.js'); _requireFile(extensionAssetsPath, 'version.json'); - _requireFile(extensionAssetsPath, p.join('assets', 'AssetManifest.json')); + _requireFile( + extensionAssetsPath, p.join('assets', 'AssetManifest.bin.json')); _requireFile(extensionAssetsPath, p.join('assets', 'FontManifest.json')); _requireFile(extensionAssetsPath, p.join('canvaskit', 'canvaskit.js')); _requireFile(extensionAssetsPath, p.join('canvaskit', 'canvaskit.wasm')); @@ -76,7 +77,7 @@ Future main(List args) async { } } -Future<_ResolvedTarget> _resolveTarget(String target) async { +Future<_ResolvedTarget> _resolveTarget(String target) { final githubTree = _parseGithubTreeTarget(target); if (githubTree != null) { return _downloadGithubTree(githubTree); @@ -89,8 +90,10 @@ Future<_ResolvedTarget> _resolveTarget(String target) async { ); } - return _ResolvedTarget( - Directory(p.normalize(p.absolute(target))), + return Future.value( + _ResolvedTarget( + Directory(p.normalize(p.absolute(target))), + ), ); } diff --git a/rohd_extension/dart/LICENSE b/rohd_extension/vscode/LICENSE similarity index 100% rename from rohd_extension/dart/LICENSE rename to rohd_extension/vscode/LICENSE diff --git a/rohd_extension/dart/lib/dtd_service.dart b/rohd_extension/vscode/lib/dtd_service.dart similarity index 97% rename from rohd_extension/dart/lib/dtd_service.dart rename to rohd_extension/vscode/lib/dtd_service.dart index 917017986..7e1103ebd 100644 --- a/rohd_extension/dart/lib/dtd_service.dart +++ b/rohd_extension/vscode/lib/dtd_service.dart @@ -44,7 +44,9 @@ class DtdService { /// /// Returns `true` if connection and registration succeeded. Future connect(String uri) async { - if (_disposed) return false; + if (_disposed) { + return false; + } try { _channel = WebSocketChannel.connect(Uri.parse(uri)); @@ -66,6 +68,7 @@ class DtdService { } on Exception catch (e) { _peer = null; _channel = null; + // Surface connection failures in the extension-host log for diagnosis. // ignore: avoid_print print('[DtdService] Failed to connect to DTD at $uri: $e'); return false; diff --git a/rohd_extension/dart/lib/flc_data.dart b/rohd_extension/vscode/lib/flc_data.dart similarity index 100% rename from rohd_extension/dart/lib/flc_data.dart rename to rohd_extension/vscode/lib/flc_data.dart diff --git a/rohd_extension/dart/lib/rohd_source_navigator.dart b/rohd_extension/vscode/lib/rohd_source_navigator.dart similarity index 100% rename from rohd_extension/dart/lib/rohd_source_navigator.dart rename to rohd_extension/vscode/lib/rohd_source_navigator.dart diff --git a/rohd_extension/dart/lib/source_navigator.dart b/rohd_extension/vscode/lib/source_navigator.dart similarity index 100% rename from rohd_extension/dart/lib/source_navigator.dart rename to rohd_extension/vscode/lib/source_navigator.dart diff --git a/rohd_extension/dart/pubspec.yaml b/rohd_extension/vscode/pubspec.yaml similarity index 85% rename from rohd_extension/dart/pubspec.yaml rename to rohd_extension/vscode/pubspec.yaml index c7807621a..0f16d0c5d 100644 --- a/rohd_extension/dart/pubspec.yaml +++ b/rohd_extension/vscode/pubspec.yaml @@ -4,8 +4,11 @@ description: > frame cycling, and DTD service for cross-probe source navigation. version: 0.1.0 +publish_to: none +resolution: workspace + environment: - sdk: ^3.0.0 + sdk: ^3.6.0 dependencies: json_rpc_2: ^3.0.0 diff --git a/rohd_extension/dart/test/flc_data_test.dart b/rohd_extension/vscode/test/flc_data_test.dart similarity index 100% rename from rohd_extension/dart/test/flc_data_test.dart rename to rohd_extension/vscode/test/flc_data_test.dart diff --git a/tool/gh_actions/analyze_source.sh b/tool/gh_actions/analyze_source.sh index 8fc260b6b..f9bfe8ee8 100755 --- a/tool/gh_actions/analyze_source.sh +++ b/tool/gh_actions/analyze_source.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022 Intel Corporation +# Copyright (C) 2022-2026 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # analyze_source.sh @@ -11,4 +11,4 @@ set -euo pipefail -dart analyze --fatal-infos +dart run tool/workspace.dart analyze diff --git a/tool/gh_actions/devtool/install_devtools.sh b/tool/gh_actions/devtool/install_devtools.sh index 6174a7c53..44163a108 100755 --- a/tool/gh_actions/devtool/install_devtools.sh +++ b/tool/gh_actions/devtool/install_devtools.sh @@ -23,8 +23,6 @@ DEST="../extension/devtools" cd rohd_devtools_extension -flutter pub get - echo "" echo "════════════════════════════════════════════════════════════" echo " Building DevTools extension..." diff --git a/tool/gh_actions/devtool/run_devtool_test.sh b/tool/gh_actions/devtool/run_devtool_test.sh index 3d1227985..14e2da071 100755 --- a/tool/gh_actions/devtool/run_devtool_test.sh +++ b/tool/gh_actions/devtool/run_devtool_test.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2024 Intel Corporation +# Copyright (C) 2024-2026 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_devtool_test.sh @@ -13,6 +13,4 @@ set -euo pipefail cd rohd_devtools_extension -flutter pub get - flutter test --platform chrome \ No newline at end of file diff --git a/tool/gh_actions/install_dependencies.sh b/tool/gh_actions/install_dependencies.sh index 7aef34082..b6d1017e0 100755 --- a/tool/gh_actions/install_dependencies.sh +++ b/tool/gh_actions/install_dependencies.sh @@ -1,14 +1,26 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2026 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # install_dependencies.sh -# GitHub Actions step: Install project dependencies. +# Installs dependencies from the repository root for local development or CI. +# Pub resolves every member of this pub workspace together, and some members +# declare `sdk: flutter`, so Flutter is required to resolve the workspace, +# including the core ROHD package. # # 2022 October 7 # Author: Chykon set -euo pipefail -dart pub get +if ! command -v flutter >/dev/null 2>&1; then + echo "Flutter is required to resolve this pub workspace. Pub resolves" >&2 + echo "every workspace member together, and some members declare" >&2 + echo "'sdk: flutter', so 'dart pub get' cannot resolve the workspace" >&2 + echo "without Flutter, even for core-ROHD-only development. Install" >&2 + echo "Flutter (see tool/gh_codespaces/install_flutter.sh) and retry." >&2 + exit 1 +fi + +flutter pub get diff --git a/tool/gh_actions/run_tests.sh b/tool/gh_actions/run_tests.sh index 160352cd2..6e5caf166 100755 --- a/tool/gh_actions/run_tests.sh +++ b/tool/gh_actions/run_tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2022-2023 Intel Corporation +# Copyright (C) 2022-2026 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_tests.sh @@ -11,8 +11,9 @@ set -euo pipefail -dart test +# Run workspace tests using each package's Dart or Flutter runner. +dart run tool/workspace.dart test -# run tests in JS (increase heap size also) +# Run workspace Dart tests in JS (increase heap size for large synthesis tests). export NODE_OPTIONS="--max-old-space-size=8192" -dart test --platform node \ No newline at end of file +dart run tool/workspace.dart test-node \ No newline at end of file diff --git a/tool/gh_codespaces/install_flutter.sh b/tool/gh_codespaces/install_flutter.sh new file mode 100755 index 000000000..5ff09a870 --- /dev/null +++ b/tool/gh_codespaces/install_flutter.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Copyright (C) 2026 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# +# install_flutter.sh +# Install the current stable Flutter SDK and its bundled Dart SDK. +# +# 2026 August +# Author: Desmond Kirkpatrick + +set -euo pipefail + +declare -r flutter_sdk_dir='/opt/flutter' +declare -r flutter_releases_url='https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json' + +if ! command -v curl >/dev/null 2>&1 || + ! command -v python3 >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y curl python3 +fi + +release_manifest="$(mktemp)" +archive="$(mktemp)" +trap 'rm -f "${release_manifest}" "${archive}"' EXIT + +curl --fail --location --silent --show-error "${flutter_releases_url}" \ + --output "${release_manifest}" + +flutter_release="$( + python3 - "${release_manifest}" <<'PYTHON' +import json +import sys + +with open(sys.argv[1]) as manifest: + release_data = json.load(manifest) +stable_hash = release_data['current_release']['stable'] +stable_release = next( + release for release in release_data['releases'] + if release['hash'] == stable_hash +) +print(stable_release['version'], stable_release['archive']) +PYTHON +)" +read -r flutter_version flutter_archive_path <<< "${flutter_release}" +declare -r flutter_version +declare -r flutter_archive_path +declare -r flutter_archive_url="https://storage.googleapis.com/flutter_infra_release/releases/${flutter_archive_path}" + +if [[ -x "${flutter_sdk_dir}/bin/flutter" ]] && + "${flutter_sdk_dir}/bin/flutter" --version | grep -q "Flutter ${flutter_version}"; then + sudo ln --force --symbolic "${flutter_sdk_dir}/bin/flutter" /usr/local/bin/flutter + sudo ln --force --symbolic "${flutter_sdk_dir}/bin/dart" /usr/local/bin/dart + echo "Flutter ${flutter_version} is already installed." + exit 0 +fi + +sudo rm -rf "${flutter_sdk_dir}" +sudo apt-get update +sudo apt-get install -y xz-utils + +curl --fail --location --silent --show-error "${flutter_archive_url}" \ + --output "${archive}" +sudo tar --extract --xz --file "${archive}" --directory /opt + +# The archive is extracted as root; transfer ownership to the invoking user so +# Flutter (run unprivileged below and by later steps) can write to its own SDK +# cache and Git does not reject the checkout for having dubious ownership. +sudo chown --recursive "$(id --user):$(id --group)" "${flutter_sdk_dir}" + +sudo ln --force --symbolic "${flutter_sdk_dir}/bin/flutter" /usr/local/bin/flutter +sudo ln --force --symbolic "${flutter_sdk_dir}/bin/dart" /usr/local/bin/dart + +flutter --version diff --git a/tool/gh_codespaces/run_setup.sh b/tool/gh_codespaces/run_setup.sh index 6523e4147..47a9bdff3 100755 --- a/tool/gh_codespaces/run_setup.sh +++ b/tool/gh_codespaces/run_setup.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright (C) 2023 Intel Corporation +# Copyright (C) 2023-2026 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # # run_setup.sh @@ -14,6 +14,10 @@ set -euo pipefail # Install Dart SDK. tool/gh_codespaces/install_dart.sh +# Install Flutter SDK. The workspace contains Flutter packages, so +# dependencies must be resolved through `flutter pub`. +tool/gh_codespaces/install_flutter.sh + # Install Pub dependencies. tool/gh_actions/install_dependencies.sh diff --git a/tool/workspace.dart b/tool/workspace.dart new file mode 100644 index 000000000..3003e5ce0 --- /dev/null +++ b/tool/workspace.dart @@ -0,0 +1,161 @@ +// Copyright (C) 2026 Intel Corporation +// SPDX-License-Identifier: BSD-3-Clause +// +// workspace.dart +// Pub workspace automation for ROHD packages. + +import 'dart:convert'; +import 'dart:io'; + +import 'package:yaml/yaml.dart'; + +const _usage = ''' +Usage: dart run tool/workspace.dart + +Commands: + analyze Analyze every workspace package. + clean Delete build artifacts from every workspace package. + test Run native non-benchmark tests in every workspace package. + test-node Run Node.js non-benchmark tests in every non-Flutter workspace package. + vscode Generate rohd-multipackage.code-workspace. +'''; + +Future main(List arguments) async { + if (arguments.length != 1 || + !{'analyze', 'clean', 'test', 'test-node', 'vscode'} + .contains(arguments.single)) { + stderr.write(_usage); + exitCode = 64; + return; + } + + final root = Directory.current.absolute; + final packages = _workspacePackages(root); + final workspaceUsesFlutter = packages.any(_usesFlutter); + + if (arguments.single == 'vscode') { + _generateVsCodeWorkspace(root, packages); + return; + } + + if (arguments.single == 'test-node' && workspaceUsesFlutter) { + await _run('flutter', const ['pub', 'get'], root); + } + + for (final package in packages) { + final packageUsesFlutter = _usesFlutter(package); + if (arguments.single == 'test-node' && packageUsesFlutter) { + continue; + } + + final command = switch (arguments.single) { + // There is no `dart clean`; `flutter clean` works for both Dart-only + // and Flutter packages. + 'clean' => 'flutter', + 'test-node' => 'dart', + _ => packageUsesFlutter ? 'flutter' : 'dart', + }; + + final commandArguments = switch (arguments.single) { + 'analyze' => const ['analyze', '--fatal-infos'], + 'clean' => const ['clean'], + 'test' => const ['test', '--exclude-tags', 'benchmark'], + 'test-node' => const [ + 'test', + '--platform', + 'node', + '--exclude-tags', + 'benchmark', + ], + _ => throw StateError('Unexpected command.'), + }; + await _run(command, commandArguments, package); + } +} + +List _workspacePackages(Directory root) { + final pubspec = _readPubspec(root); + final workspace = pubspec['workspace']; + if (workspace is! YamlList) { + throw StateError('Root pubspec.yaml must declare a workspace list.'); + } + + return [ + root, + for (final member in workspace) + Directory('${root.path}${Platform.pathSeparator}$member').absolute, + ]; +} + +bool _usesFlutter(Directory package) { + final pubspec = _readPubspec(package); + return _containsFlutterSdkDependency(pubspec['dependencies']) || + _containsFlutterSdkDependency(pubspec['dev_dependencies']); +} + +bool _containsFlutterSdkDependency(Object? dependencies) { + if (dependencies is! YamlMap) { + return false; + } + final flutter = dependencies['flutter']; + return flutter is YamlMap && flutter['sdk'] == 'flutter'; +} + +YamlMap _readPubspec(Directory package) { + final pubspec = File('${package.path}${Platform.pathSeparator}pubspec.yaml'); + final parsed = loadYaml(pubspec.readAsStringSync()); + if (parsed is! YamlMap) { + throw FormatException('Expected a YAML map in ${pubspec.path}.'); + } + return parsed; +} + +Future _run( + String command, + List arguments, + Directory workingDirectory, +) async { + stdout.writeln( + 'Running $command ${arguments.join(' ')} in ${workingDirectory.path}', + ); + final stopwatch = Stopwatch()..start(); + final process = await Process.start( + command, + arguments, + workingDirectory: workingDirectory.path, + mode: ProcessStartMode.inheritStdio, + ); + final exitCode = await process.exitCode; + stopwatch.stop(); + stdout.writeln( + 'Completed $command ${arguments.join(' ')} in ${workingDirectory.path} ' + 'in ${stopwatch.elapsed}.', + ); + if (exitCode != 0) { + throw ProcessException(command, arguments, 'Command failed.', exitCode); + } +} + +void _generateVsCodeWorkspace(Directory root, List packages) { + final workspace = { + 'folders': [ + for (final package in packages) + { + 'name': package.path == root.path ? 'rohd' : _baseName(package.path), + 'path': package.path == root.path + ? '.' + : package.path.substring(root.path.length + 1), + }, + ], + 'settings': {'dart.projectSearchDepth': 8}, + }; + File('${root.path}${Platform.pathSeparator}rohd-multipackage.code-workspace') + .writeAsStringSync( + '${const JsonEncoder.withIndent(' ').convert(workspace)}\n', + ); +} + +String _baseName(String path) { + final separator = path.lastIndexOf(Platform.pathSeparator); + return separator < 0 ? path : path.substring(separator + 1); +}