diff --git a/pkgs/dart_services/lib/server.dart b/pkgs/dart_services/lib/server.dart index 563093ca3..df6579a3c 100644 --- a/pkgs/dart_services/lib/server.dart +++ b/pkgs/dart_services/lib/server.dart @@ -20,15 +20,27 @@ import 'src/sdk.dart'; final Logger _logger = Logger('services'); Future main(List args) async { - final parser = ArgParser() - ..addOption('port', valueHelp: 'port', help: 'The port to listen on.') - ..addOption('redis-url', valueHelp: 'url', help: 'The redis server url.') - ..addOption('storage-bucket', - valueHelp: 'name', - help: 'The name of the Cloud Storage bucket for compilation artifacts.', - defaultsTo: 'nnbd_artifacts') - ..addFlag('help', - abbr: 'h', negatable: false, help: 'Show this usage information.'); + final parser = + ArgParser() + ..addOption('port', valueHelp: 'port', help: 'The port to listen on.') + ..addOption( + 'redis-url', + valueHelp: 'url', + help: 'The redis server url.', + ) + ..addOption( + 'storage-bucket', + valueHelp: 'name', + help: + 'The name of the Cloud Storage bucket for compilation artifacts.', + defaultsTo: 'nnbd_artifacts', + ) + ..addFlag( + 'help', + abbr: 'h', + negatable: false, + help: 'Show this usage information.', + ); final results = parser.parse(args); if (results['help'] as bool) { @@ -66,18 +78,23 @@ Future main(List args) async { .map((entry) => '${entry.key}:${entry.value}') .join(','); - _logger.info(''' + _logger.info( + ''' Starting dart-services: port: $port sdkPath: ${sdk.dartSdkPath} redisServerUri: $redisServerUri - Cloud Run Environment variables: $cloudRunEnvVars''' - .trim()); + Cloud Run Environment variables: $cloudRunEnvVars'''.trim(), + ); await GitHubOAuthHandler.initFromEnvironmentalVars(); - final server = - await EndpointsServer.serve(port, sdk, redisServerUri, storageBucket); + final server = await EndpointsServer.serve( + port, + sdk, + redisServerUri, + storageBucket, + ); _logger.info('Listening on port ${server.port}'); } @@ -89,8 +106,11 @@ class EndpointsServer { String? redisServerUri, String storageBucket, ) async { - final endpointsServer = - EndpointsServer._(sdk, redisServerUri, storageBucket); + final endpointsServer = EndpointsServer._( + sdk, + redisServerUri, + storageBucket, + ); await endpointsServer._init(); endpointsServer.server = await shelf.serve( @@ -113,15 +133,14 @@ class EndpointsServer { // https://cloud.google.com/run/docs/reference/container-contract#env-vars final serverVersion = Platform.environment['K_REVISION']; - final cache = redisServerUri == null - ? NoopCache() - : RedisCache(redisServerUri, sdk, serverVersion); + final cache = + redisServerUri == null + ? NoopCache() + : RedisCache(redisServerUri, sdk, serverVersion); - commonServer = CommonServerApi(CommonServerImpl( - sdk, - cache, - storageBucket: storageBucket, - )); + commonServer = CommonServerApi( + CommonServerImpl(sdk, cache, storageBucket: storageBucket), + ); // Set cache for GitHub OAuth and add GitHub OAuth routes to our router. GitHubOAuthHandler.setCache(cache); diff --git a/pkgs/dart_services/lib/src/analysis.dart b/pkgs/dart_services/lib/src/analysis.dart index 31e2a188e..c1bacfea9 100644 --- a/pkgs/dart_services/lib/src/analysis.dart +++ b/pkgs/dart_services/lib/src/analysis.dart @@ -31,12 +31,14 @@ class Analyzer { analysisServer = AnalysisServerWrapper(sdkPath: sdk.dartSdkPath); await analysisServer.init(); - unawaited(analysisServer.onExit.then((int code) { - _logger.severe('analysis server exited, code: $code'); - if (code != 0) { - exit(code); - } - })); + unawaited( + analysisServer.onExit.then((int code) { + _logger.severe('analysis server exited, code: $code'); + if (code != 0) { + exit(code); + } + }), + ); } Future analyze(String source) async { @@ -71,20 +73,19 @@ class AnalysisServerWrapper { /// Instance to handle communication with the server. late AnalysisServer analysisServer; - AnalysisServerWrapper({ - required this.sdkPath, - String? projectPath, - }) : - // During analysis, we use the Flutter project template. - projectPath = - projectPath ?? ProjectTemplates.projectTemplates.flutterPath; + AnalysisServerWrapper({required this.sdkPath, String? projectPath}) + : // During analysis, we use the Flutter project template. + projectPath = + projectPath ?? ProjectTemplates.projectTemplates.flutterPath; String get mainPath => _getPathFromName(kMainDart); Future init() async { const serverArgs = ['--client-id=DartPad']; - _logger.info('Starting analysis server ' - '(sdk: ${path.relative(sdkPath)}, args: ${serverArgs.join(' ')})'); + _logger.info( + 'Starting analysis server ' + '(sdk: ${path.relative(sdkPath)}, args: ${serverArgs.join(' ')})', + ); analysisServer = await AnalysisServer.create( sdkPath: sdkPath, @@ -93,8 +94,11 @@ class AnalysisServerWrapper { try { analysisServer.server.onError.listen((ServerError error) { - _logger.severe('server error${error.isFatal ? ' (fatal)' : ''}', - error.message, StackTrace.fromString(error.stackTrace)); + _logger.severe( + 'server error${error.isFatal ? ' (fatal)' : ''}', + error.message, + StackTrace.fromString(error.stackTrace), + ); }); await analysisServer.server.onConnected.first; await analysisServer.server.setSubscriptions(['STATUS']); @@ -128,47 +132,51 @@ class AnalysisServerWrapper { maxResults: maxResults, ); - final suggestions = - results.suggestions.where((CompletionSuggestion suggestion) { - // Filter suggestions that would require adding an import. - return suggestion.isNotImported != true; - }).where((CompletionSuggestion suggestion) { - if (suggestion.kind != 'IMPORT') return true; - - // We do not want to enable arbitrary discovery of file system resources. - // In order to avoid returning local file paths, we only allow returning - // import kinds that are dart: or package: imports. - if (suggestion.completion.startsWith('dart:')) { - return true; - } + final suggestions = results.suggestions + .where((CompletionSuggestion suggestion) { + // Filter suggestions that would require adding an import. + return suggestion.isNotImported != true; + }) + .where((CompletionSuggestion suggestion) { + if (suggestion.kind != 'IMPORT') return true; + + // We do not want to enable arbitrary discovery of file system resources. + // In order to avoid returning local file paths, we only allow returning + // import kinds that are dart: or package: imports. + if (suggestion.completion.startsWith('dart:')) { + return true; + } + + // Filter package suggestions to allowlisted packages. + if (suggestion.completion.startsWith('package:')) { + var packageName = suggestion.completion.substring( + 'package:'.length, + ); + packageName = packageName.split('/').first; + return isSupportedPackage(packageName); + } - // Filter package suggestions to allowlisted packages. - if (suggestion.completion.startsWith('package:')) { - var packageName = suggestion.completion.substring('package:'.length); - packageName = packageName.split('/').first; - return isSupportedPackage(packageName); - } - - return false; - }); + return false; + }); return api.CompleteResponse( replacementOffset: results.replacementOffset, replacementLength: results.replacementLength, - suggestions: suggestions.map((suggestion) { - return api.CompletionSuggestion( - kind: suggestion.kind, - relevance: suggestion.relevance, - completion: suggestion.completion, - deprecated: suggestion.isDeprecated, - selectionOffset: suggestion.selectionOffset, - displayText: suggestion.displayText, - parameterNames: suggestion.parameterNames, - returnType: suggestion.returnType, - elementKind: suggestion.element?.kind, - elementParameters: suggestion.element?.parameters, - ); - }).toList(), + suggestions: + suggestions.map((suggestion) { + return api.CompletionSuggestion( + kind: suggestion.kind, + relevance: suggestion.relevance, + completion: suggestion.completion, + deprecated: suggestion.isDeprecated, + selectionOffset: suggestion.selectionOffset, + displayText: suggestion.displayText, + parameterNames: suggestion.parameterNames, + returnType: suggestion.returnType, + elementKind: suggestion.element?.kind, + elementParameters: suggestion.element?.parameters, + ); + }).toList(), ); } @@ -185,17 +193,21 @@ class AnalysisServerWrapper { // Filter any source changes that want to act on files other than main.dart. fixChanges.removeWhere( - (change) => change.edits.any((edit) => edit.file != mainFile)); + (change) => change.edits.any((edit) => edit.file != mainFile), + ); assistsChanges.removeWhere( - (change) => change.edits.any((edit) => edit.file != mainFile)); + (change) => change.edits.any((edit) => edit.file != mainFile), + ); return api.FixesResponse( - fixes: fixChanges.map((change) { - return change.toApiSourceChange(); - }).toList(), - assists: assistsChanges.map((change) { - return change.toApiSourceChange(); - }).toList(), + fixes: + fixChanges.map((change) { + return change.toApiSourceChange(); + }).toList(), + assists: + assistsChanges.map((change) { + return change.toApiSourceChange(); + }).toList(), ); } @@ -203,25 +215,32 @@ class AnalysisServerWrapper { /// current cursor location and a modified offset is returned if necessary to /// maintain the cursors original position in the formatted code. Future format(String src, int? offset) { - return _formatImpl(src, offset).then((FormatResult editResult) { - final edits = editResult.edits; + return _formatImpl(src, offset) + .then((FormatResult editResult) { + final edits = editResult.edits; - edits.sort((SourceEdit e1, SourceEdit e2) => - -1 * e1.offset.compareTo(e2.offset)); + edits.sort( + (SourceEdit e1, SourceEdit e2) => + -1 * e1.offset.compareTo(e2.offset), + ); - for (final edit in edits) { - src = src.replaceRange( - edit.offset, edit.offset + edit.length, edit.replacement); - } + for (final edit in edits) { + src = src.replaceRange( + edit.offset, + edit.offset + edit.length, + edit.replacement, + ); + } - return api.FormatResponse( - source: src, - offset: offset == null ? 0 : editResult.selectionOffset, - ); - }).catchError((dynamic error) { - _logger.fine('format error: $error'); - return api.FormatResponse(source: src, offset: offset); - }); + return api.FormatResponse( + source: src, + offset: offset == null ? 0 : editResult.selectionOffset, + ); + }) + .catchError((dynamic error) { + _logger.fine('format error: $error'); + return api.FormatResponse(source: src, offset: offset); + }); } Future dartdoc(String src, int offset) async { @@ -255,41 +274,45 @@ class AnalysisServerWrapper { // Loop over all files and collect errors. for (final sourcePath in sources.keys) { - errors - .addAll((await analysisServer.analysis.getErrors(sourcePath)).errors); + errors.addAll( + (await analysisServer.analysis.getErrors(sourcePath)).errors, + ); } - final issues = errors.map((error) { - final issue = api.AnalysisIssue( - kind: error.severity.toLowerCase(), - message: utils.normalizeFilePaths(error.message), - code: error.code.toLowerCase(), - location: api.Location( - charStart: error.location.offset, - charLength: error.location.length, - line: error.location.startLine, - column: error.location.startColumn, - ), - correction: error.correction == null - ? null - : utils.normalizeFilePaths(error.correction!), - url: error.url, - contextMessages: error.contextMessages?.map((m) { - return api.DiagnosticMessage( - message: utils.normalizeFilePaths(m.message), + final issues = + errors.map((error) { + final issue = api.AnalysisIssue( + kind: error.severity.toLowerCase(), + message: utils.normalizeFilePaths(error.message), + code: error.code.toLowerCase(), location: api.Location( - charStart: m.location.offset, - charLength: m.location.length, - line: m.location.startLine, - column: m.location.startColumn, + charStart: error.location.offset, + charLength: error.location.length, + line: error.location.startLine, + column: error.location.startColumn, ), + correction: + error.correction == null + ? null + : utils.normalizeFilePaths(error.correction!), + url: error.url, + contextMessages: + error.contextMessages?.map((m) { + return api.DiagnosticMessage( + message: utils.normalizeFilePaths(m.message), + location: api.Location( + charStart: m.location.offset, + charLength: m.location.length, + line: m.location.startLine, + column: m.location.startColumn, + ), + ); + }).toList(), + hasFix: error.hasFix, ); - }).toList(), - hasFix: error.hasFix, - ); - return issue; - }).toList(); + return issue; + }).toList(); issues.sort((api.AnalysisIssue a, api.AnalysisIssue b) { // Order issues by severity. @@ -308,46 +331,57 @@ class AnalysisServerWrapper { if (import.dartImport) { final libraryName = import.packageName; if (!isSupportedCoreLibrary(libraryName)) { - importIssues.add(api.AnalysisIssue( - kind: 'error', - message: "Unsupported library on the web: 'dart:$libraryName'.", - correction: 'Try removing the import and usages of the library.', - location: import.getLocation(source), - )); + importIssues.add( + api.AnalysisIssue( + kind: 'error', + message: "Unsupported library on the web: 'dart:$libraryName'.", + correction: 'Try removing the import and usages of the library.', + location: import.getLocation(source), + ), + ); } } else if (import.packageImport) { final packageName = import.packageName; if (isFirebasePackage(packageName)) { - importIssues.add(api.AnalysisIssue( - kind: 'warning', - message: 'Firebase is no longer supported by DartPad.', - url: - 'https://github.com/dart-lang/dart-pad/wiki/Package-and-plugin-support#deprecated-firebase-packages', - location: import.getLocation(source), - )); + importIssues.add( + api.AnalysisIssue( + kind: 'warning', + message: 'Firebase is no longer supported by DartPad.', + url: + 'https://github.com/dart-lang/dart-pad/wiki/Package-and-plugin-support#deprecated-firebase-packages', + location: import.getLocation(source), + ), + ); } else if (isDeprecatedPackage(packageName)) { - importIssues.add(api.AnalysisIssue( - kind: 'warning', - message: "Deprecated package: 'package:$packageName'.", - correction: 'Try removing the import and usages of the package.', - url: 'https://github.com/dart-lang/dart-pad/wiki/' - 'Package-and-plugin-support#deprecated-packages', - location: import.getLocation(source), - )); + importIssues.add( + api.AnalysisIssue( + kind: 'warning', + message: "Deprecated package: 'package:$packageName'.", + correction: 'Try removing the import and usages of the package.', + url: + 'https://github.com/dart-lang/dart-pad/wiki/' + 'Package-and-plugin-support#deprecated-packages', + location: import.getLocation(source), + ), + ); } else if (!isSupportedPackage(packageName)) { - importIssues.add(api.AnalysisIssue( - kind: 'warning', - message: "Unsupported package: 'package:$packageName'.", - location: import.getLocation(source), - )); + importIssues.add( + api.AnalysisIssue( + kind: 'warning', + message: "Unsupported package: 'package:$packageName'.", + location: import.getLocation(source), + ), + ); } } else { - importIssues.add(api.AnalysisIssue( - kind: 'error', - message: 'Import type not supported.', - location: import.getLocation(source), - )); + importIssues.add( + api.AnalysisIssue( + kind: 'error', + message: 'Import type not supported.', + location: import.getLocation(source), + ), + ); } } @@ -399,7 +433,7 @@ class AnalysisServerWrapper { // Remove all the existing overlays. final contentOverlays = { for (final overlayPath in _overlayPaths) - overlayPath: RemoveContentOverlay() + overlayPath: RemoveContentOverlay(), }; // Add (or replace) new overlays for the given files. @@ -438,28 +472,31 @@ extension SourceChangeExtension on SourceChange { api.SourceChange toApiSourceChange() { return api.SourceChange( message: message, - edits: edits - .expand((fileEdit) => fileEdit.edits) - .map( - (edit) => api.SourceEdit( - offset: edit.offset, - length: edit.length, - replacement: edit.replacement, - ), - ) - .toList(), - linkedEditGroups: linkedEditGroups.map((editGroup) { - return api.LinkedEditGroup( - offsets: editGroup.positions.map((pos) => pos.offset).toList(), - length: editGroup.length, - suggestions: editGroup.suggestions.map((sug) { - return api.LinkedEditSuggestion( - value: sug.value, - kind: sug.kind, + edits: + edits + .expand((fileEdit) => fileEdit.edits) + .map( + (edit) => api.SourceEdit( + offset: edit.offset, + length: edit.length, + replacement: edit.replacement, + ), + ) + .toList(), + linkedEditGroups: + linkedEditGroups.map((editGroup) { + return api.LinkedEditGroup( + offsets: editGroup.positions.map((pos) => pos.offset).toList(), + length: editGroup.length, + suggestions: + editGroup.suggestions.map((sug) { + return api.LinkedEditSuggestion( + value: sug.value, + kind: sug.kind, + ); + }).toList(), ); }).toList(), - ); - }).toList(), selectionOffset: selection?.offset, ); } diff --git a/pkgs/dart_services/lib/src/caching.dart b/pkgs/dart_services/lib/src/caching.dart index 42ffe2dd1..9642c9f84 100644 --- a/pkgs/dart_services/lib/src/caching.dart +++ b/pkgs/dart_services/lib/src/caching.dart @@ -43,7 +43,7 @@ class RedisCache implements ServerCache { static const Duration cacheOperationTimeout = Duration(milliseconds: 10000); RedisCache(String redisUriString, this._sdk, this.serverVersion) - : redisUri = Uri.parse(redisUriString) { + : redisUri = Uri.parse(redisUriString) { _reconnect(); } @@ -116,21 +116,25 @@ class RedisCache implements ServerCache { _setUpConnection(newConnection); // If the client disconnects, discard the client and try to connect again. - newConnection.outputSink.done.then((_) { - _resetConnection(); - log.warning('$_logPrefix: connection terminated, reconnecting'); - _reconnect(); - }).catchError((dynamic e) { - _resetConnection(); - log.warning( - '$_logPrefix: connection terminated with error $e, reconnecting'); - _reconnect(); - }); + newConnection.outputSink.done + .then((_) { + _resetConnection(); + log.warning('$_logPrefix: connection terminated, reconnecting'); + _reconnect(); + }) + .catchError((dynamic e) { + _resetConnection(); + log.warning( + '$_logPrefix: connection terminated with error $e, reconnecting', + ); + _reconnect(); + }); }) .timeout(const Duration(milliseconds: _connectionRetryMaxMs)) .catchError((_) { log.severe( - '$_logPrefix: Unable to connect to redis server, reconnecting in ${nextRetryMs}ms ...'); + '$_logPrefix: Unable to connect to redis server, reconnecting in ${nextRetryMs}ms ...', + ); Future.delayed(Duration(milliseconds: nextRetryMs)).then((_) { _reconnect(nextRetryMs); }); @@ -159,12 +163,18 @@ class RedisCache implements ServerCache { } else { final commands = RespCommandsTier2(redisClient!); try { - value = await commands.get(key).timeout(cacheOperationTimeout, - onTimeout: () async { - log.warning('$_logPrefix: timeout on get operation for key $key'); - await _connection?.close(); - return null; - }); + value = await commands + .get(key) + .timeout( + cacheOperationTimeout, + onTimeout: () async { + log.warning( + '$_logPrefix: timeout on get operation for key $key', + ); + await _connection?.close(); + return null; + }, + ); } catch (e) { log.warning('$_logPrefix: error on get operation for key $key: $e'); } @@ -182,12 +192,18 @@ class RedisCache implements ServerCache { final commands = RespCommandsTier2(redisClient!); try { - await commands.del([key]).timeout(cacheOperationTimeout, - onTimeout: () async { - log.warning('$_logPrefix: timeout on remove operation for key $key'); - await _connection?.close(); - return 0; // 0 keys deleted - }); + await commands + .del([key]) + .timeout( + cacheOperationTimeout, + onTimeout: () async { + log.warning( + '$_logPrefix: timeout on remove operation for key $key', + ); + await _connection?.close(); + return 0; // 0 keys deleted + }, + ); } catch (e) { log.warning('$_logPrefix: error on remove operation for key $key: $e'); } @@ -208,10 +224,13 @@ class RedisCache implements ServerCache { if (expiration != null) { await commands.pexpire(key, expiration); } - }).timeout(cacheOperationTimeout, onTimeout: () { - log.warning('$_logPrefix: timeout on set operation for key $key'); - _connection?.close(); - }); + }).timeout( + cacheOperationTimeout, + onTimeout: () { + log.warning('$_logPrefix: timeout on set operation for key $key'); + _connection?.close(); + }, + ); } catch (e) { log.warning('$_logPrefix: error on set operation for key $key: $e'); } diff --git a/pkgs/dart_services/lib/src/common_server.dart b/pkgs/dart_services/lib/src/common_server.dart index 4a03f5049..9e6c788bb 100644 --- a/pkgs/dart_services/lib/src/common_server.dart +++ b/pkgs/dart_services/lib/src/common_server.dart @@ -79,7 +79,9 @@ class CommonServerApi { router.post(r'/api//compileDDC', handleCompileDDC); router.post(r'/api//compileNewDDC', handleCompileNewDDC); router.post( - r'/api//compileNewDDCReload', handleCompileNewDDCReload); + r'/api//compileNewDDCReload', + handleCompileNewDDCReload, + ); router.post(r'/api//complete', handleComplete); router.post(r'/api//fixes', handleFixes); router.post(r'/api//format', handleFormat); @@ -103,8 +105,9 @@ class CommonServerApi { Future handleAnalyze(Request request, String apiVersion) async { if (apiVersion != api3) return unhandledVersion(apiVersion); - final sourceRequest = - api.SourceRequest.fromJson(await request.readAsJson()); + final sourceRequest = api.SourceRequest.fromJson( + await request.readAsJson(), + ); final result = await serialize(() { return impl.analyzer.analyze(sourceRequest.source); @@ -116,8 +119,9 @@ class CommonServerApi { Future handleCompile(Request request, String apiVersion) async { if (apiVersion != api3) return unhandledVersion(apiVersion); - final sourceRequest = - api.SourceRequest.fromJson(await request.readAsJson()); + final sourceRequest = api.SourceRequest.fromJson( + await request.readAsJson(), + ); final results = await serialize(() { return impl.compiler.compile(sourceRequest.source); @@ -131,14 +135,15 @@ class CommonServerApi { } Future _handleCompileDDC( - Request request, - String apiVersion, - Future Function(api.CompileRequest) - compile) async { + Request request, + String apiVersion, + Future Function(api.CompileRequest) compile, + ) async { if (apiVersion != api3) return unhandledVersion(apiVersion); - final compileRequest = - api.CompileRequest.fromJson(await request.readAsJson()); + final compileRequest = api.CompileRequest.fromJson( + await request.readAsJson(), + ); final results = await serialize(() { return compile(compileRequest); @@ -149,44 +154,59 @@ class CommonServerApi { if (modulesBaseUrl != null && modulesBaseUrl.isEmpty) { modulesBaseUrl = null; } - return ok(api.CompileDDCResponse( - result: results.compiledJS!, - deltaDill: results.deltaDill, - modulesBaseUrl: modulesBaseUrl, - ).toJson()); + return ok( + api.CompileDDCResponse( + result: results.compiledJS!, + deltaDill: results.deltaDill, + modulesBaseUrl: modulesBaseUrl, + ).toJson(), + ); } else { return failure(results.problems.map((p) => p.message).join('\n')); } } Future handleCompileDDC(Request request, String apiVersion) async { - return await _handleCompileDDC(request, apiVersion, - (request) => impl.compiler.compileDDC(request.source)); + return await _handleCompileDDC( + request, + apiVersion, + (request) => impl.compiler.compileDDC(request.source), + ); } Future handleCompileNewDDC( - Request request, String apiVersion) async { - return await _handleCompileDDC(request, apiVersion, - (request) => impl.compiler.compileNewDDC(request.source)); + Request request, + String apiVersion, + ) async { + return await _handleCompileDDC( + request, + apiVersion, + (request) => impl.compiler.compileNewDDC(request.source), + ); } Future handleCompileNewDDCReload( - Request request, String apiVersion) async { + Request request, + String apiVersion, + ) async { return await _handleCompileDDC( - request, - apiVersion, - (request) => impl.compiler - .compileNewDDCReload(request.source, request.deltaDill!)); + request, + apiVersion, + (request) => + impl.compiler.compileNewDDCReload(request.source, request.deltaDill!), + ); } Future handleComplete(Request request, String apiVersion) async { if (apiVersion != api3) return unhandledVersion(apiVersion); - final sourceRequest = - api.SourceRequest.fromJson(await request.readAsJson()); + final sourceRequest = api.SourceRequest.fromJson( + await request.readAsJson(), + ); - final result = await serialize(() => - impl.analyzer.complete(sourceRequest.source, sourceRequest.offset!)); + final result = await serialize( + () => impl.analyzer.complete(sourceRequest.source, sourceRequest.offset!), + ); return ok(result.toJson()); } @@ -194,11 +214,13 @@ class CommonServerApi { Future handleFixes(Request request, String apiVersion) async { if (apiVersion != api3) return unhandledVersion(apiVersion); - final sourceRequest = - api.SourceRequest.fromJson(await request.readAsJson()); + final sourceRequest = api.SourceRequest.fromJson( + await request.readAsJson(), + ); final result = await serialize( - () => impl.analyzer.fixes(sourceRequest.source, sourceRequest.offset!)); + () => impl.analyzer.fixes(sourceRequest.source, sourceRequest.offset!), + ); return ok(result.toJson()); } @@ -206,14 +228,12 @@ class CommonServerApi { Future handleFormat(Request request, String apiVersion) async { if (apiVersion != api3) return unhandledVersion(apiVersion); - final sourceRequest = - api.SourceRequest.fromJson(await request.readAsJson()); + final sourceRequest = api.SourceRequest.fromJson( + await request.readAsJson(), + ); final result = await serialize(() { - return impl.analyzer.format( - sourceRequest.source, - sourceRequest.offset, - ); + return impl.analyzer.format(sourceRequest.source, sourceRequest.offset); }); return ok(result.toJson()); @@ -222,14 +242,12 @@ class CommonServerApi { Future handleDocument(Request request, String apiVersion) async { if (apiVersion != api3) return unhandledVersion(apiVersion); - final sourceRequest = - api.SourceRequest.fromJson(await request.readAsJson()); + final sourceRequest = api.SourceRequest.fromJson( + await request.readAsJson(), + ); final result = await serialize(() { - return impl.analyzer.dartdoc( - sourceRequest.source, - sourceRequest.offset!, - ); + return impl.analyzer.dartdoc(sourceRequest.source, sourceRequest.offset!); }); return ok(result.toJson()); @@ -251,16 +269,18 @@ class CommonServerApi { ); if (response.statusCode == 302) { - return ok(api.OpenInIdxResponse(idxUrl: response.headers['location']!) - .toJson()); + return ok( + api.OpenInIdxResponse(idxUrl: response.headers['location']!).toJson(), + ); } else { return Response.internalServerError( - body: - 'Failed to read response from IDX server. Response: $response'); + body: 'Failed to read response from IDX server. Response: $response', + ); } } catch (error) { return Response.internalServerError( - body: 'Failed to read response from IDX server. Error: $error'); + body: 'Failed to read response from IDX server. Error: $error', + ); } } @@ -281,10 +301,9 @@ class CommonServerApi { } Future serialize(Future Function() fn) { - return scheduler.schedule(ClosureTask( - fn, - timeoutDuration: const Duration(minutes: 5), - )); + return scheduler.schedule( + ClosureTask(fn, timeoutDuration: const Duration(minutes: 5)), + ); } api.VersionResponse version() { @@ -339,12 +358,14 @@ extension RequestExtension on Request { } Middleware createCustomCorsHeadersMiddleware() { - return shelf_cors.createCorsHeadersMiddleware(corsHeaders: { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', - 'Access-Control-Allow-Headers': - 'Origin, X-Requested-With, Content-Type, Accept, x-goog-api-client' - }); + return shelf_cors.createCorsHeadersMiddleware( + corsHeaders: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', + 'Access-Control-Allow-Headers': + 'Origin, X-Requested-With, Content-Type, Accept, x-goog-api-client', + }, + ); } Middleware logRequestsToLogger(Logger log) { @@ -352,18 +373,21 @@ Middleware logRequestsToLogger(Logger log) { return (request) { final watch = Stopwatch()..start(); - return Future.sync(() => innerHandler(request)).then((response) { - log.info(_formatMessage(request, watch.elapsed, response: response)); + return Future.sync(() => innerHandler(request)).then( + (response) { + log.info(_formatMessage(request, watch.elapsed, response: response)); - return response; - }, onError: (Object error, StackTrace stackTrace) { - if (error is HijackException) throw error; + return response; + }, + onError: (Object error, StackTrace stackTrace) { + if (error is HijackException) throw error; - log.info(_formatMessage(request, watch.elapsed, error: error)); + log.info(_formatMessage(request, watch.elapsed, error: error)); - // ignore: only_throw_errors - throw error; - }); + // ignore: only_throw_errors + throw error; + }, + ); }; }; } @@ -383,7 +407,8 @@ String _formatMessage( final ms = elapsedTime.inMilliseconds; final query = requestedUri.query == '' ? '' : '?${requestedUri.query}'; - var message = '${ms.toString().padLeft(5)}ms ${size.toString().padLeft(4)}k ' + var message = + '${ms.toString().padLeft(5)}ms ${size.toString().padLeft(4)}k ' '$statusCode $method ${requestedUri.path}$query'; if (error != null) { message = '$message [$error]'; diff --git a/pkgs/dart_services/lib/src/compiling.dart b/pkgs/dart_services/lib/src/compiling.dart index 24fe69da1..f4ce19074 100644 --- a/pkgs/dart_services/lib/src/compiling.dart +++ b/pkgs/dart_services/lib/src/compiling.dart @@ -27,20 +27,23 @@ class Compiler { final ProjectTemplates _projectTemplates; - Compiler( - Sdk sdk, { - required String storageBucket, - }) : this._(sdk, path.join(sdk.dartSdkPath, 'bin', 'dart'), storageBucket); + Compiler(Sdk sdk, {required String storageBucket}) + : this._(sdk, path.join(sdk.dartSdkPath, 'bin', 'dart'), storageBucket); Compiler._(this._sdk, this._dartPath, this._storageBucket) - : _ddcDriver = BazelWorkerDriver( - () => Process.start(_dartPath, [ - path.join(_sdk.dartSdkPath, 'bin', 'snapshots', - 'dartdevc.dart.snapshot'), - '--persistent_worker' - ]), - maxWorkers: 1), - _projectTemplates = ProjectTemplates.projectTemplates; + : _ddcDriver = BazelWorkerDriver( + () => Process.start(_dartPath, [ + path.join( + _sdk.dartSdkPath, + 'bin', + 'snapshots', + 'dartdevc.dart.snapshot', + ), + '--persistent_worker', + ]), + maxWorkers: 1, + ), + _projectTemplates = ProjectTemplates.projectTemplates; /// Compile the given string and return the resulting [CompilationResults]. Future compile( @@ -76,13 +79,18 @@ class Compiler { _logger.fine('About to exec: $_dartPath ${arguments.join(' ')}'); - final result = - await Process.run(_dartPath, arguments, workingDirectory: temp.path); + final result = await Process.run( + _dartPath, + arguments, + workingDirectory: temp.path, + ); if (result.exitCode != 0) { - final results = CompilationResults(problems: [ - CompilationProblem._(result.stdout as String), - ]); + final results = CompilationResults( + problems: [ + CompilationProblem._(result.stdout as String), + ], + ); return results; } else { String? sourceMap; @@ -105,8 +113,11 @@ class Compiler { } /// Compile the given string and return the resulting [DDCCompilationResults]. - Future _compileDDC(String source, - {String? deltaDill, required bool useNew}) async { + Future _compileDDC( + String source, { + String? deltaDill, + required bool useNew, + }) async { final imports = getAllImportsFor(source); final temp = Directory.systemTemp.createTempSync('dartpad'); @@ -155,10 +166,7 @@ class Compiler { '--reload-delta-kernel=$newDeltaKernelPath', if (oldDillPath != null) '--reload-last-accepted-kernel=$oldDillPath', ], - if (!useNew) ...[ - '--modules=amd', - '--module-name=dartpad_main', - ], + if (!useNew) ...['--modules=amd', '--module-name=dartpad_main'], '--no-summarize', if (usingFlutter) ...[ '-s', @@ -176,8 +184,9 @@ class Compiler { _logger.fine('About to exec dartdevc worker: ${arguments.join(' ')}"'); - final response = - await _ddcDriver.doWork(WorkRequest(arguments: arguments)); + final response = await _ddcDriver.doWork( + WorkRequest(arguments: arguments), + ); if (response.exitCode != 0) { return DDCCompilationResults.failed([ CompilationProblem._(_rewritePaths(response.output)), @@ -195,15 +204,18 @@ class Compiler { // adding the code to a script tag in an iframe rather than loading it // as an individual file from baseURL. As a workaround, this replace // statement injects a name into the module definition. - compiledJs = - compiledJs.replaceFirst('define([', "define('dartpad_main', ["); + compiledJs = compiledJs.replaceFirst( + 'define([', + "define('dartpad_main', [", + ); } final results = DDCCompilationResults( compiledJS: compiledJs, deltaDill: useNew ? base64Encode(newDeltaDill.readAsBytesSync()) : null, - modulesBaseUrl: 'https://storage.googleapis.com/$_storageBucket' + modulesBaseUrl: + 'https://storage.googleapis.com/$_storageBucket' '/${_sdk.dartVersion}/', ); return results; @@ -226,7 +238,9 @@ class Compiler { } Future compileNewDDCReload( - String source, String deltaDill) async { + String source, + String deltaDill, + ) async { return await _compileDDC(source, deltaDill: deltaDill, useNew: true); } @@ -253,9 +267,10 @@ class CompilationResults { bool get success => problems.isEmpty; @override - String toString() => success - ? 'CompilationResults: Success' - : 'Compilation errors: ${problems.join('\n')}'; + String toString() => + success + ? 'CompilationResults: Success' + : 'Compilation errors: ${problems.join('\n')}'; } /// The result of a DDC compile. @@ -266,12 +281,12 @@ class DDCCompilationResults { final List problems; DDCCompilationResults({this.compiledJS, this.deltaDill, this.modulesBaseUrl}) - : problems = const []; + : problems = const []; const DDCCompilationResults.failed(this.problems) - : compiledJS = null, - deltaDill = null, - modulesBaseUrl = null; + : compiledJS = null, + deltaDill = null, + modulesBaseUrl = null; bool get hasOutput => compiledJS != null && compiledJS!.isNotEmpty; @@ -279,9 +294,10 @@ class DDCCompilationResults { bool get success => problems.isEmpty; @override - String toString() => success - ? 'CompilationResults: Success' - : 'Compilation errors: ${problems.join('\n')}'; + String toString() => + success + ? 'CompilationResults: Success' + : 'Compilation errors: ${problems.join('\n')}'; } /// An issue associated with [CompilationResults]. @@ -339,19 +355,21 @@ bool _doNothing(String from, String to) { String _rewritePaths(String output) { final lines = output.split('\n'); - return lines.map((line) { - const token1 = 'lib/bootstrap.dart:'; - var index = line.indexOf(token1); - if (index != -1) { - return 'main.dart:${line.substring(index + token1.length)}'; - } + return lines + .map((line) { + const token1 = 'lib/bootstrap.dart:'; + var index = line.indexOf(token1); + if (index != -1) { + return 'main.dart:${line.substring(index + token1.length)}'; + } - const token2 = 'lib/main.dart:'; - index = line.indexOf(token2); - if (index != -1) { - return 'main.dart:${line.substring(index + token2.length)}'; - } + const token2 = 'lib/main.dart:'; + index = line.indexOf(token2); + if (index != -1) { + return 'main.dart:${line.substring(index + token2.length)}'; + } - return line; - }).join('\n'); + return line; + }) + .join('\n'); } diff --git a/pkgs/dart_services/lib/src/oauth_handler.dart b/pkgs/dart_services/lib/src/oauth_handler.dart index ef0fd85ec..d703fb353 100644 --- a/pkgs/dart_services/lib/src/oauth_handler.dart +++ b/pkgs/dart_services/lib/src/oauth_handler.dart @@ -60,8 +60,10 @@ class GitHubOAuthHandler { if (!initializationEndedInErrorState) { // Add our routes to the router. _logger.fine('Adding GitHub OAuth routes to passed router.'); - router.get('/$entryPointGitHubOAuthInitiate/', - _initiateHandler); + router.get( + '/$entryPointGitHubOAuthInitiate/', + _initiateHandler, + ); router.get('/$entryPointGitHubReturnAuthorize', _returnAuthorizeHandler); } else { _logger.fine('''Attempt to add GitHub OAuth routes to router FAILED @@ -89,31 +91,36 @@ because initialization of GitHubOAuthHandler failed earlier.'''); final clientId = _stripQuotes(Platform.environment['PK_GITHUB_OAUTH_CLIENT_ID']) ?? - 'MissingClientIdEnvironmentalVariable'; + 'MissingClientIdEnvironmentalVariable'; final clientSecret = _stripQuotes(Platform.environment['PK_GITHUB_OAUTH_CLIENT_SECRET']) ?? - 'MissingClientSecretEnvironmentalVariable'; + 'MissingClientSecretEnvironmentalVariable'; var authReturnUrl = _stripQuotes(Platform.environment['K_GITHUB_OAUTH_AUTH_RETURN_URL']) ?? - ''; - var returnToAppUrl = _stripQuotes( - Platform.environment['K_GITHUB_OAUTH_RETURN_TO_APP_URL']) ?? + ''; + var returnToAppUrl = + _stripQuotes( + Platform.environment['K_GITHUB_OAUTH_RETURN_TO_APP_URL'], + ) ?? ''; var missingEnvVariables = false; if (clientId == 'MissingClientIdEnvironmentalVariable') { _logger.severe( - 'PK_GITHUB_OAUTH_CLIENT_ID environmental variable not set! This is REQUIRED.'); + 'PK_GITHUB_OAUTH_CLIENT_ID environmental variable not set! This is REQUIRED.', + ); missingEnvVariables = true; } if (clientSecret == 'MissingClientSecretEnvironmentalVariable') { _logger.severe( - 'PK_GITHUB_OAUTH_CLIENT_SECRET environmental variable not set! This is REQUIRED.'); + 'PK_GITHUB_OAUTH_CLIENT_SECRET environmental variable not set! This is REQUIRED.', + ); missingEnvVariables = true; } if (missingEnvVariables) { _logger.severe( - 'GitHub OAuth Handler DISABLED - Ensure all required environmental variables are set and re-run.'); + 'GitHub OAuth Handler DISABLED - Ensure all required environmental variables are set and re-run.', + ); initializationEndedInErrorState = true; return false; } @@ -129,13 +136,15 @@ Enviroment K_GITHUB_OAUTH_RETURN_TO_APP_URL=$returnToAppUrl' // This would be the locally running dart-services server. authReturnUrl = 'http://localhost:8080/$entryPointGitHubReturnAuthorize'; _logger.fine( - 'K_GITHUB_OAUTH_AUTH_RETURN_URL environmental variable not set - defaulting to "$authReturnUrl"'); + 'K_GITHUB_OAUTH_AUTH_RETURN_URL environmental variable not set - defaulting to "$authReturnUrl"', + ); } if (returnToAppUrl.isEmpty) { // This would be the locally running dart-pad server. returnToAppUrl = 'http://localhost:8000/index.html'; _logger.fine( - 'K_GITHUB_OAUTH_RETURN_TO_APP_URL environmental variable not set - defaulting to "$returnToAppUrl"'); + 'K_GITHUB_OAUTH_RETURN_TO_APP_URL environmental variable not set - defaulting to "$returnToAppUrl"', + ); } return init(clientId, clientSecret, authReturnUrl, returnToAppUrl); } @@ -144,8 +153,12 @@ Enviroment K_GITHUB_OAUTH_RETURN_TO_APP_URL=$returnToAppUrl' /// static class variables. /// All required parameters are passed directly to this init() routine. /// Returns true if initialization was successful. - static Future init(String clientId, String clientSecret, - String authReturnUrl, String returnToAppUrl) async { + static Future init( + String clientId, + String clientSecret, + String authReturnUrl, + String returnToAppUrl, + ) async { _clientId = clientId; _clientSecret = clientSecret; _authReturnUrl = authReturnUrl; @@ -162,17 +175,20 @@ Enviroment K_GITHUB_OAUTH_RETURN_TO_APP_URL=$returnToAppUrl' } if (_authReturnUrl.isEmpty) { _logger.severe( - 'GitHubOAuthHandler no authorization return url passed to init().'); + 'GitHubOAuthHandler no authorization return url passed to init().', + ); missingParameters = true; } if (_returnToAppUrl.isEmpty) { - _logger - .severe('GitHubOAuthHandler no return ti app url passed to init().'); + _logger.severe( + 'GitHubOAuthHandler no return ti app url passed to init().', + ); missingParameters = true; } if (missingParameters) { _logger.severe( - 'GitHub OAuth Handler DISABLED - Ensure all required parameters not passed to init().'); + 'GitHub OAuth Handler DISABLED - Ensure all required parameters not passed to init().', + ); initializationEndedInErrorState = true; return false; } @@ -190,7 +206,9 @@ Enviroment K_GITHUB_OAUTH_RETURN_TO_APP_URL=$returnToAppUrl' /// The calling app will need to use the originally sent random token /// to decrypt the returned GitHub authorization token. static Future _initiateHandler( - Request request, String randomState) async { + Request request, + String randomState, + ) async { // See if we have anything stored for this random state. var timestampStr = await _cache.get(randomState); var newRequest = false; @@ -206,8 +224,11 @@ Enviroment K_GITHUB_OAUTH_RETURN_TO_APP_URL=$returnToAppUrl' // Store this state/timestamp pair within the cache so // we can later verify state on a return from GitHub. - await _cache.set(randomState, timestampStr, - expiration: tenMinuteExpiration); + await _cache.set( + randomState, + timestampStr, + expiration: tenMinuteExpiration, + ); /* Incoming Random String from DartPad. @@ -298,42 +319,46 @@ Enviroment K_GITHUB_OAUTH_RETURN_TO_APP_URL=$returnToAppUrl' final bodydata = json.encode(map); await client - .post(Uri.parse(githubExchangeCodeUri), - headers: { - 'Accept': 'application/vnd.github.v3+json', - 'Content-Type': 'application/json', - }, - body: bodydata) + .post( + Uri.parse(githubExchangeCodeUri), + headers: { + 'Accept': 'application/vnd.github.v3+json', + 'Content-Type': 'application/json', + }, + body: bodydata, + ) .then((http.Response postResponse) { - late String accessToken, scope; - if (postResponse.statusCode >= 200 && - postResponse.statusCode <= 299) { - final retObj = - jsonDecode(postResponse.body) as Map; - - accessToken = retObj['access_token'] as String; - scope = retObj['scope'] as String; - - tokenAquired = true; - - // We can delete this record because we are done. - _cache.remove(state); - - // Encrypt the auth token using the original random state. - final encrBase64AuthToken = - _encryptAndBase64EncodeAuthToken(accessToken, state); - // Build URL to redirect back to the app. - backToAppUrl += '?gh=$encrBase64AuthToken&scope=$scope'; - - _logger.fine('success - redirecting back to app'); - } else if (postResponse.statusCode == 404) { - throw Exception('contentNotFound'); - } else if (postResponse.statusCode == 403) { - throw Exception('rateLimitExceeded'); - } else if (postResponse.statusCode != 200) { - throw Exception('unknown'); - } - }); + late String accessToken, scope; + if (postResponse.statusCode >= 200 && + postResponse.statusCode <= 299) { + final retObj = + jsonDecode(postResponse.body) as Map; + + accessToken = retObj['access_token'] as String; + scope = retObj['scope'] as String; + + tokenAquired = true; + + // We can delete this record because we are done. + _cache.remove(state); + + // Encrypt the auth token using the original random state. + final encrBase64AuthToken = _encryptAndBase64EncodeAuthToken( + accessToken, + state, + ); + // Build URL to redirect back to the app. + backToAppUrl += '?gh=$encrBase64AuthToken&scope=$scope'; + + _logger.fine('success - redirecting back to app'); + } else if (postResponse.statusCode == 404) { + throw Exception('contentNotFound'); + } else if (postResponse.statusCode == 403) { + throw Exception('rateLimitExceeded'); + } else if (postResponse.statusCode != 200) { + throw Exception('unknown'); + } + }); } if (!validCallback || !tokenAquired) { @@ -361,7 +386,9 @@ Enviroment K_GITHUB_OAUTH_RETURN_TO_APP_URL=$returnToAppUrl' /// The symetric decrypting routine is used client side in Dart-Pad t /// decrypt the received token. static String _encryptAndBase64EncodeAuthToken( - String ghAuthToken, String randomStateWeWereSent) { + String ghAuthToken, + String randomStateWeWereSent, + ) { if (randomStateWeWereSent.isEmpty) { return 'ERROR-no stored initial state'; } diff --git a/pkgs/dart_services/lib/src/project_creator.dart b/pkgs/dart_services/lib/src/project_creator.dart index aa5234cfc..60e62e101 100644 --- a/pkgs/dart_services/lib/src/project_creator.dart +++ b/pkgs/dart_services/lib/src/project_creator.dart @@ -30,9 +30,9 @@ class ProjectCreator { required String dartLanguageVersion, required File dependenciesFile, required LogFunction log, - }) : _dartLanguageVersion = dartLanguageVersion, - _dependenciesFile = dependenciesFile, - _log = log; + }) : _dartLanguageVersion = dartLanguageVersion, + _dependenciesFile = dependenciesFile, + _log = log; /// Builds a basic Dart project template directory, complete with `pubspec.yaml` /// and `analysis_options.yaml`. @@ -41,20 +41,22 @@ class ProjectCreator { final projectDirectory = Directory(projectPath); await projectDirectory.create(recursive: true); final dependencies = _dependencyVersions(supportedBasicDartPackages); - File(path.join(projectPath, 'pubspec.yaml')) - .writeAsStringSync(createPubspec( - includeFlutterWeb: false, - dartLanguageVersion: _dartLanguageVersion, - dependencies: dependencies, - )); + File(path.join(projectPath, 'pubspec.yaml')).writeAsStringSync( + createPubspec( + includeFlutterWeb: false, + dartLanguageVersion: _dartLanguageVersion, + dependencies: dependencies, + ), + ); final exitCode = await runFlutterPubGet(_sdk, projectPath, log: _log); if (exitCode != 0) { throw StateError('pub get failed ($exitCode)'); } - File(path.join(projectPath, 'analysis_options.yaml')) - .writeAsStringSync(_createAnalysisOptionsContents()); + File( + path.join(projectPath, 'analysis_options.yaml'), + ).writeAsStringSync(_createAnalysisOptionsContents()); } /// Builds a Flutter project template directory, complete with `pubspec.yaml`, @@ -70,12 +72,13 @@ class ProjectCreator { ...supportedBasicDartPackages, ...supportedFlutterPackages, }); - File(path.join(projectPath, 'pubspec.yaml')) - .writeAsStringSync(createPubspec( - includeFlutterWeb: true, - dartLanguageVersion: _dartLanguageVersion, - dependencies: dependencies, - )); + File(path.join(projectPath, 'pubspec.yaml')).writeAsStringSync( + createPubspec( + includeFlutterWeb: true, + dartLanguageVersion: _dartLanguageVersion, + dependencies: dependencies, + ), + ); final exitCode = await runFlutterPubGet(_sdk, projectPath, log: _log); if (exitCode != 0) { @@ -85,16 +88,24 @@ class ProjectCreator { // Working around Flutter 3.3's deprecation of generated_plugin_registrant.dart // Context: https://github.com/flutter/flutter/pull/106921 - final pluginRegistrant = File(path.join( - projectPath, '.dart_tool', 'dartpad', 'web_plugin_registrant.dart')); + final pluginRegistrant = File( + path.join( + projectPath, + '.dart_tool', + 'dartpad', + 'web_plugin_registrant.dart', + ), + ); if (pluginRegistrant.existsSync()) { Directory(path.join(projectPath, 'lib')).createSync(); pluginRegistrant.copySync( - path.join(projectPath, 'lib', 'generated_plugin_registrant.dart')); + path.join(projectPath, 'lib', 'generated_plugin_registrant.dart'), + ); } - File(path.join(projectPath, 'analysis_options.yaml')) - .writeAsStringSync(_createAnalysisOptionsContents()); + File( + path.join(projectPath, 'analysis_options.yaml'), + ).writeAsStringSync(_createAnalysisOptionsContents()); } String _createAnalysisOptionsContents() { @@ -113,8 +124,9 @@ ${_sdk.experiments.map((experiment) => ' - $experiment').join('\n')} } Map _dependencyVersions(Iterable packages) { - final allVersions = - parsePubDependenciesFile(dependenciesFile: _dependenciesFile); + final allVersions = parsePubDependenciesFile( + dependenciesFile: _dependenciesFile, + ); return { for (final package in packages) package: allVersions[package] ?? 'any', }; diff --git a/pkgs/dart_services/lib/src/project_templates.dart b/pkgs/dart_services/lib/src/project_templates.dart index 0376be76c..e37df09a7 100644 --- a/pkgs/dart_services/lib/src/project_templates.dart +++ b/pkgs/dart_services/lib/src/project_templates.dart @@ -18,10 +18,7 @@ class ProjectTemplates { factory ProjectTemplates() { final basePath = _baseTemplateProject(); - final summaryFilePath = path.join( - 'artifacts', - 'flutter_web.dill', - ); + final summaryFilePath = path.join('artifacts', 'flutter_web.dill'); return ProjectTemplates._( dartPath: path.join(basePath, 'dart_project'), flutterPath: path.join(basePath, 'flutter_project'), diff --git a/pkgs/dart_services/lib/src/pub.dart b/pkgs/dart_services/lib/src/pub.dart index 147c176d0..43f2cbfd9 100644 --- a/pkgs/dart_services/lib/src/pub.dart +++ b/pkgs/dart_services/lib/src/pub.dart @@ -28,7 +28,8 @@ const _flutterPackages = [ /// This is expensive to calculate; they require reading from disk. /// None of them changes during execution. final Map _packageVersions = packageVersionsFromPubspecLock( - project.ProjectTemplates.projectTemplates.flutterPath); + project.ProjectTemplates.projectTemplates.flutterPath, +); /// Returns a mapping of Pub package name to package version. Map getPackageVersions() => _packageVersions; @@ -58,7 +59,8 @@ Map packageVersionsFromPubspecLock(String templatePath) { packageVersions[name] = version; } else { throw StateError( - '$name does not have a well-formatted version: $version'); + '$name does not have a well-formatted version: $version', + ); } }); diff --git a/pkgs/dart_services/lib/src/sdk.dart b/pkgs/dart_services/lib/src/sdk.dart index a0aa5cc7e..8509af455 100644 --- a/pkgs/dart_services/lib/src/sdk.dart +++ b/pkgs/dart_services/lib/src/sdk.dart @@ -69,8 +69,11 @@ final class Sdk { // looking for a 'FLUTTER_ROOT' environment variable. // /bin/cache/dart-sdk/bin/dart - final potentialFlutterSdkPath = path.dirname(path.dirname( - path.dirname(path.dirname(path.dirname(Platform.resolvedExecutable))))); + final potentialFlutterSdkPath = path.dirname( + path.dirname( + path.dirname(path.dirname(path.dirname(Platform.resolvedExecutable))), + ), + ); final String flutterSdkPath; if (_validFlutterSdk(potentialFlutterSdkPath)) { @@ -122,17 +125,21 @@ final class Sdk { // analytics disclaimer). try { - return jsonDecode(Process.runSync( - flutterToolPath, - ['--version', '--machine'], - workingDirectory: flutterSdkPath, - ).stdout.toString().trim()) as Map; + return jsonDecode( + Process.runSync(flutterToolPath, [ + '--version', + '--machine', + ], workingDirectory: flutterSdkPath).stdout.toString().trim(), + ) + as Map; } on FormatException { - return jsonDecode(Process.runSync( - flutterToolPath, - ['--version', '--machine'], - workingDirectory: flutterSdkPath, - ).stdout.toString().trim()) as Map; + return jsonDecode( + Process.runSync(flutterToolPath, [ + '--version', + '--machine', + ], workingDirectory: flutterSdkPath).stdout.toString().trim(), + ) + as Map; } } diff --git a/pkgs/dart_services/lib/src/shelf_cors.dart b/pkgs/dart_services/lib/src/shelf_cors.dart index 5314525e3..f54c720db 100644 --- a/pkgs/dart_services/lib/src/shelf_cors.dart +++ b/pkgs/dart_services/lib/src/shelf_cors.dart @@ -27,5 +27,7 @@ Middleware createCorsHeadersMiddleware({ response.change(headers: corsHeaders); return createMiddleware( - requestHandler: handleOptionsRequest, responseHandler: addCorsHeaders); + requestHandler: handleOptionsRequest, + responseHandler: addCorsHeaders, + ); } diff --git a/pkgs/dart_services/lib/src/utils.dart b/pkgs/dart_services/lib/src/utils.dart index d2403b00b..4b96e8064 100644 --- a/pkgs/dart_services/lib/src/utils.dart +++ b/pkgs/dart_services/lib/src/utils.dart @@ -52,17 +52,22 @@ Future runWithLogging( Map environment = const {}, required void Function(String) log, }) async { - log([ - '${path.basename(executable)} ${arguments.join(' ')}:', - if (workingDirectory != null) 'cwd: $workingDirectory', - if (environment.isNotEmpty) 'env: $environment', - ].join('\n ')); - - final process = await Process.start(executable, arguments, - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: true, - runInShell: Platform.isWindows); + log( + [ + '${path.basename(executable)} ${arguments.join(' ')}:', + if (workingDirectory != null) 'cwd: $workingDirectory', + if (environment.isNotEmpty) 'env: $environment', + ].join('\n '), + ); + + final process = await Process.start( + executable, + arguments, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: true, + runInShell: Platform.isWindows, + ); process.stdout.listen((out) => log(systemEncoding.decode(out).trimRight())); process.stderr.listen((out) => log(systemEncoding.decode(out).trimRight())); return process; diff --git a/pkgs/dart_services/pubspec.yaml b/pkgs/dart_services/pubspec.yaml index 0973a074e..4f26de985 100644 --- a/pkgs/dart_services/pubspec.yaml +++ b/pkgs/dart_services/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none resolution: workspace environment: - sdk: ^3.6.1 + sdk: ^3.7.0 dependencies: analysis_server_lib: ^0.2.5 diff --git a/pkgs/dart_services/test/analysis_test.dart b/pkgs/dart_services/test/analysis_test.dart index 9ee88995b..08f4e5d92 100644 --- a/pkgs/dart_services/test/analysis_test.dart +++ b/pkgs/dart_services/test/analysis_test.dart @@ -41,17 +41,19 @@ void defineTests() { expect(issue.location.line, 2); expect(issue.location.column, 7); expect(issue.kind, 'info'); - expect(issue.message, - 'An uninitialized variable should have an explicit type annotation.'); + expect( + issue.message, + 'An uninitialized variable should have an explicit type annotation.', + ); expect(issue.code, 'prefer_typing_uninitialized_variables'); }); test('completions polluted on second request (repro #126)', () async { // https://github.com/dart-lang/dart-services/issues/126 return analysisServer.complete(completionFilterCode, 17).then((results) { - return analysisServer - .complete(completionFilterCode, 17) - .then((results) { + return analysisServer.complete(completionFilterCode, 17).then(( + results, + ) { expect(results.replacementLength, 2); expect(results.replacementOffset, 16); expect(completionsContains(results, 'print'), true); @@ -68,10 +70,13 @@ void defineTests() { final completions = results.suggestions; if (completions.isNotEmpty) { - expect(completions.every((completion) { - return completion.completion.startsWith('dart:') || - completion.completion.startsWith('package:'); - }), true); + expect( + completions.every((completion) { + return completion.completion.startsWith('dart:') || + completion.completion.startsWith('package:'); + }), + true, + ); } }); @@ -91,13 +96,15 @@ void defineTests() { final completions = results.suggestions; expect( - completions - .every((completion) => completion.completion.startsWith('dart:')), + completions.every( + (completion) => completion.completion.startsWith('dart:'), + ), true, ); expect( - completions - .any((completion) => completion.completion.startsWith('dart:')), + completions.any( + (completion) => completion.completion.startsWith('dart:'), + ), true, ); }); @@ -121,7 +128,9 @@ void defineTests() { // "Insert ';'" expect(changes.map((e) => e.message), contains(startsWith('Insert '))); expect( - changes.map((e) => e.edits.first.replacement), contains(equals(';'))); + changes.map((e) => e.edits.first.replacement), + contains(equals(';')), + ); }); test('format simple', () async { @@ -130,8 +139,10 @@ void defineTests() { }); test('format good code', () async { - final results = - await analysisServer.format(formattedCode.replaceAll('\n', ' '), 0); + final results = await analysisServer.format( + formattedCode.replaceAll('\n', ' '), + 0, + ); expect(results.source, formattedCode); }); @@ -154,8 +165,10 @@ void defineTests() { // just after A final idx = 61; expect(completionLargeNamespaces.substring(idx - 1, idx), 'A'); - final results = - await analysisServer.complete(completionLargeNamespaces, 61); + final results = await analysisServer.complete( + completionLargeNamespaces, + 61, + ); expect(completionsContains(results, 'A'), true); expect(completionsContains(results, 'AB'), true); expect(completionsContains(results, 'ABC'), true); @@ -193,8 +206,9 @@ void defineTests() { }); test('analyze Draggable Physics sample', () async { - final results = - await analysisServer.analyze(sampleCodeFlutterDraggableCard); + final results = await analysisServer.analyze( + sampleCodeFlutterDraggableCard, + ); expect(results.issues, isEmpty); }); @@ -219,9 +233,10 @@ class HelloWorld extends StatelessWidget { expect(issue.location.line, 3); expect(issue.kind, 'error'); expect( - issue.message, - "A value of type 'int' can't be assigned to a variable of type " - "'String'."); + issue.message, + "A value of type 'int' can't be assigned to a variable of type " + "'String'.", + ); }); // https://github.com/dart-lang/dart-pad/issues/2005 @@ -246,8 +261,10 @@ class HelloWorld extends StatelessWidget { final issue = results.issues[0]; expect(issue.location.line, 4); expect(issue.kind, 'info'); - expect(issue.message, - 'An uninitialized variable should have an explicit type annotation.'); + expect( + issue.message, + 'An uninitialized variable should have an explicit type annotation.', + ); }); test('analyze counter app', () async { @@ -256,8 +273,9 @@ class HelloWorld extends StatelessWidget { }); test('analyze Draggable Physics sample', () async { - final results = - await analysisServer.analyze(sampleCodeFlutterDraggableCard); + final results = await analysisServer.analyze( + sampleCodeFlutterDraggableCard, + ); expect(results.issues, isEmpty); }); @@ -267,8 +285,9 @@ class HelloWorld extends StatelessWidget { }); test('analyze Draggable Physics sample', () async { - final results = - await analysisServer.analyze(sampleCodeFlutterDraggableCard); + final results = await analysisServer.analyze( + sampleCodeFlutterDraggableCard, + ); expect(results.issues, isEmpty); }); }); @@ -276,8 +295,9 @@ class HelloWorld extends StatelessWidget { /// Returns whether the completion [response] contains [expected]. bool completionsContains(api.CompleteResponse response, String expected) { - return response.suggestions - .any((completion) => completion.completion == expected); + return response.suggestions.any( + (completion) => completion.completion == expected, + ); } const completionCode = r''' diff --git a/pkgs/dart_services/test/caching_test.dart b/pkgs/dart_services/test/caching_test.dart index 4026f7a62..24bedaad2 100644 --- a/pkgs/dart_services/test/caching_test.dart +++ b/pkgs/dart_services/test/caching_test.dart @@ -38,14 +38,20 @@ void defineTests(bool hasRedis) { final singleStreamOnly = Lock(); Future startRedisProcessAndDrainIO(int port) async { - final newRedisProcess = - await Process.start('redis-server', ['--port', port.toString()]); - unawaited(singleStreamOnly.synchronized(() async { - await stdout.addStream(newRedisProcess.stdout); - })); - unawaited(singleStreamOnly.synchronized(() async { - await stderr.addStream(newRedisProcess.stderr); - })); + final newRedisProcess = await Process.start('redis-server', [ + '--port', + port.toString(), + ]); + unawaited( + singleStreamOnly.synchronized(() async { + await stdout.addStream(newRedisProcess.stdout); + }), + ); + unawaited( + singleStreamOnly.synchronized(() async { + await stderr.addStream(newRedisProcess.stderr); + }), + ); return newRedisProcess; } @@ -90,8 +96,11 @@ void defineTests(bool hasRedis) { test('Verify values expire', () async { await singleTestOnly.synchronized(() async { logMessages = []; - await redisCache.set('expiringkey', 'expiringValue', - expiration: const Duration(milliseconds: 1)); + await redisCache.set( + 'expiringkey', + 'expiringValue', + expiration: const Duration(milliseconds: 1), + ); await Future.delayed(const Duration(milliseconds: 100)); await expectLater(await redisCache.get('expiringkey'), isNull); expect(logMessages, isEmpty); @@ -99,140 +108,172 @@ void defineTests(bool hasRedis) { }); test( - 'Verify two caches with different versions give different results for keys', - () async { - await singleTestOnly.synchronized(() async { - logMessages = []; - await redisCache.set('differentVersionKey', 'value1'); - await redisCacheAlt.set('differentVersionKey', 'value2'); - await expectLater( - await redisCache.get('differentVersionKey'), 'value1'); - await expectLater( - await redisCacheAlt.get('differentVersionKey'), 'value2'); - expect(logMessages, isEmpty); - }); - }); + 'Verify two caches with different versions give different results for keys', + () async { + await singleTestOnly.synchronized(() async { + logMessages = []; + await redisCache.set('differentVersionKey', 'value1'); + await redisCacheAlt.set('differentVersionKey', 'value2'); + await expectLater( + await redisCache.get('differentVersionKey'), + 'value1', + ); + await expectLater( + await redisCacheAlt.get('differentVersionKey'), + 'value2', + ); + expect(logMessages, isEmpty); + }); + }, + ); test('Verify disconnected cache logs errors and returns nulls', () async { await singleTestOnly.synchronized(() async { logMessages = []; - final redisCacheBroken = - RedisCache('redis://localhost:9502', sdk, 'cversion'); + final redisCacheBroken = RedisCache( + 'redis://localhost:9502', + sdk, + 'cversion', + ); try { await redisCacheBroken.set('aKey', 'value'); await expectLater(await redisCacheBroken.get('aKey'), isNull); await redisCacheBroken.remove('aKey'); expect( - logMessages.join('\n'), - stringContainsInOrder([ - 'no cache available when setting key server:rc:cversion:dart:', - '+aKey', - 'no cache available when getting key server:rc:cversion:dart:', - '+aKey', - 'no cache available when removing key server:rc:cversion:dart:', - '+aKey', - ])); + logMessages.join('\n'), + stringContainsInOrder([ + 'no cache available when setting key server:rc:cversion:dart:', + '+aKey', + 'no cache available when getting key server:rc:cversion:dart:', + '+aKey', + 'no cache available when removing key server:rc:cversion:dart:', + '+aKey', + ]), + ); } finally { await redisCacheBroken.shutdown(); } }); }); - test('Verify cache that starts out disconnected retries and works (slow)', - () async { - await singleTestOnly.synchronized(() async { - logMessages = []; - final redisCacheRepairable = - RedisCache('redis://localhost:9503', sdk, 'cversion'); - try { - // Wait for a retry message. - while (logMessages.length < 2) { - await Future.delayed(const Duration(milliseconds: 50)); - } - expect( + test( + 'Verify cache that starts out disconnected retries and works (slow)', + () async { + await singleTestOnly.synchronized(() async { + logMessages = []; + final redisCacheRepairable = RedisCache( + 'redis://localhost:9503', + sdk, + 'cversion', + ); + try { + // Wait for a retry message. + while (logMessages.length < 2) { + await Future.delayed(const Duration(milliseconds: 50)); + } + expect( logMessages.join('\n'), stringContainsInOrder([ 'reconnecting to redis://localhost:9503...\n', 'Unable to connect to redis server, reconnecting in', - ])); + ]), + ); - // Start a redis server. - redisAltProcess = await startRedisProcessAndDrainIO(9503); + // Start a redis server. + redisAltProcess = await startRedisProcessAndDrainIO(9503); - // Wait for connection. - await redisCacheRepairable.connected; - expect(logMessages.join('\n'), contains('Connected to redis server')); - } finally { - await redisCacheRepairable.shutdown(); - } - }); - }); + // Wait for connection. + await redisCacheRepairable.connected; + expect( + logMessages.join('\n'), + contains('Connected to redis server'), + ); + } finally { + await redisCacheRepairable.shutdown(); + } + }); + }, + ); test( - 'Verify that cache that stops responding temporarily times out and can recover', - () async { - await singleTestOnly.synchronized(() async { - logMessages = []; - await redisCache.set('beforeStop', 'truth'); - redisProcess!.kill(ProcessSignal.sigstop); - // Don't fail the test before sending sigcont. - final beforeStop = await redisCache.get('beforeStop'); - await redisCache.disconnected; - redisProcess!.kill(ProcessSignal.sigcont); - expect(beforeStop, isNull); - await redisCache.connected; - await expectLater(await redisCache.get('beforeStop'), equals('truth')); - expect( + 'Verify that cache that stops responding temporarily times out and can recover', + () async { + await singleTestOnly.synchronized(() async { + logMessages = []; + await redisCache.set('beforeStop', 'truth'); + redisProcess!.kill(ProcessSignal.sigstop); + // Don't fail the test before sending sigcont. + final beforeStop = await redisCache.get('beforeStop'); + await redisCache.disconnected; + redisProcess!.kill(ProcessSignal.sigcont); + expect(beforeStop, isNull); + await redisCache.connected; + await expectLater( + await redisCache.get('beforeStop'), + equals('truth'), + ); + expect( logMessages.join('\n'), stringContainsInOrder([ 'timeout on get operation for key server:rc:aversion:dart:', '+beforeStop', '(aversion): reconnecting', '(aversion): Connected to redis server', - ])); - }); - }, onPlatform: { - 'windows': const Skip('Windows does not have sigstop/sigcont'), - }); + ]), + ); + }); + }, + onPlatform: { + 'windows': const Skip('Windows does not have sigstop/sigcont'), + }, + ); test( - 'Verify cache that starts out connected but breaks retries until reconnection (slow)', - () async { - await singleTestOnly.synchronized(() async { - logMessages = []; + 'Verify cache that starts out connected but breaks retries until reconnection (slow)', + () async { + await singleTestOnly.synchronized(() async { + logMessages = []; - redisAltProcess = await startRedisProcessAndDrainIO(9504); - final redisCacheHealing = - RedisCache('redis://localhost:9504', sdk, 'cversion'); - try { - await redisCacheHealing.connected; - await redisCacheHealing.set('missingKey', 'value'); - // Kill process out from under the cache. - redisAltProcess!.kill(); - await redisAltProcess!.exitCode; - redisAltProcess = null; - - // Try to talk to the cache and get an error. Wait for the disconnect - // to be recognized. - await expectLater(await redisCacheHealing.get('missingKey'), isNull); - await redisCacheHealing.disconnected; - - // Start the server and verify we connect appropriately. redisAltProcess = await startRedisProcessAndDrainIO(9504); - await redisCacheHealing.connected; - expect( + final redisCacheHealing = RedisCache( + 'redis://localhost:9504', + sdk, + 'cversion', + ); + try { + await redisCacheHealing.connected; + await redisCacheHealing.set('missingKey', 'value'); + // Kill process out from under the cache. + redisAltProcess!.kill(); + await redisAltProcess!.exitCode; + redisAltProcess = null; + + // Try to talk to the cache and get an error. Wait for the disconnect + // to be recognized. + await expectLater( + await redisCacheHealing.get('missingKey'), + isNull, + ); + await redisCacheHealing.disconnected; + + // Start the server and verify we connect appropriately. + redisAltProcess = await startRedisProcessAndDrainIO(9504); + await redisCacheHealing.connected; + expect( logMessages.join('\n'), stringContainsInOrder([ 'Connected to redis server', 'connection terminated with error SocketException', 'reconnecting to redis://localhost:9504', - ])); - expect(logMessages.last, contains('Connected to redis server')); - } finally { - await redisCacheHealing.shutdown(); - } - }); - }); + ]), + ); + expect(logMessages.last, contains('Connected to redis server')); + } finally { + await redisCacheHealing.shutdown(); + } + }); + }, + ); }, skip: hasRedis ? null : 'redis-server not installed'); } diff --git a/pkgs/dart_services/test/compiling_test.dart b/pkgs/dart_services/test/compiling_test.dart index 57f1a5d87..b97e2f521 100644 --- a/pkgs/dart_services/test/compiling_test.dart +++ b/pkgs/dart_services/test/compiling_test.dart @@ -32,17 +32,22 @@ void defineTests() { expect(result.sourceMap, isNull); }); - void testDDCEndpoint(String endpointName, - {required Future Function(String source) - restartEndpoint, - Future Function( - String source, String lastAcceptedDill)? - reloadEndpoint, - required bool expectNewDeltaDill, - required String compiledIndicator}) { + void testDDCEndpoint( + String endpointName, { + required Future Function(String source) + restartEndpoint, + Future Function( + String source, + String lastAcceptedDill, + )? + reloadEndpoint, + required bool expectNewDeltaDill, + required String compiledIndicator, + }) { Future generateDeltaDill( - Future Function(String source) restartEndpoint, - String source) async { + Future Function(String source) restartEndpoint, + String source, + ) async { final result = await restartEndpoint(source); return result.deltaDill!; } @@ -54,8 +59,10 @@ void defineTests() { if (reloadEndpoint == null) { result = await restartEndpoint(sample); } else { - final lastAcceptedDill = - await generateDeltaDill(restartEndpoint, sample); + final lastAcceptedDill = await generateDeltaDill( + restartEndpoint, + sample, + ); result = await reloadEndpoint(sample, lastAcceptedDill); } expect(result.problems, isEmpty); @@ -68,20 +75,11 @@ void defineTests() { }; } - test( - 'simple', - generateEndpointTest(sampleCode), - ); + test('simple', generateEndpointTest(sampleCode)); - test( - 'with web', - generateEndpointTest(sampleCodeWeb), - ); + test('with web', generateEndpointTest(sampleCodeWeb)); - test( - 'with Flutter', - generateEndpointTest(sampleCodeFlutter), - ); + test('with Flutter', generateEndpointTest(sampleCodeFlutter)); test( 'with Flutter Counter', @@ -103,10 +101,7 @@ void defineTests() { generateEndpointTest(sampleCodeFlutterImplicitAnimations), ); - test( - 'with async', - generateEndpointTest(sampleCodeAsync), - ); + test('with async', generateEndpointTest(sampleCodeAsync)); test('with single error', () async { DDCCompilationResults result; @@ -117,8 +112,10 @@ void defineTests() { } expect(result.success, false); expect(result.problems.length, 1); - expect(result.problems[0].toString(), - contains('Error: Expected \';\' after this.')); + expect( + result.problems[0].toString(), + contains('Error: Expected \';\' after this.'), + ); }); test('with no main', () async { @@ -126,14 +123,18 @@ void defineTests() { if (reloadEndpoint == null) { result = await restartEndpoint(sampleCodeNoMain); } else { - final lastAcceptedDill = - await generateDeltaDill(restartEndpoint, sampleCode); + final lastAcceptedDill = await generateDeltaDill( + restartEndpoint, + sampleCode, + ); result = await reloadEndpoint(sampleCodeNoMain, lastAcceptedDill); } expect(result.success, false); expect(result.problems.length, 1); - expect(result.problems.first.message, - contains("Error: Method not found: 'main'")); + expect( + result.problems.first.message, + contains("Error: Method not found: 'main'"), + ); expect(result.problems.first.message, startsWith('main.dart:')); }); @@ -142,40 +143,53 @@ void defineTests() { if (reloadEndpoint == null) { result = await restartEndpoint(sampleCodeErrors); } else { - final lastAcceptedDill = - await generateDeltaDill(restartEndpoint, sampleCode); + final lastAcceptedDill = await generateDeltaDill( + restartEndpoint, + sampleCode, + ); result = await reloadEndpoint(sampleCodeErrors, lastAcceptedDill); } expect(result.success, false); expect(result.problems.length, 1); - expect(result.problems[0].toString(), - contains('Error: Method not found: \'print1\'.')); - expect(result.problems[0].toString(), - contains('Error: Method not found: \'print2\'.')); - expect(result.problems[0].toString(), - contains('Error: Method not found: \'print3\'.')); + expect( + result.problems[0].toString(), + contains('Error: Method not found: \'print1\'.'), + ); + expect( + result.problems[0].toString(), + contains('Error: Method not found: \'print2\'.'), + ); + expect( + result.problems[0].toString(), + contains('Error: Method not found: \'print3\'.'), + ); }); }); } - testDDCEndpoint('compileDDC', - restartEndpoint: (source) => compiler.compileDDC(source), - expectNewDeltaDill: false, - compiledIndicator: "define('dartpad_main', ["); + testDDCEndpoint( + 'compileDDC', + restartEndpoint: (source) => compiler.compileDDC(source), + expectNewDeltaDill: false, + compiledIndicator: "define('dartpad_main', [", + ); if (sdk.dartMajorVersion >= 3 && sdk.dartMinorVersion >= 8) { // DDC only supports these at version 3.8 and higher. - testDDCEndpoint('compileNewDDC', - restartEndpoint: (source) => compiler.compileNewDDC(source), - expectNewDeltaDill: true, - compiledIndicator: - 'defineLibrary("package:dartpad_sample/main.dart"'); - testDDCEndpoint('compileNewDDCReload', - restartEndpoint: (source) => compiler.compileNewDDC(source), - reloadEndpoint: (source, deltaDill) => - compiler.compileNewDDCReload(source, deltaDill), - expectNewDeltaDill: true, - compiledIndicator: - 'defineLibrary("package:dartpad_sample/main.dart"'); + testDDCEndpoint( + 'compileNewDDC', + restartEndpoint: (source) => compiler.compileNewDDC(source), + expectNewDeltaDill: true, + compiledIndicator: 'defineLibrary("package:dartpad_sample/main.dart"', + ); + testDDCEndpoint( + 'compileNewDDCReload', + restartEndpoint: (source) => compiler.compileNewDDC(source), + reloadEndpoint: + (source, deltaDill) => + compiler.compileNewDDCReload(source, deltaDill), + expectNewDeltaDill: true, + compiledIndicator: 'defineLibrary("package:dartpad_sample/main.dart"', + ); } test('sourcemap', () async { @@ -245,8 +259,10 @@ void main() { missingMethod ('foo'); } '''; final result = await compiler.compile(code); expect(result.problems, hasLength(1)); - expect(result.problems.single.message, - contains("Error when reading 'lib/foo.dart'")); + expect( + result.problems.single.message, + contains("Error when reading 'lib/foo.dart'"), + ); }); test('bad import - http', () async { @@ -256,8 +272,10 @@ void main() { missingMethod ('foo'); } '''; final result = await compiler.compile(code); expect(result.problems, hasLength(1)); - expect(result.problems.single.message, - contains("Error when reading 'http://example.com'")); + expect( + result.problems.single.message, + contains("Error when reading 'http://example.com'"), + ); }); test('multiple bad imports', () async { @@ -267,10 +285,14 @@ import 'package:bar'; '''; final result = await compiler.compile(code); expect(result.problems, hasLength(1)); - expect(result.problems.single.message, - contains("Invalid package URI 'package:foo'")); - expect(result.problems.single.message, - contains("Invalid package URI 'package:bar'")); + expect( + result.problems.single.message, + contains("Invalid package URI 'package:foo'"), + ); + expect( + result.problems.single.message, + contains("Invalid package URI 'package:bar'"), + ); }); test('disallow compiler warnings', () async { diff --git a/pkgs/dart_services/test/flutter_web_test.dart b/pkgs/dart_services/test/flutter_web_test.dart index ec83520d2..b122797a1 100644 --- a/pkgs/dart_services/test/flutter_web_test.dart +++ b/pkgs/dart_services/test/flutter_web_test.dart @@ -17,8 +17,13 @@ void defineTests() { group('FlutterWebManager', () { test('initializes', () async { expect(await Directory(projectTemplates.flutterPath).exists(), isTrue); - final file = File(path.join( - projectTemplates.flutterPath, '.dart_tool', 'package_config.json')); + final file = File( + path.join( + projectTemplates.flutterPath, + '.dart_tool', + 'package_config.json', + ), + ); expect(await file.exists(), isTrue); }); @@ -64,15 +69,22 @@ void defineTests() { group('flutter web project', () { test('packagesFilePath', () async { - final packageConfig = File(path.join( - projectTemplates.flutterPath, '.dart_tool', 'package_config.json')); + final packageConfig = File( + path.join( + projectTemplates.flutterPath, + '.dart_tool', + 'package_config.json', + ), + ); expect(await packageConfig.exists(), true); final encoded = await packageConfig.readAsString(); final contents = jsonDecode(encoded) as Map; expect(contents['packages'], isNotEmpty); final packages = contents['packages'] as List; - expect(packages.where((element) => (element as Map)['name'] == 'flutter'), - isNotEmpty); + expect( + packages.where((element) => (element as Map)['name'] == 'flutter'), + isNotEmpty, + ); }); test('summaryFilePath', () { diff --git a/pkgs/dart_services/test/project_creator_test.dart b/pkgs/dart_services/test/project_creator_test.dart index 5158bf91f..e63d2b2d4 100644 --- a/pkgs/dart_services/test/project_creator_test.dart +++ b/pkgs/dart_services/test/project_creator_test.dart @@ -39,20 +39,13 @@ void defineTests() { }); test('project directory is created', () async { - await d.dir('project_templates', [ - d.dir('dart_project'), - ]).validate(); + await d.dir('project_templates', [d.dir('dart_project')]).validate(); }); test('pubspec is created', () async { await d.dir('project_templates', [ d.dir('dart_project', [ - d.file( - 'pubspec.yaml', - allOf([ - contains('sdk: ^$languageVersion'), - ]), - ), + d.file('pubspec.yaml', allOf([contains('sdk: ^$languageVersion')])), ]), ]).validate(); }); @@ -81,9 +74,7 @@ void defineTests() { }); test('project directory is created', () async { - await d.dir('project_templates', [ - d.dir('flutter_project'), - ]).validate(); + await d.dir('project_templates', [d.dir('flutter_project')]).validate(); }); test('Flutter Web directories are created', () async { @@ -91,7 +82,7 @@ void defineTests() { d.dir('flutter_project', [ d.dir('lib'), d.dir('web', [d.file('index.html', isEmpty)]), - ]) + ]), ]).validate(); }); diff --git a/pkgs/dart_services/test/pub_test.dart b/pkgs/dart_services/test/pub_test.dart index 03b704903..a11b00aa0 100644 --- a/pkgs/dart_services/test/pub_test.dart +++ b/pkgs/dart_services/test/pub_test.dart @@ -28,8 +28,10 @@ import 'dart:math'; import 'package:foo/foo.dart'; void main() { } '''; - expect(getAllImportsFor(source).map((import) => import.uri.stringValue), - unorderedEquals(['dart:math', 'package:foo/foo.dart'])); + expect( + getAllImportsFor(source).map((import) => import.uri.stringValue), + unorderedEquals(['dart:math', 'package:foo/foo.dart']), + ); }); test('two', () { @@ -41,9 +43,13 @@ import 'package:bar/bar.dart'; void main() { } '''; expect( - getAllImportsFor(source).map((import) => import.uri.stringValue), - unorderedEquals( - ['dart:math', 'package:foo/foo.dart', 'package:bar/bar.dart'])); + getAllImportsFor(source).map((import) => import.uri.stringValue), + unorderedEquals([ + 'dart:math', + 'package:foo/foo.dart', + 'package:bar/bar.dart', + ]), + ); }); test('three', () { @@ -56,14 +62,15 @@ import 'mybazfile.dart'; void main() { } '''; expect( - getAllImportsFor(source).map((import) => import.uri.stringValue), - unorderedEquals([ - 'dart:math', - 'package:foo/foo.dart', - 'package:bar/bar.dart', - 'package:baz/baz.dart', - 'mybazfile.dart' - ])); + getAllImportsFor(source).map((import) => import.uri.stringValue), + unorderedEquals([ + 'dart:math', + 'package:foo/foo.dart', + 'package:bar/bar.dart', + 'package:baz/baz.dart', + 'mybazfile.dart', + ]), + ); }); }); }); diff --git a/pkgs/dart_services/test/server_test.dart b/pkgs/dart_services/test/server_test.dart index 71b595f00..ac61250b0 100644 --- a/pkgs/dart_services/test/server_test.dart +++ b/pkgs/dart_services/test/server_test.dart @@ -44,16 +44,22 @@ void defineTests() { }); test('analyze', () async { - final result = await client.analyze(SourceRequest(source: ''' + final result = await client.analyze( + SourceRequest( + source: ''' void main() { print('hello world'); } -''')); +''', + ), + ); expect(result.issues, isEmpty); }); test('analyze flutter', () async { - final result = await client.analyze(SourceRequest(source: ''' + final result = await client.analyze( + SourceRequest( + source: ''' import 'package:flutter/material.dart'; void main() { @@ -72,69 +78,94 @@ class MyApp extends StatelessWidget { ); } } -''')); +''', + ), + ); expect(result, isNotNull); expect(result.issues, isEmpty); }); test('analyze errors', () async { - final result = await client.analyze(SourceRequest(source: r''' + final result = await client.analyze( + SourceRequest( + source: r''' void main() { int foo = 'bar'; print('hello world: $foo'); } -''')); +''', + ), + ); expect(result.issues, hasLength(1)); final issue = result.issues.first; expect(issue.kind, 'error'); expect( - issue.message, - contains( - "A value of type 'String' can't be assigned to a variable of type 'int'")); + issue.message, + contains( + "A value of type 'String' can't be assigned to a variable of type 'int'", + ), + ); expect(issue.location.line, 2); }); test('analyze unsupported import', () async { - final result = await client.analyze(SourceRequest(source: r''' + final result = await client.analyze( + SourceRequest( + source: r''' import 'package:foo_bar/foo_bar.dart'; void main() => print('hello world'); -''')); +''', + ), + ); expect(result.issues, isNotEmpty); final issue = result.issues.first; expect(issue.kind, 'warning'); expect( - issue.message, contains("Unsupported package: 'package:foo_bar'.")); + issue.message, + contains("Unsupported package: 'package:foo_bar'."), + ); expect(issue.location.line, 1); }); test('analyze firebase import', () async { - final result = await client.analyze(SourceRequest(source: r''' + final result = await client.analyze( + SourceRequest( + source: r''' import 'package:firebase_core/firebase_core.dart'; void main() => print('hello world'); -''')); +''', + ), + ); expect(result.issues, isNotEmpty); final issue = result.issues.first; expect(issue.kind, 'warning'); - expect(issue.message, - contains('Firebase is no longer supported by DartPad.')); + expect( + issue.message, + contains('Firebase is no longer supported by DartPad.'), + ); expect(issue.location.line, 1); }); test('complete', () async { - final result = await client.complete(SourceRequest(source: ''' + final result = await client.complete( + SourceRequest( + source: ''' void main() { print('hello world'); } -''', offset: 18)); +''', + offset: 18, + ), + ); expect(result.replacementOffset, 16); expect(result.replacementLength, 5); @@ -152,9 +183,13 @@ void main() { }); test('format', () async { - final result = await client.format(SourceRequest(source: ''' + final result = await client.format( + SourceRequest( + source: ''' void main() { print('hello world'); } -''')); +''', + ), + ); expect(result.source, ''' void main() { @@ -164,11 +199,15 @@ void main() { }); test('format no changes', () async { - final result = await client.format(SourceRequest(source: ''' + final result = await client.format( + SourceRequest( + source: ''' void main() { print('hello world'); } -''')); +''', + ), + ); expect(result.source, ''' void main() { @@ -178,12 +217,14 @@ void main() { }); test('format preserves offset', () async { - final result = await client.format(SourceRequest( - source: ''' + final result = await client.format( + SourceRequest( + source: ''' void main() { print('hello world'); } ''', - offset: 15, - )); + offset: 15, + ), + ); expect(result.source, ''' void main() { @@ -194,39 +235,54 @@ void main() { }); test('compile', () async { - final result = await client.compile(CompileRequest(source: ''' + final result = await client.compile( + CompileRequest( + source: ''' void main() { print('hello world'); } -''')); +''', + ), + ); expect(result.result, isNotEmpty); expect(result.result.length, greaterThanOrEqualTo(10 * 1024)); }); test('compile with error', () async { try { - await client.compile(CompileRequest(source: ''' + await client.compile( + CompileRequest( + source: ''' void main() { print('hello world') } -''')); +''', + ), + ); fail('compile error expected'); } on ApiRequestError catch (e) { expect(e.body, contains("Expected ';' after this.")); } }); - void testDDCEndpoint(String endpointName, - Future Function(CompileRequest) endpoint, - {required bool expectDeltaDill, - Future Function(String source)? generateLastAcceptedDill}) { + void testDDCEndpoint( + String endpointName, + Future Function(CompileRequest) endpoint, { + required bool expectDeltaDill, + Future Function(String source)? generateLastAcceptedDill, + }) { group(endpointName, () { test('compile', () async { - final result = await endpoint(CompileRequest(source: ''' + final result = await endpoint( + CompileRequest( + source: ''' void main() { print('hello world'); } -''', deltaDill: await generateLastAcceptedDill?.call(sampleCode))); +''', + deltaDill: await generateLastAcceptedDill?.call(sampleCode), + ), + ); expect(result.result, isNotEmpty); expect(result.result.length, greaterThanOrEqualTo(1024)); expect(result.modulesBaseUrl, isNotEmpty); @@ -234,7 +290,9 @@ void main() { }); test('compile flutter', () async { - final result = await endpoint(CompileRequest(source: ''' + final result = await endpoint( + CompileRequest( + source: ''' import 'package:flutter/material.dart'; void main() { @@ -251,7 +309,10 @@ class MyApp extends StatelessWidget { ); } } -''', deltaDill: await generateLastAcceptedDill?.call(sampleCode))); +''', + deltaDill: await generateLastAcceptedDill?.call(sampleCode), + ), + ); expect(result.result, isNotEmpty); expect(result.result.length, greaterThanOrEqualTo(10 * 1024)); expect(result.modulesBaseUrl, isNotEmpty); @@ -259,11 +320,16 @@ class MyApp extends StatelessWidget { test('compile with error', () async { try { - await endpoint(CompileRequest(source: ''' + await endpoint( + CompileRequest( + source: ''' void main() { print('hello world') } -''', deltaDill: await generateLastAcceptedDill?.call(sampleCode))); +''', + deltaDill: await generateLastAcceptedDill?.call(sampleCode), + ), + ); fail('compile error expected'); } on ApiRequestError catch (e) { expect(e.body, contains("Expected ';' after this.")); @@ -272,29 +338,40 @@ void main() { }); } - testDDCEndpoint('compileDDC', (request) => client.compileDDC(request), - expectDeltaDill: false); + testDDCEndpoint( + 'compileDDC', + (request) => client.compileDDC(request), + expectDeltaDill: false, + ); if (sdk.dartMajorVersion >= 3 && sdk.dartMinorVersion >= 8) { testDDCEndpoint( - 'compileNewDDC', (request) => client.compileNewDDC(request), - expectDeltaDill: true); - testDDCEndpoint('compileNewDDCReload', - (request) => client.compileNewDDCReload(request), - expectDeltaDill: true, - generateLastAcceptedDill: (source) async => - (await client.compileNewDDC(CompileRequest(source: source))) - .deltaDill!); + 'compileNewDDC', + (request) => client.compileNewDDC(request), + expectDeltaDill: true, + ); + testDDCEndpoint( + 'compileNewDDCReload', + (request) => client.compileNewDDCReload(request), + expectDeltaDill: true, + generateLastAcceptedDill: + (source) async => + (await client.compileNewDDC( + CompileRequest(source: source), + )).deltaDill!, + ); } test('document', () async { - final result = await client.document(SourceRequest( - source: ''' + final result = await client.document( + SourceRequest( + source: ''' void main() { print('hello world'); } ''', - offset: 18, - )); + offset: 18, + ), + ); expect( result.dartdoc!.toLowerCase(), @@ -308,14 +385,16 @@ void main() { }); test('document empty', () async { - final result = await client.document(SourceRequest( - source: ''' + final result = await client.document( + SourceRequest( + source: ''' void main() { print('hello world'); } ''', - offset: 15, - )); + offset: 15, + ), + ); expect(result.dartdoc, isNull); expect(result.elementKind, isNull); @@ -323,55 +402,65 @@ void main() { }); test('fixes', () async { - final result = await client.fixes(SourceRequest( - source: ''' + final result = await client.fixes( + SourceRequest( + source: ''' void main() { var foo = 'bar'; print('hello world'); } ''', - offset: 21, - )); + offset: 21, + ), + ); // Dart 3.5 returns 3 fixes; Dart 3.6 returns 4. expect(result.fixes, anyOf(hasLength(3), hasLength(4))); - final fix = result.fixes - .firstWhereOrNull((fix) => fix.message.contains('Ignore')); + final fix = result.fixes.firstWhereOrNull( + (fix) => fix.message.contains('Ignore'), + ); expect(fix, isNotNull); expect(fix!.edits, hasLength(1)); expect(fix.linkedEditGroups, isEmpty); - expect(fix.edits.first.replacement, - contains('// ignore: unused_local_variable')); + expect( + fix.edits.first.replacement, + contains('// ignore: unused_local_variable'), + ); }); test('fixes empty', () async { - final result = await client.fixes(SourceRequest( - source: ''' + final result = await client.fixes( + SourceRequest( + source: ''' void main() { var foo = 'bar'; print(foo); } ''', - offset: 21, - )); + offset: 21, + ), + ); expect(result.fixes, hasLength(0)); }); test('assists', () async { - final result = await client.fixes(SourceRequest( - source: ''' + final result = await client.fixes( + SourceRequest( + source: ''' void main() => print('hello world'); ''', - offset: 13, - )); + offset: 13, + ), + ); expect(result.fixes, hasLength(0)); expect(result.assists, isNotEmpty); final assist = result.assists.firstWhereOrNull( - (assist) => assist.message.contains('Convert to block body')); + (assist) => assist.message.contains('Convert to block body'), + ); expect(assist, isNotNull); expect(assist!.edits, hasLength(1)); expect(assist.linkedEditGroups, isEmpty); diff --git a/pkgs/dart_services/test/shelf_cors_test.dart b/pkgs/dart_services/test/shelf_cors_test.dart index 59951bc98..39d997309 100644 --- a/pkgs/dart_services/test/shelf_cors_test.dart +++ b/pkgs/dart_services/test/shelf_cors_test.dart @@ -13,8 +13,10 @@ void defineTests() { return shelf.Response.ok('OK'); } - final request = - shelf.Request('GET', Uri.parse('http://example.com/index.html')); + final request = shelf.Request( + 'GET', + Uri.parse('http://example.com/index.html'), + ); group('shelf_cors', () { test('adds default CORS headers to the response', () async { @@ -30,18 +32,23 @@ void defineTests() { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'POST, OPTIONS', 'Access-Control-Allow-Headers': - 'Origin, X-Requested-With, Content-Type, Accept' + 'Origin, X-Requested-With, Content-Type, Accept', }; - final middleware = - shelf_cors.createCorsHeadersMiddleware(corsHeaders: corsHeaders); + final middleware = shelf_cors.createCorsHeadersMiddleware( + corsHeaders: corsHeaders, + ); final handler = middleware(handleAll); final response = await handler(request); expect(response.headers['Access-Control-Allow-Origin'], equals('*')); - expect(response.headers['Access-Control-Allow-Methods'], - equals('POST, OPTIONS')); - expect(response.headers['Access-Control-Allow-Headers'], - equals('Origin, X-Requested-With, Content-Type, Accept')); + expect( + response.headers['Access-Control-Allow-Methods'], + equals('POST, OPTIONS'), + ); + expect( + response.headers['Access-Control-Allow-Headers'], + equals('Origin, X-Requested-With, Content-Type, Accept'), + ); }); }); } diff --git a/pkgs/dart_services/test/utils_test.dart b/pkgs/dart_services/test/utils_test.dart index 2cacfa7f2..3f65e6a23 100644 --- a/pkgs/dart_services/test/utils_test.dart +++ b/pkgs/dart_services/test/utils_test.dart @@ -42,10 +42,7 @@ void defineTests() { }); test('keeps a "dart:core" path intact', () { - expectNormalizeFilePaths( - 'dart:core/foo.dart', - 'dart:core/foo.dart', - ); + expectNormalizeFilePaths('dart:core/foo.dart', 'dart:core/foo.dart'); }); test('keeps a web URL intact', () { diff --git a/pkgs/dart_services/tool/grind.dart b/pkgs/dart_services/tool/grind.dart index 0ce070a95..ab6933081 100644 --- a/pkgs/dart_services/tool/grind.dart +++ b/pkgs/dart_services/tool/grind.dart @@ -22,10 +22,7 @@ Future main(List args) async { return grind(args); } -final List compilationArtifacts = [ - 'dart_sdk.js', - 'flutter_web.js', -]; +final List compilationArtifacts = ['dart_sdk.js', 'flutter_web.js']; final List compilationArtifactsNew = [ 'dart_sdk_new.js', @@ -33,8 +30,10 @@ final List compilationArtifactsNew = [ 'ddc_module_loader.js', ]; -@Task('validate that we have the correct compilation artifacts available in ' - 'google storage') +@Task( + 'validate that we have the correct compilation artifacts available in ' + 'google storage', +) void validateStorageArtifacts() async { final args = context.invocation.arguments; final sdk = Sdk.fromLocalFlutter(); @@ -45,7 +44,8 @@ void validateStorageArtifacts() async { }; print( - 'validate-storage-artifacts version: ${sdk.dartVersion} bucket: $bucket'); + 'validate-storage-artifacts version: ${sdk.dartVersion} bucket: $bucket', + ); final urlBase = 'https://storage.googleapis.com/$bucket/'; for (final artifact @@ -103,8 +103,9 @@ void buildStorageArtifacts() async { final temp = Directory.systemTemp.createTempSync('flutter_web_sample'); try { - instructions - .add(await _buildStorageArtifacts(temp, sdk, channel: sdk.channel)); + instructions.add( + await _buildStorageArtifacts(temp, sdk, channel: sdk.channel), + ); } finally { temp.deleteSync(recursive: true); } @@ -167,12 +168,14 @@ Future _buildStorageArtifacts( // Working around Flutter 3.3's deprecation of generated_plugin_registrant.dart // Context: https://github.com/flutter/flutter/pull/106921 - final pluginRegistrant = File(path.join( - dir.path, '.dart_tool', 'dartpad', 'web_plugin_registrant.dart')); + final pluginRegistrant = File( + path.join(dir.path, '.dart_tool', 'dartpad', 'web_plugin_registrant.dart'), + ); if (pluginRegistrant.existsSync()) { Directory(path.join(dir.path, 'lib')).createSync(); pluginRegistrant.copySync( - path.join(dir.path, 'lib', 'generated_plugin_registrant.dart')); + path.join(dir.path, 'lib', 'generated_plugin_registrant.dart'), + ); } final flutterLibraries = []; @@ -210,8 +213,10 @@ Future _buildStorageArtifacts( // Later versions of Flutter remove the "sound" suffix from files. If the // suffixed version does not exist, the unsuffixed version is the sound file. var dillPath = path.join(sdk.flutterWebSdkPath, 'ddc_outline_sound.dill'); - var sdkJsPath = - path.join(sdk.flutterWebSdkPath, 'amd-canvaskit-sound/dart_sdk.js'); + var sdkJsPath = path.join( + sdk.flutterWebSdkPath, + 'amd-canvaskit-sound/dart_sdk.js', + ); if (!getFile(dillPath).existsSync()) { dillPath = path.join(sdk.flutterWebSdkPath, 'ddc_outline.dill'); sdkJsPath = path.join(sdk.flutterWebSdkPath, 'amd-canvaskit/dart_sdk.js'); @@ -225,14 +230,10 @@ Future _buildStorageArtifacts( '--source-map', '-o', 'flutter_web.js', - ...flutterLibraries + ...flutterLibraries, ]; - await _run( - compilerPath, - arguments: arguments, - workingDirectory: dir.path, - ); + await _run(compilerPath, arguments: arguments, workingDirectory: dir.path); // Copy all to the project directory. final artifactsDir = getDir(path.join('artifacts')); @@ -250,13 +251,19 @@ Future _buildStorageArtifacts( // the suffixed version does not exist, the unsuffixed version is the sound // file. var newSdkJsPath = path.join( - sdk.flutterWebSdkPath, 'ddcLibraryBundle-canvaskit-sound/dart_sdk.js'); + sdk.flutterWebSdkPath, + 'ddcLibraryBundle-canvaskit-sound/dart_sdk.js', + ); if (!getFile(newSdkJsPath).existsSync()) { newSdkJsPath = path.join( - sdk.flutterWebSdkPath, 'ddcLibraryBundle-canvaskit/dart_sdk.js'); + sdk.flutterWebSdkPath, + 'ddcLibraryBundle-canvaskit/dart_sdk.js', + ); } - final ddcModuleLoaderPath = - path.join(sdk.dartSdkPath, 'lib/dev_compiler/ddc/ddc_module_loader.js'); + final ddcModuleLoaderPath = path.join( + sdk.dartSdkPath, + 'lib/dev_compiler/ddc/ddc_module_loader.js', + ); final argumentsNew = [ path.join(sdk.dartSdkPath, 'bin', 'snapshots', 'dartdevc.dart.snapshot'), @@ -269,7 +276,7 @@ Future _buildStorageArtifacts( '--no-summarize', '-o', 'flutter_web_new.js', - ...flutterLibraries + ...flutterLibraries, ]; await _run( @@ -281,10 +288,12 @@ Future _buildStorageArtifacts( copy(getFile(ddcModuleLoaderPath), artifactsDir); copy(getFile(newSdkJsPath), artifactsDir); copy(getFile('$newSdkJsPath.map'), artifactsDir); - joinFile(artifactsDir, ['dart_sdk.js']) - .copySync(path.join('artifacts', 'dart_sdk_new.js')); - joinFile(artifactsDir, ['dart_sdk.js.map']) - .copySync(path.join('artifacts', 'dart_sdk_new.js.map')); + joinFile(artifactsDir, [ + 'dart_sdk.js', + ]).copySync(path.join('artifacts', 'dart_sdk_new.js')); + joinFile(artifactsDir, [ + 'dart_sdk.js.map', + ]).copySync(path.join('artifacts', 'dart_sdk_new.js.map')); copy(joinFile(dir, ['flutter_web_new.js']), artifactsDir); copy(joinFile(dir, ['flutter_web_new.js.map']), artifactsDir); @@ -315,11 +324,13 @@ Future _run( String? workingDirectory, Map environment = const {}, }) async { - final process = await runWithLogging(executable, - arguments: arguments, - workingDirectory: workingDirectory, - environment: environment, - log: log); + final process = await runWithLogging( + executable, + arguments: arguments, + workingDirectory: workingDirectory, + environment: environment, + log: log, + ); final exitCode = await process.exitCode; if (exitCode != 0) { fail('Unable to exec $executable, failed with code $exitCode'); diff --git a/pkgs/dartpad_shared/lib/model.dart b/pkgs/dartpad_shared/lib/model.dart index 779a7bad0..612e26635 100644 --- a/pkgs/dartpad_shared/lib/model.dart +++ b/pkgs/dartpad_shared/lib/model.dart @@ -26,9 +26,7 @@ class SourceRequest { class AnalysisResponse { final List issues; - AnalysisResponse({ - required this.issues, - }); + AnalysisResponse({required this.issues}); factory AnalysisResponse.fromJson(Map json) => _$AnalysisResponseFromJson(json); @@ -105,10 +103,7 @@ class DiagnosticMessage { final String message; final Location location; - DiagnosticMessage({ - required this.message, - required this.location, - }); + DiagnosticMessage({required this.message, required this.location}); factory DiagnosticMessage.fromJson(Map json) => _$DiagnosticMessageFromJson(json); @@ -164,10 +159,7 @@ class FormatResponse { final String source; final int? offset; - FormatResponse({ - required this.source, - required this.offset, - }); + FormatResponse({required this.source, required this.offset}); factory FormatResponse.fromJson(Map json) => _$FormatResponseFromJson(json); @@ -180,18 +172,12 @@ class FormatResponse { @JsonSerializable() class FixesResponse { - static final FixesResponse empty = FixesResponse( - fixes: [], - assists: [], - ); + static final FixesResponse empty = FixesResponse(fixes: [], assists: []); final List fixes; final List assists; - FixesResponse({ - required this.fixes, - required this.assists, - }); + FixesResponse({required this.fixes, required this.assists}); factory FixesResponse.fromJson(Map json) => _$FixesResponseFromJson(json); @@ -266,10 +252,7 @@ class LinkedEditSuggestion { final String value; final String kind; - LinkedEditSuggestion({ - required this.value, - required this.kind, - }); + LinkedEditSuggestion({required this.value, required this.kind}); factory LinkedEditSuggestion.fromJson(Map json) => _$LinkedEditSuggestionFromJson(json); @@ -388,9 +371,7 @@ class VersionResponse { class OpenInIdxRequest { final String code; - OpenInIdxRequest({ - required this.code, - }); + OpenInIdxRequest({required this.code}); factory OpenInIdxRequest.fromJson(Map json) => _$OpenInIdxRequestFromJson(json); @@ -405,9 +386,7 @@ class OpenInIdxRequest { class OpenInIdxResponse { final String idxUrl; - OpenInIdxResponse({ - required this.idxUrl, - }); + OpenInIdxResponse({required this.idxUrl}); factory OpenInIdxResponse.fromJson(Map json) => _$OpenInIdxResponseFromJson(json); diff --git a/pkgs/dartpad_shared/lib/model.g.dart b/pkgs/dartpad_shared/lib/model.g.dart index 31d3494b9..d73cf8e87 100644 --- a/pkgs/dartpad_shared/lib/model.g.dart +++ b/pkgs/dartpad_shared/lib/model.g.dart @@ -13,22 +13,18 @@ SourceRequest _$SourceRequestFromJson(Map json) => ); Map _$SourceRequestToJson(SourceRequest instance) => - { - 'source': instance.source, - 'offset': instance.offset, - }; + {'source': instance.source, 'offset': instance.offset}; AnalysisResponse _$AnalysisResponseFromJson(Map json) => AnalysisResponse( - issues: (json['issues'] as List) - .map((e) => AnalysisIssue.fromJson(e as Map)) - .toList(), + issues: + (json['issues'] as List) + .map((e) => AnalysisIssue.fromJson(e as Map)) + .toList(), ); Map _$AnalysisResponseToJson(AnalysisResponse instance) => - { - 'issues': instance.issues, - }; + {'issues': instance.issues}; AnalysisIssue _$AnalysisIssueFromJson(Map json) => AnalysisIssue( @@ -39,9 +35,12 @@ AnalysisIssue _$AnalysisIssueFromJson(Map json) => correction: json['correction'] as String?, url: json['url'] as String?, hasFix: json['hasFix'] as bool?, - contextMessages: (json['contextMessages'] as List?) - ?.map((e) => DiagnosticMessage.fromJson(e as Map)) - .toList(), + contextMessages: + (json['contextMessages'] as List?) + ?.map( + (e) => DiagnosticMessage.fromJson(e as Map), + ) + .toList(), ); Map _$AnalysisIssueToJson(AnalysisIssue instance) => @@ -57,18 +56,18 @@ Map _$AnalysisIssueToJson(AnalysisIssue instance) => }; Location _$LocationFromJson(Map json) => Location( - charStart: (json['charStart'] as num?)?.toInt() ?? -1, - charLength: (json['charLength'] as num?)?.toInt() ?? 0, - line: (json['line'] as num?)?.toInt() ?? -1, - column: (json['column'] as num?)?.toInt() ?? -1, - ); + charStart: (json['charStart'] as num?)?.toInt() ?? -1, + charLength: (json['charLength'] as num?)?.toInt() ?? 0, + line: (json['line'] as num?)?.toInt() ?? -1, + column: (json['column'] as num?)?.toInt() ?? -1, +); Map _$LocationToJson(Location instance) => { - 'charStart': instance.charStart, - 'charLength': instance.charLength, - 'line': instance.line, - 'column': instance.column, - }; + 'charStart': instance.charStart, + 'charLength': instance.charLength, + 'line': instance.line, + 'column': instance.column, +}; DiagnosticMessage _$DiagnosticMessageFromJson(Map json) => DiagnosticMessage( @@ -95,14 +94,10 @@ Map _$CompileRequestToJson(CompileRequest instance) => }; CompileResponse _$CompileResponseFromJson(Map json) => - CompileResponse( - result: json['result'] as String, - ); + CompileResponse(result: json['result'] as String); Map _$CompileResponseToJson(CompileResponse instance) => - { - 'result': instance.result, - }; + {'result': instance.result}; CompileDDCResponse _$CompileDDCResponseFromJson(Map json) => CompileDDCResponse( @@ -125,37 +120,35 @@ FormatResponse _$FormatResponseFromJson(Map json) => ); Map _$FormatResponseToJson(FormatResponse instance) => - { - 'source': instance.source, - 'offset': instance.offset, - }; + {'source': instance.source, 'offset': instance.offset}; FixesResponse _$FixesResponseFromJson(Map json) => FixesResponse( - fixes: (json['fixes'] as List) - .map((e) => SourceChange.fromJson(e as Map)) - .toList(), - assists: (json['assists'] as List) - .map((e) => SourceChange.fromJson(e as Map)) - .toList(), + fixes: + (json['fixes'] as List) + .map((e) => SourceChange.fromJson(e as Map)) + .toList(), + assists: + (json['assists'] as List) + .map((e) => SourceChange.fromJson(e as Map)) + .toList(), ); Map _$FixesResponseToJson(FixesResponse instance) => - { - 'fixes': instance.fixes, - 'assists': instance.assists, - }; + {'fixes': instance.fixes, 'assists': instance.assists}; SourceChange _$SourceChangeFromJson(Map json) => SourceChange( - message: json['message'] as String, - edits: (json['edits'] as List) + message: json['message'] as String, + edits: + (json['edits'] as List) .map((e) => SourceEdit.fromJson(e as Map)) .toList(), - linkedEditGroups: (json['linkedEditGroups'] as List) + linkedEditGroups: + (json['linkedEditGroups'] as List) .map((e) => LinkedEditGroup.fromJson(e as Map)) .toList(), - selectionOffset: (json['selectionOffset'] as num?)?.toInt(), - ); + selectionOffset: (json['selectionOffset'] as num?)?.toInt(), +); Map _$SourceChangeToJson(SourceChange instance) => { @@ -166,10 +159,10 @@ Map _$SourceChangeToJson(SourceChange instance) => }; SourceEdit _$SourceEditFromJson(Map json) => SourceEdit( - offset: (json['offset'] as num).toInt(), - length: (json['length'] as num).toInt(), - replacement: json['replacement'] as String, - ); + offset: (json['offset'] as num).toInt(), + length: (json['length'] as num).toInt(), + replacement: json['replacement'] as String, +); Map _$SourceEditToJson(SourceEdit instance) => { @@ -180,13 +173,17 @@ Map _$SourceEditToJson(SourceEdit instance) => LinkedEditGroup _$LinkedEditGroupFromJson(Map json) => LinkedEditGroup( - offsets: (json['offsets'] as List) - .map((e) => (e as num).toInt()) - .toList(), + offsets: + (json['offsets'] as List) + .map((e) => (e as num).toInt()) + .toList(), length: (json['length'] as num).toInt(), - suggestions: (json['suggestions'] as List) - .map((e) => LinkedEditSuggestion.fromJson(e as Map)) - .toList(), + suggestions: + (json['suggestions'] as List) + .map( + (e) => LinkedEditSuggestion.fromJson(e as Map), + ) + .toList(), ); Map _$LinkedEditGroupToJson(LinkedEditGroup instance) => @@ -197,18 +194,15 @@ Map _$LinkedEditGroupToJson(LinkedEditGroup instance) => }; LinkedEditSuggestion _$LinkedEditSuggestionFromJson( - Map json) => - LinkedEditSuggestion( - value: json['value'] as String, - kind: json['kind'] as String, - ); + Map json, +) => LinkedEditSuggestion( + value: json['value'] as String, + kind: json['kind'] as String, +); Map _$LinkedEditSuggestionToJson( - LinkedEditSuggestion instance) => - { - 'value': instance.value, - 'kind': instance.kind, - }; + LinkedEditSuggestion instance, +) => {'value': instance.value, 'kind': instance.kind}; DocumentResponse _$DocumentResponseFromJson(Map json) => DocumentResponse( @@ -234,9 +228,12 @@ CompleteResponse _$CompleteResponseFromJson(Map json) => CompleteResponse( replacementOffset: (json['replacementOffset'] as num).toInt(), replacementLength: (json['replacementLength'] as num).toInt(), - suggestions: (json['suggestions'] as List) - .map((e) => CompletionSuggestion.fromJson(e as Map)) - .toList(), + suggestions: + (json['suggestions'] as List) + .map( + (e) => CompletionSuggestion.fromJson(e as Map), + ) + .toList(), ); Map _$CompleteResponseToJson(CompleteResponse instance) => @@ -247,36 +244,37 @@ Map _$CompleteResponseToJson(CompleteResponse instance) => }; CompletionSuggestion _$CompletionSuggestionFromJson( - Map json) => - CompletionSuggestion( - kind: json['kind'] as String, - relevance: (json['relevance'] as num).toInt(), - completion: json['completion'] as String, - deprecated: json['deprecated'] as bool, - selectionOffset: (json['selectionOffset'] as num).toInt(), - displayText: json['displayText'] as String?, - parameterNames: (json['parameterNames'] as List?) + Map json, +) => CompletionSuggestion( + kind: json['kind'] as String, + relevance: (json['relevance'] as num).toInt(), + completion: json['completion'] as String, + deprecated: json['deprecated'] as bool, + selectionOffset: (json['selectionOffset'] as num).toInt(), + displayText: json['displayText'] as String?, + parameterNames: + (json['parameterNames'] as List?) ?.map((e) => e as String) .toList(), - returnType: json['returnType'] as String?, - elementKind: json['elementKind'] as String?, - elementParameters: json['elementParameters'] as String?, - ); + returnType: json['returnType'] as String?, + elementKind: json['elementKind'] as String?, + elementParameters: json['elementParameters'] as String?, +); Map _$CompletionSuggestionToJson( - CompletionSuggestion instance) => - { - 'kind': instance.kind, - 'relevance': instance.relevance, - 'completion': instance.completion, - 'deprecated': instance.deprecated, - 'selectionOffset': instance.selectionOffset, - 'displayText': instance.displayText, - 'parameterNames': instance.parameterNames, - 'returnType': instance.returnType, - 'elementKind': instance.elementKind, - 'elementParameters': instance.elementParameters, - }; + CompletionSuggestion instance, +) => { + 'kind': instance.kind, + 'relevance': instance.relevance, + 'completion': instance.completion, + 'deprecated': instance.deprecated, + 'selectionOffset': instance.selectionOffset, + 'displayText': instance.displayText, + 'parameterNames': instance.parameterNames, + 'returnType': instance.returnType, + 'elementKind': instance.elementKind, + 'elementParameters': instance.elementParameters, +}; VersionResponse _$VersionResponseFromJson(Map json) => VersionResponse( @@ -284,12 +282,14 @@ VersionResponse _$VersionResponseFromJson(Map json) => flutterVersion: json['flutterVersion'] as String, engineVersion: json['engineVersion'] as String, serverRevision: json['serverRevision'] as String?, - experiments: (json['experiments'] as List) - .map((e) => e as String) - .toList(), - packages: (json['packages'] as List) - .map((e) => PackageInfo.fromJson(e as Map)) - .toList(), + experiments: + (json['experiments'] as List) + .map((e) => e as String) + .toList(), + packages: + (json['packages'] as List) + .map((e) => PackageInfo.fromJson(e as Map)) + .toList(), ); Map _$VersionResponseToJson(VersionResponse instance) => @@ -303,30 +303,22 @@ Map _$VersionResponseToJson(VersionResponse instance) => }; OpenInIdxRequest _$OpenInIdxRequestFromJson(Map json) => - OpenInIdxRequest( - code: json['code'] as String, - ); + OpenInIdxRequest(code: json['code'] as String); Map _$OpenInIdxRequestToJson(OpenInIdxRequest instance) => - { - 'code': instance.code, - }; + {'code': instance.code}; OpenInIdxResponse _$OpenInIdxResponseFromJson(Map json) => - OpenInIdxResponse( - idxUrl: json['idxUrl'] as String, - ); + OpenInIdxResponse(idxUrl: json['idxUrl'] as String); Map _$OpenInIdxResponseToJson(OpenInIdxResponse instance) => - { - 'idxUrl': instance.idxUrl, - }; + {'idxUrl': instance.idxUrl}; PackageInfo _$PackageInfoFromJson(Map json) => PackageInfo( - name: json['name'] as String, - version: json['version'] as String, - supported: json['supported'] as bool, - ); + name: json['name'] as String, + version: json['version'] as String, + supported: json['supported'] as bool, +); Map _$PackageInfoToJson(PackageInfo instance) => { diff --git a/pkgs/dartpad_shared/lib/services.dart b/pkgs/dartpad_shared/lib/services.dart index 05c79da71..01f6ed296 100644 --- a/pkgs/dartpad_shared/lib/services.dart +++ b/pkgs/dartpad_shared/lib/services.dart @@ -42,11 +42,17 @@ class ServicesClient { Future compileNewDDC(CompileRequest request) => _requestPost( - 'compileNewDDC', request.toJson(), CompileDDCResponse.fromJson); + 'compileNewDDC', + request.toJson(), + CompileDDCResponse.fromJson, + ); Future compileNewDDCReload(CompileRequest request) => _requestPost( - 'compileNewDDCReload', request.toJson(), CompileDDCResponse.fromJson); + 'compileNewDDCReload', + request.toJson(), + CompileDDCResponse.fromJson, + ); Future openInIdx(OpenInIdxRequest request) => _requestPost('openInIDX', request.toJson(), OpenInIdxResponse.fromJson); @@ -64,7 +70,8 @@ class ServicesClient { } else { try { return responseFactory( - json.decode(response.body) as Map); + json.decode(response.body) as Map, + ); } on FormatException catch (e) { throw ApiRequestError('$action: $e', response.body); } @@ -86,7 +93,8 @@ class ServicesClient { } else { try { return responseFactory( - json.decode(response.body) as Map); + json.decode(response.body) as Map, + ); } on FormatException catch (e) { throw ApiRequestError('$action: $e', response.body); } diff --git a/pkgs/dartpad_shared/pubspec.yaml b/pkgs/dartpad_shared/pubspec.yaml index 563003820..c7b37fc60 100644 --- a/pkgs/dartpad_shared/pubspec.yaml +++ b/pkgs/dartpad_shared/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none resolution: workspace environment: - sdk: ^3.6.1 + sdk: ^3.7.0 dependencies: http: ^1.2.1 diff --git a/pkgs/dartpad_ui/lib/console.dart b/pkgs/dartpad_ui/lib/console.dart index be036ea00..35fb34d6a 100644 --- a/pkgs/dartpad_ui/lib/console.dart +++ b/pkgs/dartpad_ui/lib/console.dart @@ -49,48 +49,51 @@ class _ConsoleWidgetState extends State { return Container( decoration: BoxDecoration( color: theme.scaffoldBackgroundColor, - border: widget.showDivider - ? Border( - top: Divider.createBorderSide( - context, - width: 8.0, - color: theme.colorScheme.surface, - )) - : null, + border: + widget.showDivider + ? Border( + top: Divider.createBorderSide( + context, + width: 8.0, + color: theme.colorScheme.surface, + ), + ) + : null, ), padding: const EdgeInsets.all(denseSpacing), child: ValueListenableBuilder( valueListenable: widget.output, - builder: (context, value, _) => Stack( - children: [ - SizedBox.expand( - child: SingleChildScrollView( - controller: scrollController, - child: SelectableText( - value, - maxLines: null, - style: GoogleFonts.robotoMono( - fontSize: theme.textTheme.bodyMedium?.fontSize, + builder: + (context, value, _) => Stack( + children: [ + SizedBox.expand( + child: SingleChildScrollView( + controller: scrollController, + child: SelectableText( + value, + maxLines: null, + style: GoogleFonts.robotoMono( + fontSize: theme.textTheme.bodyMedium?.fontSize, + ), + ), ), ), - ), - ), - Padding( - padding: const EdgeInsets.all(denseSpacing), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MiniIconButton( - icon: Icons.playlist_remove, - tooltip: 'Clear console', - onPressed: value.isEmpty ? null : _clearConsole, + Padding( + padding: const EdgeInsets.all(denseSpacing), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MiniIconButton( + icon: Icons.playlist_remove, + tooltip: 'Clear console', + onPressed: value.isEmpty ? null : _clearConsole, + ), + ], ), - ], - ), + ), + ], ), - ], - ), ), ); } diff --git a/pkgs/dartpad_ui/lib/editor/codemirror.dart b/pkgs/dartpad_ui/lib/editor/codemirror.dart index 389b1c4fe..5cc918184 100644 --- a/pkgs/dartpad_ui/lib/editor/codemirror.dart +++ b/pkgs/dartpad_ui/lib/editor/codemirror.dart @@ -91,10 +91,17 @@ extension type Doc._(JSObject _) implements JSObject { external void setSelection(Position position, [Position head]); external JSArray getAllMarks(); external TextMarker markText( - Position from, Position to, MarkTextOptions options); + Position from, + Position to, + MarkTextOptions options, + ); external int? indexFromPos(Position pos); - external void replaceRange(String replacement, Position from, - [Position? to, String? origin]); + external void replaceRange( + String replacement, + Position from, [ + Position? to, + String? origin, + ]); external Position posFromIndex(int index); } @@ -125,10 +132,7 @@ extension type MarkTextOptions._(JSObject _) implements JSObject { external String className; external String title; - external factory MarkTextOptions({ - String className, - String title, - }); + external factory MarkTextOptions({String className, String title}); } @anonymous @@ -181,7 +185,7 @@ extension type Vim._(JSObject _) implements JSObject { external void exitInsertMode(CodeMirror cm); void handleEsc(CodeMirror cm) => switch (cm.getKeymap()) { - 'vim-insert' => exitInsertMode(cm), - _ => _, - }; + 'vim-insert' => exitInsertMode(cm), + _ => _, + }; } diff --git a/pkgs/dartpad_ui/lib/editor/editor.dart b/pkgs/dartpad_ui/lib/editor/editor.dart index fee9e9a9b..a757fa4c6 100644 --- a/pkgs/dartpad_ui/lib/editor/editor.dart +++ b/pkgs/dartpad_ui/lib/editor/editor.dart @@ -29,24 +29,28 @@ void _initViewFactory() { if (_viewFactoryInitialized) return; _viewFactoryInitialized = true; - ui_web.platformViewRegistry - .registerViewFactory(_viewType, _codeMirrorFactory); + ui_web.platformViewRegistry.registerViewFactory( + _viewType, + _codeMirrorFactory, + ); } web.Element _codeMirrorFactory(int viewId) { - final div = web.document.createElement('div') as web.HTMLDivElement - ..style.width = '100%' - ..style.height = '100%'; + final div = + web.document.createElement('div') as web.HTMLDivElement + ..style.width = '100%' + ..style.height = '100%'; codeMirrorInstance = CodeMirror( - div, - { - 'lineNumbers': true, - 'lineWrapping': true, - 'mode': 'dart', - 'theme': 'darkpad', - ...codeMirrorOptions, - }.jsify()); + div, + { + 'lineNumbers': true, + 'lineWrapping': true, + 'mode': 'dart', + 'theme': 'darkpad', + ...codeMirrorOptions, + }.jsify(), + ); CodeMirror.commands.goLineLeft = ((JSObject? _) => _handleGoLineLeft(codeMirrorInstance!)).toJS; @@ -75,11 +79,7 @@ class EditorWidget extends StatefulWidget { final AppModel appModel; final AppServices appServices; - EditorWidget({ - required this.appModel, - required this.appServices, - super.key, - }) { + EditorWidget({required this.appModel, required this.appServices, super.key}) { _initViewFactory(); } @@ -160,9 +160,9 @@ class _EditorWidgetState extends State implements EditorService { if (issue.location.line != -1) { codeMirror!.getDoc().setSelection( - Position(line: line, ch: column), - Position(line: line, ch: column + issue.location.charLength), - ); + Position(line: line, ch: column), + Position(line: line, ch: column + issue.location.charLength), + ); } else { codeMirror?.getDoc().setSelection(Position(line: 0, ch: 0)); } @@ -218,7 +218,9 @@ class _EditorWidgetState extends State implements EditorService { // Use a longer delay so that the platform view is displayed // correctly when compiled to Wasm Future.delayed( - const Duration(milliseconds: 80), () => codeMirror!.refresh()); + const Duration(milliseconds: 80), + () => codeMirror!.refresh(), + ); codeMirror!.on( 'change', @@ -242,38 +244,46 @@ class _EditorWidgetState extends State implements EditorService { ); appModel.sourceCodeController.addListener(_updateCodemirrorFromModel); - appModel.analysisIssues - .addListener(() => _updateIssues(appModel.analysisIssues.value)); + appModel.analysisIssues.addListener( + () => _updateIssues(appModel.analysisIssues.value), + ); appModel.vimKeymapsEnabled.addListener(_updateCodemirrorKeymap); widget.appServices.registerEditorService(this); - CodeMirror.commands.autocomplete = (CodeMirror codeMirror) { - _completions().then((completions) { - codeMirror.showHint( - HintOptions(hint: CodeMirror.hint.dart, results: completions)); - }); - return JSObject(); - }.toJS; + CodeMirror.commands.autocomplete = + (CodeMirror codeMirror) { + _completions().then((completions) { + codeMirror.showHint( + HintOptions(hint: CodeMirror.hint.dart, results: completions), + ); + }); + return JSObject(); + }.toJS; CodeMirror.registerHelper( - 'hint', - 'dart', - (CodeMirror editor, [HintOptions? options]) { - return options!.results; - }.toJS); + 'hint', + 'dart', + (CodeMirror editor, [HintOptions? options]) { + return options!.results; + }.toJS, + ); // Listen for document body to be visible, then force a code mirror refresh. final observer = web.IntersectionObserver( - (JSArray entries, - web.IntersectionObserver observer) { + ( + JSArray entries, + web.IntersectionObserver observer, + ) { for (final entry in entries.toDart) { if (entry.isIntersecting) { observer.unobserve(web.document.body!); // Use a longer delay so that the platform view is displayed // correctly when compiled to Wasm Future.delayed( - const Duration(milliseconds: 80), () => codeMirror!.refresh()); + const Duration(milliseconds: 80), + () => codeMirror!.refresh(), + ); return; } } @@ -311,8 +321,8 @@ class _EditorWidgetState extends State implements EditorService { child: HtmlElementView( key: _elementViewKey, viewType: _viewType, - onPlatformViewCreated: (id) => - _platformViewCreated(id, darkMode: darkMode), + onPlatformViewCreated: + (id) => _platformViewCreated(id, darkMode: darkMode), ), ); } @@ -324,8 +334,9 @@ class _EditorWidgetState extends State implements EditorService { widget.appServices.registerEditorService(null); - widget.appModel.sourceCodeController - .removeListener(_updateCodemirrorFromModel); + widget.appModel.sourceCodeController.removeListener( + _updateCodemirrorFromModel, + ); widget.appModel.appReady.removeListener(_updateEditableStatus); widget.appModel.vimKeymapsEnabled.removeListener(_updateCodemirrorKeymap); @@ -377,10 +388,7 @@ class _EditorWidgetState extends State implements EditorService { doc.markText( Position(line: line, ch: column), Position(line: line, ch: column + issue.location.charLength), - MarkTextOptions( - className: 'squiggle-$kind', - title: issue.message, - ), + MarkTextOptions(className: 'squiggle-$kind', title: issue.message), ); } } @@ -410,24 +418,27 @@ class _EditorWidgetState extends State implements EditorService { } return HintResults( - list: [ - ...response.fixes.map((change) => change.toHintResult(editor)), - ...response.assists.map((change) => change.toHintResult(editor)), - ].toJS, + list: + [ + ...response.fixes.map((change) => change.toHintResult(editor)), + ...response.assists.map((change) => change.toHintResult(editor)), + ].toJS, from: doc.posFromIndex(sourceOffset), to: doc.posFromIndex(0), ); } else { final response = await appServices.services .complete( - services.SourceRequest(source: source, offset: sourceOffset)) + services.SourceRequest(source: source, offset: sourceOffset), + ) .onError((error, st) => services.CompleteResponse.empty); final offset = response.replacementOffset; final length = response.replacementLength; - final hints = response.suggestions - .map((suggestion) => suggestion.toHintResult()) - .toList(); + final hints = + response.suggestions + .map((suggestion) => suggestion.toHintResult()) + .toList(); // Remove hints where both the replacement text and the display text are // the same. @@ -491,15 +502,10 @@ void _weHandleElsewhere(CodeMirror editor) { const codeMirrorOptions = { 'autoCloseBrackets': true, - 'autoCloseTags': { - 'whenOpening': true, - 'whenClosing': true, - }, + 'autoCloseTags': {'whenOpening': true, 'whenClosing': true}, 'autofocus': false, 'cursorHeight': 0.85, - 'continueComments': { - 'continueLineComment': false, - }, + 'continueComments': {'continueLineComment': false}, 'extraKeys': { 'Esc': '...', 'Esc Tab': false, @@ -522,32 +528,22 @@ const codeMirrorOptions = { 'Shift-Cmd-F': 'weHandleElsewhere', 'Cmd-Alt-F': false, }, - 'gutters': [ - 'CodeMirror-linenumbers', - ], + 'gutters': ['CodeMirror-linenumbers'], 'highlightSelectionMatches': { 'style': 'highlight-selection-matches', 'showToken': false, 'annotateScrollbar': true, }, - 'hintOptions': { - 'completeSingle': false, - }, + 'hintOptions': {'completeSingle': false}, 'indentUnit': 2, 'matchBrackets': true, - 'matchTags': { - 'bothTags': true, - }, + 'matchTags': {'bothTags': true}, 'tabSize': 2, 'viewportMargin': 100, 'scrollbarStyle': 'simple', }; -enum CompletionType { - auto, - manual, - quickfix, -} +enum CompletionType { auto, manual, quickfix } extension CompletionSuggestionExtension on services.CompletionSuggestion { HintResult toHintResult() { diff --git a/pkgs/dartpad_ui/lib/embed.dart b/pkgs/dartpad_ui/lib/embed.dart index 129b9f05c..cc72e8f70 100644 --- a/pkgs/dartpad_ui/lib/embed.dart +++ b/pkgs/dartpad_ui/lib/embed.dart @@ -17,8 +17,10 @@ void handleEmbedMessage(AppServices services, {bool runOnInject = false}) { web.window.addEventListener( 'message', (web.MessageEvent event) { - if (event.data case _SourceCodeMessage(:final type?, :final sourceCode?) - when type == 'sourceCode') { + if (event.data case _SourceCodeMessage( + :final type?, + :final sourceCode?, + ) when type == 'sourceCode') { if (sourceCode.isNotEmpty) { services.appModel.sourceCodeController.text = sourceCode; if (runOnInject) { diff --git a/pkgs/dartpad_ui/lib/execution/execution.dart b/pkgs/dartpad_ui/lib/execution/execution.dart index ca3897ab1..1312fa725 100644 --- a/pkgs/dartpad_ui/lib/execution/execution.dart +++ b/pkgs/dartpad_ui/lib/execution/execution.dart @@ -28,15 +28,16 @@ void _initViewFactory() { web.Element _iFrameFactory(int viewId) { // 'allow-popups' allows plugins like url_launcher to open popups. - final frame = web.document.createElement('iframe') as web.HTMLIFrameElement - ..sandbox.add('allow-scripts') - ..sandbox.add('allow-popups') - ..sandbox.add('allow-popups-to-escape-sandbox') - ..allow += 'clipboard-write; ' - ..src = 'frame.html' - ..style.border = 'none' - ..style.width = '100%' - ..style.height = '100%'; + final frame = + web.document.createElement('iframe') as web.HTMLIFrameElement + ..sandbox.add('allow-scripts') + ..sandbox.add('allow-popups') + ..sandbox.add('allow-popups-to-escape-sandbox') + ..allow += 'clipboard-write; ' + ..src = 'frame.html' + ..style.border = 'none' + ..style.width = '100%' + ..style.height = '100%'; executionServiceInstance = ExecutionServiceImpl(frame); @@ -78,8 +79,9 @@ class _ExecutionWidgetState extends State { key: _elementViewKey, viewType: _viewType, onPlatformViewCreated: (int id) { - widget.appServices - .registerExecutionService(executionServiceInstance); + widget.appServices.registerExecutionService( + executionServiceInstance, + ); }, ), ); diff --git a/pkgs/dartpad_ui/lib/execution/frame.dart b/pkgs/dartpad_ui/lib/execution/frame.dart index d36972531..dc8250ee9 100644 --- a/pkgs/dartpad_ui/lib/execution/frame.dart +++ b/pkgs/dartpad_ui/lib/execution/frame.dart @@ -125,10 +125,12 @@ function contextLoaded() { }'''); if (isFlutter) { script.writeln( - 'require(["dart_sdk_new", "flutter_web_new", "ddc_module_loader"], contextLoaded);'); + 'require(["dart_sdk_new", "flutter_web_new", "ddc_module_loader"], contextLoaded);', + ); } else { script.writeln( - 'require(["dart_sdk_new", "ddc_module_loader"], contextLoaded);'); + 'require(["dart_sdk_new", "ddc_module_loader"], contextLoaded);', + ); } } else { // Redirect print messages to the host. @@ -212,10 +214,7 @@ require(["dartpad_main", "dart_sdk"], function(dartpad_main, dart_sdk) { Future _send(String command, Map params) { // TODO: Use dartpad.dev instead of '*'? _frame.contentWindowCrossOrigin?.postMessage( - { - 'command': command, - ...params, - }.jsify(), + {'command': command, ...params}.jsify(), '*'.toJS, ); return Future.value(); @@ -234,10 +233,12 @@ require(["dartpad_main", "dart_sdk"], function(dartpad_main, dart_sdk) { _frame = clone; } - return _readyCompleter.future.timeout(const Duration(seconds: 1), - onTimeout: () { - if (!_readyCompleter.isCompleted) _readyCompleter.complete(); - }); + return _readyCompleter.future.timeout( + const Duration(seconds: 1), + onTimeout: () { + if (!_readyCompleter.isCompleted) _readyCompleter.complete(); + }, + ); } void _initListener() { diff --git a/pkgs/dartpad_ui/lib/flutter_samples.dart b/pkgs/dartpad_ui/lib/flutter_samples.dart index 82c15fe72..10a42ad8a 100644 --- a/pkgs/dartpad_ui/lib/flutter_samples.dart +++ b/pkgs/dartpad_ui/lib/flutter_samples.dart @@ -14,16 +14,17 @@ class FlutterSampleLoader { // There are only two hosted versions of the docs: master/main and stable. final sampleUrl = switch (channel) { 'master' || - 'main' => - 'https://main-api.flutter.dev/snippets/$sampleId.dart', + 'main' => 'https://main-api.flutter.dev/snippets/$sampleId.dart', _ => 'https://api.flutter.dev/snippets/$sampleId.dart', }; final response = await client.get(Uri.parse(sampleUrl)); if (response.statusCode != 200) { - throw Exception('Unable to load sample ' - '(${response.statusCode} ${response.reasonPhrase}})'); + throw Exception( + 'Unable to load sample ' + '(${response.statusCode} ${response.reasonPhrase}})', + ); } return response.body; diff --git a/pkgs/dartpad_ui/lib/gists.dart b/pkgs/dartpad_ui/lib/gists.dart index c9b3b44e7..5d85ffbd3 100644 --- a/pkgs/dartpad_ui/lib/gists.dart +++ b/pkgs/dartpad_ui/lib/gists.dart @@ -11,12 +11,15 @@ class GistLoader { final http.Client client = http.Client(); Future load(String gistId) async { - final response = - await client.get(Uri.parse('https://api.github.com/gists/$gistId')); + final response = await client.get( + Uri.parse('https://api.github.com/gists/$gistId'), + ); if (response.statusCode != 200) { - throw Exception('Unable to load gist ' - '(${response.statusCode} ${response.reasonPhrase}})'); + throw Exception( + 'Unable to load gist ' + '(${response.statusCode} ${response.reasonPhrase}})', + ); } return Gist.fromJson(jsonDecode(response.body) as Map); @@ -46,7 +49,7 @@ class Gist { } factory Gist.fromJson(Map json) { -/* { + /* { "id": "d3bd83918d21b6d5f778bdc69c3d36d6", "description": "Fibonacci", "owner": { @@ -77,10 +80,11 @@ class Gist { id: json['id'] as String, description: json['description'] as String?, owner: owner['login'] as String?, - files: files.values - .cast>() - .map(GistFile.fromJson) - .toList(), + files: + files.values + .cast>() + .map(GistFile.fromJson) + .toList(), ); } @@ -98,14 +102,17 @@ class Gist { } void _validateGist() { - final file = - files.singleWhereOrNull((file) => file.fileName.endsWith('.dart')); + final file = files.singleWhereOrNull( + (file) => file.fileName.endsWith('.dart'), + ); if (file == null) { validationIssues.add('Warning: no Dart file found in the gist'); } else if (file.fileName != defaultFileName) { - validationIssues.add('Warning: no gist content in $defaultFileName ' - '(loading from ${file.fileName})'); + validationIssues.add( + 'Warning: no gist content in $defaultFileName ' + '(loading from ${file.fileName})', + ); } } } diff --git a/pkgs/dartpad_ui/lib/keys.dart b/pkgs/dartpad_ui/lib/keys.dart index d4fb0639a..56d3482ea 100644 --- a/pkgs/dartpad_ui/lib/keys.dart +++ b/pkgs/dartpad_ui/lib/keys.dart @@ -86,19 +86,12 @@ extension SingleActivatorExtension on SingleActivator { ), borderRadius: const BorderRadius.all(Radius.circular(4)), ), - padding: const EdgeInsets.symmetric( - vertical: 2, - horizontal: 6, - ), + padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 6), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (shift) - const Icon( - Icons.arrow_upward, - size: 16, - color: subtleColor, - ), + const Icon(Icons.arrow_upward, size: 16, color: subtleColor), if (alt) Icon( _mac ? Icons.keyboard_option_key : Icons.keyboard_alt, diff --git a/pkgs/dartpad_ui/lib/main.dart b/pkgs/dartpad_ui/lib/main.dart index 02faaa4a5..e4be4929c 100644 --- a/pkgs/dartpad_ui/lib/main.dart +++ b/pkgs/dartpad_ui/lib/main.dart @@ -44,9 +44,7 @@ void main() async { } class DartPadApp extends StatefulWidget { - const DartPadApp({ - super.key, - }); + const DartPadApp({super.key}); @override State createState() => _DartPadAppState(); @@ -56,14 +54,15 @@ class _DartPadAppState extends State { late final GoRouter router = GoRouter( initialLocation: '/', routes: [ - GoRoute( - path: '/', - builder: _homePageBuilder, - ), + GoRoute(path: '/', builder: _homePageBuilder), GoRoute( path: '/:gistId', - builder: (context, state) => _homePageBuilder(context, state, - gist: state.pathParameters['gistId']), + builder: + (context, state) => _homePageBuilder( + context, + state, + gist: state.pathParameters['gistId'], + ), ), ], ); @@ -118,8 +117,11 @@ class _DartPadAppState extends State { }); } - Widget _homePageBuilder(BuildContext context, GoRouterState state, - {String? gist}) { + Widget _homePageBuilder( + BuildContext context, + GoRouterState state, { + String? gist, + }) { final gistId = gist ?? state.uri.queryParameters['id']; final builtinSampleId = state.uri.queryParameters['sample']; final flutterSampleId = state.uri.queryParameters['sample_id']; @@ -156,9 +158,7 @@ class _DartPadAppState extends State { ), brightness: Brightness.light, dividerColor: lightDividerColor, - dividerTheme: const DividerThemeData( - color: lightDividerColor, - ), + dividerTheme: const DividerThemeData(color: lightDividerColor), scaffoldBackgroundColor: Colors.white, menuButtonTheme: MenuButtonThemeData( style: MenuItemButton.styleFrom( @@ -179,9 +179,7 @@ class _DartPadAppState extends State { ), brightness: Brightness.dark, dividerColor: darkDividerColor, - dividerTheme: const DividerThemeData( - color: darkDividerColor, - ), + dividerTheme: const DividerThemeData(color: darkDividerColor), textButtonTheme: const TextButtonThemeData( style: ButtonStyle( foregroundColor: WidgetStatePropertyAll(darkLinkButtonColor), @@ -216,10 +214,10 @@ class DartPadMainPage extends StatefulWidget { this.builtinSampleId, this.flutterSampleId, }) : super( - key: ValueKey( - 'sample:$builtinSampleId gist:$gistId flutter:$flutterSampleId', - ), - ); + key: ValueKey( + 'sample:$builtinSampleId gist:$gistId flutter:$flutterSampleId', + ), + ); @override State createState() => _DartPadMainPageState(); @@ -233,8 +231,9 @@ class _DartPadMainPageState extends State late final TabController tabController; final Key _executionWidgetKey = GlobalKey(debugLabel: 'execution-widget'); - final ValueKey _loadingOverlayKey = - const ValueKey('loading-overlay-widget'); + final ValueKey _loadingOverlayKey = const ValueKey( + 'loading-overlay-widget', + ); final ValueKey _editorKey = const ValueKey('editor'); final ValueKey _consoleKey = const ValueKey('console'); final ValueKey _tabBarKey = const ValueKey('tab-bar'); @@ -245,50 +244,47 @@ class _DartPadMainPageState extends State void initState() { super.initState(); - tabController = TabController(length: 2, vsync: this) - ..addListener( - () { - // Rebuild when the user changes tabs so that the IndexedStack updates - // its active child view. - setState(() {}); - }, - ); + tabController = TabController(length: 2, vsync: this)..addListener(() { + // Rebuild when the user changes tabs so that the IndexedStack updates + // its active child view. + setState(() {}); + }); final leftPanelSize = widget.embedMode ? 0.62 : 0.50; - mainSplitter = - SplitViewController(weights: [leftPanelSize, 1.0 - leftPanelSize]) - ..addListener(() { - appModel.splitDragStateManager.handleSplitChanged(); - }); + mainSplitter = SplitViewController( + weights: [leftPanelSize, 1.0 - leftPanelSize], + )..addListener(() { + appModel.splitDragStateManager.handleSplitChanged(); + }); - final channel = widget.initialChannel != null - ? Channel.forName(widget.initialChannel!) - : null; + final channel = + widget.initialChannel != null + ? Channel.forName(widget.initialChannel!) + : null; appModel = AppModel(); - appServices = AppServices( - appModel, - channel ?? Channel.defaultChannel, - ); + appServices = AppServices(appModel, channel ?? Channel.defaultChannel); appServices.populateVersions(); appServices .performInitialLoad( - gistId: widget.gistId, - sampleId: widget.builtinSampleId, - flutterSampleId: widget.flutterSampleId, - channel: widget.initialChannel, - keybinding: LocalStorage.instance.getUserKeybinding(), - getFallback: () => - LocalStorage.instance.getUserCode() ?? Samples.defaultSnippet(), - ) + gistId: widget.gistId, + sampleId: widget.builtinSampleId, + flutterSampleId: widget.flutterSampleId, + channel: widget.initialChannel, + keybinding: LocalStorage.instance.getUserKeybinding(), + getFallback: + () => + LocalStorage.instance.getUserCode() ?? + Samples.defaultSnippet(), + ) .then((value) { - // Start listening for inject code messages. - handleEmbedMessage(appServices, runOnInject: widget.runOnLoad); - if (widget.runOnLoad) { - appServices.performCompileAndRun(); - } - }); + // Start listening for inject code messages. + handleEmbedMessage(appServices, runOnInject: widget.runOnLoad); + if (widget.runOnLoad) { + appServices.performCompileAndRun(); + } + }); appModel.compilingState.addListener(_handleRunStarted); } @@ -328,10 +324,7 @@ class _DartPadMainPageState extends State final tabBar = TabBar( controller: tabController, - tabs: const [ - Tab(text: 'Code'), - Tab(text: 'Output'), - ], + tabs: const [Tab(text: 'Code'), Tab(text: 'Output')], // Remove the divider line at the bottom of the tab bar. dividerHeight: 0, key: _tabBarKey, @@ -346,8 +339,9 @@ class _DartPadMainPageState extends State return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { final domHeight = mode.calcDomHeight(constraints.maxHeight); - final consoleHeight = - mode.calcConsoleHeight(constraints.maxHeight); + final consoleHeight = mode.calcConsoleHeight( + constraints.maxHeight, + ); return Column( children: [ @@ -370,69 +364,67 @@ class _DartPadMainPageState extends State ], ); - final scaffold = LayoutBuilder(builder: (context, constraints) { - // Use the mobile UI layout for small screen widths. - if (constraints.maxWidth <= smallScreenWidth) { - return Scaffold( - key: _scaffoldKey, - appBar: widget.embedMode - ? tabBar - : DartPadAppBar( - theme: theme, - appServices: appServices, - appModel: appModel, - widget: widget, - bottom: tabBar, - ), - body: Column( - children: [ - Expanded( - child: IndexedStack( - index: tabController.index, - children: [ - editor, - executionStack, - ], - ), - ), - if (!widget.embedMode) - const StatusLineWidget(mobileVersion: true), - ], - ), - ); - } else { - // Return the desktop UI. - return Scaffold( - key: _scaffoldKey, - appBar: widget.embedMode - ? null - : DartPadAppBar( - theme: theme, - appServices: appServices, - appModel: appModel, - widget: widget, + final scaffold = LayoutBuilder( + builder: (context, constraints) { + // Use the mobile UI layout for small screen widths. + if (constraints.maxWidth <= smallScreenWidth) { + return Scaffold( + key: _scaffoldKey, + appBar: + widget.embedMode + ? tabBar + : DartPadAppBar( + theme: theme, + appServices: appServices, + appModel: appModel, + widget: widget, + bottom: tabBar, + ), + body: Column( + children: [ + Expanded( + child: IndexedStack( + index: tabController.index, + children: [editor, executionStack], + ), ), - body: Column( - children: [ - Expanded( - child: SplitView( - viewMode: SplitViewMode.Horizontal, - gripColor: theme.colorScheme.surface, - gripColorActive: theme.colorScheme.surface, - gripSize: defaultGripSize, - controller: mainSplitter, - children: [ - editor, - executionStack, - ], + if (!widget.embedMode) + const StatusLineWidget(mobileVersion: true), + ], + ), + ); + } else { + // Return the desktop UI. + return Scaffold( + key: _scaffoldKey, + appBar: + widget.embedMode + ? null + : DartPadAppBar( + theme: theme, + appServices: appServices, + appModel: appModel, + widget: widget, + ), + body: Column( + children: [ + Expanded( + child: SplitView( + viewMode: SplitViewMode.Horizontal, + gripColor: theme.colorScheme.surface, + gripColorActive: theme.colorScheme.surface, + gripSize: defaultGripSize, + controller: mainSplitter, + children: [editor, executionStack], + ), ), - ), - if (!widget.embedMode) const StatusLineWidget(), - ], - ), - ); - } - }); + if (!widget.embedMode) const StatusLineWidget(), + ], + ), + ); + } + }, + ); return Provider.value( value: appServices, @@ -474,10 +466,7 @@ class _DartPadMainPageState extends State appServices.editorService?.showQuickFixes(); }, }, - child: Focus( - autofocus: true, - child: scaffold, - ), + child: Focus(autofocus: true, child: scaffold), ), ), ); @@ -520,10 +509,7 @@ class _DartPadMainPageState extends State } class LoadingOverlay extends StatelessWidget { - const LoadingOverlay({ - super.key, - required this.appModel, - }); + const LoadingOverlay({super.key, required this.appModel}); final AppModel appModel; @@ -542,9 +528,10 @@ class LoadingOverlay extends StatelessWidget { color: color.withValues(alpha: compiling ? 0.8 : 0), duration: animationDelay, curve: animationCurve, - child: compiling - ? const GoldenRatioCenter(child: CircularProgressIndicator()) - : const SizedBox(width: 1), + child: + compiling + ? const GoldenRatioCenter(child: CircularProgressIndicator()) + : const SizedBox(width: 1), ); }, ); @@ -569,68 +556,72 @@ class DartPadAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, constraints) { - return AppBar( - backgroundColor: theme.colorScheme.surface, - title: SizedBox( - height: toolbarItemHeight, - child: Row( - children: [ - const Logo(width: 32, type: 'dart'), - const SizedBox(width: denseSpacing), - Text(appName, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface)), - // Hide new snippet buttons when the screen width is too small. - if (constraints.maxWidth > smallScreenWidth) ...[ - const SizedBox(width: defaultSpacing * 4), - NewSnippetWidget(appServices: appServices), + return LayoutBuilder( + builder: (context, constraints) { + return AppBar( + backgroundColor: theme.colorScheme.surface, + title: SizedBox( + height: toolbarItemHeight, + child: Row( + children: [ + const Logo(width: 32, type: 'dart'), const SizedBox(width: denseSpacing), - const ListSamplesWidget(), - ] else ...[ - const SizedBox(width: defaultSpacing), - NewSnippetWidget(appServices: appServices, smallIcon: true), - const SizedBox(width: defaultSpacing), - const ListSamplesWidget(smallIcon: true), - ], + Text( + appName, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + ), + ), + // Hide new snippet buttons when the screen width is too small. + if (constraints.maxWidth > smallScreenWidth) ...[ + const SizedBox(width: defaultSpacing * 4), + NewSnippetWidget(appServices: appServices), + const SizedBox(width: denseSpacing), + const ListSamplesWidget(), + ] else ...[ + const SizedBox(width: defaultSpacing), + NewSnippetWidget(appServices: appServices, smallIcon: true), + const SizedBox(width: defaultSpacing), + const ListSamplesWidget(smallIcon: true), + ], - const SizedBox(width: defaultSpacing), - // Hide the snippet title when the screen width is too small. - if (constraints.maxWidth > smallScreenWidth) - Expanded( - child: Center( - child: ValueListenableBuilder( - valueListenable: appModel.title, - builder: (_, String value, __) => Text(value), + const SizedBox(width: defaultSpacing), + // Hide the snippet title when the screen width is too small. + if (constraints.maxWidth > smallScreenWidth) + Expanded( + child: Center( + child: ValueListenableBuilder( + valueListenable: appModel.title, + builder: (_, String value, __) => Text(value), + ), ), ), - ), - const SizedBox(width: defaultSpacing), - ], - ), - ), - bottom: bottom, - actions: [ - // Hide the Install SDK button when the screen width is too small. - if (constraints.maxWidth > smallScreenWidth) - ContinueInMenu( - openInIdx: _openInIDX, + const SizedBox(width: defaultSpacing), + ], ), - const SizedBox(width: denseSpacing), - _BrightnessButton( - handleBrightnessChange: widget.handleBrightnessChanged, ), - const OverflowMenu(), - ], - ); - }); + bottom: bottom, + actions: [ + // Hide the Install SDK button when the screen width is too small. + if (constraints.maxWidth > smallScreenWidth) + ContinueInMenu(openInIdx: _openInIDX), + const SizedBox(width: denseSpacing), + _BrightnessButton( + handleBrightnessChange: widget.handleBrightnessChanged, + ), + const OverflowMenu(), + ], + ); + }, + ); } @override // kToolbarHeight is set to 56.0 in the framework. - Size get preferredSize => bottom == null - ? const Size(double.infinity, 56.0) - : const Size(double.infinity, 112.0); + Size get preferredSize => + bottom == null + ? const Size(double.infinity, 56.0) + : const Size(double.infinity, 112.0); Future _openInIDX() async { final code = appModel.sourceCodeController.text; @@ -664,10 +655,7 @@ class EditorWithButtons extends StatelessWidget { child: SectionWidget( child: Stack( children: [ - EditorWidget( - appModel: appModel, - appServices: appServices, - ), + EditorWidget(appModel: appModel, appServices: appServices), Padding( padding: const EdgeInsets.symmetric( vertical: denseSpacing, @@ -714,21 +702,21 @@ class EditorWithButtons extends StatelessWidget { const SizedBox(width: defaultSpacing), // Run action ValueListenableBuilder( - valueListenable: appModel.showReload, - builder: (_, bool value, __) { - if (!value) return const SizedBox(); - return ValueListenableBuilder( - valueListenable: appModel.canReload, - builder: (_, bool value, __) { - return PointerInterceptor( - child: ReloadButton( - onPressed: - value ? onCompileAndReload : null, - ), - ); - }, - ); - }), + valueListenable: appModel.showReload, + builder: (_, bool value, __) { + if (!value) return const SizedBox(); + return ValueListenableBuilder( + valueListenable: appModel.canReload, + builder: (_, bool value, __) { + return PointerInterceptor( + child: ReloadButton( + onPressed: value ? onCompileAndReload : null, + ), + ); + }, + ); + }, + ), const SizedBox(width: defaultSpacing), // Run action ValueListenableBuilder( @@ -748,9 +736,7 @@ class EditorWithButtons extends StatelessWidget { Container( alignment: Alignment.bottomRight, padding: const EdgeInsets.all(denseSpacing), - child: StatusWidget( - status: appModel.editorStatus, - ), + child: StatusWidget(status: appModel.editorStatus), ), ], ), @@ -806,10 +792,7 @@ class EditorWithButtons extends StatelessWidget { } return MediumDialog( title: title, - child: DocsWidget( - appModel: appModel, - documentResponse: result, - ), + child: DocsWidget(appModel: appModel, documentResponse: result), ); }, ); @@ -827,10 +810,7 @@ class EditorWithButtons extends StatelessWidget { class StatusLineWidget extends StatelessWidget { final bool mobileVersion; - const StatusLineWidget({ - this.mobileVersion = false, - super.key, - }); + const StatusLineWidget({this.mobileVersion = false, super.key}); @override Widget build(BuildContext context) { @@ -839,9 +819,7 @@ class StatusLineWidget extends StatelessWidget { final appModel = Provider.of(context); return Container( - decoration: BoxDecoration( - color: theme.colorScheme.surface, - ), + decoration: BoxDecoration(color: theme.colorScheme.surface), padding: const EdgeInsets.symmetric( vertical: denseSpacing, horizontal: defaultSpacing, @@ -852,17 +830,19 @@ class StatusLineWidget extends StatelessWidget { message: 'Keyboard shortcuts', waitDuration: tooltipDelay, child: TextButton( - onPressed: () => showDialog( - context: context, - builder: (context) => MediumDialog( - title: 'Keyboard shortcuts', - smaller: true, - child: KeyBindingsTable( - bindings: keys.keyBindings, - appModel: appModel, + onPressed: + () => showDialog( + context: context, + builder: + (context) => MediumDialog( + title: 'Keyboard shortcuts', + smaller: true, + child: KeyBindingsTable( + bindings: keys.keyBindings, + appModel: appModel, + ), + ), ), - ), - ), child: Icon( Icons.keyboard, color: Theme.of(context).colorScheme.onPrimary, @@ -954,16 +934,8 @@ class NewSnippetWidget extends StatelessWidget { final bool smallIcon; static const _menuItems = [ - ( - label: 'Dart snippet', - icon: Logo(type: 'dart'), - kind: 'dart', - ), - ( - label: 'Flutter snippet', - icon: Logo(type: 'flutter'), - kind: 'flutter', - ), + (label: 'Dart snippet', icon: Logo(type: 'dart'), kind: 'dart'), + (label: 'Flutter snippet', icon: Logo(type: 'flutter'), kind: 'flutter'), ]; const NewSnippetWidget({ @@ -999,7 +971,7 @@ class NewSnippetWidget extends StatelessWidget { ), onPressed: () => appServices.resetTo(type: item.kind), ), - ) + ), ], ); } @@ -1035,22 +1007,20 @@ class ListSamplesWidget extends StatelessWidget { in Samples.categories.entries) ...[ MenuItemButton( onPressed: null, - child: Text( - category, - style: Theme.of(context).textTheme.bodyLarge, - ), + child: Text(category, style: Theme.of(context).textTheme.bodyLarge), ), for (final sample in samples) MenuItemButton( leadingIcon: Logo(type: sample.icon), - onPressed: () => - GoRouter.of(context).replaceQueryParam('sample', sample.id), + onPressed: + () => + GoRouter.of(context).replaceQueryParam('sample', sample.id), child: Padding( padding: const EdgeInsets.only(right: 32), child: Text(sample.name), ), ), - ] + ], ]; return menuItems.map((e) => PointerInterceptor(child: e)).toList(); @@ -1058,9 +1028,7 @@ class ListSamplesWidget extends StatelessWidget { } class SelectChannelWidget extends StatelessWidget { - const SelectChannelWidget({ - super.key, - }); + const SelectChannelWidget({super.key}); @override Widget build(BuildContext context) { @@ -1069,27 +1037,28 @@ class SelectChannelWidget extends StatelessWidget { return ValueListenableBuilder( valueListenable: appServices.channel, - builder: (context, Channel value, _) => MenuAnchor( - builder: (context, MenuController controller, Widget? child) { - return TextButton.icon( - onPressed: () => controller.toggleMenuState(), - icon: const Icon(Icons.tune, size: smallIconSize), - label: Text('${value.displayName} channel'), - ); - }, - menuChildren: [ - for (final channel in channels) - PointerInterceptor( - child: MenuItemButton( - onPressed: () => _onTap(context, channel), - child: Padding( - padding: const EdgeInsets.fromLTRB(0, 0, 32, 0), - child: Text('${channel.displayName} channel'), + builder: + (context, Channel value, _) => MenuAnchor( + builder: (context, MenuController controller, Widget? child) { + return TextButton.icon( + onPressed: () => controller.toggleMenuState(), + icon: const Icon(Icons.tune, size: smallIconSize), + label: Text('${value.displayName} channel'), + ); + }, + menuChildren: [ + for (final channel in channels) + PointerInterceptor( + child: MenuItemButton( + onPressed: () => _onTap(context, channel), + child: Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 32, 0), + child: Text('${channel.displayName} channel'), + ), + ), ), - ), - ), - ], - ), + ], + ), ); } @@ -1112,13 +1081,10 @@ class OverflowMenu extends StatelessWidget { const OverflowMenu({super.key}); static const _menuItems = [ - ( - label: 'Install SDK', - uri: 'https://flutter.dev/get-started', - ), + (label: 'Install SDK', uri: 'https://flutter.dev/get-started'), ( label: 'Sharing guide', - uri: 'https://github.com/dart-lang/dart-pad/wiki/Sharing-Guide' + uri: 'https://github.com/dart-lang/dart-pad/wiki/Sharing-Guide', ), ]; @@ -1142,7 +1108,7 @@ class OverflowMenu extends StatelessWidget { child: Text(item.label), ), ), - ) + ), ], ); } @@ -1177,7 +1143,7 @@ class ContinueInMenu extends StatelessWidget { child: Text('IDX'), ), ), - ].map((widget) => PointerInterceptor(child: widget)) + ].map((widget) => PointerInterceptor(child: widget)), ], ); } @@ -1232,7 +1198,8 @@ class KeyBindingsTable extends StatelessWidget { } first = false; children.add( - (shortcut as SingleActivator).renderToWidget(context)); + (shortcut as SingleActivator).renderToWidget(context), + ); } return Row(children: children); }, @@ -1241,9 +1208,7 @@ class KeyBindingsTable extends StatelessWidget { ), ), const Divider(), - _VimModeSwitch( - appModel: appModel, - ), + _VimModeSwitch(appModel: appModel), ], ); } @@ -1252,10 +1217,7 @@ class KeyBindingsTable extends StatelessWidget { class VersionInfoWidget extends StatefulWidget { final ValueListenable versions; - const VersionInfoWidget( - this.versions, { - super.key, - }); + const VersionInfoWidget(this.versions, {super.key}); @override State createState() => _VersionInfoWidgetState(); @@ -1293,9 +1255,7 @@ class _VersionInfoWidgetState extends State { } class _BrightnessButton extends StatelessWidget { - const _BrightnessButton({ - required this.handleBrightnessChange, - }); + const _BrightnessButton({required this.handleBrightnessChange}); final void Function(BuildContext, bool) handleBrightnessChange; @@ -1306,9 +1266,10 @@ class _BrightnessButton extends StatelessWidget { preferBelow: true, message: 'Toggle brightness', child: IconButton( - icon: Theme.of(context).brightness == Brightness.light - ? const Icon(Icons.dark_mode_outlined) - : const Icon(Icons.light_mode_outlined), + icon: + Theme.of(context).brightness == Brightness.light + ? const Icon(Icons.dark_mode_outlined) + : const Icon(Icons.light_mode_outlined), onPressed: () { handleBrightnessChange(context, !isBright); }, @@ -1320,9 +1281,7 @@ class _BrightnessButton extends StatelessWidget { class _VimModeSwitch extends StatelessWidget { final AppModel appModel; - const _VimModeSwitch({ - required this.appModel, - }); + const _VimModeSwitch({required this.appModel}); @override Widget build(BuildContext context) { diff --git a/pkgs/dartpad_ui/lib/model.dart b/pkgs/dartpad_ui/lib/model.dart index ab68b74e8..683789c9e 100644 --- a/pkgs/dartpad_ui/lib/model.dart +++ b/pkgs/dartpad_ui/lib/model.dart @@ -54,8 +54,9 @@ class AppModel { final ValueNotifier consoleOutput = ValueNotifier(''); final ValueNotifier formattingBusy = ValueNotifier(false); - final ValueNotifier compilingState = - ValueNotifier(CompilingState.none); + final ValueNotifier compilingState = ValueNotifier( + CompilingState.none, + ); final ValueNotifier docHelpBusy = ValueNotifier(false); final ValueNotifier hasRun = ValueNotifier(false); @@ -68,8 +69,9 @@ class AppModel { final ValueNotifier _layoutMode = ValueNotifier(LayoutMode.both); ValueListenable get layoutMode => _layoutMode; - final ValueNotifier splitViewDragState = - ValueNotifier(SplitDragState.inactive); + final ValueNotifier splitViewDragState = ValueNotifier( + SplitDragState.inactive, + ); final SplitDragStateManager splitDragStateManager = SplitDragStateManager(); late final StreamSubscription _splitSubscription; @@ -84,9 +86,11 @@ class AppModel { AppModel() { consoleOutput.addListener(_recalcLayout); - void updateCanReload() => canReload.value = hasRun.value && - !compilingState.value.busy && - currentDeltaDill.value != null; + void updateCanReload() => + canReload.value = + hasRun.value && + !compilingState.value.busy && + currentDeltaDill.value != null; hasRun.addListener(updateCanReload); compilingState.addListener(updateCanReload); currentDeltaDill.addListener(updateCanReload); @@ -98,8 +102,9 @@ class AppModel { useNewDDC.addListener(updateShowReload); _appIsFlutter.addListener(updateShowReload); - _splitSubscription = - splitDragStateManager.onSplitDragUpdated.listen((SplitDragState value) { + _splitSubscription = splitDragStateManager.onSplitDragUpdated.listen(( + SplitDragState value, + ) { splitViewDragState.value = value; }); } @@ -190,8 +195,9 @@ class AppServices { appModel.analysisIssues.addListener(_updateEditorProblemsStatus); void updateUseNewDDC() { - appModel.useNewDDC.value = - _hotReloadableChannels.contains(_channel.value); + appModel.useNewDDC.value = _hotReloadableChannels.contains( + _channel.value, + ); } updateUseNewDDC(); @@ -212,8 +218,9 @@ class AppServices { void resetTo({String? type}) { type ??= 'dart'; - final source = - Samples.defaultSnippet(forFlutter: type.toLowerCase() == 'flutter'); + final source = Samples.defaultSnippet( + forFlutter: type.toLowerCase() == 'flutter', + ); // Reset the source. appModel.sourceCodeController.text = source; @@ -265,8 +272,9 @@ class AppServices { if (flutterSampleId != null) { final loader = FlutterSampleLoader(); - final progress = - appModel.editorStatus.showMessage(initialText: 'Loading…'); + final progress = appModel.editorStatus.showMessage( + initialText: 'Loading…', + ); try { final sample = await loader.loadFlutterSample( sampleId: flutterSampleId, @@ -295,8 +303,9 @@ class AppServices { if (gistId != null) { final gistLoader = GistLoader(); - final progress = - appModel.editorStatus.showMessage(initialText: 'Loading…'); + final progress = appModel.editorStatus.showMessage( + initialText: 'Loading…', + ); try { final gist = await gistLoader.load(gistId); progress.close(); @@ -350,16 +359,21 @@ class AppServices { final willUseReload = reload && appModel.useNewDDC.value; final source = appModel.sourceCodeController.text; - final progress = appModel.editorStatus - .showMessage(initialText: willUseReload ? 'Reloading…' : 'Compiling…'); + final progress = appModel.editorStatus.showMessage( + initialText: willUseReload ? 'Reloading…' : 'Compiling…', + ); try { CompileDDCResponse response; if (!appModel.useNewDDC.value) { response = await _compileDDC(CompileRequest(source: source)); } else if (reload) { - response = await _compileNewDDCReload(CompileRequest( - source: source, deltaDill: appModel.currentDeltaDill.value!)); + response = await _compileNewDDCReload( + CompileRequest( + source: source, + deltaDill: appModel.currentDeltaDill.value!, + ), + ); } else { response = await _compileNewDDC(CompileRequest(source: source)); } @@ -442,7 +456,8 @@ class AppServices { } Future _compileNewDDCReload( - CompileRequest request) async { + CompileRequest request, + ) async { try { appModel.compilingState.value = CompilingState.reloading; return await services.compileNewDDCReload(request); @@ -460,8 +475,9 @@ class AppServices { // register the new if (_executionService != null) { - stdoutSub = - _executionService!.onStdout.listen(appModel.appendLineToConsole); + stdoutSub = _executionService!.onStdout.listen( + appModel.appendLineToConsole, + ); } } @@ -524,8 +540,10 @@ class AppServices { } else { final message = '${issues.length} ${pluralize('issue', issues.length)}'; if (progress == null) { - appModel.editorStatus - .showMessage(initialText: message, name: 'problems'); + appModel.editorStatus.showMessage( + initialText: message, + name: 'problems', + ); } else { progress.updateText(message); } @@ -572,12 +590,15 @@ class SplitDragStateManager { StreamController.broadcast(); late final Stream onSplitDragUpdated; - SplitDragStateManager( - {Duration timeout = const Duration(milliseconds: 100)}) { - onSplitDragUpdated = _splitDragStateController.stream.timeout(timeout, - onTimeout: (eventSink) { - eventSink.add(SplitDragState.inactive); - }); + SplitDragStateManager({ + Duration timeout = const Duration(milliseconds: 100), + }) { + onSplitDragUpdated = _splitDragStateController.stream.timeout( + timeout, + onTimeout: (eventSink) { + eventSink.add(SplitDragState.inactive); + }, + ); } void handleSplitChanged() { diff --git a/pkgs/dartpad_ui/lib/problems.dart b/pkgs/dartpad_ui/lib/problems.dart index c81514b1e..3ead1fac2 100644 --- a/pkgs/dartpad_ui/lib/problems.dart +++ b/pkgs/dartpad_ui/lib/problems.dart @@ -19,10 +19,7 @@ const _rowPadding = 2.0; class ProblemsTableWidget extends StatelessWidget { final List problems; - const ProblemsTableWidget({ - required this.problems, - super.key, - }); + const ProblemsTableWidget({required this.problems, super.key}); @override Widget build(BuildContext context) { @@ -35,7 +32,8 @@ class ProblemsTableWidget extends StatelessWidget { var height = 0.0; // ignore: prefer_is_empty if (problems.length > 0) { - height = lineHeight * math.min(problems.length, visibleIssues) + + height = + lineHeight * math.min(problems.length, visibleIssues) + 1 + denseSpacing * 2; } @@ -45,9 +43,7 @@ class ProblemsTableWidget extends StatelessWidget { duration: animationDelay, curve: animationCurve, child: Container( - decoration: BoxDecoration( - color: colorScheme.surfaceContainerHighest, - ), + decoration: BoxDecoration(color: colorScheme.surfaceContainerHighest), padding: const EdgeInsets.all(denseSpacing), child: ListView.builder( itemCount: problems.length, @@ -65,10 +61,7 @@ class ProblemWidget extends StatelessWidget { final MenuController _menuController = MenuController(); final AnalysisIssue issue; - ProblemWidget({ - required this.issue, - super.key, - }); + ProblemWidget({required this.issue, super.key}); @override Widget build(BuildContext context) { @@ -84,7 +77,8 @@ class ProblemWidget extends StatelessWidget { issue.errorIcon, size: smallIconSize, color: issue.colorFor( - darkMode: colorScheme.brightness == Brightness.dark), + darkMode: colorScheme.brightness == Brightness.dark, + ), ), const SizedBox(width: denseSpacing), Expanded( @@ -100,7 +94,7 @@ class ProblemWidget extends StatelessWidget { overflow: TextOverflow.clip, textAlign: TextAlign.end, style: subtleText, - ) + ), ], ), if (issue.correction case final correction?) ...[ @@ -171,16 +165,16 @@ class ProblemWidget extends StatelessWidget { extension AnalysisIssueExtension on AnalysisIssue { Color colorFor({bool darkMode = true}) => switch (kind) { - 'error' => darkMode ? darkErrorColor : lightErrorColor, - 'warning' => darkMode ? darkWarningColor : lightWarningColor, - 'info' => darkMode ? darkInfoColor : lightInfoColor, - _ => darkMode ? darkIssueColor : lightIssueColor - }; + 'error' => darkMode ? darkErrorColor : lightErrorColor, + 'warning' => darkMode ? darkWarningColor : lightWarningColor, + 'info' => darkMode ? darkInfoColor : lightInfoColor, + _ => darkMode ? darkIssueColor : lightIssueColor, + }; IconData get errorIcon => switch (kind) { - 'error' => Icons.error_outline, - 'warning' => Icons.warning_outlined, - 'info' => Icons.info_outline, - _ => Icons.error_outline - }; + 'error' => Icons.error_outline, + 'warning' => Icons.warning_outlined, + 'info' => Icons.info_outline, + _ => Icons.error_outline, + }; } diff --git a/pkgs/dartpad_ui/lib/samples.g.dart b/pkgs/dartpad_ui/lib/samples.g.dart index 72effebf0..1d00858ef 100644 --- a/pkgs/dartpad_ui/lib/samples.g.dart +++ b/pkgs/dartpad_ui/lib/samples.g.dart @@ -42,18 +42,9 @@ abstract final class Samples { ]; static const Map> categories = { - 'Dart': [ - _fibonacci, - _helloWorld, - ], - 'Flutter': [ - _counter, - _sunflower, - ], - 'Ecosystem': [ - _flameGame, - _googleSdk, - ], + 'Dart': [_fibonacci, _helloWorld], + 'Flutter': [_counter, _sunflower], + 'Ecosystem': [_flameGame, _googleSdk], }; static Sample? getById(String? id) => all.firstWhereOrNull((s) => s.id == id); diff --git a/pkgs/dartpad_ui/lib/utils.dart b/pkgs/dartpad_ui/lib/utils.dart index ca6b991b6..90fc1034c 100644 --- a/pkgs/dartpad_ui/lib/utils.dart +++ b/pkgs/dartpad_ui/lib/utils.dart @@ -24,8 +24,9 @@ RelativeRect calculatePopupMenuPosition( }) { final render = context.findRenderObject() as RenderBox; final size = render.size; - final offset = - render.localToGlobal(Offset(0, growUpwards ? -size.height : size.height)); + final offset = render.localToGlobal( + Offset(0, growUpwards ? -size.height : size.height), + ); return RelativeRect.fromLTRB( offset.dx, @@ -104,8 +105,9 @@ class StatusController { return message; } - final ValueNotifier _state = - ValueNotifier(MessageStatus.empty); + final ValueNotifier _state = ValueNotifier( + MessageStatus.empty, + ); ValueListenable get state => _state; @@ -147,8 +149,8 @@ class Message { MessageState _state = MessageState.opening; Message._(StatusController parent, String message, {this.name}) - : _parent = parent, - _message = message; + : _parent = parent, + _message = message; MessageState get state => _state; @@ -163,8 +165,10 @@ class Message { } class MessageStatus { - static final MessageStatus empty = - MessageStatus(message: '', state: MessageState.closing); + static final MessageStatus empty = MessageStatus( + message: '', + state: MessageState.closing, + ); final String message; final MessageState state; @@ -184,11 +188,7 @@ class MessageStatus { String toString() => '[$state] $message'; } -enum MessageState { - opening, - showing, - closing; -} +enum MessageState { opening, showing, closing } extension StringUtils on String { String? get nullIfEmpty => isEmpty ? null : this; diff --git a/pkgs/dartpad_ui/lib/versions.dart b/pkgs/dartpad_ui/lib/versions.dart index 88c39e94c..731a2acd9 100644 --- a/pkgs/dartpad_ui/lib/versions.dart +++ b/pkgs/dartpad_ui/lib/versions.dart @@ -11,16 +11,14 @@ import 'theme.dart'; class VersionTable extends StatelessWidget { final VersionResponse version; - const VersionTable({ - required this.version, - super.key, - }); + const VersionTable({required this.version, super.key}); @override Widget build(BuildContext context) { final packages = version.packages.where((p) => p.supported).toList(); - var versionText = 'Based on Dart SDK ${version.dartVersion} ' + var versionText = + 'Based on Dart SDK ${version.dartVersion} ' 'and Flutter SDK ${version.flutterVersion}'; final experiments = version.experiments.join(', '); if (experiments.isNotEmpty) { diff --git a/pkgs/dartpad_ui/lib/widgets.dart b/pkgs/dartpad_ui/lib/widgets.dart index deb9304a5..c6456d32b 100644 --- a/pkgs/dartpad_ui/lib/widgets.dart +++ b/pkgs/dartpad_ui/lib/widgets.dart @@ -14,12 +14,7 @@ class Hyperlink extends StatefulWidget { final String? displayText; final TextStyle? style; - const Hyperlink({ - required this.url, - this.displayText, - this.style, - super.key, - }); + const Hyperlink({required this.url, this.displayText, this.style, super.key}); @override State createState() => _HyperlinkState(); @@ -95,26 +90,18 @@ class MiniIconButton extends StatelessWidget { class RunButton extends ActionButton { const RunButton({super.key, super.onPressed}) - : super( - text: 'Run', - icon: const Icon( - Icons.play_arrow, - color: Colors.black, - size: 20, - ), - ); + : super( + text: 'Run', + icon: const Icon(Icons.play_arrow, color: Colors.black, size: 20), + ); } class ReloadButton extends ActionButton { const ReloadButton({super.key, super.onPressed}) - : super( - text: 'Reload', - icon: const Icon( - Icons.refresh, - color: Colors.black, - size: 20, - ), - ); + : super( + text: 'Reload', + icon: const Icon(Icons.refresh, color: Colors.black, size: 20), + ); } abstract class ActionButton extends StatelessWidget { @@ -122,8 +109,12 @@ abstract class ActionButton extends StatelessWidget { final String text; final Icon icon; - const ActionButton( - {this.onPressed, super.key, required this.text, required this.icon}); + const ActionButton({ + this.onPressed, + super.key, + required this.text, + required this.icon, + }); @override Widget build(BuildContext context) { @@ -132,27 +123,25 @@ abstract class ActionButton extends StatelessWidget { waitDuration: tooltipDelay, child: TextButton( style: ButtonStyle( - shape: const WidgetStatePropertyAll(RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(4)))), - backgroundColor: WidgetStateProperty.resolveWith( - (states) { - if (states.contains(WidgetState.disabled)) { - return runButtonColor.withValues(alpha: 0.4); - } - - return runButtonColor; - }, + shape: const WidgetStatePropertyAll( + RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(4)), + ), ), + backgroundColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.disabled)) { + return runButtonColor.withValues(alpha: 0.4); + } + + return runButtonColor; + }), ), onPressed: onPressed, child: Row( children: [ icon, const SizedBox(width: 8), - Text( - text, - style: const TextStyle(color: Colors.black), - ), + Text(text, style: const TextStyle(color: Colors.black)), ], ), ), @@ -165,10 +154,7 @@ abstract class ActionButton extends StatelessWidget { class StatusWidget extends StatelessWidget { final StatusController status; - const StatusWidget({ - required this.status, - super.key, - }); + const StatusWidget({required this.status, super.key}); @override Widget build(BuildContext context) { @@ -185,9 +171,10 @@ class StatusWidget extends StatelessWidget { builder: (context, MessageStatus status, _) { return AnimatedOpacity( opacity: status.state == MessageState.closing ? 0.0 : 1.0, - duration: status.state == MessageState.showing - ? Duration.zero - : animationDelay, + duration: + status.state == MessageState.showing + ? Duration.zero + : animationDelay, curve: animationCurve, child: Material( shape: const StadiumBorder(), @@ -223,54 +210,55 @@ class MediumDialog extends StatelessWidget { @override Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, constraints) { - final width = smaller ? 400.0 : 500.0; - final height = smaller ? 325.0 : 400.0; - final theme = Theme.of(context); - - return PointerInterceptor( - child: AlertDialog( - backgroundColor: theme.scaffoldBackgroundColor, - title: Text(title, maxLines: 1), - contentTextStyle: theme.textTheme.bodyMedium, - contentPadding: const EdgeInsets.fromLTRB(24, defaultSpacing, 24, 8), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: width, - height: height, - child: ClipRect(child: child), + return LayoutBuilder( + builder: (context, constraints) { + final width = smaller ? 400.0 : 500.0; + final height = smaller ? 325.0 : 400.0; + final theme = Theme.of(context); + + return PointerInterceptor( + child: AlertDialog( + backgroundColor: theme.scaffoldBackgroundColor, + title: Text(title, maxLines: 1), + contentTextStyle: theme.textTheme.bodyMedium, + contentPadding: const EdgeInsets.fromLTRB( + 24, + defaultSpacing, + 24, + 8, + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: width, + height: height, + child: ClipRect(child: child), + ), + const Divider(), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('OK'), ), - const Divider(), ], ), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('OK'), - ), - ], - ), - ); - }); + ); + }, + ); } } class GoldenRatioCenter extends StatelessWidget { final Widget child; - const GoldenRatioCenter({ - required this.child, - super.key, - }); + const GoldenRatioCenter({required this.child, super.key}); @override Widget build(BuildContext context) { - return Align( - alignment: const Alignment(0.0, -(1.618 / 4)), - child: child, - ); + return Align(alignment: const Alignment(0.0, -(1.618 / 4)), child: child); } } @@ -279,7 +267,7 @@ final class Logo extends StatelessWidget { final double width; const Logo({super.key, this.width = defaultIconSize, String? type}) - : _type = type; + : _type = type; @override Widget build(BuildContext context) { diff --git a/pkgs/dartpad_ui/pubspec.yaml b/pkgs/dartpad_ui/pubspec.yaml index a304d44e5..6c87deed3 100644 --- a/pkgs/dartpad_ui/pubspec.yaml +++ b/pkgs/dartpad_ui/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none resolution: workspace environment: - sdk: ^3.6.1 + sdk: ^3.7.0 dependencies: collection: ^1.19.0 diff --git a/pkgs/dartpad_ui/test/autosave_test.dart b/pkgs/dartpad_ui/test/autosave_test.dart index 28d23c450..54681ca96 100644 --- a/pkgs/dartpad_ui/test/autosave_test.dart +++ b/pkgs/dartpad_ui/test/autosave_test.dart @@ -34,9 +34,7 @@ void main() { LocalStorage.instance.saveUserCode(''); expect(LocalStorage.instance.getUserCode(), isNull); - await services.performInitialLoad( - getFallback: getFallback, - ); + await services.performInitialLoad(getFallback: getFallback); expect(model.sourceCodeController.text, equals(Samples.defaultSnippet())); }); @@ -49,9 +47,7 @@ void main() { final services = AppServices(model, channel); expect(LocalStorage.instance.getUserCode(), equals(sample)); - await services.performInitialLoad( - getFallback: getFallback, - ); + await services.performInitialLoad(getFallback: getFallback); expect(model.sourceCodeController.text, equals(sample)); }); diff --git a/pkgs/dartpad_ui/test/gists_test.dart b/pkgs/dartpad_ui/test/gists_test.dart index b63a9f4f1..aa30f8ed7 100644 --- a/pkgs/dartpad_ui/test/gists_test.dart +++ b/pkgs/dartpad_ui/test/gists_test.dart @@ -10,8 +10,9 @@ import 'package:test/test.dart'; void main() { group('gists', () { test('parses json', () { - final gist = - Gist.fromJson(jsonDecode(jsonSample) as Map); + final gist = Gist.fromJson( + jsonDecode(jsonSample) as Map, + ); expect(gist.id, 'd3bd83918d21b6d5f778bdc69c3d36d6'); expect(gist.description, 'Fibonacci'); @@ -20,29 +21,33 @@ void main() { }); test('finds main.dart', () { - final gist = - Gist.fromJson(jsonDecode(jsonSample) as Map); + final gist = Gist.fromJson( + jsonDecode(jsonSample) as Map, + ); expect(gist.mainDartSource, isNotNull); }); test('recognizes main.dart missing', () { - final gist = - Gist.fromJson(jsonDecode(jsonSampleNoMain) as Map); + final gist = Gist.fromJson( + jsonDecode(jsonSampleNoMain) as Map, + ); expect(gist.mainDartSource, isNull); }); test('validates main.dart missing', () { - final gist = - Gist.fromJson(jsonDecode(jsonSampleNoMain) as Map); + final gist = Gist.fromJson( + jsonDecode(jsonSampleNoMain) as Map, + ); expect(gist.validationIssues, isNotEmpty); }); test('validates unexpected dart content file', () { final gist = Gist.fromJson( - jsonDecode(jsonSampleAlternativeFile) as Map); + jsonDecode(jsonSampleAlternativeFile) as Map, + ); expect(gist.validationIssues, isNotEmpty); }); diff --git a/pkgs/dartpad_ui/test/model_test.dart b/pkgs/dartpad_ui/test/model_test.dart index 54b5d2d35..8dabeaadb 100644 --- a/pkgs/dartpad_ui/test/model_test.dart +++ b/pkgs/dartpad_ui/test/model_test.dart @@ -14,16 +14,7 @@ void main() { test('supported channels', () { final result = Channel.valuesWithoutLocalhost.map((c) => c.name).toList(); - expect( - result, - unorderedMatches( - [ - 'main', - 'beta', - 'stable', - ], - ), - ); + expect(result, unorderedMatches(['main', 'beta', 'stable'])); }); }); } diff --git a/pkgs/samples/lib/brick_breaker.dart b/pkgs/samples/lib/brick_breaker.dart index 313d93c3d..fcc8c3133 100644 --- a/pkgs/samples/lib/brick_breaker.dart +++ b/pkgs/samples/lib/brick_breaker.dart @@ -50,10 +50,7 @@ class _GameAppState extends State { gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [ - Color(0xffa9d6e5), - Color(0xfff2e8cf), - ], + colors: [Color(0xffa9d6e5), Color(0xfff2e8cf)], ), ), child: SafeArea( @@ -64,9 +61,7 @@ class _GameAppState extends State { child: SizedBox( width: gameWidth, height: gameHeight, - child: GameWidget( - game: game, - ), + child: GameWidget(game: game), ), ), ), @@ -81,9 +76,12 @@ class _GameAppState extends State { class BrickBreaker extends FlameGame with HasCollisionDetection, KeyboardEvents, TapDetector { BrickBreaker() - : super( - camera: CameraComponent.withFixedResolution( - width: gameWidth, height: gameHeight)); + : super( + camera: CameraComponent.withFixedResolution( + width: gameWidth, + height: gameHeight, + ), + ); final rand = math.Random(); double get width => size.x; @@ -102,20 +100,27 @@ class BrickBreaker extends FlameGame world.removeAll(world.children.query()); world.removeAll(world.children.query()); - world.add(Ball( - difficultyModifier: difficultyModifier, - radius: ballRadius, - position: size / 2, - velocity: - Vector2((rand.nextDouble() - 0.5) * width, height * 0.3).normalized() - ..scale(height / 4), - )); - - world.add(Paddle( - size: Vector2(paddleWidth, paddleHeight), - cornerRadius: const Radius.circular(ballRadius / 2), - position: Vector2(width / 2, height * 0.95), - )); + world.add( + Ball( + difficultyModifier: difficultyModifier, + radius: ballRadius, + position: size / 2, + velocity: + Vector2( + (rand.nextDouble() - 0.5) * width, + height * 0.3, + ).normalized() + ..scale(height / 4), + ), + ); + + world.add( + Paddle( + size: Vector2(paddleWidth, paddleHeight), + cornerRadius: const Radius.circular(ballRadius / 2), + position: Vector2(width / 2, height * 0.95), + ), + ); world.addAll([ for (var i = 0; i < brickColors.length; i++) @@ -162,12 +167,14 @@ class Ball extends CircleComponent required double radius, required this.difficultyModifier, }) : super( - radius: radius, - anchor: Anchor.center, - paint: Paint() - ..color = const Color(0xff1e6091) - ..style = PaintingStyle.fill, - children: [CircleHitbox()]); + radius: radius, + anchor: Anchor.center, + paint: + Paint() + ..color = const Color(0xff1e6091) + ..style = PaintingStyle.fill, + children: [CircleHitbox()], + ); final Vector2 velocity; final double difficultyModifier; @@ -180,7 +187,9 @@ class Ball extends CircleComponent @override void onCollisionStart( - Set intersectionPoints, PositionComponent other) { + Set intersectionPoints, + PositionComponent other, + ) { super.onCollisionStart(intersectionPoints, other); if (other is PlayArea) { if (intersectionPoints.first.y <= 0) { @@ -190,16 +199,19 @@ class Ball extends CircleComponent } else if (intersectionPoints.first.x >= game.width) { velocity.x = -velocity.x; } else if (intersectionPoints.first.y >= game.height) { - add(RemoveEffect( - delay: 0.35, - onComplete: () { - game.startGame(); - }, - )); + add( + RemoveEffect( + delay: 0.35, + onComplete: () { + game.startGame(); + }, + ), + ); } } else if (other is Paddle) { velocity.y = -velocity.y; - velocity.x = velocity.x + + velocity.x = + velocity.x + (position.x - other.position.x) / other.size.x * game.width * 0.3; } else if (other is Brick) { if (position.y < other.position.y - other.size.y / 2) { @@ -226,9 +238,10 @@ class Paddle extends PositionComponent final Radius cornerRadius; - final _paint = Paint() - ..color = const Color(0xff1e6091) - ..style = PaintingStyle.fill; + final _paint = + Paint() + ..color = const Color(0xff1e6091) + ..style = PaintingStyle.fill; @override void update(double dt) { @@ -237,12 +250,16 @@ class Paddle extends PositionComponent final keysPressed = HardwareKeyboard.instance.logicalKeysPressed; if (keysPressed.contains(LogicalKeyboardKey.arrowLeft) || keysPressed.contains(LogicalKeyboardKey.keyA)) { - position.x = - (position.x - (dt * 500)).clamp(width / 2, game.width - width / 2); + position.x = (position.x - (dt * 500)).clamp( + width / 2, + game.width - width / 2, + ); } else if (keysPressed.contains(LogicalKeyboardKey.arrowRight) || keysPressed.contains(LogicalKeyboardKey.keyD)) { - position.x = - (position.x + (dt * 500)).clamp(width / 2, game.width - width / 2); + position.x = (position.x + (dt * 500)).clamp( + width / 2, + game.width - width / 2, + ); } } @@ -250,10 +267,7 @@ class Paddle extends PositionComponent void render(Canvas canvas) { super.render(canvas); canvas.drawRRect( - RRect.fromRectAndRadius( - Offset.zero & size.toSize(), - cornerRadius, - ), + RRect.fromRectAndRadius(Offset.zero & size.toSize(), cornerRadius), _paint, ); } @@ -262,27 +276,32 @@ class Paddle extends PositionComponent void onDragUpdate(DragUpdateEvent event) { if (isRemoved) return; super.onDragUpdate(event); - position.x = (position.x + event.localDelta.x) - .clamp(width / 2, game.width - width / 2); + position.x = (position.x + event.localDelta.x).clamp( + width / 2, + game.width - width / 2, + ); } } class Brick extends RectangleComponent with CollisionCallbacks, HasGameReference { Brick(Vector2 position, Color color) - : super( - position: position, - size: Vector2(brickWidth, brickHeight), - anchor: Anchor.center, - paint: Paint() - ..color = color - ..style = PaintingStyle.fill, - children: [RectangleHitbox()], - ); + : super( + position: position, + size: Vector2(brickWidth, brickHeight), + anchor: Anchor.center, + paint: + Paint() + ..color = color + ..style = PaintingStyle.fill, + children: [RectangleHitbox()], + ); @override void onCollisionStart( - Set intersectionPoints, PositionComponent other) { + Set intersectionPoints, + PositionComponent other, + ) { super.onCollisionStart(intersectionPoints, other); removeFromParent(); diff --git a/pkgs/samples/lib/default_flutter.dart b/pkgs/samples/lib/default_flutter.dart index 277190914..ed9d0cc13 100644 --- a/pkgs/samples/lib/default_flutter.dart +++ b/pkgs/samples/lib/default_flutter.dart @@ -15,11 +15,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return const MaterialApp( debugShowCheckedModeBanner: false, - home: Scaffold( - body: Center( - child: Text('Hello, World!'), - ), - ), + home: Scaffold(body: Center(child: Text('Hello, World!'))), ); } } diff --git a/pkgs/samples/lib/google_ai.dart b/pkgs/samples/lib/google_ai.dart index 966ce9597..b0766dd60 100644 --- a/pkgs/samples/lib/google_ai.dart +++ b/pkgs/samples/lib/google_ai.dart @@ -46,14 +46,14 @@ class _ChatScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), + appBar: AppBar(title: Text(widget.title)), body: switch (apiKey) { final providedKey? => ChatWidget(apiKey: providedKey), - _ => ApiKeyWidget(onSubmitted: (key) { + _ => ApiKeyWidget( + onSubmitted: (key) { setState(() => apiKey = key); - }), + }, + ), }, ); } @@ -82,18 +82,21 @@ class ApiKeyWidget extends StatelessWidget { Link( uri: Uri.https('makersuite.google.com', '/app/apikey'), target: LinkTarget.blank, - builder: (context, followLink) => TextButton( - onPressed: followLink, - child: const Text('Get an API Key'), - ), + builder: + (context, followLink) => TextButton( + onPressed: followLink, + child: const Text('Get an API Key'), + ), ), const SizedBox(height: 8), Row( children: [ Expanded( child: TextField( - decoration: - textFieldDecoration(context, 'Enter your API key'), + decoration: textFieldDecoration( + context, + 'Enter your API key', + ), controller: _textController, onSubmitted: (value) { onSubmitted(value); @@ -136,10 +139,7 @@ class _ChatWidgetState extends State { @override void initState() { super.initState(); - _model = GenerativeModel( - model: 'gemini-pro', - apiKey: widget.apiKey, - ); + _model = GenerativeModel(model: 'gemini-pro', apiKey: widget.apiKey); _chat = _model.startChat(); } @@ -147,9 +147,7 @@ class _ChatWidgetState extends State { WidgetsBinding.instance.addPostFrameCallback( (_) => _scrollController.animateTo( _scrollController.position.maxScrollExtent, - duration: const Duration( - milliseconds: 750, - ), + duration: const Duration(milliseconds: 750), curve: Curves.easeOutCirc, ), ); @@ -182,18 +180,17 @@ class _ChatWidgetState extends State { ), ), Padding( - padding: const EdgeInsets.symmetric( - vertical: 25, - horizontal: 15, - ), + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 15), child: Row( children: [ Expanded( child: TextField( autofocus: true, focusNode: _textFieldFocus, - decoration: - textFieldDecoration(context, 'Enter a prompt...'), + decoration: textFieldDecoration( + context, + 'Enter a prompt...', + ), controller: _textController, onSubmitted: (String value) { _sendChatMessage(value); @@ -227,9 +224,7 @@ class _ChatWidgetState extends State { }); try { - final response = await _chat.sendMessage( - Content.text(message), - ); + final response = await _chat.sendMessage(Content.text(message)); final text = response.text; if (text == null) { @@ -261,16 +256,14 @@ class _ChatWidgetState extends State { builder: (context) { return AlertDialog( title: const Text('Something went wrong'), - content: SingleChildScrollView( - child: Text(message), - ), + content: SingleChildScrollView(child: Text(message)), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, child: const Text('OK'), - ) + ), ], ); }, @@ -298,15 +291,13 @@ class MessageWidget extends StatelessWidget { child: Container( constraints: const BoxConstraints(maxWidth: 480), decoration: BoxDecoration( - color: isFromUser - ? Theme.of(context).colorScheme.primaryContainer - : Theme.of(context).colorScheme.surfaceContainerHighest, + color: + isFromUser + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(18), ), - padding: const EdgeInsets.symmetric( - vertical: 15, - horizontal: 20, - ), + padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 20), margin: const EdgeInsets.only(bottom: 8), child: MarkdownBody(data: text), ), @@ -321,19 +312,11 @@ InputDecoration textFieldDecoration(BuildContext context, String hintText) => contentPadding: const EdgeInsets.all(15), hintText: hintText, border: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(14), - ), - borderSide: BorderSide( - color: Theme.of(context).colorScheme.secondary, - ), + borderRadius: const BorderRadius.all(Radius.circular(14)), + borderSide: BorderSide(color: Theme.of(context).colorScheme.secondary), ), focusedBorder: OutlineInputBorder( - borderRadius: const BorderRadius.all( - Radius.circular(14), - ), - borderSide: BorderSide( - color: Theme.of(context).colorScheme.secondary, - ), + borderRadius: const BorderRadius.all(Radius.circular(14)), + borderSide: BorderSide(color: Theme.of(context).colorScheme.secondary), ), ); diff --git a/pkgs/samples/lib/main.dart b/pkgs/samples/lib/main.dart index 9c417491e..00db983f3 100644 --- a/pkgs/samples/lib/main.dart +++ b/pkgs/samples/lib/main.dart @@ -14,9 +14,7 @@ class MyApp extends StatelessWidget { return MaterialApp( title: 'Flutter Demo', debugShowCheckedModeBanner: false, - theme: ThemeData( - colorSchemeSeed: Colors.blue, - ), + theme: ThemeData(colorSchemeSeed: Colors.blue), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } @@ -25,10 +23,7 @@ class MyApp extends StatelessWidget { class MyHomePage extends StatefulWidget { final String title; - const MyHomePage({ - super.key, - required this.title, - }); + const MyHomePage({super.key, required this.title}); @override State createState() => _MyHomePageState(); @@ -46,16 +41,12 @@ class _MyHomePageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), + appBar: AppBar(title: Text(widget.title)), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( - 'You have pushed the button this many times:', - ), + const Text('You have pushed the button this many times:'), Text( '$_counter', style: Theme.of(context).textTheme.headlineMedium, diff --git a/pkgs/samples/lib/sunflower.dart b/pkgs/samples/lib/sunflower.dart index c7beffb3f..21c8e200d 100644 --- a/pkgs/samples/lib/sunflower.dart +++ b/pkgs/samples/lib/sunflower.dart @@ -33,16 +33,12 @@ class _SunflowerState extends State { ), debugShowCheckedModeBanner: false, home: Scaffold( - appBar: AppBar( - title: const Text('Sunflower'), - ), + appBar: AppBar(title: const Text('Sunflower')), body: Center( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Expanded( - child: SunflowerWidget(seeds), - ), + Expanded(child: SunflowerWidget(seeds)), const SizedBox(height: 20), Text('Showing ${seeds.round()} seeds'), SizedBox( @@ -84,26 +80,30 @@ class SunflowerWidget extends StatelessWidget { final theta = i * tau / phi; final r = math.sqrt(i) * scaleFactor; - seedWidgets.add(AnimatedAlign( - key: ValueKey(i), - duration: Duration(milliseconds: rng.nextInt(500) + 250), - curve: Curves.easeInOut, - alignment: Alignment(r * math.cos(theta), -1 * r * math.sin(theta)), - child: const Dot(true), - )); + seedWidgets.add( + AnimatedAlign( + key: ValueKey(i), + duration: Duration(milliseconds: rng.nextInt(500) + 250), + curve: Curves.easeInOut, + alignment: Alignment(r * math.cos(theta), -1 * r * math.sin(theta)), + child: const Dot(true), + ), + ); } for (var j = seeds; j < maxSeeds; j++) { final x = math.cos(tau * j / (maxSeeds - 1)) * 0.9; final y = math.sin(tau * j / (maxSeeds - 1)) * 0.9; - seedWidgets.add(AnimatedAlign( - key: ValueKey(j), - duration: Duration(milliseconds: rng.nextInt(500) + 250), - curve: Curves.easeInOut, - alignment: Alignment(x, y), - child: const Dot(false), - )); + seedWidgets.add( + AnimatedAlign( + key: ValueKey(j), + duration: Duration(milliseconds: rng.nextInt(500) + 250), + curve: Curves.easeInOut, + alignment: Alignment(x, y), + child: const Dot(false), + ), + ); } return FittedBox( @@ -132,10 +132,7 @@ class Dot extends StatelessWidget { color: lit ? Colors.orange : Colors.grey.shade700, borderRadius: BorderRadius.circular(radius), ), - child: const SizedBox( - height: size, - width: size, - ), + child: const SizedBox(height: size, width: size), ); } } diff --git a/pkgs/samples/pubspec.yaml b/pkgs/samples/pubspec.yaml index 98460b04c..485c9e228 100644 --- a/pkgs/samples/pubspec.yaml +++ b/pkgs/samples/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none resolution: workspace environment: - sdk: ^3.6.1 + sdk: ^3.7.0 dependencies: flame: ^1.25.0 diff --git a/pkgs/samples/tool/samples.dart b/pkgs/samples/tool/samples.dart index 5e2940100..ee9d206c5 100644 --- a/pkgs/samples/tool/samples.dart +++ b/pkgs/samples/tool/samples.dart @@ -9,11 +9,19 @@ import 'package:args/args.dart'; import 'package:path/path.dart' as p; void main(List args) { - final argParser = ArgParser() - ..addFlag('verify', - negatable: false, help: 'Verify the generated samples files.') - ..addFlag('help', - abbr: 'h', negatable: false, help: 'Display this help output.'); + final argParser = + ArgParser() + ..addFlag( + 'verify', + negatable: false, + help: 'Verify the generated samples files.', + ) + ..addFlag( + 'help', + abbr: 'h', + negatable: false, + help: 'Display this help output.', + ); final argResults = argParser.parse(args); @@ -31,20 +39,16 @@ void main(List args) { } } -const Set categories = { - 'Defaults', - 'Dart', - 'Flutter', - 'Ecosystem', -}; +const Set categories = {'Defaults', 'Dart', 'Flutter', 'Ecosystem'}; class Samples { late final List samples; void parse() { // read the samples - final json = - jsonDecode(File(p.join('lib', 'samples.json')).readAsStringSync()); + final json = jsonDecode( + File(p.join('lib', 'samples.json')).readAsStringSync(), + ); samples = (json as List).map((j) => Sample.fromJson(j)).toList(); @@ -236,7 +240,8 @@ class Sample implements Comparable { var gen = id; while (gen.contains('-')) { final index = id.indexOf('-'); - gen = gen.substring(0, index) + + gen = + gen.substring(0, index) + gen.substring(index + 1, index + 2).toUpperCase() + gen.substring(index + 2); } @@ -280,6 +285,9 @@ $source static String _idFromName(String name) => name.trim().toLowerCase().replaceAll(' ', '-'); - static final RegExp _copyrightCommentPattern = - RegExp(r'^\/\/ Copyright.*LICENSE file.', multiLine: true, dotAll: true); + static final RegExp _copyrightCommentPattern = RegExp( + r'^\/\/ Copyright.*LICENSE file.', + multiLine: true, + dotAll: true, + ); }