From f9688c1464bf2b8f9f39e02fef5820c2f19c1fc8 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 10 Sep 2026 13:52:19 -0400 Subject: [PATCH] Apply Scalafmt format (Scala 3 syntax) --- build.sbt | 64 +- .../main/scala/sbt/internal/DslEntry.scala | 43 +- .../src/main/scala/sbt/internal/Eval.scala | 17 +- .../sbt/internal/EvaluateConfigurations.scala | 82 +-- .../scala/sbt/internal/LoadedSbtFile.scala | 22 +- .../scala/sbt/internal/parser/SbtParser.scala | 32 +- .../sbt/internal/parser/SbtRefactorings.scala | 22 +- .../internal/parser/CheckIfParsedSpec.scala | 12 +- .../internal/parser/CommentedXmlSpec.scala | 4 +- .../sbt/internal/parser/EmbeddedXmlSpec.scala | 10 +- .../sbt/internal/parser/NewFormatSpec.scala | 4 +- .../internal/parser/SbtParserErrorSpec.scala | 4 +- .../internal/parser/SessionSettingsSpec.scala | 4 +- .../parser/SplitExpressionsBehavior.scala | 13 +- .../sbt/internal/util/appmacro/Cont.scala | 14 +- .../internal/util/appmacro/ContextUtil.scala | 3 + .../sbt/internal/util/appmacro/Convert.scala | 1 + .../scala/sbt/internal/util/LineReader.scala | 105 +-- .../internal/util/complete/Completions.scala | 52 +- .../internal/util/complete/EditDistance.scala | 28 +- .../util/complete/ExampleSource.scala | 13 +- .../sbt/internal/util/complete/History.scala | 24 +- .../util/complete/HistoryCommands.scala | 17 +- .../util/complete/JLineCompletion.scala | 97 +-- .../sbt/internal/util/complete/Parser.scala | 350 ++++----- .../sbt/internal/util/complete/Parsers.scala | 56 +- .../internal/util/complete/ProcessError.scala | 22 +- .../internal/util/complete/SizeParser.scala | 19 +- .../util/complete/TokenCompletions.scala | 34 +- .../internal/util/complete/TypeString.scala | 40 +- .../internal/util/complete/UpperBound.scala | 13 +- .../src/test/scala/DefaultParsersSpec.scala | 7 +- .../src/test/scala/ParserTest.scala | 42 +- .../scala/sbt/complete/FileExamplesTest.scala | 15 +- .../sbt/complete/FixedSetExamplesTest.scala | 12 +- .../sbt/complete/ParserWithExamplesTest.scala | 35 +- .../sbt/internal/util/ErrorHandling.scala | 30 +- .../scala/sbt/internal/util/ExitHook.scala | 14 +- .../sbt/internal/util/RunningProcesses.scala | 3 +- .../internal/util/hashing/FarmHash64.scala | 3 + .../hashing/StreamingXXHash64VarHandle.scala | 2 + .../sbt/internal/util/hashing/XXHash64.scala | 2 + .../util/appmacro/StringTypeTag.scala | 5 +- .../main/scala/sbt/internal/util/Util.scala | 26 +- .../scala/sbt/internal/util/BasicLogger.scala | 3 +- .../sbt/internal/util/BufferedLogger.scala | 101 ++- .../sbt/internal/util/ConsoleAppender.scala | 133 ++-- .../scala/sbt/internal/util/ConsoleOut.scala | 67 +- .../scala/sbt/internal/util/EscHelpers.scala | 85 +-- .../sbt/internal/util/FilterLogger.scala | 27 +- .../scala/sbt/internal/util/FullLogger.scala | 24 +- .../sbt/internal/util/GlobalLogging.scala | 17 +- .../main/scala/sbt/internal/util/JLine3.scala | 147 ++-- .../sbt/internal/util/LoggerWriter.scala | 19 +- .../sbt/internal/util/MainAppender.scala | 22 +- .../sbt/internal/util/ManagedLogger.scala | 20 +- .../scala/sbt/internal/util/MultiLogger.scala | 20 +- .../scala/sbt/internal/util/ObjectEvent.scala | 6 +- .../sbt/internal/util/ProgressState.scala | 118 ++- .../main/scala/sbt/internal/util/Prompt.scala | 17 +- .../scala/sbt/internal/util/StackTrace.scala | 36 +- .../scala/sbt/internal/util/Terminal.scala | 439 +++++------ .../internal/util/WindowsInputStream.scala | 76 +- .../internal/util/codec/JValueFormats.scala | 26 +- .../internal/util/codec/PositionFormats.scala | 20 +- .../internal/util/codec/ProblemFormats.scala | 20 +- .../internal/util/codec/SeverityFormats.scala | 22 +- .../util/codec/SuccessEventShowLines.scala | 7 +- .../util/codec/ThrowableShowLines.scala | 14 +- .../main/scala/sbt/util/AbstractLogger.scala | 10 +- .../main/scala/sbt/util/InterfaceUtil.scala | 88 +-- .../src/main/scala/sbt/util/Level.scala | 6 +- .../src/main/scala/sbt/util/LogEvent.scala | 6 +- .../src/main/scala/sbt/util/LogExchange.scala | 16 +- .../src/main/scala/sbt/util/Logger.scala | 26 +- .../main/scala/sbt/util/LoggerContext.scala | 64 +- .../src/main/scala/sbt/util/ShowLines.scala | 10 +- .../util-logging/src/test/scala/Escapes.scala | 54 +- .../src/test/scala/LogWriterTest.scala | 71 +- .../src/test/scala/ProblemTest.scala | 63 +- .../src/test/scala/TestLogger.scala | 6 +- .../sbt/internal/util/CleanStringSpec.scala | 4 +- .../sbt/internal/util/TerminalColorSpec.scala | 7 +- .../scala/sbt/internal/util/logic/Logic.scala | 118 ++- .../src/test/scala/sbt/logic/Test.scala | 34 +- .../internal/util/SourcePositionMacro.scala | 9 +- .../scala/sbt/internal/util/Positions.scala | 12 +- .../internal/util/SourcePositionSpec.scala | 3 +- .../scala/sbt/internal/util/Relation.scala | 36 +- .../src/test/scala/RelationTest.scala | 12 +- .../internal/scripted/CommentHandler.scala | 3 +- .../sbt/internal/scripted/FileCommands.scala | 49 +- .../internal/scripted/FilteredLoader.scala | 11 +- .../internal/scripted/HandlersProvider.scala | 3 +- .../sbt/internal/scripted/ScriptRunner.scala | 45 +- .../sbt/internal/scripted/ScriptedTests.scala | 124 ++-- .../internal/scripted/StatementHandler.scala | 9 +- .../internal/scripted/TestScriptParser.scala | 35 +- launcher-package/build.sbt | 131 ++-- .../src/test/scala/BspConfigTest.scala | 7 +- .../src/test/scala/ExtendedRunnerTest.scala | 39 +- .../src/test/scala/IntegrationTestPaths.scala | 15 +- .../src/test/scala/LauncherTestHelper.scala | 3 +- .../src/test/scala/RunnerScriptTest.scala | 14 +- .../src/test/scala/ShellScriptUtil.scala | 80 +- .../CompatibilityWarning.scala | 24 +- .../librarymanagement/InternalDefaults.scala | 3 +- .../librarymanagement/IvyConfigurations.scala | 11 +- .../internal/librarymanagement/IvySbt.scala | 3 +- .../internal/librarymanagement/JsonUtil.scala | 40 +- .../librarymanagement/LMSysProp.scala | 20 +- .../librarymanagement/SemSelOperator.scala | 9 +- .../SemanticSelectorExtra.scala | 102 ++- .../librarymanagement/StringUtilities.scala | 5 +- .../UpdateClassifiersUtil.scala | 23 +- .../UpdateReportInterner.scala | 13 +- .../UpdateReportPersistence.scala | 2 + .../librarymanagement/VersionRange.scala | 137 ++-- .../librarymanagement/VersionSchemes.scala | 10 +- .../librarymanagement/WeakPools.scala | 34 +- .../cross/CrossVersionUtil.scala | 44 +- .../formats/GlobalLockFormat.scala | 10 +- .../formats/LoggerFormat.scala | 4 +- .../formats/LogicalClockFormats.scala | 4 +- .../formats/NodeSeqFormat.scala | 4 +- .../sbt/librarymanagement/ArtifactExtra.scala | 37 +- .../sbt/librarymanagement/ConfigRef.scala | 22 +- .../librarymanagement/ConfigRefFormats.scala | 16 +- .../sbt/librarymanagement/Configuration.scala | 10 +- .../ConfigurationExtra.scala | 21 +- .../ConfigurationFormats.scala | 16 +- .../librarymanagement/ConflictWarning.scala | 36 +- .../sbt/librarymanagement/Credentials.scala | 11 +- .../sbt/librarymanagement/CrossVersion.scala | 306 +++----- .../librarymanagement/CrossVersionExtra.scala | 42 +- .../DependencyBuilders.scala | 40 +- .../librarymanagement/DependencyFilter.scala | 23 +- .../DependencyResolution.scala | 32 +- .../sbt/librarymanagement/EvictionError.scala | 60 +- .../librarymanagement/EvictionWarning.scala | 155 ++-- .../scala/sbt/librarymanagement/Extra.scala | 16 +- .../scala/sbt/librarymanagement/Http.scala | 3 +- .../LibraryManagementInterface.scala | 12 +- .../LibraryManagementSyntax.scala | 7 +- .../sbt/librarymanagement/LogicalClock.scala | 12 +- .../MakePomConfigurationExtra.scala | 3 +- .../MemoizedFileFormats.scala | 22 +- .../sbt/librarymanagement/ModuleIDExtra.scala | 52 +- .../sbt/librarymanagement/Publisher.scala | 7 +- .../sbt/librarymanagement/ResolverExtra.scala | 150 ++-- .../librarymanagement/RichUpdateReport.scala | 26 +- .../librarymanagement/ScalaArtifacts.scala | 51 +- .../sbt/librarymanagement/TrackLevel.scala | 20 +- .../librarymanagement/UnresolvedWarning.scala | 25 +- .../librarymanagement/UpdateReportExtra.scala | 60 +- .../sbt/librarymanagement/VersionNumber.scala | 65 +- lm-core/src/test/scala/ConfigMacroSpec.scala | 16 +- .../tests/CrossVersionCompatTest.scala | 34 +- .../UpdateReportCacheCodecSpec.scala | 1 + .../UpdateReportFormatSpec.scala | 1 + .../UpdateReportInternerSpec.scala | 1 + .../UpdateReportPersistenceBenchmark.scala | 5 +- .../UpdateReportPersistenceSpec.scala | 2 + .../librarymanagement/WeakInternerSpec.scala | 1 + .../sbt/librarymanagement/ArtifactTest.scala | 4 +- .../librarymanagement/ConfigMacroTest.scala | 3 +- .../librarymanagement/CrossVersionTest.scala | 4 +- .../MemoizedFileFormatsSpec.scala | 1 + .../sbt/librarymanagement/ModuleIdTest.scala | 4 +- .../sbt/librarymanagement/ResolverTest.scala | 4 +- .../SemanticSelectorSpec.scala | 4 +- .../librarymanagement/VersionNumberSpec.scala | 15 +- .../librarymanagement/VersionRangeSpec.scala | 4 +- .../credentials/DirectCredentials.scala | 3 +- .../definitions/Authentication.scala | 3 +- .../CoursierDependencyResolution.scala | 127 ++-- .../src/main/scala/lmcoursier/FromSbt.scala | 53 +- .../src/main/scala/lmcoursier/Inputs.scala | 65 +- .../src/main/scala/lmcoursier/IvyXml.scala | 26 +- .../lmcoursier/definitions/CacheLogger.scala | 7 +- .../lmcoursier/definitions/CachePolicy.scala | 4 +- .../lmcoursier/definitions/FromCoursier.scala | 6 +- .../definitions/Reconciliation.scala | 6 +- .../lmcoursier/definitions/ToCoursier.scala | 16 +- .../lmcoursier/internal/ArtifactsRun.scala | 36 +- .../lmcoursier/internal/BuildClock.scala | 11 +- .../internal/CacheUrlConversion.scala | 34 +- .../internal/CoursierModuleDescriptor.scala | 3 +- .../internal/InterProjectRepository.scala | 7 +- .../main/scala/lmcoursier/internal/Lock.scala | 5 +- .../scala/lmcoursier/internal/LockFile.scala | 22 +- .../internal/LockFileConstants.scala | 3 +- .../lmcoursier/internal/LockFileFormats.scala | 66 +- .../internal/LockedArtifactsRun.scala | 32 +- .../internal/ResolutionParams.scala | 29 +- .../lmcoursier/internal/ResolutionRun.scala | 110 ++- .../internal/ResolutionSerializer.scala | 25 +- .../scala/lmcoursier/internal/Resolvers.scala | 63 +- .../lmcoursier/internal/SbtBootJars.scala | 3 +- .../internal/SbtCoursierCache.scala | 13 +- .../lmcoursier/internal/SbtUpdateReport.scala | 73 +- .../TemporaryInMemoryRepository.scala | 83 +-- .../lmcoursier/internal/UpdateParams.scala | 16 +- .../scala/lmcoursier/internal/UpdateRun.scala | 11 +- .../scala/lmcoursier/syntax/package.scala | 40 +- .../CoursierDependencyResolutionTests.scala | 23 +- .../scala/lmcoursier/EvictionErrorSpec.scala | 7 +- .../lmcoursier/EvictionWarningSpec.scala | 4 +- .../lmcoursier/FromSbtPlatformSpec.scala | 4 +- .../test/scala/lmcoursier/InclExclSpec.scala | 16 +- .../test/scala/lmcoursier/IvyXmlTests.scala | 5 +- .../test/scala/lmcoursier/LockFileSpec.scala | 4 +- .../scala/lmcoursier/ResolutionSpec.scala | 17 +- .../src/test/scala/lmcoursier/TestKit.scala | 1 + .../internal/ResolutionRunSpec.scala | 1 + main-actions/src/main/scala/sbt/Console.scala | 33 +- .../src/main/scala/sbt/DotGraph.scala | 17 +- .../src/main/scala/sbt/ForkTests.scala | 6 +- main-actions/src/main/scala/sbt/Pkg.scala | 46 +- .../src/main/scala/sbt/RawCompileLike.scala | 37 +- main-actions/src/main/scala/sbt/Sync.scala | 59 +- .../src/main/scala/sbt/TestResultLogger.scala | 43 +- main-actions/src/main/scala/sbt/Tests.scala | 120 ++- .../scala/sbt/internal/WorkerExchange.scala | 17 +- .../PackageDeploymentValidationError.scala | 18 +- .../main/scala/sbt/internal/sona/Sona.scala | 92 +-- .../src/test/scala/sbt/CacheIvyTest.scala | 46 +- .../sbt/internal/sona/SonaClientTest.scala | 8 +- .../main/scala/sbt/BasicCommandStrings.scala | 13 +- .../src/main/scala/sbt/BasicCommands.scala | 151 ++-- .../src/main/scala/sbt/BasicKeys.scala | 4 +- main-command/src/main/scala/sbt/Command.scala | 62 +- .../src/main/scala/sbt/CommandUtil.scala | 47 +- .../main/scala/sbt/ExceptionCategory.scala | 14 +- .../src/main/scala/sbt/Highlight.scala | 18 +- .../src/main/scala/sbt/MainControl.scala | 12 +- main-command/src/main/scala/sbt/State.scala | 109 ++- main-command/src/main/scala/sbt/Watched.scala | 10 +- .../sbt/internal/BootServerSocketProbe.scala | 1 + .../scala/sbt/internal/CommandChannel.scala | 29 +- .../scala/sbt/internal/ConsoleChannel.scala | 6 +- .../scala/sbt/internal/LabeledFunctions.scala | 34 +- .../internal/classpath/ClassLoaderCache.scala | 111 +-- .../scala/sbt/internal/client/BspClient.scala | 42 +- .../sbt/internal/client/NetworkClient.scala | 694 ++++++++---------- .../scala/sbt/internal/server/Server.scala | 89 +-- .../sbt/internal/server/ServerHandler.scala | 15 +- .../main/scala/sbt/internal/ui/UITask.scala | 69 +- .../scala/sbt/internal/ui/UserThread.scala | 46 +- main-command/src/main/scala/xsbt/IPC.scala | 34 +- .../sbt/internal/BootServerSocketSpec.scala | 6 +- .../internal/client/ClientConnectSpec.scala | 1 + .../NetworkClientSilentDisconnectTest.scala | 4 +- .../client/NetworkClientSysPropsTest.scala | 1 + .../internal/server/ServerAcceptSpec.scala | 1 + .../sbt/internal/server/ServerTokenSpec.scala | 1 + main-settings/src/main/scala/sbt/Append.scala | 23 +- .../src/main/scala/sbt/ConfigKey.scala | 3 +- main-settings/src/main/scala/sbt/Def.scala | 65 +- .../src/main/scala/sbt/DelegateIndex.scala | 18 +- .../src/main/scala/sbt/InputTask.scala | 3 +- .../src/main/scala/sbt/KeyRanks.scala | 4 +- .../src/main/scala/sbt/Plugins.scala | 133 ++-- .../src/main/scala/sbt/Previous.scala | 56 +- .../src/main/scala/sbt/Project.scala | 44 +- .../src/main/scala/sbt/PromiseWrap.scala | 6 +- .../src/main/scala/sbt/Reference.scala | 28 +- main-settings/src/main/scala/sbt/Remove.scala | 22 +- main-settings/src/main/scala/sbt/Scope.scala | 98 +-- .../src/main/scala/sbt/ScopeMask.scala | 18 +- .../src/main/scala/sbt/Structure.scala | 45 +- .../src/main/scala/sbt/dsl/LinterLevel.scala | 6 +- .../main/scala/sbt/std/InputTaskMacro.scala | 10 +- .../src/main/scala/sbt/std/Instances.scala | 1 + .../src/main/scala/sbt/std/TaskMacro.scala | 14 +- .../src/test/scala/sbt/AppendSpec.scala | 3 +- .../scala/sbt/BuildSettingsInstances.scala | 1 + .../src/test/scala/sbt/SlashSyntaxTest.scala | 4 +- .../src/test/scala/sbt/TupleSyntaxTest.scala | 3 +- .../src/test/scala/sbt/std/TaskPosSpec.scala | 42 +- .../src/test/scala/sbt/std/UsageTest.scala | 6 +- .../main/scala/sbt/BackgroundJobService.scala | 25 +- main/src/main/scala/sbt/BuildPaths.scala | 26 +- .../sbt/ClassLoaderLayeringStrategy.scala | 4 +- .../main/scala/sbt/CommandLineUIService.scala | 20 +- main/src/main/scala/sbt/Cross.scala | 160 ++-- main/src/main/scala/sbt/Defaults.scala | 626 +++++++--------- main/src/main/scala/sbt/EvaluateTask.scala | 178 ++--- .../src/main/scala/sbt/ExecuteProgress2.scala | 13 +- main/src/main/scala/sbt/Extracted.scala | 16 +- .../main/scala/sbt/InteractionService.scala | 3 +- main/src/main/scala/sbt/Main.scala | 424 +++++------ main/src/main/scala/sbt/MainLoop.scala | 69 +- main/src/main/scala/sbt/OptionSyntax.scala | 18 +- main/src/main/scala/sbt/Opts.scala | 35 +- main/src/main/scala/sbt/PluginCross.scala | 28 +- main/src/main/scala/sbt/ProjectExtra.scala | 63 +- main/src/main/scala/sbt/ProjectMatrix.scala | 117 ++- main/src/main/scala/sbt/RemoteCache.scala | 3 +- .../scala/sbt/RepositoryUpdateStrategy.scala | 4 +- main/src/main/scala/sbt/Resolvers.scala | 161 ++-- main/src/main/scala/sbt/RichURI.scala | 27 +- main/src/main/scala/sbt/ScopeFilter.scala | 84 +-- main/src/main/scala/sbt/ScopedKeyData.scala | 3 +- main/src/main/scala/sbt/ScriptedPlugin.scala | 41 +- main/src/main/scala/sbt/ScriptedRun.scala | 90 +-- main/src/main/scala/sbt/SessionVar.scala | 23 +- main/src/main/scala/sbt/Tags.scala | 31 +- .../main/scala/sbt/TemplateCommandUtil.scala | 125 ++-- main/src/main/scala/sbt/Terminal.scala | 10 +- main/src/main/scala/sbt/UpperStateOps.scala | 21 +- main/src/main/scala/sbt/VersionScheme.scala | 3 +- main/src/main/scala/sbt/VirtualAxis.scala | 50 +- .../coursierint/CoursierArtifactsTasks.scala | 51 +- .../sbt/coursierint/CoursierInputsTasks.scala | 26 +- .../CoursierRepositoriesTasks.scala | 31 +- .../scala/sbt/coursierint/LMCoursier.scala | 39 +- .../sbt/coursierint/ResolutionProgress.scala | 45 +- .../main/scala/sbt/internal/APIMappings.scala | 4 +- .../AbstractTaskExecuteProgress.scala | 58 +- main/src/main/scala/sbt/internal/Act.scala | 137 ++-- .../main/scala/sbt/internal/AddSettings.scala | 16 +- .../main/scala/sbt/internal/Aggregation.scala | 47 +- .../scala/sbt/internal/AnalysisUtil.scala | 4 +- .../main/scala/sbt/internal/BuildDef.scala | 17 +- .../sbt/internal/BuildDependencies.scala | 9 +- .../main/scala/sbt/internal/BuildLoader.scala | 63 +- .../scala/sbt/internal/BuildStructure.scala | 69 +- .../main/scala/sbt/internal/BuildUtil.scala | 41 +- .../sbt/internal/ClassLoaderWarmup.scala | 22 +- .../scala/sbt/internal/ClassLoaders.scala | 46 +- .../scala/sbt/internal/ClasspathImpl.scala | 52 +- main/src/main/scala/sbt/internal/Clean.scala | 34 +- .../scala/sbt/internal/CommandExchange.scala | 289 +++----- .../scala/sbt/internal/CommandStrings.scala | 9 +- .../sbt/internal/CompileDebugLogger.scala | 13 +- .../main/scala/sbt/internal/Compiler.scala | 15 +- .../scala/sbt/internal/ConsoleProject.scala | 7 +- .../main/scala/sbt/internal/Continuous.scala | 486 +++++------- .../main/scala/sbt/internal/CrossJava.scala | 207 +++--- .../DefaultBackgroundJobService.scala | 207 ++---- .../sbt/internal/DefaultRunProfiler.scala | 4 +- .../scala/sbt/internal/DynamicInput.scala | 21 +- .../sbt/internal/FastTrackCommands.scala | 18 +- .../scala/sbt/internal/FileChangesMacro.scala | 1 + .../main/scala/sbt/internal/GCMonitor.scala | 20 +- main/src/main/scala/sbt/internal/GCUtil.scala | 15 +- .../scala/sbt/internal/GlobalPlugin.scala | 17 +- .../sbt/internal/GroupedAutoPlugins.scala | 9 +- .../sbt/internal/InMemoryCacheStore.scala | 68 +- .../scala/sbt/internal/IncrementalTest.scala | 2 + .../src/main/scala/sbt/internal/Inspect.scala | 35 +- .../main/scala/sbt/internal/InstallSbtn.scala | 106 ++- .../sbt/internal/InternalDependencies.scala | 9 +- .../main/scala/sbt/internal/IvyConsole.scala | 15 +- .../scala/sbt/internal/JarClassPath.scala | 19 +- .../main/scala/sbt/internal/KeyIndex.scala | 107 ++- .../sbt/internal/LayeredClassLoaders.scala | 76 +- .../sbt/internal/LibraryManagement.scala | 138 ++-- .../main/scala/sbt/internal/LintUnused.scala | 42 +- main/src/main/scala/sbt/internal/Load.scala | 271 +++---- .../main/scala/sbt/internal/LogManager.scala | 53 +- main/src/main/scala/sbt/internal/Output.scala | 25 +- .../scala/sbt/internal/PluginDiscovery.scala | 41 +- .../scala/sbt/internal/PluginManagement.scala | 15 +- .../scala/sbt/internal/PluginsDebug.scala | 140 ++-- .../scala/sbt/internal/PomGenerator.scala | 4 +- .../main/scala/sbt/internal/PrettyPrint.scala | 6 +- .../sbt/internal/ProjectNavigation.scala | 38 +- .../scala/sbt/internal/RelayAppender.scala | 6 +- .../src/main/scala/sbt/internal/Resolve.scala | 21 +- .../scala/sbt/internal/RetrieveUnit.scala | 25 +- .../src/main/scala/sbt/internal/RunUtil.scala | 23 +- main/src/main/scala/sbt/internal/Script.scala | 34 +- .../scala/sbt/internal/SessionSettings.scala | 52 +- .../sbt/internal/SettingCompletions.scala | 118 ++- .../scala/sbt/internal/SettingGraph.scala | 54 +- .../scala/sbt/internal/ShutdownHooks.scala | 21 +- .../src/main/scala/sbt/internal/SysProp.scala | 76 +- .../main/scala/sbt/internal/TaskName.scala | 3 +- .../scala/sbt/internal/TaskProgress.scala | 116 ++- .../scala/sbt/internal/TaskSequential.scala | 10 +- .../main/scala/sbt/internal/TaskTimings.scala | 28 +- .../scala/sbt/internal/TaskTraceEvent.scala | 33 +- .../main/scala/sbt/internal/TestCommand.scala | 3 + .../scala/sbt/internal/VcsUriFragment.scala | 13 +- .../sbt/internal/VirtualFileValueCache.scala | 33 +- .../WatchTransitiveDependencies.scala | 62 +- .../internal/graph/GraphTransformations.scala | 26 +- .../graph/backend/SbtUpdateReport.scala | 23 +- .../main/scala/sbt/internal/graph/model.scala | 10 +- .../scala/sbt/internal/graph/package.scala | 3 +- .../internal/graph/rendering/AsciiTree.scala | 7 +- .../sbt/internal/graph/rendering/DOT.scala | 17 +- .../internal/graph/rendering/DagreHTML.scala | 10 +- .../internal/graph/rendering/FlatList.scala | 3 +- .../internal/graph/rendering/GraphML.scala | 1 + .../graph/rendering/LicenseInfo.scala | 7 +- .../internal/graph/rendering/Statistics.scala | 20 +- .../internal/graph/rendering/TreeView.scala | 34 +- .../DependencyLockManager.scala | 2 + .../librarymanagement/GenericPublisher.scala | 30 +- .../librarymanagement/Publishing.scala | 28 +- .../internal/server/BspCompileProgress.scala | 17 +- .../sbt/internal/server/BspCompileTask.scala | 22 +- .../server/BuildServerEvalReporter.scala | 18 +- .../internal/server/BuildServerProtocol.scala | 201 ++--- .../internal/server/BuildServerReporter.scala | 70 +- .../internal/server/CoalescingFlusher.scala | 4 +- .../sbt/internal/server/Definition.scala | 100 +-- .../server/LanguageServerProtocol.scala | 19 +- .../sbt/internal/server/NetworkChannel.scala | 451 +++++------- .../sbt/internal/server/SettingQuery.scala | 30 +- .../sbt/internal/server/VirtualTerminal.scala | 75 +- .../scala/sbt/nio/CheckBuildSources.scala | 78 +- main/src/main/scala/sbt/nio/FileChanges.scala | 6 +- main/src/main/scala/sbt/nio/FileStamp.scala | 127 ++-- main/src/main/scala/sbt/nio/Keys.scala | 4 +- main/src/main/scala/sbt/nio/Settings.scala | 88 +-- main/src/main/scala/sbt/nio/Watch.scala | 205 ++---- .../main/scala/sbt/plugins/CorePlugin.scala | 3 +- .../sbt/plugins/DependencyTreePlugin.scala | 4 +- .../sbt/plugins/DependencyTreeSettings.scala | 17 +- .../sbt/plugins/Giter8TemplatePlugin.scala | 3 +- .../main/scala/sbt/plugins/IvyPlugin.scala | 4 +- .../sbt/plugins/JUnitXmlReportPlugin.scala | 7 +- .../main/scala/sbt/plugins/JvmPlugin.scala | 4 +- .../scala/sbt/plugins/SemanticdbPlugin.scala | 25 +- main/src/test/scala/ClasspathsTest.scala | 10 +- main/src/test/scala/Delegates.scala | 22 +- main/src/test/scala/ParseKeySpec.scala | 69 +- main/src/test/scala/ParserSpec.scala | 8 +- main/src/test/scala/PluginCommandTest.scala | 28 +- main/src/test/scala/ProjectMacro.scala | 16 +- main/src/test/scala/ProjectSpec.scala | 7 +- .../test/scala/ScopeFilterOrderingSpec.scala | 4 +- main/src/test/scala/SelectedFilterTest.scala | 4 +- main/src/test/scala/TagsTest.scala | 13 +- .../scala/sbt/TemplateCommandUtilTest.scala | 5 +- .../coursierint/ResolutionProgressSpec.scala | 3 +- .../scala/sbt/internal/AggregationSpec.scala | 4 +- .../scala/sbt/internal/ClassStamperTest.scala | 5 + .../sbt/internal/CompileDebugLoggerSpec.scala | 12 +- .../scala/sbt/internal/CrossJavaTest.scala | 28 +- .../scala/sbt/internal/GCMonitorTest.scala | 12 +- .../sbt/internal/InMemoryCacheStoreTest.scala | 1 + .../scala/sbt/internal/InstallSbtnSpec.scala | 19 +- .../test/scala/sbt/internal/OutputSpec.scala | 3 +- .../test/scala/sbt/internal/ScriptTest.scala | 20 +- .../scala/sbt/internal/StressGCMonitor.scala | 9 +- .../scala/sbt/internal/TaskProgressSpec.scala | 4 +- .../test/scala/sbt/internal/TestBuild.scala | 161 ++-- .../graph/backend/SbtUpdateReportTest.scala | 4 +- .../graph/rendering/TreeViewTest.scala | 4 +- .../server/CoalescingFlusherSpec.scala | 17 +- .../test/scala/testpkg/CompletionSpec.scala | 8 +- project/DatatypeConfig.scala | 88 +-- project/Dependencies.scala | 13 +- project/Docs.scala | 3 +- project/HouseRulesPlugin.scala | 18 +- project/Mima.scala | 11 +- project/NightlyPlugin.scala | 9 +- project/PackageSignerPlugin.scala | 13 +- project/PublishBinPlugin.scala | 14 +- project/SbtLauncherPlugin.scala | 25 +- project/Scripted.scala | 70 +- project/Transform.scala | 7 +- project/Utils.scala | 48 +- .../internal/bsp/BuildServerConnection.scala | 18 +- .../sbt/internal/bsp/BuildServerTasks.scala | 3 +- .../sbt/internal/bsp/BuildTargetName.scala | 9 +- .../sbt/internal/bsp/BuildTargetTag.scala | 6 +- .../sbt/internal/bsp/DiagnosticSeverity.scala | 4 +- .../sbt/internal/bsp/OutputPathItemKind.scala | 3 +- .../sbt/internal/bsp/ScalaPlatform.scala | 3 +- .../sbt/internal/bsp/SourceItemKind.scala | 3 +- .../scala/sbt/internal/bsp/StatusCode.scala | 3 +- .../langserver/DiagnosticSeverity.scala | 4 +- .../sbt/internal/langserver/MessageType.scala | 9 +- .../JsonRpcNotificationMessageFormats.scala | 16 +- .../codec/JsonRpcRequestMessageFormats.scala | 25 +- .../codec/JsonRpcResponseErrorFormats.scala | 16 +- .../codec/JsonRpcResponseMessageFormats.scala | 37 +- .../scala/sbt/internal/util/JoinThread.scala | 23 +- .../scala/sbt/protocol/ClientSocket.scala | 18 +- .../scala/sbt/protocol/JsonRpcReader.scala | 42 +- .../scala/sbt/protocol/JsonRpcWriter.scala | 11 +- .../scala/sbt/protocol/Serialization.scala | 102 +-- .../scala/sbt/protocol/ServerSession.scala | 32 +- .../sbt/protocol/ServerSessionImpl.scala | 108 ++- .../bsp/BuildServerConnectionSpec.scala | 1 + .../sbt/protocol/ClientSocketDuplexTest.scala | 1 + .../sbt/protocol/ServerSessionImplSpec.scala | 4 +- run/src/main/scala/sbt/Fork.scala | 70 +- run/src/main/scala/sbt/OutputStrategy.scala | 67 +- run/src/main/scala/sbt/Run.scala | 117 ++- run/src/main/scala/sbt/SelectMainClass.scala | 30 +- run/src/main/scala/sbt/TrapExit.scala | 12 +- run/src/test/scala/sbt/ForkTest.scala | 35 +- run/src/test/scala/sbt/TestLogger.scala | 6 +- sbt-app/src/main/scala/package.scala | 3 +- sbt-app/src/main/scala/sbt/AllSyntax.scala | 6 +- sbt-app/src/main/scala/sbt/Import.scala | 8 +- .../test/scala/sbt/RunFromSourceMain.scala | 58 +- .../scala/sbt/RunFromSourceMainSpec.scala | 3 +- .../sbt/internal/GrpcActionCacheStore.scala | 7 + .../scala/sbt/plugins/RemoteCachePlugin.scala | 1 + sbtw/src/main/scala/sbtw/ArgParser.scala | 2 + sbtw/src/main/scala/sbtw/ConfigLoader.scala | 10 +- sbtw/src/main/scala/sbtw/Main.scala | 7 +- sbtw/src/main/scala/sbtw/Runner.scala | 10 +- .../sbt/scriptedtest/BatchScriptRunner.scala | 42 +- .../sbt/scriptedtest/RemoteSbtCreator.scala | 30 +- .../scala/sbt/scriptedtest/SbtHandler.scala | 38 +- .../sbt/scriptedtest/ScriptedTests.scala | 256 +++---- .../scala/testpkg/AbstractServerTest.scala | 57 +- .../test/scala/testpkg/BuildServerTest.scala | 58 +- .../scala/testpkg/ChannelCursorTest.scala | 13 +- .../scala/testpkg/ClientBootTimeoutTest.scala | 22 +- .../testpkg/ClientSubscriptionTest.scala | 6 +- .../src/test/scala/testpkg/ClientTest.scala | 49 +- .../src/test/scala/testpkg/EventsTest.scala | 4 +- .../testpkg/ExecRequiresInitializeTest.scala | 28 +- .../scala/testpkg/FailedLoadPromptTest.scala | 25 +- .../test/scala/testpkg/HandshakeTest.scala | 3 +- .../scala/testpkg/PortfileReloadTest.scala | 3 +- .../src/test/scala/testpkg/PortfileTest.scala | 3 +- .../testpkg/QueuedNotificationTest.scala | 4 +- .../src/test/scala/testpkg/RebootTest.scala | 3 +- .../src/test/scala/testpkg/ResponseTest.scala | 10 +- .../scala/testpkg/ServerCompletionsTest.scala | 4 +- .../scala/testpkg/ServerSysPropsTest.scala | 63 +- .../scala/testpkg/TerminalMapsDrainTest.scala | 13 +- .../TerminalPropertiesFreezeTest.scala | 7 +- .../src/main/scala/sbt/std/Streams.scala | 52 +- .../src/main/scala/sbt/std/TaskExtra.scala | 67 +- .../src/main/scala/sbt/std/Transform.scala | 9 +- tasks-standard/src/test/scala/Execute.scala | 4 +- tasks-standard/src/test/scala/TaskGen.scala | 15 +- .../src/test/scala/TaskRunnerCircular.scala | 33 +- .../src/test/scala/TaskRunnerFork.scala | 7 +- .../src/test/scala/TaskSerial.scala | 24 +- tasks-standard/src/test/scala/Test.scala | 5 +- .../src/test/scala/TestRunnerCall.scala | 25 +- .../src/test/scala/TestRunnerSort.scala | 23 +- .../src/test/scala/checkResult.scala | 13 +- .../main/scala/sbt/CompletionService.scala | 45 +- .../scala/sbt/ConcurrentRestrictions.scala | 106 ++- tasks/src/main/scala/sbt/Execute.scala | 154 ++-- .../src/main/scala/sbt/ExecuteProgress.scala | 38 +- tasks/src/main/scala/sbt/Incomplete.scala | 25 +- tasks/src/main/scala/sbt/Result.scala | 9 +- .../scala/sbt/JUnitXmlTestsListener.scala | 152 ++-- .../src/main/scala/sbt/TestFramework.scala | 137 ++-- .../main/scala/sbt/TestReportListener.scala | 38 +- .../sbt/internal/testing/StatusFormats.scala | 23 +- .../sbt/internal/testing/TestLogger.scala | 31 +- .../src/test/scala/sbt/TestRunnerSpec.scala | 37 +- .../util/codec/ByteBufferFormats.scala | 4 +- .../codec/HashedVirtualFileRefFormats.scala | 10 +- .../src/main/scala/sbt/util/ActionCache.scala | 7 + .../scala/sbt/util/ActionCacheStore.scala | 8 +- .../scala/sbt/util/BasicCacheImplicits.scala | 7 +- .../src/main/scala/sbt/util/Cache.scala | 19 +- .../src/main/scala/sbt/util/CacheStore.scala | 28 +- .../src/main/scala/sbt/util/Digest.scala | 4 + .../main/scala/sbt/util/DigestHasher.scala | 1 + .../src/main/scala/sbt/util/FileInfo.scala | 132 ++-- .../src/main/scala/sbt/util/Input.scala | 60 +- .../src/main/scala/sbt/util/Output.scala | 21 +- .../main/scala/sbt/util/SeparatedCache.scala | 24 +- .../main/scala/sbt/util/StampedFormat.scala | 21 +- .../test/scala/sbt/util/ActionCacheTest.scala | 55 +- .../scala/sbt/util/GzipCacheStoreSpec.scala | 1 + .../scala/sbt/internal/util/Attributes.scala | 12 +- .../main/scala/sbt/internal/util/Dag.scala | 80 +- .../main/scala/sbt/internal/util/IDSet.scala | 23 +- .../main/scala/sbt/internal/util/INode.scala | 34 +- .../main/scala/sbt/internal/util/PMap.scala | 32 +- .../main/scala/sbt/internal/util/Par.scala | 3 +- .../scala/sbt/internal/util/Settings.scala | 137 ++-- .../main/scala/sbt/internal/util/Signal.scala | 64 +- .../sbt/internal/util/TupleMapExtension.scala | 2 + .../scala/sbt/internal/util/WrappedMap.scala | 3 +- .../main/scala/sbt/util/OptJsonWriter.scala | 12 +- .../src/test/scala/DagSpecification.scala | 33 +- .../src/test/scala/SettingsExample.scala | 17 +- .../main/scala/sbt/util/ChangeReport.scala | 26 +- .../main/scala/sbt/util/FileFunction.scala | 16 +- .../src/main/scala/sbt/util/Tracked.scala | 94 +-- .../main/scala/sbt/internal/inc/Errors.scala | 9 +- .../scala/sbt/internal/inc/IfMissing.scala | 6 +- .../sbt/internal/inc/ResourceLoader.scala | 25 +- .../internal/inc/ZincComponentCompiler.scala | 78 +- .../internal/inc/ZincComponentManager.scala | 60 +- .../scala/sbt/internal/inc/ZincLmUtil.scala | 27 +- .../inc/BridgeProviderSpecification.scala | 15 +- .../inc/ZincComponentCompilerSpec.scala | 4 +- 598 files changed, 9294 insertions(+), 13894 deletions(-) diff --git a/build.sbt b/build.sbt index 76f2ff288..89ec6842f 100644 --- a/build.sbt +++ b/build.sbt @@ -82,13 +82,13 @@ def commonSettings: Seq[Setting[?]] = Def.settings( Global / concurrentRestrictions += Utils.testExclusiveRestriction, // On Windows, limit to one task at a time to avoid OverlappingFileLockException when // multiple tasks (e.g. scalafix plugin and sbt Coursier) write to the same cache. - Global / concurrentRestrictions ++= (if (scala.util.Properties.isWin) Seq(Tags.limitAll(1)) + Global / concurrentRestrictions ++= (if scala.util.Properties.isWin then Seq(Tags.limitAll(1)) else Nil), Test / testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-w", "1"), Test / testOptions += Tests.Argument(TestFrameworks.ScalaCheck, "-verbosity", "2"), compile / javacOptions ++= Seq("-Xlint", "-Xlint:-serial"), Compile / doc / scalacOptions ++= { - if (Dependencies.sbtIoPath.isEmpty && Dependencies.sbtZincPath.isEmpty) { + if Dependencies.sbtIoPath.isEmpty && Dependencies.sbtZincPath.isEmpty then import scala.sys.process.* val devnull = ProcessLogger(_ => ()) val tagOrSha = @@ -98,9 +98,7 @@ def commonSettings: Seq[Setting[?]] = Def.settings( "-revision", tagOrSha ) - } else { - Nil - } + else Nil }, Compile / javafmtOnCompile := scalafmtOnCompile.value, Test / javafmtOnCompile := (Test / scalafmtOnCompile).value, @@ -132,8 +130,8 @@ val sbt20Plus = val mimaSettings = mimaSettingsSince(sbt20Plus) def mimaSettingsSince(versions: Seq[String]): Seq[Def.Setting[?]] = Def.settings( mimaPreviousArtifacts := { - val crossVersion = if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled - if (sbtPlugin.value) { + val crossVersion = if crossPaths.value then CrossVersion.binary else CrossVersion.disabled + if sbtPlugin.value then versions .map(v => Defaults.sbtPluginExtra( @@ -143,9 +141,7 @@ def mimaSettingsSince(versions: Seq[String]): Seq[Def.Setting[?]] = Def.settings ) ) .toSet - } else { - versions.map(v => (organization.value % moduleName.value % v).cross(crossVersion)).toSet - } + else versions.map(v => (organization.value % moduleName.value % v).cross(crossVersion)).toSet }, mimaBinaryIssueFilters ++= Seq( ), @@ -183,8 +179,7 @@ lazy val sbtRoot: Project = (project in file(".")) | /____/_.___/\__/ |Welcome to the build for sbt. |""".stripMargin + - (if (version != "17") - s"""!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + (if version != "17" then s"""!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | Java version is $version. We recommend java 17. |!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""".stripMargin else "") @@ -204,16 +199,14 @@ lazy val sbtRoot: Project = (project in file(".")) installNativeThinClient := { // nativeInstallDirectory can be set globally or in a gitignored local file val dir = nativeInstallDirectory.?.value - val target = Def.spaceDelimited("").parsed.headOption match { + val target = Def.spaceDelimited("").parsed.headOption match case Some(p) => file(p).toPath case _ => - dir match { + dir match case Some(d) => d / "sbtn" case _ => val msg = "Expected input parameter : installNativeExecutable /usr/local/bin" throw new IllegalStateException(msg) - } - } val base = baseDirectory.value.toPath val exec = fileConverter.value.toPath((sbtClientProj / nativeImage).value) streams.value.log.info(s"installing thin client ${base.relativize(exec)} to ${target}") @@ -351,7 +344,7 @@ lazy val utilLogging = project Compile / generateContrabands / contrabandFormatsForType := { tpe => val old = (Compile / generateContrabands / contrabandFormatsForType).value val name = tpe.removeTypeParameters.name - if (name == "Throwable") Nil + if name == "Throwable" then Nil else old(tpe) }, Test / fork := true, @@ -905,9 +898,9 @@ lazy val serverTestProj = (project in file("server-test")) .map(_.toFile.getAbsolutePath) .mkString(java.io.File.pathSeparator) val cp = - if (scala.util.Properties.isWin) rawClasspath.replace("\\", "\\\\") + if scala.util.Properties.isWin then rawClasspath.replace("\\", "\\\\") else rawClasspath - val content = { + val content = s"""| |package testpkg | @@ -917,7 +910,6 @@ lazy val serverTestProj = (project in file("server-test")) | val scalaVersion = "${scalaVersion.value}" |} """.stripMargin - } val file = (Test / target).value / "generated" / "src" / "test" / "scala" / "testpkg" / "TestProperties.scala" IO.write(file, content) @@ -956,17 +948,14 @@ lazy val sbtClientProj = (project in file("client")) nativeImageJvm := "graalvm-java23", nativeImageOutput := { val outputDir = (target.value / "bin").toPath - if (!Files.exists(outputDir)) { - Files.createDirectories(outputDir) - } + if !Files.exists(outputDir) then Files.createDirectories(outputDir) fileConverter.value.toVirtualFile(outputDir.resolve("sbtn")) }, nativeImageCommand := { val orig = nativeImageCommand.value - sys.env.get("ARCHS") match { + sys.env.get("ARCHS") match case Some(a) => Seq("arch", s"-$a") ++ orig case None => orig - } }, nativeImageOptions ++= Seq( "--no-fallback", @@ -983,16 +972,16 @@ lazy val sbtClientProj = (project in file("client")) ), buildThinClient := { val isFish = Def.spaceDelimited("").parsed.headOption.fold(false)(_ == "--fish") - val ext = if (isWin) ".bat" else if (isFish) ".fish" else ".sh" - val output = target.value.toPath / "bin" / s"${if (isFish) "fish-" else ""}client$ext" + val ext = if isWin then ".bat" else if isFish then ".fish" else ".sh" + val output = target.value.toPath / "bin" / s"${if isFish then "fish-" else ""}client$ext" java.nio.file.Files.createDirectories(output.getParent) val cp = (Compile / fullClasspathAsJars).value.map(_.data) val args = - if (isWin) "%*" else if (isFish) s"$$argv" else s"$$*" + if isWin then "%*" else if isFish then s"$$argv" else s"$$*" java.nio.file.Files.write( output, s""" - |${if (isWin) "@echo off" else s"#!/usr/bin/env ${if (isFish) "fish" else "sh"}"} + |${if isWin then "@echo off" else s"#!/usr/bin/env ${if isFish then "fish" else "sh"}"} | |java -cp ${cp.mkString(java.io.File.pathSeparator)} sbt.client.Client --jna $args """.stripMargin.linesIterator.toSeq.tail.mkString("\n").getBytes @@ -1058,11 +1047,10 @@ lazy val upperModules = (project in (file("internal") / "upper")) Utils.noPublish ) -lazy val sbtIgnoredProblems = { +lazy val sbtIgnoredProblems = import com.typesafe.tools.mima.core.* Vector( ) -} def scriptedTask(launch: Boolean): Def.Initialize[InputTask[Unit]] = Def.inputTask { val _ = publishLocalBinAll.value @@ -1075,7 +1063,7 @@ def scriptedTask(launch: Boolean): Def.Initialize[InputTask[Unit]] = Def.inputTa scriptedBufferLog.value, Def.setting(Scripted.scriptedParser(scriptedSource.value)).parsed, scriptedPrescripted.value, - scriptedLaunchOpts.value ++ (if (launch) Some(launchJar) else None), + scriptedLaunchOpts.value ++ (if launch then Some(launchJar) else None), scalaVersion.value, version.value, (scriptedSbtProj / Test / fullClasspathAsJars).value @@ -1171,10 +1159,9 @@ def otherRootSettings = "-server", s"-Dsbt.build.root=${(ThisBuild / baseDirectory).value.getAbsolutePath}" ) ::: - (sys.props.get("sbt.ivy.home") match { + (sys.props.get("sbt.ivy.home") match case Some(home) => List(s"-Dsbt.ivy.home=$home") - case _ => Nil - }), + case _ => Nil), publishLocalBinAll := { val _ = (Compile / publishLocalBin).all(scriptedProjects).value }, @@ -1185,10 +1172,9 @@ def otherRootSettings = "-Xms512M", "-server" ) ::: - (sys.props.get("sbt.ivy.home") match { + (sys.props.get("sbt.ivy.home") match case Some(home) => List(s"-Dsbt.ivy.home=$home") - case _ => Nil - }), + case _ => Nil), scripted := scriptedTask(true).evaluated, scriptedUnpublished := scriptedTask(true).evaluated, scriptedSource := (sbtProj / sourceDirectory).value / "repo-override-test" @@ -1248,7 +1234,7 @@ ThisBuild / pomIncludeRepository := (_ => false) // drop repos other than Maven ThisBuild / publishTo := { val centralSnapshots = "https://central.sonatype.com/repository/maven-snapshots/" val v = (ThisBuild / version).value - if (v.endsWith("SNAPSHOT")) Some("central-snapshots" at centralSnapshots) + if v.endsWith("SNAPSHOT") then Some("central-snapshots" at centralSnapshots) else localStaging.value } ThisBuild / publishMavenStyle := true diff --git a/buildfile/src/main/scala/sbt/internal/DslEntry.scala b/buildfile/src/main/scala/sbt/internal/DslEntry.scala index 77852d72d..72a29d134 100644 --- a/buildfile/src/main/scala/sbt/internal/DslEntry.scala +++ b/buildfile/src/main/scala/sbt/internal/DslEntry.scala @@ -16,72 +16,59 @@ import sbt.librarymanagement.Configuration import Def.* /** This represents a `Setting` expression configured by the sbt DSL. */ -sealed trait DslEntry { +sealed trait DslEntry: /** Called by the parser. Sets the position where this entry was defined in the build.sbt file. */ def withPos(pos: RangePosition): DslEntry -} -object DslEntry { +object DslEntry: implicit def fromSettingsDef(inc: SettingsDefinition): DslEntry = DslSetting(inc) implicit def fromSettingsDef(inc: Seq[Setting[?]]): DslEntry = DslSetting(inc) /** Represents a DSL entry which adds settings to the current project. */ - sealed trait ProjectSettings extends DslEntry { + sealed trait ProjectSettings extends DslEntry: def toSettings: Seq[Setting[?]] - } - object ProjectSettings { + object ProjectSettings: def unapply(e: DslEntry): Option[Seq[Setting[?]]] = - e match { + e match case e: ProjectSettings => Some(e.toSettings) case _ => None - } - } /** Represents a DSL entry which manipulates the current project. */ - sealed trait ProjectManipulation extends DslEntry { + sealed trait ProjectManipulation extends DslEntry: def toFunction: Project => Project // TODO - Should we store this? final def withPos(pos: RangePosition): DslEntry = this - } - object ProjectManipulation { + object ProjectManipulation: def unapply(e: DslEntry): Option[Project => Project] = - e match { + e match case e: ProjectManipulation => Some(e.toFunction) case _ => None - } - } /** this represents an actually Setting[_] or Seq[Setting[_]] configured by the sbt DSL. */ - case class DslSetting(settings: SettingsDefinition) extends ProjectSettings { + case class DslSetting(settings: SettingsDefinition) extends ProjectSettings: def toSettings = settings.settings final def withPos(pos: RangePosition): DslEntry = DslSetting(settings.settings.map(_.withPos(pos))) - } /** this represents an `enablePlugins()` in the sbt DSL */ - case class DslEnablePlugins(plugins: Seq[AutoPlugin]) extends ProjectManipulation { + case class DslEnablePlugins(plugins: Seq[AutoPlugin]) extends ProjectManipulation: override val toFunction: Project => Project = _.enablePlugins(plugins*) - } /** this represents an `disablePlugins()` in the sbt DSL */ - case class DslDisablePlugins(plugins: Seq[AutoPlugin]) extends ProjectManipulation { + case class DslDisablePlugins(plugins: Seq[AutoPlugin]) extends ProjectManipulation: override val toFunction: Project => Project = _.disablePlugins(plugins*) - } /** Represents registering an internal dependency for the current project */ - case class DslDependsOn(cs: Seq[ClasspathDep[ProjectReference]]) extends ProjectManipulation { + case class DslDependsOn(cs: Seq[ClasspathDep[ProjectReference]]) extends ProjectManipulation: override val toFunction: Project => Project = _.dependsOn(cs*) - } /** Represents registering a set of configurations with the current project. */ - case class DslConfigs(cs: Seq[Configuration]) extends ProjectManipulation { + case class DslConfigs(cs: Seq[Configuration]) extends ProjectManipulation: override val toFunction: Project => Project = _.configs(cs*) - } /** this represents an `aggregateProjects()` in the sbt DSL */ - case class DslAggregate(refs: Seq[ProjectReference]) extends ProjectManipulation { + case class DslAggregate(refs: Seq[ProjectReference]) extends ProjectManipulation: override val toFunction: Project => Project = _.aggregate(refs*) - } -} +end DslEntry diff --git a/buildfile/src/main/scala/sbt/internal/Eval.scala b/buildfile/src/main/scala/sbt/internal/Eval.scala index 9a793430d..14efe3119 100644 --- a/buildfile/src/main/scala/sbt/internal/Eval.scala +++ b/buildfile/src/main/scala/sbt/internal/Eval.scala @@ -255,6 +255,7 @@ class Eval( generated = generatedFiles, enclosingModule = moduleName, ) + end evalCommon // location of the cached type or definition information private def cacheFile(base: Path, moduleName: String): Path = @@ -271,12 +272,12 @@ class Eval( val source = ev.makeSource(moduleName) run.compileSources(source :: Nil) val unit = run.units.head - val traverser = new tpd.TreeTraverser { - override def traverse(tree: tpd.Tree)(using Context): Unit = { - tree match { + val traverser = new tpd.TreeTraverser: + override def traverse(tree: tpd.Tree)(using Context): Unit = + tree match case x: tpd.TypeDef if x.name.mangledString == s"${moduleName}${NameTransformer.MODULE_SUFFIX_STRING}" => - x.rhs match { + x.rhs match case template: tpd.Template => template.body.foreach { case defdef: tpd.DefDef if defdef.name.mangledString == WrapValName => @@ -314,13 +315,9 @@ class Eval( case _ => } case _ => - } case _: tpd.PackageDef => traverseChildren(tree) case _ => - } - } - } traverser.traverse(unit.tpdTree) checkError("an error in expression") val extra: A = ev.extract(run, unit) @@ -329,6 +326,7 @@ class Eval( } val loader = (parent: ClassLoader) => AbstractFileClassLoader(outputDir, parent) (extra, loader) + end compileAndLoad private final class EvalIntermediate[A]( val extra: A, @@ -534,11 +532,10 @@ final class EvalDefinitions( val enclosingModule: String, val valNames: Seq[String] ): - def values(parent: ClassLoader): Seq[Any] = { + def values(parent: ClassLoader): Seq[Any] = val module = Eval.getModule(enclosingModule, loader(parent)) for n <- valNames yield module.getClass.getMethod(n).invoke(module) - } end EvalDefinitions final class EvalException(msg: String) extends RuntimeException(msg) diff --git a/buildfile/src/main/scala/sbt/internal/EvaluateConfigurations.scala b/buildfile/src/main/scala/sbt/internal/EvaluateConfigurations.scala index 6fa7cebea..1e829c4f5 100644 --- a/buildfile/src/main/scala/sbt/internal/EvaluateConfigurations.scala +++ b/buildfile/src/main/scala/sbt/internal/EvaluateConfigurations.scala @@ -33,7 +33,7 @@ import dotty.tools.dotc.ast.untpd.{ Annotated, ValOrDefDef, Tree } * 2. Compiling scala code into local .class files * 3. Evaluating the expressions and obtaining in-memory objects of the results (Setting[_] instances, or val references). */ -private[sbt] object EvaluateConfigurations { +private[sbt] object EvaluateConfigurations: type LazyClassLoaded[A] = ClassLoader => A @@ -61,7 +61,7 @@ private[sbt] object EvaluateConfigurations { eval: Eval, srcs: Seq[VirtualFile], imports: Seq[String], - ): LazyClassLoaded[LoadedSbtFile] = { + ): LazyClassLoaded[LoadedSbtFile] = val loadFiles = srcs.sortBy(_.name) map { src => evaluateSbtFile(eval, src, IO.readStream(src.input()).linesIterator.toList, imports, 0) } @@ -69,7 +69,6 @@ private[sbt] object EvaluateConfigurations { loadFiles.foldLeft(LoadedSbtFile.empty) { (loaded, load) => loaded.merge(load(loader)) } - } /** * Reads a given .sbt file and evaluates it into a sequence of setting values. @@ -95,7 +94,7 @@ private[sbt] object EvaluateConfigurations { builtinImports: Seq[String], offset: Int, options: Seq[String] - ): ParsedFile = { + ): ParsedFile = def loseTree(l: (String, Tree, LineRange)): (String, LineRange) = (l._1, l._3) val (importStatements, settingsAndDefinitions) = splitExpressions(file, lines, options) val allImports = builtinImports.map(s => (s, -1)) ++ addOffset(offset, importStatements) @@ -103,7 +102,6 @@ private[sbt] object EvaluateConfigurations { addOffsetToRange(offset, settingsAndDefinitions) ) new ParsedFile(allImports, definitions.map(loseTree), settings.map(loseTree)) - } /** * Evaluates a parsed sbt configuration file. @@ -121,10 +119,9 @@ private[sbt] object EvaluateConfigurations { lines: Seq[String], imports: Seq[String], offset: Int - ): LazyClassLoaded[Seq[Setting[?]]] = { + ): LazyClassLoaded[Seq[Setting[?]]] = val l = evaluateSbtFile(eval, file, lines, imports, offset) loader => l(loader).settings - } /** * Evaluates a parsed sbt configuration file. @@ -142,7 +139,7 @@ private[sbt] object EvaluateConfigurations { lines: Seq[String], imports: Seq[String], offset: Int - ): LazyClassLoaded[LoadedSbtFile] = { + ): LazyClassLoaded[LoadedSbtFile] = // TODO - Store the file on the LoadedSbtFile (or the parent dir) so we can accurately do // detection for which project manipulations should be applied. val name = file match @@ -150,13 +147,12 @@ private[sbt] object EvaluateConfigurations { case file => file.id val parsed = parseConfiguration(file, lines, imports, offset, eval.nonCpOptions) val (importDefs, definitions) = - if (parsed.definitions.isEmpty) (Nil, DefinedSbtValues.empty) - else { + if parsed.definitions.isEmpty then (Nil, DefinedSbtValues.empty) + else val definitions = evaluateDefinitions(eval, name, parsed.imports, parsed.definitions, Some(file)) val imp = BuildUtilLite.importAllRoot(definitions.enclosingModule :: Nil) (imp, DefinedSbtValues(definitions)) - } val allImports = importDefs.map(s => (s, -1)) ++ parsed.imports val dslEntries = parsed.settings map { (dslExpression, range) => evaluateDslEntry(eval, name, allImports, dslExpression, range) @@ -167,13 +163,12 @@ private[sbt] object EvaluateConfigurations { // Tracks all the files we generated from evaluating the sbt file. val allGeneratedFiles: Seq[Path] = (definitions.generated ++ dslEntries.flatMap(_.generated)) - loader => { - val projects = { + loader => + val projects = val compositeProjects = definitions .values(loader) .collect { case p: CompositeProject => p } CompositeProject.expand(compositeProjects) - } val loadedDslEntries = dslEntries.map(_.result.apply(loader)) val settings = loadedDslEntries.collect { case DslEntry.ProjectSettings(s) => s }.flatten val manipulations = loadedDslEntries.collect { case DslEntry.ProjectManipulation(f) => f } @@ -186,8 +181,7 @@ private[sbt] object EvaluateConfigurations { definitions, allGeneratedFiles ) - } - } + end evaluateSbtFile private def addOffset(offset: Int, lines: Seq[(String, Int)]): Seq[(String, Int)] = lines.map { (s, i) => (s, i + offset) } @@ -201,11 +195,10 @@ private[sbt] object EvaluateConfigurations { /** * The name of the class we cast DSL "setting" (vs. definition) lines to. */ - val SettingsDefinitionName = { + val SettingsDefinitionName = val _ = classOf[DslEntry] // this line exists to try to provide a compile-time error when the following line needs to be changed "sbt.internal.DslEntry" - } /** * This actually compiles a scala expression which represents a sbt.internals.DslEntry. @@ -225,11 +218,11 @@ private[sbt] object EvaluateConfigurations { imports: Seq[(String, Int)], expression: String, range: LineRange - ): TrackedEvalResult[DslEntry] = { + ): TrackedEvalResult[DslEntry] = // TODO - Should we try to namespace these between.sbt files? IF they hash to the same value, they may actually be // exactly the same setting, so perhaps we don't care? val result = - try { + try eval.eval( expression, imports = new EvalImports(imports.map(_._1)), // name @@ -237,18 +230,16 @@ private[sbt] object EvaluateConfigurations { tpeName = Some(SettingsDefinitionName), line = range.start ) - } catch { + catch case e: EvalException => throw new MessageOnlyException(e.getMessage) - } // TODO - keep track of configuration classes defined. TrackedEvalResult( result.generated, - loader => { + loader => val pos = RangePosition(name, range.shift(1)) result.getValue(loader).asInstanceOf[DslEntry].withPos(pos) - } ) - } + end evaluateDslEntry /** * This actually compiles a scala expression which represents a Seq[Setting[_]], although the @@ -308,13 +299,11 @@ private[sbt] object EvaluateConfigurations { lines partition { case (_, tree, _) => isDefinition(tree) } @tailrec - private def isDefinition(tree: Tree): Boolean = { - tree match { + private def isDefinition(tree: Tree): Boolean = + tree match case Annotated(arg, annot) => isDefinition(arg) case _: ValOrDefDef => true case _ => false - } - } private def extractedValTypes: Seq[String] = Seq( @@ -330,7 +319,7 @@ private[sbt] object EvaluateConfigurations { imports: Seq[(String, Int)], definitions: Seq[(String, LineRange)], file: Option[VirtualFileRef], - ): EvalDefinitions = { + ): EvalDefinitions = val convertedRanges = definitions.map { (s, r) => (s, r.start to r.end) } eval.evalDefinitions( convertedRanges, @@ -339,13 +328,12 @@ private[sbt] object EvaluateConfigurations { // file, extractedValTypes ) - } -} +end EvaluateConfigurations object BuildUtilLite: /** Import just the names. */ def importNames(names: Seq[String]): Seq[String] = - if (names.isEmpty) Nil else names.mkString("import ", ", ", "") :: Nil + if names.isEmpty then Nil else names.mkString("import ", ", ", "") :: Nil /** Prepend `_root_` and import just the names. */ def importNamesRoot(names: Seq[String]): Seq[String] = importNames(names map rootedName) @@ -353,26 +341,24 @@ object BuildUtilLite: /** Wildcard import `.{*, given}` for all values. */ def importAll(values: Seq[String]): Seq[String] = importNames(values map { _ + ".{*, given}" }) def importAllRoot(values: Seq[String]): Seq[String] = importAll(values map rootedName) - def rootedName(s: String): String = if (s contains '.') "_root_." + s else s + def rootedName(s: String): String = if s contains '.' then "_root_." + s else s end BuildUtilLite -object Index { - def allKeys(settings: Seq[Setting[?]]): Set[ScopedKey[?]] = { +object Index: + def allKeys(settings: Seq[Setting[?]]): Set[ScopedKey[?]] = val result = new java.util.HashSet[ScopedKey[?]] settings.foreach { s => - if (!s.key.key.isLocal && result.add(s.key)) { - s.dependencies.foreach(k => if (!k.key.isLocal) result.add(s.key)) - } + if !s.key.key.isLocal && result.add(s.key) then + s.dependencies.foreach(k => if !k.key.isLocal then result.add(s.key)) } result.asScala.toSet - } def stringToKeyMap(settings: Set[AttributeKey[?]]): Map[String, AttributeKey[?]] = stringToKeyMap0(settings)(_.label) private def stringToKeyMap0( settings: Set[AttributeKey[?]] - )(label: AttributeKey[?] => String): Map[String, AttributeKey[?]] = { + )(label: AttributeKey[?] => String): Map[String, AttributeKey[?]] = val multiMap = settings.groupBy(label) val duplicates = multiMap.iterator .collect { case (k, xs) if xs.size > 1 => (k, xs.map(_.tag)) } @@ -384,11 +370,10 @@ object Index { .map { (k, tps) => s"'$k' (${tps.mkString(", ")})" } .mkString(",") sys.error(s"Some keys were defined with the same name but different types: $duplicateStr") - } private type TriggerMap = collection.mutable.HashMap[TaskId[?], Seq[TaskId[?]]] - def triggers(ss: Settings): Triggers = { + def triggers(ss: Settings): Triggers = val runBefore = new TriggerMap val triggeredBy = new TriggerMap ss.values.collect { case base: Task[?] => @@ -400,7 +385,10 @@ object Index { update(triggeredBy, Def.triggeredBy) } val onComplete = (GlobalScope / Def.onComplete).get(ss).getOrElse(() => ()) - new Triggers(runBefore, triggeredBy, map => { onComplete(); map }) - } - -} + new Triggers( + runBefore, + triggeredBy, + map => + onComplete(); map + ) +end Index diff --git a/buildfile/src/main/scala/sbt/internal/LoadedSbtFile.scala b/buildfile/src/main/scala/sbt/internal/LoadedSbtFile.scala index b0a0997d0..bba19a4af 100644 --- a/buildfile/src/main/scala/sbt/internal/LoadedSbtFile.scala +++ b/buildfile/src/main/scala/sbt/internal/LoadedSbtFile.scala @@ -26,7 +26,7 @@ private[sbt] final case class LoadedSbtFile( // rather than what we have now... definitions: DefinedSbtValues, generatedFiles: Seq[Path] -) { +): // We still use merge for now. We track originating sbt file in an alternative manner. def merge(o: LoadedSbtFile): LoadedSbtFile = new LoadedSbtFile( @@ -40,13 +40,13 @@ private[sbt] final case class LoadedSbtFile( def clearProjects = new LoadedSbtFile(settings, Nil, importedDefs, manipulations, definitions, generatedFiles) -} +end LoadedSbtFile /** * Represents the `val`/`lazy val` definitions defined within a build.sbt file * which we can reference in other settings. */ -private[sbt] final class DefinedSbtValues(val sbtFiles: Seq[EvalDefinitions]) { +private[sbt] final class DefinedSbtValues(val sbtFiles: Seq[EvalDefinitions]): def values(parent: ClassLoader): Seq[Any] = sbtFiles flatMap (_.values(parent)) @@ -56,15 +56,14 @@ private[sbt] final class DefinedSbtValues(val sbtFiles: Seq[EvalDefinitions]) { e.loader(cl) } - def imports: Seq[String] = { + def imports: Seq[String] = // TODO - Sanity check duplicates and such, so users get a nice warning rather // than explosion. - for { + for file <- sbtFiles m = file.enclosingModule v <- file.valNames.map(NameTransformer.decode) - } yield s"import ${m}.`${v}`" - } + yield s"import ${m}.`${v}`" def generated: Seq[Path] = sbtFiles.flatMap(_.generated) @@ -79,8 +78,8 @@ private[sbt] final class DefinedSbtValues(val sbtFiles: Seq[EvalDefinitions]) { */ def zip(other: DefinedSbtValues): DefinedSbtValues = new DefinedSbtValues(sbtFiles ++ other.sbtFiles) -} -private[sbt] object DefinedSbtValues { +end DefinedSbtValues +private[sbt] object DefinedSbtValues: /** Construct a DefinedSbtValues object directly from the underlying representation. */ def apply(eval: EvalDefinitions): DefinedSbtValues = @@ -89,10 +88,7 @@ private[sbt] object DefinedSbtValues { /** Construct an empty value object. */ def empty = new DefinedSbtValues(Nil) -} - -private[sbt] object LoadedSbtFile { +private[sbt] object LoadedSbtFile: /** Represents an empty .sbt file: no Projects, imports, or settings. */ def empty = new LoadedSbtFile(Nil, Nil, Nil, Nil, DefinedSbtValues.empty, Nil) -} diff --git a/buildfile/src/main/scala/sbt/internal/parser/SbtParser.scala b/buildfile/src/main/scala/sbt/internal/parser/SbtParser.scala index 63fe498b0..1ed4e721b 100644 --- a/buildfile/src/main/scala/sbt/internal/parser/SbtParser.scala +++ b/buildfile/src/main/scala/sbt/internal/parser/SbtParser.scala @@ -76,7 +76,7 @@ private[sbt] object SbtParser: * when we know for a fact that the user-provided snippet doesn't * parse. */ - private[sbt] class UniqueParserReporter extends Reporter { + private[sbt] class UniqueParserReporter extends Reporter: private val reporters = new ConcurrentHashMap[String, StoreReporter]() @@ -91,47 +91,43 @@ private[sbt] object SbtParser: val reporter = getReporter(sourcePath) reporter.report(dia) - override def hasErrors: Boolean = { + override def hasErrors: Boolean = var result = false - reporters.forEachValue(100, r => if (r.hasErrors) result = true) + reporters.forEachValue(100, r => if r.hasErrors then result = true) result - } def createReporter(uniqueFileName: String): StoreReporter = val r = new StoreReporter(null) reporters.put(uniqueFileName, r) r - def getOrCreateReporter(uniqueFileName: String): StoreReporter = { + def getOrCreateReporter(uniqueFileName: String): StoreReporter = val r = reporters.get(uniqueFileName) - if (r == null) createReporter(uniqueFileName) + if r == null then createReporter(uniqueFileName) else r - } - private def getReporter(fileName: String) = { + private def getReporter(fileName: String) = val reporter = reporters.get(fileName) - if (reporter == null) { + if reporter == null then scalacGlobalInitReporter.getOrElse( sys.error(s"sbt forgot to initialize `scalacGlobalInitReporter`.") ) - } else reporter - } + else reporter def throwParserErrorsIfAny(reporter: StoreReporter, fileName: String)(using context: Context ): Unit = - if reporter.hasErrors then { + if reporter.hasErrors then val seq = reporter.pendingMessages.map { info => s"""[$fileName]:${info.pos.line}: ${info.msg}""" } val errorMessage = seq.mkString(System.lineSeparator) val error: String = - if (errorMessage.contains(XML_ERROR)) - s"$errorMessage\n${SbtParser.XmlErrorMessage}" + if errorMessage.contains(XML_ERROR) then s"$errorMessage\n${SbtParser.XmlErrorMessage}" else errorMessage throw new MessageOnlyException(error) - } else () - } + else () + end UniqueParserReporter private[sbt] var scalacGlobalInitReporter: Option[ConsoleReporter] = None @@ -251,7 +247,7 @@ private[sbt] case class SbtParser( private def splitExpressions( path: VirtualFileRef, lines: Seq[String] - ): (Seq[(String, Int)], Seq[(String, LineRange)], Seq[(String, Tree)]) = { + ): (Seq[(String, Int)], Seq[(String, LineRange)], Seq[(String, Tree)]) = // import sbt.internal.parser.MissingBracketHandler.findMissingText val code = lines.toIndexedSeq.mkString(END_OF_LINE) val wrapCode = s"""object SyntheticModule { @@ -301,7 +297,7 @@ private[sbt] case class SbtParser( (stmt, tree) } ) - } + end splitExpressions private def importsToLineRanges( sourceFile: SourceFile, diff --git a/buildfile/src/main/scala/sbt/internal/parser/SbtRefactorings.scala b/buildfile/src/main/scala/sbt/internal/parser/SbtRefactorings.scala index 36d67a9cd..69c166e84 100644 --- a/buildfile/src/main/scala/sbt/internal/parser/SbtRefactorings.scala +++ b/buildfile/src/main/scala/sbt/internal/parser/SbtRefactorings.scala @@ -34,7 +34,7 @@ private[sbt] object SbtRefactorings: def applySessionSettings( lines: Seq[String], commands: Seq[SessionSetting] - ): Seq[String] = { + ): Seq[String] = val split = SbtParser(FAKE_FILE, lines) given ctx: Context = SbtParser.defaultGlobalForParser.compileCtx val recordedCommands = recordCommands(commands, split) @@ -42,24 +42,21 @@ private[sbt] object SbtRefactorings: val newContent = replaceFromBottomToTop(lines.mkString(END_OF_LINE), sortedRecordedCommands) newContent.linesIterator.toList - } private def replaceFromBottomToTop( modifiedContent: String, sortedRecordedCommands: Seq[(Int, String, String)] - ) = { + ) = sortedRecordedCommands.foldLeft(modifiedContent) { case (acc, (from, old, replacement)) => val before = acc.substring(0, from) val after = acc.substring(from + old.length, acc.length) val afterLast = emptyStringForEmptyString(after) before + replacement + afterLast } - } - private def emptyStringForEmptyString(text: String) = { + private def emptyStringForEmptyString(text: String) = val trimmed = text.trim - if (trimmed.isEmpty) trimmed else text - } + if trimmed.isEmpty then trimmed else text private def recordCommands(commands: Seq[SessionSetting], split: SbtParser)(using Context) = commands.flatMap { case (_, command) => @@ -72,25 +69,22 @@ private[sbt] object SbtRefactorings: ) = split.settingsTrees.foldLeft(Seq.empty[(Int, String, String)]) { case (acc, (st, tree)) => val treeName = extractSettingName(tree) - if (name == treeName) { + if name == treeName then val replacement = - if (acc.isEmpty) command.mkString(END_OF_LINE) + if acc.isEmpty then command.mkString(END_OF_LINE) else emptyString val pos = tree.sourcePos.start - SbtParser.WRAPPER_POSITION_OFFSET (pos, st, replacement) +: acc - } else { - acc - } + else acc } - private def toTreeStringMap(command: Seq[String]) = { + private def toTreeStringMap(command: Seq[String]) = val split = SbtParser(FAKE_FILE, command) val trees = split.settingsTrees val seq = trees.map { (statement, tree) => (extractSettingName(tree), statement) } seq.toMap - } @tailrec private def extractSettingName(tree: untpd.Tree): String = tree match diff --git a/buildfile/src/test/scala/sbt/internal/parser/CheckIfParsedSpec.scala b/buildfile/src/test/scala/sbt/internal/parser/CheckIfParsedSpec.scala index 65c6c2605..aacf497f9 100644 --- a/buildfile/src/test/scala/sbt/internal/parser/CheckIfParsedSpec.scala +++ b/buildfile/src/test/scala/sbt/internal/parser/CheckIfParsedSpec.scala @@ -11,7 +11,7 @@ package parser abstract class CheckIfParsedSpec( val splitter: SplitExpressions.SplitExpression = EvaluateConfigurations.splitExpressions -) extends AbstractSpec { +) extends AbstractSpec: test(s"${this.getClass.getName} should parse sbt file") { files foreach { (content, description, nonEmptyImports, nonEmptyStatements) => @@ -34,13 +34,9 @@ abstract class CheckIfParsedSpec( private def shouldContains(b: Boolean): String = s"""Should ${ - if (b) { - "contain" - } else { - "not contain" - } + if b then "contain" + else "not contain" }""" protected def files: Seq[(String, String, Boolean, Boolean)] - -} +end CheckIfParsedSpec diff --git a/buildfile/src/test/scala/sbt/internal/parser/CommentedXmlSpec.scala b/buildfile/src/test/scala/sbt/internal/parser/CommentedXmlSpec.scala index 5e13f6f45..2c921e150 100644 --- a/buildfile/src/test/scala/sbt/internal/parser/CommentedXmlSpec.scala +++ b/buildfile/src/test/scala/sbt/internal/parser/CommentedXmlSpec.scala @@ -8,7 +8,7 @@ package sbt.internal.parser -object CommentedXmlSpec extends CheckIfParsedSpec { +object CommentedXmlSpec extends CheckIfParsedSpec: override protected val files = Seq( ( @@ -95,4 +95,4 @@ object CommentedXmlSpec extends CheckIfParsedSpec { false ) ) -} +end CommentedXmlSpec diff --git a/buildfile/src/test/scala/sbt/internal/parser/EmbeddedXmlSpec.scala b/buildfile/src/test/scala/sbt/internal/parser/EmbeddedXmlSpec.scala index b3f2a043b..d06826d3a 100644 --- a/buildfile/src/test/scala/sbt/internal/parser/EmbeddedXmlSpec.scala +++ b/buildfile/src/test/scala/sbt/internal/parser/EmbeddedXmlSpec.scala @@ -10,7 +10,7 @@ package sbt.internal.parser import sbt.internal.util.MessageOnlyException -object EmbeddedXmlSpec extends CheckIfParsedSpec { +object EmbeddedXmlSpec extends CheckIfParsedSpec: test("File with xml content should Handle last xml part") { val errorLine = """4.0""" @@ -35,17 +35,16 @@ object EmbeddedXmlSpec extends CheckIfParsedSpec { | |""".stripMargin - try { + try splitter(buildSbt) sys.error("expected MessageOnlyException") - } catch { + catch case exception: MessageOnlyException => val index = buildSbt.linesIterator.indexWhere(line => line.contains(errorLine)) + 1 val numberRegex = """(\d+)""".r val message = exception.getMessage val list = numberRegex.findAllIn(message).toList assert(list.contains(index.toString)) - } } protected val files = Seq( @@ -169,5 +168,4 @@ object EmbeddedXmlSpec extends CheckIfParsedSpec { true ) ) - -} +end EmbeddedXmlSpec diff --git a/buildfile/src/test/scala/sbt/internal/parser/NewFormatSpec.scala b/buildfile/src/test/scala/sbt/internal/parser/NewFormatSpec.scala index 659734bb5..c5f187dd8 100644 --- a/buildfile/src/test/scala/sbt/internal/parser/NewFormatSpec.scala +++ b/buildfile/src/test/scala/sbt/internal/parser/NewFormatSpec.scala @@ -15,7 +15,7 @@ import java.io.File import scala.io.Source import sbt.internal.inc.PlainVirtualFileConverter -object NewFormatSpec extends AbstractSpec { +object NewFormatSpec extends AbstractSpec: val converter = PlainVirtualFileConverter.converter given splitter: SplitExpressions.SplitExpression = EvaluateConfigurations.splitExpressions @@ -36,4 +36,4 @@ object NewFormatSpec extends AbstractSpec { ) } } -} +end NewFormatSpec diff --git a/buildfile/src/test/scala/sbt/internal/parser/SbtParserErrorSpec.scala b/buildfile/src/test/scala/sbt/internal/parser/SbtParserErrorSpec.scala index e8cc39b05..7f03d3596 100644 --- a/buildfile/src/test/scala/sbt/internal/parser/SbtParserErrorSpec.scala +++ b/buildfile/src/test/scala/sbt/internal/parser/SbtParserErrorSpec.scala @@ -17,7 +17,7 @@ import java.nio.file.Files import java.nio.file.Paths import verify.sourcecode.SourceLocation -object SbtParserErrorSpec extends AbstractSpec { +object SbtParserErrorSpec extends AbstractSpec: val converter = PlainVirtualFileConverter.converter @@ -59,4 +59,4 @@ object SbtParserErrorSpec extends AbstractSpec { callback throw new AssertionError(s"$pos: expected a MessageOnlyException to be thrown") catch case ex: MessageOnlyException => ex.getMessage -} +end SbtParserErrorSpec diff --git a/buildfile/src/test/scala/sbt/internal/parser/SessionSettingsSpec.scala b/buildfile/src/test/scala/sbt/internal/parser/SessionSettingsSpec.scala index b1a0b2953..2394b5356 100644 --- a/buildfile/src/test/scala/sbt/internal/parser/SessionSettingsSpec.scala +++ b/buildfile/src/test/scala/sbt/internal/parser/SessionSettingsSpec.scala @@ -17,7 +17,7 @@ import java.nio.file.Path import java.nio.file.Paths import scala.jdk.CollectionConverters.* -abstract class AbstractSessionSettingsSpec(folder: String) extends AbstractSpec { +abstract class AbstractSessionSettingsSpec(folder: String) extends AbstractSpec: private val rootDir = Paths.get(getClass.getResource("/" + folder).toURI) println(s"Reading files from: $rootDir") private val converter = PlainVirtualFileConverter.converter @@ -62,7 +62,7 @@ abstract class AbstractSessionSettingsSpec(folder: String) extends AbstractSpec private def readLines(file: Path): Seq[String] = Files.readAllLines(file).asScala.toList -} +end AbstractSessionSettingsSpec object SessionSettingsSpec extends AbstractSessionSettingsSpec("session-settings") diff --git a/buildfile/src/test/scala/sbt/internal/parser/SplitExpressionsBehavior.scala b/buildfile/src/test/scala/sbt/internal/parser/SplitExpressionsBehavior.scala index d3260f998..a5ba9fd76 100644 --- a/buildfile/src/test/scala/sbt/internal/parser/SplitExpressionsBehavior.scala +++ b/buildfile/src/test/scala/sbt/internal/parser/SplitExpressionsBehavior.scala @@ -14,15 +14,15 @@ import sbt.internal.util.LineRange import xsbti.VirtualFileRef import dotty.tools.dotc.ast.untpd.Tree -trait SplitExpression { +trait SplitExpression: extension (splitter: SplitExpressions.SplitExpression) def apply(s: String): (Seq[(String, Int)], Seq[(String, Tree, LineRange)]) = splitter(VirtualFileRef.of("noFile"), s.split('\n').toSeq) -} -trait SplitExpressionsBehavior extends SplitExpression { this: verify.BasicTestSuite => +trait SplitExpressionsBehavior extends SplitExpression: + this: verify.BasicTestSuite => - def newExpressionsSplitter(splitter: SplitExpressions.SplitExpression) = { + def newExpressionsSplitter(splitter: SplitExpressions.SplitExpression) = test("parse a two settings without intervening blank line") { val (imports, settings) = splitter( @@ -64,6 +64,5 @@ trait SplitExpressionsBehavior extends SplitExpression { this: verify.BasicTestS assert(imports.size == 1) assert(settingsAndDefs.size == 1) } - } - -} + end newExpressionsSplitter +end SplitExpressionsBehavior diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala index 8393c6d91..abbd305c2 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/Cont.scala @@ -236,7 +236,7 @@ trait Cont: val lambda = Lambda( owner = Symbol.spliceOwner, tpe = tpe, - rhsFn = (sym, params) => { + rhsFn = (sym, params) => val param = params.head.asInstanceOf[Term] // Called when transforming the tree to add an input. // For `qual` of type F[A], and a `selection` qual.value, @@ -261,7 +261,6 @@ trait Cont: ).asTerm .changeOwner(sym) case None => modifiedBody.asTerm - } ).asExprOf[a => A1] val expr = input.term.asExprOf[F[a]] typed[F[A1]]( @@ -274,6 +273,7 @@ trait Cont: genMap0[Effect[A]](inner(body).asExprOf[Effect[A]]) case Right(_) => flatten(genMap0[F[Effect[A]]](inner(body).asExprOf[F[Effect[A]]])) + end genMap def genMapN(body: Term, inputs: List[Input]): Expr[F[Effect[A]]] = def genMapN0[A1: Type](body: Expr[A1]): Expr[F[A1]] = @@ -285,7 +285,7 @@ trait Cont: val lambda = Lambda( owner = Symbol.spliceOwner, tpe = lambdaTpe, - rhsFn = (sym, params) => { + rhsFn = (sym, params) => val p0 = params.head.asInstanceOf[Term] // Called when transforming the tree to add an input. // For `qual` of type F[A], and a `selection` qual.value, @@ -342,9 +342,10 @@ trait Cont: body = modifiedBody, input = unitExpr, ).asTerm.changeOwner(sym) + end if case None => modifiedBody.asTerm - } + end match ) val tupleMapRepr = TypeRepr .of[Tuple.Map] @@ -358,11 +359,14 @@ trait Cont: ${ lambda.asExprOf[inputTypeTpe & Tuple => A1] } ) } + end match + end genMapN0 eitherTree match case Left(_) => genMapN0[Effect[A]](inner(body).asExprOf[Effect[A]]) case Right(_) => flatten(genMapN0[F[Effect[A]]](inner(body).asExprOf[F[Effect[A]]])) + end genMapN // call `ActionCache.cache` def callActionCache[A1: Type, A2: Type]( @@ -512,6 +516,8 @@ trait Cont: if hasPrintTreeMacroSetting then Console.err.println(Printer.TreeStructure.show(r.asTerm)) else Console.err.println(r.show) r + end contImpl + end extension end Cont private[sbt] object Cont: diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala index 2d1414553..f8459aac6 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/ContextUtil.scala @@ -116,6 +116,8 @@ trait ContextUtil[C <: Quotes & scala.Singleton](val valStart: Int): case Inlined(_, _, tree) => extractTags(tree) case Apply(_, List(arg)) => extractTags(arg) case _ => extractTags0(tree) + end extractTags + end Input def cacheLevels(tree: Term): Seq[CacheLevelTag] = tree.underlying match @@ -202,6 +204,7 @@ trait ContextUtil[C <: Quotes & scala.Singleton](val valStart: Int): end scanner scanner.traverseTree(tree)(Symbol.spliceOwner) result.toSet + end collectNowarnQuals def collectDefs(tree: Term, isWrapper: (String, TypeRepr, Term) => Boolean): Set[Symbol] = val defs = mutable.HashSet[Symbol]() diff --git a/core-macros/src/main/scala/sbt/internal/util/appmacro/Convert.scala b/core-macros/src/main/scala/sbt/internal/util/appmacro/Convert.scala index f4b359371..8abd0a706 100644 --- a/core-macros/src/main/scala/sbt/internal/util/appmacro/Convert.scala +++ b/core-macros/src/main/scala/sbt/internal/util/appmacro/Convert.scala @@ -70,6 +70,7 @@ trait Convert[C <: Quotes & Singleton] extends ContextUtil[C]: super.transformTerm(tree)(owner) end appTransformer appTransformer.transformTerm(tree)(owner) + end transformWrappers object Converted: def success(tree: Term) = Converted.Success(tree, Types.idFun) diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala index 9c821ec20..adb2e9856 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/LineReader.scala @@ -32,18 +32,17 @@ import java.net.MalformedURLException import org.jline.builtins.InputRC -trait LineReader extends AutoCloseable { +trait LineReader extends AutoCloseable: def readLine(prompt: String, mask: Option[Char] = None): Option[String] override def close(): Unit = {} -} -object LineReader { +object LineReader: val HandleCONT = !java.lang.Boolean.getBoolean("sbt.disable.cont") && Signals.supported(Signals.CONT) val MaxHistorySize = 500 - private def completer(parser: Parser[?]): Completer = new Completer { - def complete(lr: JLineReader, pl: ParsedLine, candidates: JList[Candidate]): Unit = { + private def completer(parser: Parser[?]): Completer = new Completer: + def complete(lr: JLineReader, pl: ParsedLine, candidates: JList[Candidate]): Unit = Parser.completions(parser, pl.line(), 10).get.foreach { c => /* * For commands like `~` that delegate parsing to another parser, the `~` may be @@ -61,39 +60,32 @@ object LineReader { * the prefix, so that `testOnly com` might expand to something like: * `testOnly testOnly\ com.foo.FooSpec` instead of `testOnly com.foo.FooSpec`. */ - if (c.append.nonEmpty) { - val cand = pl.line() match { + if c.append.nonEmpty then + val cand = pl.line() match case line if line.endsWith(" ") => c.append case line => line.split(" ").last + c.append - } // https://github.com/jline/jline3/blob/9a4971868e4bdd29a36e454de01f54d3cd6071e0/reader/src/main/java/org/jline/reader/Candidate.java#L123-L131 // "If the candidate is complete and is selected, a space separator will be added." val complete = false candidates.add(new Candidate(cand, cand, null, null, null, null, complete)) - } } - } - } - private def inputrcFileUrl(): Option[URL] = { + private def inputrcFileUrl(): Option[URL] = // keep jline2 compatibility // https://github.com/jline/jline2/blob/12b98d94589e3bd6a6/src/main/java/jline/console/ConsoleReader.java#L291-L306 sys.props .get("jline.inputrc") .flatMap { path => - try { - Some(url(path)) - } catch { + try Some(url(path)) + catch case _: MalformedURLException => Some(file(path).toURI.toURL) - } } .orElse { sys.props.get("user.home").map { home => val f = file(home) / ".inputrc" - (if (f.isFile) f else file("/etc/inputrc")).toURI.toURL + (if f.isFile then f else file("/etc/inputrc")).toURI.toURL } } - } // cache on memory. private lazy val inputrcFileContents: Option[Array[Byte]] = inputrcFileUrl().map(in => sbt.io.IO.readBytes(in.openStream())) @@ -101,11 +93,11 @@ object LineReader { historyPath: Option[File], parser: Parser[?], terminal: Terminal, - ): LineReader = { + ): LineReader = // We may want to consider in-sourcing LineReader.java from jline. We don't otherwise // directly need jline3 for sbt. - new LineReader { - override def readLine(prompt: String, mask: Option[Char]): Option[String] = { + new LineReader: + override def readLine(prompt: String, mask: Option[Char]): Option[String] = val term = JLine3(terminal) val reader = LineReaderBuilder .builder() @@ -113,69 +105,62 @@ object LineReader { .completer(completer(parser)) .option(JLineReader.Option.CASE_INSENSITIVE, true) .build() - try { + try inputrcFileContents.foreach { bytes => InputRC.configure( reader, new ByteArrayInputStream(bytes) ) } - } catch { + catch case NonFatal(_) => // ignore - } historyPath.foreach(f => reader.setVariable(JLineReader.HISTORY_FILE, f)) - val signalRegistration = terminal match { + val signalRegistration = terminal match case _: Terminal.ConsoleTerminal => Some(Signals.register(() => terminal.write(-1))) case _ => None - } try terminal.withRawInput { Option(mask.map(reader.readLine(prompt, _)).getOrElse(reader.readLine(prompt))) } - catch { + catch case e: EndOfFileException => - if (terminal == Terminal.console && !Terminal.hasConsole) None + if terminal == Terminal.console && !Terminal.hasConsole then None else Some("exit") case _: IOError | _: ClosedException => Some("exit") case _: UserInterruptException | _: ClosedByInterruptException | _: UncheckedIOException => throw new InterruptedException - } finally { + finally signalRegistration.foreach(_.remove()) terminal.prompt.reset() term.close() - } - } - } - } + end readLine def createJLine2Reader( historyPath: Option[File], terminal: Terminal, prompt: Prompt = Prompt.Running, - ): ConsoleReader = { + ): ConsoleReader = val cr = Terminal.createReader(terminal, prompt) cr.setExpandEvents(false) // https://issues.scala-lang.org/browse/SI-7650 cr.setBellEnabled(false) - val h = historyPath match { + val h = historyPath match case None => new MemoryHistory case Some(file) => new FileHistory(file): MemoryHistory - } h.setMaxSize(MaxHistorySize) cr.setHistory(h) cr.setHistoryEnabled(true) cr - } def simple(terminal: Terminal): LineReader = new SimpleReader(None, HandleCONT, terminal) def simple( historyPath: Option[File], handleCONT: Boolean = HandleCONT, injectThreadSleep: Boolean = false ): LineReader = new SimpleReader(historyPath, handleCONT, injectThreadSleep) -} +end LineReader -abstract class JLine extends LineReader { +abstract class JLine extends LineReader: protected def handleCONT: Boolean protected def reader: ConsoleReader @@ -188,79 +173,67 @@ abstract class JLine extends LineReader { } private def readLineWithHistory(prompt: String, mask: Option[Char]): Option[String] = - reader.getHistory match { + reader.getHistory match case fh: FileHistory => try readLineDirect(prompt, mask) finally fh.flush() case _ => readLineDirect(prompt, mask) - } private def readLineDirect(prompt: String, mask: Option[Char]): Option[String] = - if (handleCONT) + if handleCONT then Signals.withHandler(() => resume(), signal = Signals.CONT)(() => readLineDirectRaw(prompt, mask) ) - else - readLineDirectRaw(prompt, mask) + else readLineDirectRaw(prompt, mask) - private def readLineDirectRaw(prompt: String, mask: Option[Char]): Option[String] = { + private def readLineDirectRaw(prompt: String, mask: Option[Char]): Option[String] = val newprompt = handleMultilinePrompt(prompt) - mask match { + mask match case Some(m) => Option(reader.readLine(newprompt, m)) case None => Option(reader.readLine(newprompt)) - } - } - private def handleMultilinePrompt(prompt: String): String = { + private def handleMultilinePrompt(prompt: String): String = val lines0 = """\r?\n""".r.split(prompt) - lines0.length match { + lines0.length match case 0 | 1 => handleProgress(prompt) case _ => val lines = lines0.toList map handleProgress // Workaround for regression jline/jline2#205 reader.getOutput.write(lines.init.mkString("\n") + "\n") lines.last - } - } - private def handleProgress(prompt: String): String = { + private def handleProgress(prompt: String): String = import ConsoleAppender.* - if (showProgress) s"$DeleteLine" + prompt + if showProgress then s"$DeleteLine" + prompt else prompt - } - private def resume(): Unit = { + private def resume(): Unit = Terminal.reset() reader.drawLine() reader.flush() - } -} +end JLine final class FullReader( historyPath: Option[File], complete: Parser[?], val handleCONT: Boolean, terminal: Terminal -) extends JLine { - protected val reader: ConsoleReader = { +) extends JLine: + protected val reader: ConsoleReader = val cr = LineReader.createJLine2Reader(historyPath, terminal) sbt.internal.util.complete.JLineCompletion.installCustomCompletor(cr, complete) cr - } -} class SimpleReader private[sbt] ( historyPath: Option[File], val handleCONT: Boolean, terminal: Terminal -) extends JLine { +) extends JLine: def this(historyPath: Option[File], handleCONT: Boolean, injectThreadSleep: Boolean) = this(historyPath, handleCONT, Terminal.console) protected lazy val reader: ConsoleReader = LineReader.createJLine2Reader(historyPath, terminal) -} -object SimpleReader extends SimpleReader(None, LineReader.HandleCONT, false) { +object SimpleReader extends SimpleReader(None, LineReader.HandleCONT, false): def apply(terminal: Terminal): SimpleReader = new SimpleReader(None, LineReader.HandleCONT, terminal) -} diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala index 99f6ed088..31c71b448 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Completions.scala @@ -13,7 +13,7 @@ package complete * Represents a set of completions. It exists instead of implicitly defined operations on top of * Set[Completion] for laziness. */ -sealed trait Completions { +sealed trait Completions: def get: Set[Completion] final infix def x(o: Completions): Completions = flatMap(_ x o) @@ -30,17 +30,15 @@ sealed trait Completions { final def map(f: Completion => Completion): Completions = Completions(get map f) override final def hashCode = get.hashCode - override final def equals(o: Any) = o match { - case c: Completions => get == c.get; case _ => false - } -} + override final def equals(o: Any) = o match + case c: Completions => get == c.get; + case _ => false -object Completions { +object Completions: /** Returns a lazy Completions instance using the provided Completion Set. */ - def apply(cs: => Set[Completion]): Completions = new Completions { + def apply(cs: => Set[Completion]): Completions = new Completions: lazy val get = cs - } /** Returns a strict Completions instance using the provided Completion Set. */ def strict(cs: Set[Completion]): Completions = apply(cs) @@ -59,8 +57,7 @@ object Completions { /** Returns a strict Completions instance containing only the provided Completion. */ def single(c: Completion): Completions = strict(Set.empty + c) - -} +end Completions /** * Represents a completion. The abstract members `display` and `append` are best explained with an @@ -74,7 +71,7 @@ object Completions { * input if a completion is selected 2) the full token being completed, which is useful for * presenting a user with choices to select */ -sealed trait Completion { +sealed trait Completion: /** * The proposed suffix to append to the existing input to complete the last token in the input. @@ -95,58 +92,51 @@ sealed trait Completion { else Completions.strict(Set.empty + this) override final lazy val hashCode = Completion.hashCode(this) - override final def equals(o: Any) = o match { - case c: Completion => Completion.equal(this, c); case _ => false - } -} + override final def equals(o: Any) = o match + case c: Completion => Completion.equal(this, c); + case _ => false +end Completion -final class DisplayOnly(val display: String) extends Completion { +final class DisplayOnly(val display: String) extends Completion: def isEmpty = display.isEmpty def append = "" override def toString = "{" + display + "}" -} -final class Token(val display: String, val append: String) extends Completion { +final class Token(val display: String, val append: String) extends Completion: def isEmpty = display.isEmpty && append.isEmpty override final def toString = "[" + display + "]++" + append -} -final class Suggestion(val append: String) extends Completion { +final class Suggestion(val append: String) extends Completion: def isEmpty = append.isEmpty def display = append override def toString = append -} -object Completion { +object Completion: def concat(a: Completion, b: Completion): Completion = - (a, b) match { + (a, b) match case (as: Suggestion, bs: Suggestion) => suggestion(as.append + bs.append) case (at: Token, _) if at.append.isEmpty => b case _ if a.isEmpty => b case _ => a - } def evaluatesRight(a: Completion): Boolean = - a match { + a match case _: Suggestion => true case at: Token if at.append.isEmpty => true case _ => a.isEmpty - } def equal(a: Completion, b: Completion): Boolean = - (a, b) match { + (a, b) match case (as: Suggestion, bs: Suggestion) => as.append == bs.append case (ad: DisplayOnly, bd: DisplayOnly) => ad.display == bd.display case (at: Token, bt: Token) => at.display == bt.display && at.append == bt.append case _ => false - } def hashCode(a: Completion): Int = - a match { + a match case as: Suggestion => (0, as.append).hashCode case ad: DisplayOnly => (1, ad.display).hashCode case at: Token => (2, at.display, at.append).hashCode - } val empty: Completion = suggestion("") def single(c: Char): Completion = suggestion(c.toString) @@ -160,4 +150,4 @@ object Completion { def tokenDisplay(append: String, display: String): Completion = new Token(display, append) def suggestion(value: String): Completion = new Suggestion(value) -} +end Completion diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala index ea3a130c2..3e46a8615 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/EditDistance.scala @@ -12,7 +12,7 @@ package complete import java.lang.Character.toLowerCase as lower /** @author Paul Phillips */ -object EditDistance { +object EditDistance: /** * Translated from the java version at @@ -29,23 +29,21 @@ object EditDistance { matchCost: Int = 0, caseCost: Int = 1, transpositions: Boolean = false - ): Int = { + ): Int = val _ = transposeCost val n = s.length val m = t.length - if (n == 0) { - m - } else if (m == 0) { - n - } else { + if n == 0 then m + else if m == 0 then n + else val d = Array.ofDim[Int](n + 1, m + 1) 0 to n foreach (x => d(x)(0) = x) 0 to m foreach (x => d(0)(x) = x) - for (i <- 1 to n; s_i = s(i - 1); j <- 1 to m) { + for i <- 1 to n; s_i = s(i - 1); j <- 1 to m do val t_j = t(j - 1) val cost = - if (s_i == t_j) matchCost else if (lower(s_i) == lower(t_j)) caseCost else subCost + if s_i == t_j then matchCost else if lower(s_i) == lower(t_j) then caseCost else subCost val c1 = d(i - 1)(j) + deleteCost val c2 = d(i)(j - 1) + insertCost @@ -53,13 +51,11 @@ object EditDistance { d(i)(j) = c1 min c2 min c3 - if (transpositions) { - if (i > 1 && j > 1 && s(i - 1) == t(j - 2) && s(i - 2) == t(j - 1)) + if transpositions then + if i > 1 && j > 1 && s(i - 1) == t(j - 2) && s(i - 2) == t(j - 1) then d(i)(j) = d(i)(j) min (d(i - 2)(j - 2) + cost) - } - } d(n)(m) - } - } -} + end if + end levenshtein +end EditDistance diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala index bf247b52b..a9239d2a0 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ExampleSource.scala @@ -17,7 +17,7 @@ import sbt.io.IO * source is the [[sbt.internal.util.complete.FileExamples]] class, which provides a list of * suggested files to the user as they press the TAB key in the console. */ -trait ExampleSource { +trait ExampleSource: /** * @return @@ -35,14 +35,12 @@ trait ExampleSource { */ def withAddedPrefix(addedPrefix: String): ExampleSource -} - /** * A convenience example source that wraps any collection of strings into a source of examples. * @param examples * the examples that will be displayed to the user when they press the TAB key. */ -sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSource { +sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSource: override def withAddedPrefix(addedPrefix: String): ExampleSource = FixedSetExamples(examplesWithRemovedPrefix(addedPrefix)) @@ -51,7 +49,6 @@ sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSo private def examplesWithRemovedPrefix(prefix: String) = examples.collect { case example if example.startsWith(prefix) => example.substring(prefix.length) } -} /** * Provides path completion examples based on files in the base directory. @@ -60,13 +57,13 @@ sealed case class FixedSetExamples(examples: Iterable[String]) extends ExampleSo * @param prefix * the part of the path already written by the user. */ -class FileExamples(base: File, prefix: String = "") extends ExampleSource { +class FileExamples(base: File, prefix: String = "") extends ExampleSource: override def apply(): LazyList[String] = files(base).map(_.substring(prefix.length)) override def withAddedPrefix(addedPrefix: String): FileExamples = new FileExamples(base, prefix + addedPrefix) - protected def files(directory: File): LazyList[String] = { + protected def files(directory: File): LazyList[String] = val childPaths = LazyList(IO.listFiles(directory)*) val prefixedDirectChildPaths = childPaths .map(IO.relativize(base, _).get) @@ -76,8 +73,6 @@ class FileExamples(base: File, prefix: String = "") extends ExampleSource { .map(IO.relativize(base, _).get) .filter(dirStartsWithPrefix) prefixedDirectChildPaths ++ dirsToRecurseInto.flatMap(dir => files(new File(base, dir))) - } private def dirStartsWithPrefix(relativizedPath: String): Boolean = relativizedPath.startsWith(prefix) || prefix.startsWith(relativizedPath) -} diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala index 3285cdb67..e4de4fd74 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/History.scala @@ -12,7 +12,7 @@ package complete import History.number import java.io.File -final class History private (val lines: IndexedSeq[String], val path: Option[File]) { +final class History private (val lines: IndexedSeq[String], val path: Option[File]): private def reversed = lines.reverse def all: Seq[String] = lines @@ -20,28 +20,23 @@ final class History private (val lines: IndexedSeq[String], val path: Option[Fil def !! : Option[String] = !-(1) def apply(i: Int): Option[String] = - if (0 <= i && i < size) Some(lines(i)) - else { - sys.error("Invalid history index: " + i) - } + if 0 <= i && i < size then Some(lines(i)) + else sys.error("Invalid history index: " + i) def !(i: Int): Option[String] = apply(i) def !(s: String): Option[String] = - number(s) match { - case Some(n) => if (n < 0) !-(-n) else apply(n) + number(s) match + case Some(n) => if n < 0 then !-(-n) else apply(n) case None => nonEmpty(s) { reversed.find(_.startsWith(s)) } - } def !-(n: Int): Option[String] = apply(size - n - 1) def !?(s: String): Option[String] = nonEmpty(s) { reversed.drop(1).find(_.contains(s)) } private def nonEmpty[T](s: String)(act: => Option[T]): Option[T] = - if (s.isEmpty) - sys.error("No action specified to history command") - else - act + if s.isEmpty then sys.error("No action specified to history command") + else act def list(historySize: Int, show: Int): Seq[String] = lines.toList @@ -49,13 +44,12 @@ final class History private (val lines: IndexedSeq[String], val path: Option[Fil .zipWithIndex .map { (line, number) => " " + number + " " + line } .takeRight(show max 1) -} +end History -object History { +object History: def apply(lines: Seq[String], path: Option[File], error: String => Unit): History = new History(lines.toIndexedSeq, path) def apply(lines: Seq[String], path: Option[File]): History = new History(lines.toIndexedSeq, path) def number(s: String): Option[Int] = s.toIntOption -} diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala index f446eb35e..0e9a216f1 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/HistoryCommands.scala @@ -12,7 +12,7 @@ package complete import sbt.io.IO import Util.* -object HistoryCommands { +object HistoryCommands: val Start = "!" // second characters val Contains = "?" @@ -59,20 +59,22 @@ object HistoryCommands { lazy val last = Last ^^^ { execute(_.!!) } lazy val list = ListCommands ~> (num ?? Int.MaxValue) map { show => (h: History) => - { printHistory(h, MaxLines, show); nil[String].some } + printHistory(h, MaxLines, show); nil[String].some } lazy val execStr = flag('?') ~ token(any.+.string, "") map { (contains, str) => - execute(h => if (contains) h !? str else h ! str) + execute(h => if contains then h !? str else h ! str) } lazy val execInt = flag('-') ~ num map { (neg, value) => - execute(h => if (neg) h !- value else h ! value) + execute(h => if neg then h !- value else h ! value) } - lazy val help = success((h: History) => { printHelp(); nil[String].some }) + lazy val help = success((h: History) => + printHelp(); nil[String].some + ) - def execute(f: History => Option[String]): History => Option[List[String]] = (h: History) => { + def execute(f: History => Option[String]): History => Option[List[String]] = (h: History) => val command = f(h).filterNot(_.startsWith(Start)) val lines = h.lines.toArray command.foreach(lines(lines.length - 1) = _) @@ -80,8 +82,7 @@ object HistoryCommands { IO.writeLines(h, lines.toSeq) } command.toList.some - } val actionParser: Parser[complete.History => Option[List[String]]] = Start ~> (help | last | execInt | list | execStr) // execStr must come last -} +end HistoryCommands diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala index a784104f9..d743b69d5 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/JLineCompletion.scala @@ -14,7 +14,7 @@ import jline.console.completer.{ Completer, CompletionHandler } import scala.annotation.tailrec import scala.jdk.CollectionConverters.* -object JLineCompletion { +object JLineCompletion: def installCustomCompletor(reader: ConsoleReader, parser: Parser[?]): Unit = installCustomCompletor(reader)(parserAsCompletor(parser)) @@ -26,14 +26,13 @@ object JLineCompletion { def installCustomCompletor( complete: (ConsoleReader, Int) => Boolean, reader: ConsoleReader - ): Unit = { + ): Unit = reader.removeCompleter(DummyCompletor) reader.addCompleter(DummyCompletor) reader.setCompletionHandler(new CustomHandler(complete)) - } private final class CustomHandler(completeImpl: (ConsoleReader, Int) => Boolean) - extends CompletionHandler { + extends CompletionHandler: private var previous: Option[(String, Int)] = None private var level: Int = 1 @@ -41,144 +40,120 @@ object JLineCompletion { reader: ConsoleReader, candidates: java.util.List[CharSequence], position: Int - ) = { + ) = val current = Some(bufferSnapshot(reader)) - level = if (current == previous) level + 1 else 1 + level = if current == previous then level + 1 else 1 previous = current try completeImpl(reader, level) - catch { + catch case e: Exception => reader.print("\nException occurred while determining completions.") e.printStackTrace() false - } - } - } // always provides dummy completions so that the custom completion handler gets called // (ConsoleReader doesn't call the handler if there aren't any completions) // the custom handler will then throw away the candidates and call the custom function - private object DummyCompletor extends Completer { + private object DummyCompletor extends Completer: override def complete( buffer: String, cursor: Int, candidates: java.util.List[CharSequence] - ): Int = { + ): Int = candidates.add("dummy") 0 - } - } def parserAsCompletor(p: Parser[?]): (String, Int) => (Seq[String], Seq[String]) = (str, level) => convertCompletions(Parser.completions(p, str, level)) - def convertCompletions(c: Completions): (Seq[String], Seq[String]) = { + def convertCompletions(c: Completions): (Seq[String], Seq[String]) = val cs = c.get - if (cs.isEmpty) - (Nil, "{invalid input}" :: Nil) - else - convertCompletions(cs) - } + if cs.isEmpty then (Nil, "{invalid input}" :: Nil) + else convertCompletions(cs) - def convertCompletions(cs: Set[Completion]): (Seq[String], Seq[String]) = { + def convertCompletions(cs: Set[Completion]): (Seq[String], Seq[String]) = val (insert, display) = cs.foldLeft((Set.empty[String], Set.empty[String])) { case (t @ (insert, display), comp) => - if (comp.isEmpty) t + if comp.isEmpty then t else (appendNonEmpty(insert, comp.append), appendNonEmpty(display, comp.display)) } (insert.toSeq, display.toSeq.sorted) - } - def appendNonEmpty(set: Set[String], add: String) = if (add.trim.isEmpty) set else set + add + def appendNonEmpty(set: Set[String], add: String) = if add.trim.isEmpty then set else set + add def customCompletor( f: (String, Int) => (Seq[String], Seq[String]) ): (ConsoleReader, Int) => Boolean = - (reader, level) => { + (reader, level) => val success = complete(beforeCursor(reader), string => f(string, level), reader) reader.flush() success - } - def bufferSnapshot(reader: ConsoleReader): (String, Int) = { + def bufferSnapshot(reader: ConsoleReader): (String, Int) = val b = reader.getCursorBuffer (b.buffer.toString, b.cursor) - } - def beforeCursor(reader: ConsoleReader): String = { + def beforeCursor(reader: ConsoleReader): String = val b = reader.getCursorBuffer b.buffer.substring(0, b.cursor) - } // returns false if there was nothing to insert and nothing to display def complete( beforeCursor: String, completions: String => (Seq[String], Seq[String]), reader: ConsoleReader - ): Boolean = { + ): Boolean = val (insert, display) = completions(beforeCursor) val common = commonPrefix(insert) - if (common.isEmpty) - if (display.isEmpty) - () - else - showCompletions(display, reader) - else - appendCompletion(common, reader) + if common.isEmpty then + if display.isEmpty then () + else showCompletions(display, reader) + else appendCompletion(common, reader) !(common.isEmpty && display.isEmpty) - } - def appendCompletion(common: String, reader: ConsoleReader): Unit = { + def appendCompletion(common: String, reader: ConsoleReader): Unit = reader.getCursorBuffer.write(common) reader.redrawLine() - } /** * `display` is assumed to be the exact strings requested to be displayed. In particular, * duplicates should have been removed already. */ - def showCompletions(display: Seq[String], reader: ConsoleReader): Unit = { + def showCompletions(display: Seq[String], reader: ConsoleReader): Unit = printCompletions(display, reader) reader.drawLine() - } - def printCompletions(cs: Seq[String], reader: ConsoleReader): Unit = { + def printCompletions(cs: Seq[String], reader: ConsoleReader): Unit = val print = shouldPrint(cs, reader) reader.println() - if (print) printLinesAndColumns(cs, reader) - } + if print then printLinesAndColumns(cs, reader) - def printLinesAndColumns(cs: Seq[String], reader: ConsoleReader): Unit = { + def printLinesAndColumns(cs: Seq[String], reader: ConsoleReader): Unit = val (lines, columns) = cs partition hasNewline - for (line <- lines) { + for line <- lines do reader.print(line) - if (line.charAt(line.length - 1) != '\n') - reader.println() - } + if line.charAt(line.length - 1) != '\n' then reader.println() reader.printColumns(columns.map(_.trim).asJava) - } def hasNewline(s: String): Boolean = s.indexOf('\n') >= 0 - def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean = { + def shouldPrint(cs: Seq[String], reader: ConsoleReader): Boolean = val size = cs.size (size <= reader.getAutoprintThreshold) || confirm("Display all %d possibilities? (y or n) ".format(size), 'y', 'n', reader) - } - def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean = { + def confirm(prompt: String, trueC: Char, falseC: Char, reader: ConsoleReader): Boolean = reader.println() reader.print(prompt) reader.flush() reader.readCharacter(trueC, falseC) == trueC - } - def commonPrefix(s: Seq[String]): String = if (s.isEmpty) "" else s reduceLeft commonPrefix + def commonPrefix(s: Seq[String]): String = if s.isEmpty then "" else s reduceLeft commonPrefix - def commonPrefix(a: String, b: String): String = { + def commonPrefix(a: String, b: String): String = val len = scala.math.min(a.length, b.length) - @tailrec def loop(i: Int): Int = if (i >= len) len else if (a(i) != b(i)) i else loop(i + 1) + @tailrec def loop(i: Int): Int = + if i >= len then len else if a(i) != b(i) then i else loop(i + 1) a.substring(0, loop(0)) - } -} +end JLineCompletion diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala index 979f1251a..f2872ce68 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parser.scala @@ -31,7 +31,7 @@ trait Parser[+A1]: def valid: Boolean end Parser -sealed trait RichParser[A] { +sealed trait RichParser[A]: /** * Apply the original Parser and then apply `next` (in order). The result of both is provides as a @@ -153,11 +153,11 @@ sealed trait RichParser[A] { * parser and uses its result for the overall result. */ def flatMap[B](f: A => Parser[B]): Parser[B] -} +end RichParser /** Contains Parser implementation helper methods not typically needed for using parsers. */ object Parser extends ParserMain: - sealed abstract class Result[+T] { + sealed abstract class Result[+T]: def isFailure: Boolean def isValid: Boolean def errors: Seq[String] @@ -170,44 +170,42 @@ object Parser extends ParserMain: def seq[B](b: => Result[B]): Result[(T, B)] = app(b)((m, n) => (m, n)) def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] def toEither: Either[() => Seq[String], T] - } - final case class Value[+T](value: T) extends Result[T] { + final case class Value[+T](value: T) extends Result[T]: def isFailure = false def isValid: Boolean = true def errors = Nil - def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] = b match { + def app[B, C](b: => Result[B])(f: (T, B) => C): Result[C] = b match case fail: Failure => fail case Value(bv) => Value(f(value, bv)) - } - infix def &&(b: => Result[?]): Result[T] = b match { case f: Failure => f; case _ => this } + infix def &&(b: => Result[?]): Result[T] = b match + case f: Failure => f; + case _ => this infix def or[B >: T](b: => Result[B]): Result[B] = this def either[B](b: => Result[B]): Result[Either[T, B]] = Value(Left(value)) def map[B](f: T => B): Result[B] = Value(f(value)) def flatMap[B](f: T => Result[B]): Result[B] = f(value) - def filter(f: T => Boolean, msg: => String): Result[T] = if (f(value)) this else mkFailure(msg) + def filter(f: T => Boolean, msg: => String): Result[T] = + if f(value) then this else mkFailure(msg) def toEither = Right(value) - } final class Failure private[sbt] (mkErrors: => Seq[String], val definitive: Boolean) - extends Result[Nothing] { + extends Result[Nothing]: lazy val errors: Seq[String] = mkErrors def isFailure = true def isValid = false def map[B](f: Nothing => B) = this def flatMap[B](f: Nothing => Result[B]) = this - infix def or[B](b: => Result[B]): Result[B] = b match { + infix def or[B](b: => Result[B]): Result[B] = b match case v: Value[B] => v - case f: Failure => if (definitive) this else this ++ f - } + case f: Failure => if definitive then this else this ++ f - def either[B](b: => Result[B]): Result[Either[Nothing, B]] = b match { + def either[B](b: => Result[B]): Result[Either[Nothing, B]] = b match case Value(v) => Value(Right(v)) - case f: Failure => if (definitive) this else this ++ f - } + case f: Failure => if definitive then this else this ++ f def filter(f: Nothing => Boolean, msg: => String) = this def app[B, C](b: => Result[B])(f: (Nothing, B) => C): Result[C] = this @@ -215,7 +213,7 @@ object Parser extends ParserMain: def toEither = Left(() => errors) private[sbt] def ++(f: Failure) = mkFailures(errors ++ f.errors) - } + end Failure def mkFailures(errors: => Seq[String], definitive: Boolean = false): Failure = new Failure(errors.distinct, definitive) @@ -224,26 +222,25 @@ object Parser extends ParserMain: new Failure(error :: Nil, definitive) def tuple[A, B](a: Option[A], b: Option[B]): Option[(A, B)] = - (a, b) match { case (Some(av), Some(bv)) => Some((av, bv)); case _ => None } + (a, b) match + case (Some(av), Some(bv)) => Some((av, bv)); + case _ => None def mapParser[A, B](a: Parser[A], f: A => B): Parser[B] = a.ifValid { - a.result match { + a.result match case Some(av) => success(f(av)) case None => - a match { + a match case m: MapParser[?, ?] => m.map(f) case _ => new MapParser(a, f) - } - } } def bindParser[A, B](a: Parser[A], f: A => Parser[B]): Parser[B] = a.ifValid { - a.result match { + a.result match case Some(av) => f(av) case None => new BindParser(a, f) - } } def filterParser[T]( @@ -253,21 +250,19 @@ object Parser extends ParserMain: msg: String => String ): Parser[T] = a.ifValid { - a.result match { + a.result match case Some(av) if f(av) => success(av) case _ => new Filter(a, f, seen, msg) - } } def seqParser[A, B](a: Parser[A], b: Parser[B]): Parser[(A, B)] = a.ifValid { b.ifValid { - (a.result, b.result) match { + (a.result, b.result) match case (Some(av), Some(bv)) => success((av, bv)) case (Some(av), None) => b.map(bv => (av, bv)) case (None, Some(bv)) => a.map(av => (av, bv)) case (None, None) => new SeqParser(a, b) - } } } @@ -300,7 +295,7 @@ object Parser extends ParserMain: min: Int, max: UpperBound, revAcc: List[T] - ): Parser[Seq[T]] = { + ): Parser[Seq[T]] = assume(min >= 0, "Minimum must be greater than or equal to zero (was " + min + ")") assume( max >= min, @@ -308,39 +303,35 @@ object Parser extends ParserMain: ) def checkRepeated(invalidButOptional: => Parser[Seq[T]]): Parser[Seq[T]] = - repeated match { + repeated match case _: Invalid if min == 0 => invalidButOptional case i: Invalid => i case _ => - repeated.result match { + repeated.result match case Some(value) => success(revAcc reverse_::: value :: Nil) // revAcc should be Nil here case None => - if (max.isZero) success(revAcc.reverse) + if max.isZero then success(revAcc.reverse) else new Repeat(partial, repeated, min, max, revAcc) - } - } - partial match { + partial match case Some(part) => part.ifValid { - part.result match { + part.result match case Some(value) => repeat(None, repeated, min, max, value :: revAcc) case None => checkRepeated(part.map(lv => (lv :: revAcc).reverse)) - } } case None => checkRepeated(success(Nil)) - } - } + end repeat def and[T](a: Parser[T], b: Parser[?]): Parser[T] = a.ifValid(b.ifValid(new And(a, b))) end Parser -trait ParserMain { +trait ParserMain: /** Provides combinators for Parsers. */ - implicit def richParser[A](a: Parser[A]): RichParser[A] = new RichParser[A] { + implicit def richParser[A](a: Parser[A]): RichParser[A] = new RichParser[A]: def ~[B](b: Parser[B]) = seqParser(a, b) def ||[B](b: Parser[B]) = choiceParser(a, b) def |[B >: A](b: Parser[B]) = homParser[B](a, b) @@ -375,7 +366,6 @@ trait ParserMain { def filter(f: A => Boolean, msg: String => String): Parser[A] = filterParser(a, f, "", msg) def string(using ev: A <:< Seq[Char]): Parser[String] = map(_.mkString) def flatMap[B](f: A => Parser[B]) = bindParser(a, f) - } implicit def literalRichCharParser(c: Char): RichParser[Char] = richParser(c) implicit def literalRichStringParser(s: String): RichParser[String] = richParser(s) @@ -405,21 +395,19 @@ trait ParserMain { invalid(msg :: Nil, definitive) /** Defines a parser that always succeeds on empty input with the result `value`. */ - def success[T](value: T): Parser[T] = new ValidParser[T] { + def success[T](value: T): Parser[T] = new ValidParser[T]: override def result = Some(value) def resultEmpty = Value(value) def derive(c: Char) = Parser.failure("Expected end of input.") def completions(level: Int) = Completions.empty override def toString = "success(" + value + ")" - } /** * Presents a Char range as a Parser. A single Char is parsed only if it is in the given range. */ - implicit def range(r: collection.immutable.NumericRange[Char]): Parser[Char] = { + implicit def range(r: collection.immutable.NumericRange[Char]): Parser[Char] = val label = r.map(_.toString).toString range(r, label) - } /** * Presents a Char range as a Parser. A single Char is parsed only if it is in the given range. @@ -428,10 +416,9 @@ trait ParserMain { charClass(r contains _, label).examples(r.map(_.toString)*) /** Defines a Parser that parses a single character only if it is contained in `legal`. */ - def chars(legal: String): Parser[Char] = { + def chars(legal: String): Parser[Char] = val set = legal.toSet charClass(set, "character in '" + legal + "'").examples(set.map(_.toString)) - } /** * Defines a Parser that parses a single character only if the predicate `f` returns true for that @@ -441,14 +428,13 @@ trait ParserMain { new CharacterClass(f, label) /** Presents a single Char `ch` as a Parser that only parses that exact character. */ - implicit def literal(ch: Char): Parser[Char] = new ValidParser[Char] { + implicit def literal(ch: Char): Parser[Char] = new ValidParser[Char]: def result = None private lazy val fail = mkFailure("Expected '" + ch + "'") def resultEmpty = fail - def derive(c: Char) = if (c == ch) success(ch) else new Invalid(fail) + def derive(c: Char) = if c == ch then success(ch) else new Invalid(fail) def completions(level: Int) = Completions.single(Completion.suggestion(ch.toString)) override def toString = "'" + ch + "'" - } /** * Presents a literal String `s` as a Parser that only parses that exact text and provides it as @@ -457,7 +443,7 @@ trait ParserMain { implicit def literal(s: String): Parser[String] = stringLiteral(s, 0) /** See [[unapply]]. */ - object ~ { + object `~`: /** Convenience for destructuring a tuple that mirrors the `~` combinator. */ def unapply[A, B](t: (A, B)): (A, B) = t @@ -469,8 +455,6 @@ trait ParserMain { @deprecated("Use unapply instead", "2.0.0") private[sbt] def unapplyDeprecated[A, B](t: (A, B)): Some[(A, B)] = Some(t) - } - /** * Parses input `str` using `parser`. If successful, the result is provided wrapped in `Right`. If * unsuccessful, an error message is provided in `Left`. @@ -490,7 +474,7 @@ trait ParserMain { * See also [[sampleParse]] and [[sampleCompletions]]. */ def sample(str: String, parser: Parser[?], completions: Boolean = false): Unit = - if (completions) sampleCompletions(str, parser) else sampleParse(str, parser) + if completions then sampleCompletions(str, parser) else sampleParse(str, parser) /** * Convenience method to use when developing a parser. `parser` is applied to the input `str` and @@ -498,10 +482,9 @@ trait ParserMain { * error message is displayed. */ def sampleParse(str: String, parser: Parser[?]): Unit = - parse(str, parser) match { + parse(str, parser) match case Left(msg) => println(msg) case Right(v) => println(v) - } /** * Convenience method to use when developing a parser. `parser` is applied to the input `str` and @@ -512,24 +495,21 @@ trait ParserMain { Parser.completions(parser, str, level).get foreach println // intended to be temporary pending proper error feedback - def result[T](p: Parser[T], s: String): Either[() => (Seq[String], Int), T] = { + def result[T](p: Parser[T], s: String): Either[() => (Seq[String], Int), T] = @tailrec def loop(i: Int, a: Parser[T]): Either[() => (Seq[String], Int), T] = - a match { + a match case Invalid(f) => Left(() => (f.errors, i)) case _ => val ci = i + 1 - if (ci >= s.length) + if ci >= s.length then a.resultEmpty.toEither.left.map { msgs0 => () => val msgs = msgs0() - val nonEmpty = if (msgs.isEmpty) Seq("Unexpected end of input") else msgs + val nonEmpty = if msgs.isEmpty then Seq("Unexpected end of input") else msgs (nonEmpty, ci) } - else - loop(ci, a.derive(s((ci)))) - } + else loop(ci, a.derive(s((ci)))) loop(-1, p) - } /** Applies parser `p` to input `s`. */ def apply[T](p: Parser[T])(s: String): Parser[T] = @@ -537,7 +517,7 @@ trait ParserMain { /** Applies parser `p` to a single character of input. */ def derive1[T](p: Parser[T], c: Char): Parser[T] = - if (p.valid) p.derive(c) else p + if p.valid then p.derive(c) else p /** * Applies parser `p` to input `s` and returns the completions at verbosity `level`. The @@ -575,27 +555,23 @@ trait ParserMain { maxNumberOfExamples: Int, removeInvalidExamples: Boolean ): Parser[A] = - if (a.valid) { - a.result match { + if a.valid then + a.result match case Some(av) => success(av) case None => new ParserWithExamples(a, completions, maxNumberOfExamples, removeInvalidExamples) - } - } else a + else a def matched( t: Parser[?], seen: Vector[Char] = Vector.empty, partial: Boolean = false ): Parser[String] = - t match { - case i: Invalid => if (partial && seen.nonEmpty) success(seen.mkString) else i + t match + case i: Invalid => if partial && seen.nonEmpty then success(seen.mkString) else i case _ => - if (t.result.isEmpty) - new MatchedString(t, seen, partial) - else - success(seen.mkString) - } + if t.result.isEmpty then new MatchedString(t, seen, partial) + else success(seen.mkString) /** * Establishes delegate parser `t` as a single token of tab completion. When tab completion of @@ -633,50 +609,44 @@ trait ParserMain { mkToken(t, "", complete) private[sbt] def mkToken[T](t: Parser[T], seen: String, complete: TokenCompletions): Parser[T] = - if (t.valid && !t.isTokenStart) - if (t.result.isEmpty) new TokenStart(t, seen, complete) else t - else - t + if t.valid && !t.isTokenStart then + if t.result.isEmpty then new TokenStart(t, seen, complete) else t + else t - def homParser[A](a: Parser[A], b: Parser[A]): Parser[A] = (a, b) match { + def homParser[A](a: Parser[A], b: Parser[A]): Parser[A] = (a, b) match case (Invalid(af), Invalid(bf)) => Invalid(af ++ bf) case (Invalid(_), bv) => bv case (av, Invalid(_)) => av case (_, _) => new HomParser(a, b) - } - def not(p: Parser[?], failMessage: String): Parser[Unit] = p.result match { + def not(p: Parser[?], failMessage: String): Parser[Unit] = p.result match case None => new Not(p, failMessage) case Some(_) => failure(failMessage) - } def oneOf[T](p: Seq[Parser[T]]): Parser[T] = p.reduceLeft(_ | _) def seq[T](p: Seq[Parser[T]]): Parser[Seq[T]] = seq0(p, Nil) - def seq0[T](p: Seq[Parser[T]], errors: => Seq[String]): Parser[Seq[T]] = { + def seq0[T](p: Seq[Parser[T]], errors: => Seq[String]): Parser[Seq[T]] = val (newErrors, valid) = separate(p) { case Invalid(f) => Left(() => f.errors): Either[() => Seq[String], Parser[T]] case ok => Right(ok): Either[() => Seq[String], Parser[T]] } def combinedErrors = errors ++ newErrors.flatMap(_()) - if (valid.isEmpty) invalid(combinedErrors) else new ParserSeq(valid, combinedErrors) - } + if valid.isEmpty then invalid(combinedErrors) else new ParserSeq(valid, combinedErrors) - def stringLiteral(s: String, start: Int): Parser[String] = { + def stringLiteral(s: String, start: Int): Parser[String] = val len = s.length - if (len == 0) sys.error("String literal cannot be empty") - else if (start >= len) success(s) + if len == 0 then sys.error("String literal cannot be empty") + else if start >= len then success(s) else new StringLiteral(s, start) - } -} +end ParserMain -sealed trait ValidParser[T] extends Parser[T] { +sealed trait ValidParser[T] extends Parser[T]: final def valid = true final def failure = None final def ifValid[S](p: => Parser[S]): Parser[S] = p -} -private final case class Invalid(fail: Failure) extends Parser[Nothing] { +private final case class Invalid(fail: Failure) extends Parser[Nothing]: def failure = Some(fail) def result = None def resultEmpty = fail @@ -685,138 +655,114 @@ private final case class Invalid(fail: Failure) extends Parser[Nothing] { override def toString = fail.errors.mkString("; ") def valid = false def ifValid[S](p: => Parser[S]): Parser[S] = this -} -private final case class SoftInvalid(fail: Failure) extends ValidParser[Nothing] { +private final case class SoftInvalid(fail: Failure) extends ValidParser[Nothing]: def result = None def resultEmpty = fail def derive(c: Char) = Invalid(fail) def completions(level: Int) = Completions.nil override def toString = fail.errors.mkString("; ") -} -private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A] { +private final class TrapAndFail[A](a: Parser[A]) extends ValidParser[A]: def result = - try { - a.result - } catch { case _: Exception => None } + try a.result + catch case _: Exception => None def resultEmpty = - try { - a.resultEmpty - } catch { case e: Exception => fail(e) } + try a.resultEmpty + catch case e: Exception => fail(e) def derive(c: Char) = - try { - trapAndFail(a.derive(c)) - } catch { - case e: Exception => Invalid(fail(e)) - } + try trapAndFail(a.derive(c)) + catch case e: Exception => Invalid(fail(e)) def completions(level: Int) = - try { - a.completions(level) - } catch { - case _: Exception => Completions.nil - } + try a.completions(level) + catch case _: Exception => Completions.nil override def toString = "trap(" + a + ")" override def isTokenStart = a.isTokenStart private def fail(e: Exception): Failure = mkFailure(e.toString) -} -private final class OnFailure[A](a: Parser[A], message: String) extends ValidParser[A] { +private final class OnFailure[A](a: Parser[A], message: String) extends ValidParser[A]: def result = a.result - def resultEmpty = a.resultEmpty match { - case _: Failure => mkFailure(message); case v: Value[A] => v - } + def resultEmpty = a.resultEmpty match + case _: Failure => mkFailure(message); + case v: Value[A] => v def derive(c: Char) = onFailure(a.derive(c), message) def completions(level: Int) = a.completions(level) override def toString = "(" + a + " !!! \"" + message + "\" )" override def isTokenStart = a.isTokenStart -} -private final class SeqParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[(A, B)] { +private final class SeqParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[(A, B)]: lazy val result = tuple(a.result, b.result) lazy val resultEmpty = a.resultEmpty.seq(b.resultEmpty) - def derive(c: Char) = { + def derive(c: Char) = val common = a.derive(c) ~ b - a.resultEmpty match { + a.resultEmpty match case Value(av) => common | b.derive(c).map(br => (av, br)) case _: Failure => common - } - } def completions(level: Int) = a.completions(level).x(b.completions(level)) override def toString = "(" + a + " ~ " + b + ")" -} -private final class HomParser[A](a: Parser[A], b: Parser[A]) extends ValidParser[A] { +private final class HomParser[A](a: Parser[A], b: Parser[A]) extends ValidParser[A]: lazy val result = tuple(a.result, b.result) map (_._1) def derive(c: Char) = a.derive(c) | b.derive(c) lazy val resultEmpty = a.resultEmpty or b.resultEmpty def completions(level: Int) = a.completions(level) ++ b.completions(level) override def toString = "(" + a + " | " + b + ")" -} -private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[Either[A, B]] { +private final class HetParser[A, B](a: Parser[A], b: Parser[B]) extends ValidParser[Either[A, B]]: lazy val result = tuple(a.result, b.result) map { case (a, _) => Left(a) } def derive(c: Char) = a.derive(c) || b.derive(c) lazy val resultEmpty = a.resultEmpty.either(b.resultEmpty) def completions(level: Int) = a.completions(level) ++ b.completions(level) override def toString = "(" + a + " || " + b + ")" -} private final class ParserSeq[T](a: Seq[Parser[T]], errors: => Seq[String]) - extends ValidParser[Seq[T]] { + extends ValidParser[Seq[T]]: assert(a.nonEmpty) - lazy val resultEmpty: Result[Seq[T]] = { + lazy val resultEmpty: Result[Seq[T]] = val res = a.map(_.resultEmpty) val (failures, values) = separate(res)(_.toEither) // if(failures.isEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors) - if (values.nonEmpty) Value(values) else mkFailures(failures.flatMap(_()) ++ errors) - } + if values.nonEmpty then Value(values) else mkFailures(failures.flatMap(_()) ++ errors) - def result = { + def result = val success = a.flatMap(_.result) - if (success.length == a.length) Some(success) else None - } + if success.length == a.length then Some(success) else None def completions(level: Int) = a.map(_.completions(level)).reduceLeft(_ ++ _) def derive(c: Char) = seq0(a.map(_.derive(c)), errors) override def toString = "seq(" + a + ")" -} -private final class BindParser[A, B](a: Parser[A], f: A => Parser[B]) extends ValidParser[B] { +private final class BindParser[A, B](a: Parser[A], f: A => Parser[B]) extends ValidParser[B]: lazy val result = a.result flatMap (av => f(av).result) lazy val resultEmpty = a.resultEmpty.flatMap(av => f(av).resultEmpty) def completions(level: Int) = a.completions(level) flatMap { c => - apply(a)(c.append).resultEmpty match { + apply(a)(c.append).resultEmpty match case _: Failure => Completions.strict(Set.empty + c) case Value(av) => c.x(f(av).completions(level)) - } } - def derive(c: Char) = { + def derive(c: Char) = val common = a.derive(c).flatMap(f) - a.resultEmpty match { + a.resultEmpty match case Value(av) => common | derive1(f(av), c) case _: Failure => common - } - } override def isTokenStart = a.isTokenStart override def toString = "bind(" + a + ")" -} -private final class MapParser[A, B](a: Parser[A], f: A => B) extends ValidParser[B] { +private final class MapParser[A, B](a: Parser[A], f: A => B) extends ValidParser[B]: lazy val result = a.result map f lazy val resultEmpty = a.resultEmpty.map(f) def derive(c: Char) = a.derive(c).map(f) @@ -824,10 +770,9 @@ private final class MapParser[A, B](a: Parser[A], f: A => B) extends ValidParser override def isTokenStart = a.isTokenStart override def toString = "map(" + a + ")" def map[C](g: B => C) = new MapParser[A, C](a, f.andThen(g)) -} private final class Filter[T](p: Parser[T], f: T => Boolean, seen: String, msg: String => String) - extends ValidParser[T] { + extends ValidParser[T]: def filterResult(r: Result[T]) = r.filter(f, msg(seen)) lazy val result = p.result filter f lazy val resultEmpty = filterResult(p.resultEmpty) @@ -839,59 +784,52 @@ private final class Filter[T](p: Parser[T], f: T => Boolean, seen: String, msg: override def toString = "filter(" + p + ")" override def isTokenStart = p.isTokenStart -} private final class MatchedString(delegate: Parser[?], seenV: Vector[Char], partial: Boolean) - extends ValidParser[String] { + extends ValidParser[String]: lazy val seen = seenV.mkString def derive(c: Char) = matched(delegate.derive(c), seenV :+ c, partial) def completions(level: Int) = delegate.completions(level) - def result = if (delegate.result.isDefined) Some(seen) else None + def result = if delegate.result.isDefined then Some(seen) else None - def resultEmpty = delegate.resultEmpty match { - case f: Failure if !partial => f; case _ => Value(seen) - } + def resultEmpty = delegate.resultEmpty match + case f: Failure if !partial => f; + case _ => Value(seen) override def isTokenStart = delegate.isTokenStart override def toString = "matched(" + partial + ", " + seen + ", " + delegate + ")" -} private final class TokenStart[T](delegate: Parser[T], seen: String, complete: TokenCompletions) - extends ValidParser[T] { + extends ValidParser[T]: def derive(c: Char) = mkToken(delegate.derive(c), seen + c, complete) - def completions(level: Int) = complete match { + def completions(level: Int) = complete match case dc: TokenCompletions.Delegating => dc.completions(seen, level, delegate.completions(level)) case fc: TokenCompletions.Fixed => fc.completions(seen, level) - } def result = delegate.result def resultEmpty = delegate.resultEmpty override def isTokenStart = true override def toString = "token('" + complete + ", " + delegate + ")" -} -private final class And[T](a: Parser[T], b: Parser[?]) extends ValidParser[T] { +private final class And[T](a: Parser[T], b: Parser[?]) extends ValidParser[T]: lazy val result = tuple(a.result, b.result) map { _._1 } def derive(c: Char) = a.derive(c) & b.derive(c) def completions(level: Int) = a.completions(level).filterS(s => apply(b)(s).resultEmpty.isValid) lazy val resultEmpty = a.resultEmpty && b.resultEmpty override def toString = s"(${a}) && (${b})" -} -private final class Not(delegate: Parser[?], failMessage: String) extends ValidParser[Unit] { - def derive(c: Char) = if (delegate.valid) not(delegate.derive(c), failMessage) else this +private final class Not(delegate: Parser[?], failMessage: String) extends ValidParser[Unit]: + def derive(c: Char) = if delegate.valid then not(delegate.derive(c), failMessage) else this def completions(level: Int) = Completions.empty def result = None - lazy val resultEmpty = delegate.resultEmpty match { + lazy val resultEmpty = delegate.resultEmpty match case _: Failure => Value(()) case _: Value[?] => mkFailure(failMessage) - } override def toString = s" -(${delegate})" -} /** * This class wraps an existing parser (the delegate), and replaces the delegate's completions with @@ -919,7 +857,7 @@ private final class ParserWithExamples[T]( exampleSource: ExampleSource, maxNumberOfExamples: Int, removeInvalidExamples: Boolean -) extends ValidParser[T] { +) extends ValidParser[T]: def derive(c: Char) = examples( @@ -933,29 +871,24 @@ private final class ParserWithExamples[T]( lazy val resultEmpty = delegate.resultEmpty - def completions(level: Int) = { - if (exampleSource().isEmpty) if (resultEmpty.isValid) Completions.nil else Completions.empty - else { + def completions(level: Int) = + if exampleSource().isEmpty then + if resultEmpty.isValid then Completions.nil else Completions.empty + else val examplesBasedOnTheResult = filteredExamples.take(maxNumberOfExamples).toSet Completions(examplesBasedOnTheResult.map(ex => Completion.suggestion(ex))) - } - } override def toString = "examples(" + delegate + ", " + exampleSource().take(2).toList + ")" - private def filteredExamples: Iterable[String] = { - if (removeInvalidExamples) - exampleSource().filter(isExampleValid) - else - exampleSource() - } + private def filteredExamples: Iterable[String] = + if removeInvalidExamples then exampleSource().filter(isExampleValid) + else exampleSource() - private def isExampleValid(example: String): Boolean = { + private def isExampleValid(example: String): Boolean = apply(delegate)(example).resultEmpty.isValid - } -} +end ParserWithExamples -private final class StringLiteral(str: String, start: Int) extends ValidParser[String] { +private final class StringLiteral(str: String, start: Int) extends ValidParser[String]: assert(0 <= start && start < str.length) def failMsg = "Expected '" + str + "'" @@ -964,28 +897,25 @@ private final class StringLiteral(str: String, start: Int) extends ValidParser[S def result = None def derive(c: Char) = - if (str.charAt(start) == c) stringLiteral(str, start + 1) else new Invalid(fail) + if str.charAt(start) == c then stringLiteral(str, start + 1) else new Invalid(fail) def completions(level: Int) = Completions.single(Completion.suggestion(str.substring(start))) override def toString = "\"" + str + "\"" -} -private final class CharacterClass(f: Char => Boolean, label: String) extends ValidParser[Char] { +private final class CharacterClass(f: Char => Boolean, label: String) extends ValidParser[Char]: def result = None private def fail: Failure = mkFailure("Expected " + label) def resultEmpty = fail - def derive(c: Char) = if (f(c)) success(c) else Invalid(fail) + def derive(c: Char) = if f(c) then success(c) else Invalid(fail) def completions(level: Int) = Completions.empty override def toString = "class(" + label + ")" -} -private final class Optional[A](delegate: Parser[A]) extends ValidParser[Option[A]] { +private final class Optional[A](delegate: Parser[A]) extends ValidParser[Option[A]]: def result = delegate.result.map(some[A]) def resultEmpty = Value(None) def derive(c: Char) = delegate.derive(c).map(some[A]) def completions(level: Int) = Completion.empty +: delegate.completions(level) override def toString = delegate.toString + "?" -} private final class Repeat[T]( partial: Option[Parser[T]], @@ -993,56 +923,48 @@ private final class Repeat[T]( min: Int, max: UpperBound, accumulatedReverse: List[T] -) extends ValidParser[Seq[T]] { +) extends ValidParser[Seq[T]]: assume(0 <= min, "Minimum occurrences must be non-negative") assume(max >= min, "Minimum occurrences must be less than the maximum occurrences") def derive(c: Char) = - partial match { + partial match case Some(part) => val partD = repeat(Some(part.derive(c)), repeated, min, max, accumulatedReverse) - part.resultEmpty match { + part.resultEmpty match case Value(pv) => partD | repeatDerive(c, pv :: accumulatedReverse) case _: Failure => partD - } case None => repeatDerive(c, accumulatedReverse) - } def repeatDerive(c: Char, accRev: List[T]): Parser[Seq[T]] = repeat(Some(repeated.derive(c)), repeated, scala.math.max(0, min - 1), max.decrement, accRev) - def completions(level: Int) = { + def completions(level: Int) = @tailrec def pow(comp: Completions, exp: Completions, n: Int): Completions = - if (n == 1) comp else pow(comp.x(exp), exp, n - 1) + if n == 1 then comp else pow(comp.x(exp), exp, n - 1) val repC = repeated.completions(level) - val fin = if (min == 0) Completion.empty +: repC else pow(repC, repC, min) - partial match { + val fin = if min == 0 then Completion.empty +: repC else pow(repC, repC, min) + partial match case Some(p) => p.completions(level).x(fin) case None => fin - } - } def result = None - lazy val resultEmpty: Result[Seq[T]] = { + lazy val resultEmpty: Result[Seq[T]] = val partialAccumulatedOption = - partial match { + partial match case None => (Value(accumulatedReverse): Result[List[T]]) case Some(partialPattern) => partialPattern.resultEmpty.map(_ :: accumulatedReverse) - } (partialAccumulatedOption.app(repeatedParseEmpty))((x, y) => (x reverse_::: y): Seq[T]) - } - private def repeatedParseEmpty: Result[List[T]] = { - if (min == 0) - Value(Nil) + private def repeatedParseEmpty: Result[List[T]] = + if min == 0 then Value(Nil) else // forced determinism - for (value <- repeated.resultEmpty) yield makeList(min, value) - } + for value <- repeated.resultEmpty yield makeList(min, value) override def toString = "repeat(" + min + "," + max + "," + partial + "," + repeated + ")" -} +end Repeat diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala index 4efcc6e26..4c24f8ad0 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/Parsers.scala @@ -26,7 +26,7 @@ import scala.annotation.tailrec import sbt.internal.util.Util.nilSeq /** Provides standard implementations of commonly useful [[Parser]]s. */ -trait Parsers { +trait Parsers: /** Matches the end of input, providing no useful result on success. */ lazy val EOF = not(any, "Expected EOF") @@ -116,22 +116,18 @@ trait Parsers { start ~ rep.* map { case x ~ xs => (x +: xs).mkString } def opOrIDSpaced(s: String): Parser[Char] = - if (DefaultParsers.matches(ID, s)) - OpChar | SpaceClass - else if (DefaultParsers.matches(Op, s)) - IDChar | SpaceClass - else - any + if DefaultParsers.matches(ID, s) then OpChar | SpaceClass + else if DefaultParsers.matches(Op, s) then IDChar | SpaceClass + else any /** Returns true if `c` an operator character. */ def isOpChar(c: Char) = !isDelimiter(c) && isOpType(getType(c)) - def isOpType(cat: Int) = cat match { + def isOpType(cat: Int) = cat match case MATH_SYMBOL | OTHER_SYMBOL | DASH_PUNCTUATION | OTHER_PUNCTUATION | MODIFIER_SYMBOL | CURRENCY_SYMBOL => true case _ => false - } /** Returns true if `c` is a dash `-`, a letter, digit, or an underscore `_`. */ def isIDChar(c: Char) = isScalaIDChar(c) || c == '-' @@ -139,9 +135,9 @@ trait Parsers { /** Returns true if `c` is a letter, digit, or an underscore `_`. */ def isScalaIDChar(c: Char) = c.isLetterOrDigit || c == '_' - def isDelimiter(c: Char) = c match { - case '`' | '\'' | '\"' | /*';' | */ ',' | '.' => true; case _ => false - } + def isDelimiter(c: Char) = c match + case '`' | '\'' | '\"' | /*';' | */ ',' | '.' => true; + case _ => false /** Matches a single character that is not a whitespace character. */ lazy val NotSpaceClass = charClass(!_.isWhitespace, "non-whitespace character") @@ -187,14 +183,14 @@ trait Parsers { /** Matches any character except a double quote or whitespace. */ lazy val NotDQuoteSpaceClass = charClass( - (c: Char) => { (c != DQuoteChar) && !c.isWhitespace }, + (c: Char) => (c != DQuoteChar) && !c.isWhitespace, "non-double-quote-space character" ) /** Matches any character except a double quote or backslash. */ lazy val NotDQuoteBackslashClass = charClass( - (c: Char) => { (c != DQuoteChar) && (c != BackslashChar) }, + (c: Char) => (c != DQuoteChar) && (c != BackslashChar), "non-double-quote-backslash character" ) @@ -281,9 +277,9 @@ trait Parsers { * @return * a parser for the brace enclosed string. */ - private[sbt] def braces(open: Char, close: Char): Parser[String] = { + private[sbt] def braces(open: Char, close: Char): Parser[String] = val notDelim = charClass(c => c != open && c != close).*.string - def impl(): Parser[String] = { + def impl(): Parser[String] = (open ~ (notDelim ~ close).?).flatMap { case (l, Some((content, r))) => Parser.success(s"$l$content$r") case (l, None) => @@ -293,9 +289,7 @@ trait Parsers { s"$l${nested.mkString}$suffix$r" } } - } impl() - } /** * Parses a single escape sequence into the represented Char. Escapes start with a backslash and @@ -351,9 +345,8 @@ trait Parsers { */ def mapOrFail[S, T](p: Parser[S])(f: S => T): Parser[T] = p flatMap { s => - try { - success(f(s)) - } catch { case e: Exception => failure(e.toString) } + try success(f(s)) + catch case e: Exception => failure(e.toString) } /** @@ -376,17 +369,15 @@ trait Parsers { * discarded and only the sequence of values from the parsers returned by `p` is used for the * result. */ - def repeatDep[A](p: Seq[A] => Parser[A], sep: Parser[Any]): Parser[Seq[A]] = { - def loop(acc: Seq[A]): Parser[Seq[A]] = { + def repeatDep[A](p: Seq[A] => Parser[A], sep: Parser[Any]): Parser[Seq[A]] = + def loop(acc: Seq[A]): Parser[Seq[A]] = val next = (sep ~> p(acc)) flatMap { result => loop(acc :+ result) } next ?? acc - } p(Vector()) flatMap { first => loop(Seq(first)) } - } /** Applies String.trim to the result of `p`. */ def trimmed(p: Parser[String]) = p map { _.trim } @@ -399,29 +390,24 @@ trait Parsers { * `ex` as tab completion examples. */ def Uri(ex: Set[URI]) = basicUri.examples(ex.map(_.toString)) -} +end Parsers /** Provides standard [[Parser]] implementations. */ object Parsers extends Parsers /** Provides common [[Parser]] implementations and helper methods. */ -object DefaultParsers extends Parsers with ParserMain { +object DefaultParsers extends Parsers with ParserMain: /** Applies parser `p` to input `s` and returns `true` if the parse was successful. */ def matches(p: Parser[?], s: String): Boolean = apply(p)(s).resultEmpty.isValid /** Returns `true` if `s` parses successfully according to [[ID]]. */ - def validID(s: String): Boolean = { + def validID(s: String): Boolean = // Handwritten version of `matches(ID, s)` because validID turned up in profiling. def isIdChar(c: Char): Boolean = Character.isLetterOrDigit(c) || (c == '-') || (c == '_') @tailrec def isRestIdChar(cur: Int, s: String, length: Int): Boolean = - if (cur < length) - isIdChar(s.charAt(cur)) && isRestIdChar(cur + 1, s, length) - else - true + if cur < length then isIdChar(s.charAt(cur)) && isRestIdChar(cur + 1, s, length) + else true !s.isEmpty && Character.isLetter(s.charAt(0)) && isRestIdChar(1, s, s.length) - } - -} diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala index f15a70f06..28045261a 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/ProcessError.scala @@ -11,32 +11,26 @@ package complete import scala.annotation.tailrec -object ProcessError { - def apply(command: String, msgs: Seq[String], index: Int): String = { +object ProcessError: + def apply(command: String, msgs: Seq[String], index: Int): String = val (line, modIndex) = extractLine(command, index) val point = pointerSpace(command, modIndex) msgs.mkString("\n") + "\n" + line + "\n" + point + "^" - } - def extractLine(s: String, i: Int): (String, Int) = { + def extractLine(s: String, i: Int): (String, Int) = val notNewline = (c: Char) => c != '\n' && c != '\r' val left = takeRightWhile(s.substring(0, i))(notNewline) val right = s.substring(i).takeWhile(notNewline) (left + right, left.length) - } - def takeRightWhile(s: String)(pred: Char => Boolean): String = { + def takeRightWhile(s: String)(pred: Char => Boolean): String = @tailrec def loop(i: Int): String = - if (i < 0) - s - else if (pred(s(i))) - loop(i - 1) - else - s.substring(i + 1) + if i < 0 then s + else if pred(s(i)) then loop(i - 1) + else s.substring(i + 1) loop(s.length - 1) - } def pointerSpace(s: String, i: Int): String = (s take i) map { case '\t' => '\t'; case _ => ' ' } mkString "" -} +end ProcessError diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/SizeParser.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/SizeParser.scala index 5f066b5ed..bf201a619 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/SizeParser.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/SizeParser.scala @@ -10,21 +10,18 @@ package sbt.internal.util.complete import sbt.internal.util.complete.DefaultParsers.* -private[sbt] object SizeParser { +private[sbt] object SizeParser: def apply(s: String): Option[Long] = Parser.parse(s, value).toOption - private enum SizeUnit { + private enum SizeUnit: case Bytes, KiloBytes, MegaBytes, GigaBytes - } private def parseDouble(s: String): Parser[Either[Double, Long]] = - s.toDoubleOption match { + s.toDoubleOption match case Some(x) => Parser.success(Left(x)) case _ => Parser.failure(s"Couldn't parse $s as double.") - } private def parseLong(s: String): Parser[Either[Double, Long]] = - s.toLongOption match { + s.toLongOption match case Some(x) => Parser.success(Right(x)) case _ => Parser.failure(s"Couldn't parse $s as double.") - } private val digit = charClass(_.isDigit, "digit") private val numberParser: Parser[Either[Double, Long]] = (digit.+ ~ ('.'.examples() ~> digit.+).?).flatMap { @@ -39,20 +36,18 @@ private[sbt] object SizeParser { case "k" | "K" => SizeUnit.KiloBytes case "m" | "M" => SizeUnit.MegaBytes } - private def multiply(left: Either[Double, Long], right: Long): Long = left match { + private def multiply(left: Either[Double, Long], right: Long): Long = left match case Left(d) => (d * right).toLong case Right(l) => l * right - } private[sbt] val value: Parser[Long] = ((numberParser <~ SpaceClass .examples(" ", "b", "B", "g", "G", "k", "K", "m", "M") .*) ~ unitParser.?) .map { (number, unit) => - unit match { + unit match case None | Some(SizeUnit.Bytes) => multiply(number, right = 1L) case Some(SizeUnit.KiloBytes) => multiply(number, right = 1024L) case Some(SizeUnit.MegaBytes) => multiply(number, right = 1024L * 1024) case Some(SizeUnit.GigaBytes) => multiply(number, right = 1024L * 1024 * 1024) - } } -} +end SizeParser diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala index 9c76ae411..96f80e442 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TokenCompletions.scala @@ -11,43 +11,37 @@ package complete import Completion.{ token as ctoken, tokenDisplay } -sealed trait TokenCompletions { +sealed trait TokenCompletions: def hideWhen(f: Int => Boolean): TokenCompletions -} -object TokenCompletions { - private[sbt] abstract class Delegating extends TokenCompletions { outer => +object TokenCompletions: + private[sbt] abstract class Delegating extends TokenCompletions: + outer => def completions(seen: String, level: Int, delegate: Completions): Completions - final def hideWhen(hide: Int => Boolean): TokenCompletions = new Delegating { + final def hideWhen(hide: Int => Boolean): TokenCompletions = new Delegating: def completions(seen: String, level: Int, delegate: Completions): Completions = - if (hide(level)) Completions.nil else outer.completions(seen, level, delegate) - } - } + if hide(level) then Completions.nil else outer.completions(seen, level, delegate) - private[sbt] abstract class Fixed extends TokenCompletions { outer => + private[sbt] abstract class Fixed extends TokenCompletions: + outer => def completions(seen: String, level: Int): Completions - final def hideWhen(hide: Int => Boolean): TokenCompletions = new Fixed { + final def hideWhen(hide: Int => Boolean): TokenCompletions = new Fixed: def completions(seen: String, level: Int) = - if (hide(level)) Completions.nil else outer.completions(seen, level) - } - } + if hide(level) then Completions.nil else outer.completions(seen, level) val default: TokenCompletions = mapDelegateCompletions((seen, level, c) => ctoken(seen, c.append)) - def displayOnly(msg: String): TokenCompletions = new Fixed { + def displayOnly(msg: String): TokenCompletions = new Fixed: def completions(seen: String, level: Int) = Completions.single(Completion.displayOnly(msg)) - } def overrideDisplay(msg: String): TokenCompletions = mapDelegateCompletions((seen, level, c) => tokenDisplay(display = msg, append = c.append)) - def fixed(f: (String, Int) => Completions): TokenCompletions = new Fixed { + def fixed(f: (String, Int) => Completions): TokenCompletions = new Fixed: def completions(seen: String, level: Int) = f(seen, level) - } def mapDelegateCompletions(f: (String, Int, Completion) => Completion): TokenCompletions = - new Delegating { + new Delegating: def completions(seen: String, level: Int, delegate: Completions) = Completions(delegate.get.map(c => f(seen, level, c))) - } -} +end TokenCompletions diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala index fa5a961ee..5da1098c8 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/TypeString.scala @@ -17,24 +17,20 @@ import TypeString.* * structure of parameterized types. All other types are represented by a TypeString with an empty * `args`. */ -private[sbt] final class TypeString(val base: String, val args: List[TypeString]) { +private[sbt] final class TypeString(val base: String, val args: List[TypeString]): override def toString = - if (base.startsWith(FunctionName)) + if base.startsWith(FunctionName) then args.dropRight(1).mkString("(", ",", ")") + " => " + args.last - else if (base.startsWith(TupleName)) - args.mkString("(", ",", ")") - else - cleanupTypeName(base) + (if (args.isEmpty) "" else args.mkString("[", ",", "]")) -} + else if base.startsWith(TupleName) then args.mkString("(", ",", ")") + else cleanupTypeName(base) + (if args.isEmpty then "" else args.mkString("[", ",", "]")) -private[sbt] object TypeString { +private[sbt] object TypeString: /** Makes the string representation of a type as returned by Manifest.toString more readable. */ def cleanup(typeString: String): String = - parse(typeString, typeStringParser) match { + parse(typeString, typeStringParser) match case Right(ts) => ts.toString case Left(_) => typeString - } /** * Makes a fully qualified type name provided by Manifest.toString more readable. The argument @@ -49,16 +45,13 @@ private[sbt] object TypeString { * conservative approximation. */ def dropPrefix(base: String): String = - if (base.startsWith(SbtPrefix)) base.substring(SbtPrefix.length) - else if (base.startsWith(CollectionPrefix)) { + if base.startsWith(SbtPrefix) then base.substring(SbtPrefix.length) + else if base.startsWith(CollectionPrefix) then val simple = base.substring(CollectionPrefix.length) - if (ShortenCollection(simple)) simple else base - } else if (base.startsWith(ScalaPrefix)) - base.substring(ScalaPrefix.length) - else if (base.startsWith(JavaPrefix)) - base.substring(JavaPrefix.length) - else - TypeMap.getOrElse(base, base) + if ShortenCollection(simple) then simple else base + else if base.startsWith(ScalaPrefix) then base.substring(ScalaPrefix.length) + else if base.startsWith(JavaPrefix) then base.substring(JavaPrefix.length) + else TypeMap.getOrElse(base, base) final val CollectionPrefix = "scala.collection." final val FunctionName = "scala.Function" @@ -80,13 +73,12 @@ private[sbt] object TypeString { * Manifest.toString. This is rudimentary and essentially only decomposes the string into names * and arguments for parameterized types. */ - lazy val typeStringParser: Parser[TypeString] = { + lazy val typeStringParser: Parser[TypeString] = def isFullScalaIDChar(c: Char) = isScalaIDChar(c) || c == '.' || c == '$' lazy val fullScalaID = identifier(IDStart, charClass(isFullScalaIDChar, "Scala identifier character")) lazy val tpe: Parser[TypeString] = - for (id <- fullScalaID; args <- ('[' ~> rep1sep(tpe, ',') <~ ']').?) - yield new TypeString(id, args.toList.flatten) + for id <- fullScalaID; args <- ('[' ~> rep1sep(tpe, ',') <~ ']').? + yield new TypeString(id, args.toList.flatten) tpe - } -} +end TypeString diff --git a/internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala b/internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala index abf7ed8d4..9086c7951 100644 --- a/internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala +++ b/internal/util-complete/src/main/scala/sbt/internal/util/complete/UpperBound.scala @@ -9,7 +9,7 @@ package sbt.internal.util package complete -sealed trait UpperBound { +sealed trait UpperBound: /** True if and only if the given value meets this bound. */ def >=(min: Int): Boolean @@ -30,10 +30,8 @@ sealed trait UpperBound { /** True if and only if this is unbounded. */ def isInfinite: Boolean -} - /** Represents unbounded. */ -case object Infinite extends UpperBound { +case object Infinite extends UpperBound: /** All finite numbers meet this bound. */ def >=(min: Int) = true @@ -44,13 +42,12 @@ case object Infinite extends UpperBound { def isInfinite = true override def toString = "Infinity" -} /** * Represents a finite upper bound. The maximum allowed value is 'value', inclusive. It must * positive. */ -final case class Finite(value: Int) extends UpperBound { +final case class Finite(value: Int) extends UpperBound: assume(value >= 0, "Maximum occurrences must be nonnegative.") def >=(min: Int) = value >= min @@ -59,8 +56,6 @@ final case class Finite(value: Int) extends UpperBound { def decrement = Finite(scala.math.max(0, value - 1)) def isInfinite = false override def toString = value.toString -} -object UpperBound { +object UpperBound: implicit def intToFinite(i: Int): Finite = Finite(i) -} diff --git a/internal/util-complete/src/test/scala/DefaultParsersSpec.scala b/internal/util-complete/src/test/scala/DefaultParsersSpec.scala index 47cc6eba8..a190c0bba 100644 --- a/internal/util-complete/src/test/scala/DefaultParsersSpec.scala +++ b/internal/util-complete/src/test/scala/DefaultParsersSpec.scala @@ -11,7 +11,7 @@ package complete import org.scalacheck.*, Gen.*, Prop.* -object DefaultParsersSpec extends Properties("DefaultParsers") { +object DefaultParsersSpec extends Properties("DefaultParsers"): import DefaultParsers.{ ID, isIDChar, matches, validID } property("∀ s ∈ String: validID(s) == matches(ID, s)") = @@ -22,8 +22,7 @@ object DefaultParsersSpec extends Properties("DefaultParsers") { private val chars: Seq[Char] = Char.MinValue to Char.MaxValue private val genID: Gen[String] = - for { + for c <- oneOf(chars filter (_.isLetter)) cs <- listOf(oneOf(chars filter isIDChar)) - } yield (c :: cs).mkString -} + yield (c :: cs).mkString diff --git a/internal/util-complete/src/test/scala/ParserTest.scala b/internal/util-complete/src/test/scala/ParserTest.scala index 811dc3035..14f8c9f1a 100644 --- a/internal/util-complete/src/test/scala/ParserTest.scala +++ b/internal/util-complete/src/test/scala/ParserTest.scala @@ -11,7 +11,7 @@ package complete import scala.annotation.tailrec -object JLineTest { +object JLineTest: import DefaultParsers.* val one = "blue" | "green" | "black" @@ -27,7 +27,7 @@ object JLineTest { } val parsers = Map("1" -> one, "2" -> two, "3" -> three, "4" -> four, "5" -> five) - def main(args: Array[String]): Unit = { + def main(args: Array[String]): Unit = import jline.TerminalFactory import jline.console.ConsoleReader val reader = new ConsoleReader() @@ -36,21 +36,18 @@ object JLineTest { val parser = parsers(args(0)) JLineCompletion.installCustomCompletor(reader, parser) @tailrec - def loop(): Unit = { + def loop(): Unit = val line = reader.readLine("> ") - if (line ne null) { + if line ne null then println("Result: " + apply(parser)(line).resultEmpty) loop() - } - } loop() - } -} +end JLineTest import Parser.* import org.scalacheck.* -object ParserTest extends Properties("Completing Parser") { +object ParserTest extends Properties("Completing Parser"): import Parsers.* import DefaultParsers.matches @@ -59,7 +56,8 @@ object ParserTest extends Properties("Completing Parser") { val spacePort = token(Space) ~> Port - def p[T](f: T): T = { println(f); f } + def p[T](f: T): T = + println(f); f def checkSingle(in: String, expect: Completion)(expectDisplay: Completion = expect) = (("token '" + in + "'") |: checkOne(in, nested, expect)) && @@ -68,19 +66,17 @@ object ParserTest extends Properties("Completing Parser") { def checkOne(in: String, parser: Parser[?], expect: Completion): Prop = completions(parser, in, 1) == Completions.single(expect) - def checkAll(in: String, parser: Parser[?], expect: Completions): Prop = { + def checkAll(in: String, parser: Parser[?], expect: Completions): Prop = val cs = completions(parser, in, 1) ("completions: " + cs) |: ("Expected: " + expect) |: (cs == expect: Prop) - } def checkInvalid(in: String) = (("token '" + in + "'") |: checkInv(in, nested)) && (("display '" + in + "'") |: checkInv(in, nestedDisplay)) - def checkInv(in: String, parser: Parser[?]): Prop = { + def checkInv(in: String, parser: Parser[?]): Prop = val cs = completions(parser, in, 1) ("completions: " + cs) |: (cs == Completions.nil: Prop) - } property("nested tokens a") = checkSingle("", Completion.token("", "a1"))(Completion.displayOnly("")) @@ -113,21 +109,19 @@ object ParserTest extends Properties("Completing Parser") { property("repeatDep no suggestions for bad input") = checkInv(".", repeat) property("repeatDep suggest all") = checkAll("", repeat, completionStrings(colors)) - property("repeatDep suggest remaining two") = { + property("repeatDep suggest remaining two") = val first = colors.toSeq.head checkAll(first + " ", repeat, completionStrings(colors - first)) - } - property("repeatDep suggest remaining one") = { + property("repeatDep suggest remaining one") = val take = colors.toSeq.take(2) checkAll(take.mkString("", " ", " "), repeat, completionStrings(colors -- take)) - } property("repeatDep requires at least one token") = !matches(repeat, "") property("repeatDep accepts one token") = matches(repeat, colors.toSeq.head) property("repeatDep accepts two tokens") = matches(repeat, colors.toSeq.take(2).mkString(" ")) property("parses string that doesn't start with quotes, but includes quotes within it") = matches(StringBasic, "-Dsilicon:z3ConfigArgs=\"model=true model_validate=true\"") -} -object ParserExample { +end ParserTest +object ParserExample: val ws = charClass(_.isWhitespace, "whitespace").+ val notws = charClass(!_.isWhitespace, "not whitespace").+ @@ -151,7 +145,7 @@ object ParserExample { println(apply(t)("test w").resultEmpty) println(apply(t)("test was were").resultEmpty) - def run(n: Int): Unit = { + def run(n: Int): Unit = val a = 'a'.id val aq = a.? val aqn = repeat(aq, min = n, max = n) @@ -160,10 +154,8 @@ object ParserExample { def r = apply(ann)("a" * (n * 2)).resultEmpty println(r.isValid) - } - def run2(n: Int): Unit = { + def run2(n: Int): Unit = val ab = "ab".?.* val r = apply(ab)("a" * n).resultEmpty println(r) - } -} +end ParserExample diff --git a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala index 6fe4bb625..23208a7d8 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FileExamplesTest.scala @@ -13,7 +13,7 @@ import java.io.File import org.scalatest.Assertion import sbt.io.IO -class FileExamplesTest extends UnitSpec { +class FileExamplesTest extends UnitSpec: "listing all files in an absolute base directory" should "produce the entire base directory's contents" in { @@ -60,16 +60,15 @@ class FileExamplesTest extends UnitSpec { def withDirectoryStructure(withCompletionPrefix: String = "")( thunk: DirectoryStructure => Assertion - ): Assertion = { + ): Assertion = IO.withTemporaryDirectory { tempDir => val ds = new DirectoryStructure(withCompletionPrefix) ds.createSampleDirStructure(tempDir) ds.fileExamples = new FileExamples(ds.baseDir, withCompletionPrefix) thunk(ds) } - } - final class DirectoryStructure(withCompletionPrefix: String) { + final class DirectoryStructure(withCompletionPrefix: String): var fileExamples: FileExamples = scala.compiletime.uninitialized var baseDir: File = scala.compiletime.uninitialized var childFiles: List[File] = scala.compiletime.uninitialized @@ -86,7 +85,7 @@ class FileExamplesTest extends UnitSpec { .withFilter(_.startsWith(withCompletionPrefix)) .map(_.substring(withCompletionPrefix.length)) - def createSampleDirStructure(tempDir: File): Unit = { + def createSampleDirStructure(tempDir: File): Unit = childFiles = toChildFiles(tempDir, List("foo", "bar", "bazaar")) childDirectories = toChildFiles(tempDir, List("moo", "far")) nestedFiles = toChildFiles(childDirectories(1), List("farfile1", "barfile2")) @@ -96,10 +95,8 @@ class FileExamplesTest extends UnitSpec { (childFiles ++ nestedFiles).foreach(_.createNewFile()) baseDir = tempDir - } private def toChildFiles(baseDir: File, files: List[String]): List[File] = files.map(new File(baseDir, _)) - } - -} + end DirectoryStructure +end FileExamplesTest diff --git a/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala index 835afb593..f91e08781 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/FixedSetExamplesTest.scala @@ -9,25 +9,21 @@ package sbt.internal.util package complete -class FixedSetExamplesTest extends UnitSpec { +class FixedSetExamplesTest extends UnitSpec: "adding a prefix" should "produce a smaller set of examples with the prefix removed" in { - val _ = new Examples { + val _ = new Examples: fixedSetExamples.withAddedPrefix("f")() should contain theSameElementsAs (List("oo", "ool", "u")) fixedSetExamples.withAddedPrefix("fo")() should contain theSameElementsAs (List("o", "ol")) fixedSetExamples.withAddedPrefix("b")() should contain theSameElementsAs (List("ar")) - } } "without a prefix" should "produce the original set" in { - val _ = new Examples { + val _ = new Examples: fixedSetExamples() shouldBe exampleSet - } } - trait Examples { + trait Examples: val exampleSet = List("foo", "bar", "fool", "fu") val fixedSetExamples = FixedSetExamples(exampleSet) - } -} diff --git a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala index d2b4a2aec..c1c392ee6 100644 --- a/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala +++ b/internal/util-complete/src/test/scala/sbt/complete/ParserWithExamplesTest.scala @@ -11,19 +11,18 @@ package complete import Completion.* -class ParserWithExamplesTest extends UnitSpec { +class ParserWithExamplesTest extends UnitSpec: "listing a limited number of completions" should "grab only the needed number of elements from the iterable source of examples" in { - val _ = new ParserWithLazyExamples { + val _ = new ParserWithLazyExamples: parserWithExamples.completions(0) examples.size shouldEqual maxNumberOfExamples - } } "listing only valid completions" should "use the delegate parser to remove invalid examples" in { - val _ = new ParserWithValidExamples { + val _ = new ParserWithValidExamples: val validCompletions = Completions( Set( suggestion("blue"), @@ -31,32 +30,29 @@ class ParserWithExamplesTest extends UnitSpec { ) ) parserWithExamples.completions(0) shouldEqual validCompletions - } } "listing valid completions in a derived parser" should "produce only valid examples that start with the character of the derivation" in { - val _ = new ParserWithValidExamples { + val _ = new ParserWithValidExamples: val derivedCompletions = Completions( Set( suggestion("lue") ) ) parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions - } } "listing valid and invalid completions" should "produce the entire source of examples" in { - val _ = new parserWithAllExamples { + val _ = new parserWithAllExamples: val completions = Completions(examples.map(suggestion(_)).toSet) parserWithExamples.completions(0) shouldEqual completions - } } "listing valid and invalid completions in a derived parser" should "produce only examples that start with the character of the derivation" in { - val _ = new parserWithAllExamples { + val _ = new parserWithAllExamples: val derivedCompletions = Completions( Set( suggestion("lue"), @@ -64,7 +60,6 @@ class ParserWithExamplesTest extends UnitSpec { ) ) parserWithExamples.derive('b').completions(0) shouldEqual derivedCompletions - } } class ParserWithLazyExamples @@ -82,7 +77,7 @@ class ParserWithExamplesTest extends UnitSpec { examples: Iterable[String] = Set("blue", "yellow", "green_", "block", "red"), maxNumberOfExamples: Int = 25, removeInvalidExamples: Boolean - ) { + ): import DefaultParsers.* @@ -93,26 +88,20 @@ class ParserWithExamplesTest extends UnitSpec { maxNumberOfExamples, removeInvalidExamples ) - } - case class GrowableSourceOfExamples() extends Iterable[String] { + case class GrowableSourceOfExamples() extends Iterable[String]: private var numberOfIteratedElements: Int = 0 - override def iterator: Iterator[String] = { - new Iterator[String] { + override def iterator: Iterator[String] = + new Iterator[String]: var currentElement = 0 - override def next(): String = { + override def next(): String = currentElement += 1 numberOfIteratedElements = Math.max(currentElement, numberOfIteratedElements) numberOfIteratedElements.toString - } override def hasNext: Boolean = true - } - } override def size: Int = numberOfIteratedElements - } - -} +end ParserWithExamplesTest diff --git a/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala b/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala index 1c79e5365..544464181 100644 --- a/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala +++ b/internal/util-control/src/main/scala/sbt/internal/util/ErrorHandling.scala @@ -10,19 +10,16 @@ package sbt.internal.util import java.io.IOException -object ErrorHandling { +object ErrorHandling: def translate[T](msg: => String)(f: => T) = - try { - f - } catch { + try f + catch case e: IOException => throw new TranslatedIOException(msg + e.toString, e) case e: Exception => throw new TranslatedException(msg + e.toString, e) - } def wideConvert[T](f: => T): Either[Throwable, T] = - try { - Right(f) - } catch { + try Right(f) + catch case ex @ (_: Exception | _: StackOverflowError) => Left(ex) case err: VirtualMachineError => @@ -33,24 +30,21 @@ object ErrorHandling { throw err case x: Throwable => Left(x) - } def convert[T](f: => T): Either[Exception, T] = - try { - Right(f) - } catch { case e: Exception => Left(e) } + try Right(f) + catch case e: Exception => Left(e) def reducedToString(e: Throwable): String = - if (e.getClass == classOf[RuntimeException]) { + if e.getClass == classOf[RuntimeException] then val msg = e.getMessage - if (msg == null || msg.isEmpty) e.toString else msg - } else e.toString -} + if msg == null || msg.isEmpty then e.toString else msg + else e.toString +end ErrorHandling sealed class TranslatedException private[sbt] (msg: String, cause: Throwable) - extends RuntimeException(msg, cause) { + extends RuntimeException(msg, cause): override def toString = msg -} final class TranslatedIOException private[sbt] (msg: String, cause: IOException) extends TranslatedException(msg, cause) diff --git a/internal/util-control/src/main/scala/sbt/internal/util/ExitHook.scala b/internal/util-control/src/main/scala/sbt/internal/util/ExitHook.scala index d73b60065..b7d650bdc 100644 --- a/internal/util-control/src/main/scala/sbt/internal/util/ExitHook.scala +++ b/internal/util-control/src/main/scala/sbt/internal/util/ExitHook.scala @@ -9,18 +9,16 @@ package sbt.internal.util /** Defines a function to call as sbt exits. */ -trait ExitHook { +trait ExitHook: /** Subclasses should implement this method, which is called when this hook is executed. */ def runBeforeExiting(): Unit -} +object ExitHook: + def apply(f: => Unit): ExitHook = new ExitHook: + def runBeforeExiting() = f -object ExitHook { - def apply(f: => Unit): ExitHook = new ExitHook { def runBeforeExiting() = f } -} - -object ExitHooks { +object ExitHooks: /** * Calls each registered exit hook, trapping any exceptions so that each hook is given a chance to @@ -28,5 +26,3 @@ object ExitHooks { */ def runExitHooks(exitHooks: Seq[ExitHook]): Seq[Throwable] = exitHooks.flatMap(hook => ErrorHandling.wideConvert(hook.runBeforeExiting()).left.toOption) - -} diff --git a/internal/util-control/src/main/scala/sbt/internal/util/RunningProcesses.scala b/internal/util-control/src/main/scala/sbt/internal/util/RunningProcesses.scala index ce325d423..f704d8fec 100644 --- a/internal/util-control/src/main/scala/sbt/internal/util/RunningProcesses.scala +++ b/internal/util-control/src/main/scala/sbt/internal/util/RunningProcesses.scala @@ -17,7 +17,7 @@ import scala.sys.process.Process * killed with the killAll method. In particular, this can be used in a signal handler to kill these * processes when the user inputs ctrl+c. */ -private[sbt] object RunningProcesses { +private[sbt] object RunningProcesses: val active = ConcurrentHashMap.newKeySet[AnyRef] def add(process: AnyRef): Unit = active.synchronized { active.add(process) @@ -35,4 +35,3 @@ private[sbt] object RunningProcesses { } active.clear() } -} diff --git a/internal/util-control/src/main/scala/sbt/internal/util/hashing/FarmHash64.scala b/internal/util-control/src/main/scala/sbt/internal/util/hashing/FarmHash64.scala index 61d9ada40..507fdb371 100644 --- a/internal/util-control/src/main/scala/sbt/internal/util/hashing/FarmHash64.scala +++ b/internal/util-control/src/main/scala/sbt/internal/util/hashing/FarmHash64.scala @@ -143,6 +143,7 @@ object FarmHash64: off += 64 off != fin do () + end while off = last64.toInt @@ -183,6 +184,8 @@ object FarmHash64: z = x x = t hashLen16(hashLen16(v1, w1, m) + shiftMix(y) * K0 + z, hashLen16(v2, w2, m) + x, m) + end if + end naHash64 end FarmHash64 object FarmNaSeedlessHash64: diff --git a/internal/util-control/src/main/scala/sbt/internal/util/hashing/StreamingXXHash64VarHandle.scala b/internal/util-control/src/main/scala/sbt/internal/util/hashing/StreamingXXHash64VarHandle.scala index 965dc5898..b157df4f2 100644 --- a/internal/util-control/src/main/scala/sbt/internal/util/hashing/StreamingXXHash64VarHandle.scala +++ b/internal/util-control/src/main/scala/sbt/internal/util/hashing/StreamingXXHash64VarHandle.scala @@ -56,6 +56,7 @@ class StreamingXXHash64VarHandle(seed: Long) extends AbstractStreamingXXHash64Sc h64 ^= v4 h64 = h64 * PRIME64_1 + PRIME64_4 else h64 = seed + PRIME64_5 + end if h64 += totalLen @@ -123,6 +124,7 @@ class StreamingXXHash64VarHandle(seed: Long) extends AbstractStreamingXXHash64Sc off += 32 - memSize memSize = 0 else () + end if { val limit: Int = end - 32 diff --git a/internal/util-control/src/main/scala/sbt/internal/util/hashing/XXHash64.scala b/internal/util-control/src/main/scala/sbt/internal/util/hashing/XXHash64.scala index 02905dcc3..4adc131f3 100644 --- a/internal/util-control/src/main/scala/sbt/internal/util/hashing/XXHash64.scala +++ b/internal/util-control/src/main/scala/sbt/internal/util/hashing/XXHash64.scala @@ -71,6 +71,7 @@ class XXHash64[A1: Access](seed: Long) extends HashAlgo[A1]: off += 8 off <= limit do () + end while h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18) @@ -98,6 +99,7 @@ class XXHash64[A1: Access](seed: Long) extends HashAlgo[A1]: h64 ^= v4 h64 = h64 * PRIME64_1 + PRIME64_4 else h64 = seed + PRIME64_5 + end if h64 += len diff --git a/internal/util-core/src/main/scala-3/sbt/internal/util/appmacro/StringTypeTag.scala b/internal/util-core/src/main/scala-3/sbt/internal/util/appmacro/StringTypeTag.scala index 11975a4c3..4d17bb8fa 100644 --- a/internal/util-core/src/main/scala-3/sbt/internal/util/appmacro/StringTypeTag.scala +++ b/internal/util-core/src/main/scala-3/sbt/internal/util/appmacro/StringTypeTag.scala @@ -9,10 +9,9 @@ package sbt.internal.util.appmacro final class StringTypeTag[A](val key: String): override def toString(): String = key - override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { + override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match case x: StringTypeTag[?] => (this.key == x.key) - case _ => false - }) + case _ => false) override def hashCode: Int = key.## end StringTypeTag diff --git a/internal/util-core/src/main/scala/sbt/internal/util/Util.scala b/internal/util-core/src/main/scala/sbt/internal/util/Util.scala index b109f38be..c34554db1 100644 --- a/internal/util-core/src/main/scala/sbt/internal/util/Util.scala +++ b/internal/util-core/src/main/scala/sbt/internal/util/Util.scala @@ -19,16 +19,14 @@ import scala.util.control.NonFatal object Util: def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value) - def separate[T, A, B](ps: Seq[T])(f: T => Either[A, B]): (Seq[A], Seq[B]) = { + def separate[T, A, B](ps: Seq[T])(f: T => Either[A, B]): (Seq[A], Seq[B]) = val (a, b) = ps.foldLeft((Nil: Seq[A], Nil: Seq[B]))((xs, y) => prependEither(xs, f(y))) (a.reverse, b.reverse) - } def prependEither[A, B](acc: (Seq[A], Seq[B]), next: Either[A, B]): (Seq[A], Seq[B]) = - next match { + next match case Left(l) => (l +: acc._1, acc._2) case Right(r) => (acc._1, r +: acc._2) - } def pairID[A, B] = (a: A, b: B) => (a, b) @@ -37,14 +35,14 @@ object Util: def hasHyphen(s: String): Boolean = s.indexOf('-') >= 0 def hyphenToCamel(s: String): String = - if (hasHyphen(s)) Hyphen.replaceAllIn(s, _.group(1).toUpperCase(Locale.ENGLISH)) else s + if hasHyphen(s) then Hyphen.replaceAllIn(s, _.group(1).toUpperCase(Locale.ENGLISH)) else s private lazy val Camel = """(\p{javaLowerCase})(\p{javaUpperCase})""".r def camelToHyphen(s: String): String = Camel.replaceAllIn(s, m => m.group(1) + "-" + m.group(2).toLowerCase(Locale.ENGLISH)) - def quoteIfKeyword(s: String): String = if (ScalaKeywords.values(s)) s"`${s}`" else s + def quoteIfKeyword(s: String): String = if ScalaKeywords.values(s) then s"`${s}`" else s def quoteIfNotScalaId(s: String): String = if isValidScalaId(s) && !ScalaKeywords.values(s) then s @@ -54,10 +52,9 @@ object Util: s.nonEmpty && (s.charAt(0).isLetter || s.charAt(0) == '_') && s.forall(c => c.isLetterOrDigit || c == '_') - def ignoreResult[A](f: => A): Unit = { + def ignoreResult[A](f: => A): Unit = val _ = f () - } def ignoreTry[A](f: => A): Unit = ignoreResult(Try(f)) @@ -67,13 +64,11 @@ object Util: lazy val isWindows: Boolean = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows") - lazy val isCygwin: Boolean = { + lazy val isCygwin: Boolean = val os = sys.env.get("OSTYPE") - os match { + os match case Some(x) => x.toLowerCase(Locale.ENGLISH).contains("cygwin") case _ => false - } - } lazy val isNonCygwinWindows: Boolean = isWindows && !isCygwin lazy val isCygwinWindows: Boolean = isWindows && isCygwin @@ -88,14 +83,11 @@ object Util: def nilSeq[A]: Seq[A] = Seq.empty[A] def none[A]: Option[A] = (None: Option[A]) - extension [A](value: A) { - def some: Option[A] = (Some(value): Option[A]) - } + extension [A](value: A) def some: Option[A] = (Some(value): Option[A]) - private[sbt] def withCaching[A1, A2](f: A1 => A2): A1 => A2 = { + private[sbt] def withCaching[A1, A2](f: A1 => A2): A1 => A2 = val cache = TrieMap.empty[A1, A2] x => cache.getOrElseUpdate(x, f(x)) - } lazy val javaHome: Path = sys.env.get("JAVA_HOME") match diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/BasicLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/BasicLogger.scala index 56c3ddbc9..2432a8d29 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/BasicLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/BasicLogger.scala @@ -11,7 +11,7 @@ package sbt.internal.util import sbt.util.* /** Implements the level-setting methods of Logger. */ -abstract class BasicLogger extends AbstractLogger { +abstract class BasicLogger extends AbstractLogger: private var traceEnabledVar: Int = java.lang.Integer.MAX_VALUE private var level: Level.Value = Level.Info private var successEnabledVar = true @@ -21,4 +21,3 @@ abstract class BasicLogger extends AbstractLogger { def setLevel(newLevel: Level.Value): Unit = synchronized { level = newLevel } def setTrace(level: Int): Unit = synchronized { traceEnabledVar = level } def getTrace: Int = synchronized { traceEnabledVar } -} diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala index 8ec136c62..866d010f2 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/BufferedLogger.scala @@ -12,7 +12,7 @@ import sbt.util.* import scala.collection.mutable.ListBuffer import java.util.concurrent.atomic.AtomicInteger -object BufferedAppender { +object BufferedAppender: def generateName: String = "buffered-" + generateId.incrementAndGet @@ -23,7 +23,6 @@ object BufferedAppender { def apply(name: String, delegate: Appender): BufferedAppender = new BufferedAppender(name, delegate) -} /** * An appender that can buffer the logging done on it and then can flush the buffer to the delegate @@ -31,7 +30,7 @@ object BufferedAppender { * the buffer to the backing appender. The logging level set at the time a message is originally * logged is used, not the level at the time 'play' is called. */ -class BufferedAppender(override val name: String, delegate: Appender) extends Appender { +class BufferedAppender(override val name: String, delegate: Appender) extends Appender: override def close(): Unit = () override private[sbt] def properties: ConsoleAppender.Properties = delegate.properties override private[sbt] def suppressedMessage: SuppressedTraceContext => Option[String] = @@ -41,40 +40,37 @@ class BufferedAppender(override val name: String, delegate: Appender) extends Ap new java.util.Vector[(Level.Value, Option[String], Option[ObjectEvent[?]])] private var recording = false - override def appendLog(level: Level.Value, message: => String): Unit = { - if (recording) Util.ignoreResult(buffer.add((level, Some(message), None))) + override def appendLog(level: Level.Value, message: => String): Unit = + if recording then Util.ignoreResult(buffer.add((level, Some(message), None))) else delegate.appendLog(level, message) - } override private[sbt] def appendObjectEvent[T]( level: Level.Value, message: => ObjectEvent[T] - ): Unit = { - if (recording) Util.ignoreResult(buffer.add(((level, None, Some(message))))) + ): Unit = + if recording then Util.ignoreResult(buffer.add(((level, None, Some(message))))) else delegate.appendObjectEvent(level, message) - } /** Enables buffering. */ def record() = synchronized { recording = true } - def buffer[T](f: => T): T = { + def buffer[T](f: => T): T = record() - try { + try f - } finally { + finally stopQuietly() - } - } - def bufferQuietly[T](f: => T): T = { + def bufferQuietly[T](f: => T): T = record() - try { + try val result = f clearBuffer() result - } catch { case e: Throwable => stopQuietly(); throw e } - } + catch + case e: Throwable => + stopQuietly() + throw e def stopQuietly() = synchronized { - try { - stopBuffer() - } catch { case _: Exception => () } + try stopBuffer() + catch case _: Exception => () } /** @@ -95,8 +91,7 @@ class BufferedAppender(override val name: String, delegate: Appender) extends Ap /** Plays buffered events and disables buffering. */ def stopBuffer(): Unit = synchronized { play(); clearBuffer() } - -} +end BufferedAppender /** * A logger that can buffer the logging done on it and then can flush the buffer to the delegate @@ -106,32 +101,31 @@ class BufferedAppender(override val name: String, delegate: Appender) extends Ap * * This class assumes that it is the only client of the delegate logger. */ -class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { +class BufferedLogger(delegate: AbstractLogger) extends BasicLogger: private val buffer = new ListBuffer[LogEvent] private var recording = false /** Enables buffering. */ def record() = synchronized { recording = true } - def buffer[T](f: => T): T = { + def buffer[T](f: => T): T = record() - try { + try f - } finally { + finally stopQuietly() - } - } - def bufferQuietly[T](f: => T): T = { + def bufferQuietly[T](f: => T): T = record() - try { + try val result = f clear() result - } catch { case e: Throwable => stopQuietly(); throw e } - } + catch + case e: Throwable => + stopQuietly() + throw e def stopQuietly() = synchronized { - try { - stop() - } catch { case _: Exception => () } + try stop() + catch case _: Exception => () } /** @@ -148,28 +142,22 @@ class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { override def setLevel(newLevel: Level.Value): Unit = synchronized { super.setLevel(newLevel) - if (recording) - buffer += new SetLevel(newLevel) - else - delegate.setLevel(newLevel) + if recording then buffer += new SetLevel(newLevel) + else delegate.setLevel(newLevel) () } override def setSuccessEnabled(flag: Boolean): Unit = synchronized { super.setSuccessEnabled(flag) - if (recording) - buffer += new SetSuccess(flag) - else - delegate.setSuccessEnabled(flag) + if recording then buffer += new SetSuccess(flag) + else delegate.setSuccessEnabled(flag) () } override def setTrace(level: Int): Unit = synchronized { super.setTrace(level) - if (recording) - buffer += new SetTrace(level) - else - delegate.setTrace(level) + if recording then buffer += new SetTrace(level) + else delegate.setTrace(level) () } @@ -182,10 +170,8 @@ class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { doBufferable(level, new Log(level, message), _.log(level, message)) def logAll(events: Seq[LogEvent]): Unit = synchronized { - if (recording) - buffer ++= events - else - delegate.logAll(events) + if recording then buffer ++= events + else delegate.logAll(events) () } @@ -204,12 +190,9 @@ class BufferedLogger(delegate: AbstractLogger) extends BasicLogger { appendIfBuffered: => LogEvent, doUnbuffered: AbstractLogger => Unit ): Unit = synchronized { - if (condition) { - if (recording) - buffer += appendIfBuffered - else - doUnbuffered(delegate) - } + if condition then + if recording then buffer += appendIfBuffered + else doUnbuffered(delegate) () } -} +end BufferedLogger diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala index 739244b99..82014291d 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleAppender.scala @@ -15,7 +15,7 @@ import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger } import sbt.internal.util.ConsoleAppender.* import sbt.util.* -object ConsoleLogger { +object ConsoleLogger: /** * A new `ConsoleLogger` that logs to `out`. @@ -59,7 +59,7 @@ object ConsoleLogger { ConsoleAppender.noSuppressedMessage ): ConsoleLogger = new ConsoleLogger(out, ansiCodesSupported, useFormat, suppressedMessage) -} +end ConsoleLogger /** * A logger that logs to the console. On supported systems, the level labels are colored. @@ -69,7 +69,7 @@ class ConsoleLogger private[ConsoleLogger] ( ansiCodesSupported: Boolean, useFormat: Boolean, suppressedMessage: SuppressedTraceContext => Option[String] -) extends BasicLogger { +) extends BasicLogger: private[sbt] val appender: Appender = ConsoleAppender(generateName(), out, ansiCodesSupported, useFormat, suppressedMessage) @@ -78,22 +78,18 @@ class ConsoleLogger private[ConsoleLogger] ( appender.control(event, message) override def log(level: Level.Value, message: => String): Unit = - if (atLevel(level)) { - appender.appendLog(level, message) - } + if atLevel(level) then appender.appendLog(level, message) override def success(message: => String): Unit = - if (successEnabled) { - appender.success(message) - } + if successEnabled then appender.success(message) override def trace(t: => Throwable): Unit = appender.trace(t, getTrace) override def logAll(events: Seq[LogEvent]) = events.foreach(log) -} +end ConsoleLogger -object ConsoleAppender { +object ConsoleAppender: private[sbt] def cursorLeft(n: Int): String = s"\u001B[${n}D" private[sbt] def cursorUp(n: Int): String = s"\u001B[${n}A" private[sbt] def cursorDown(n: Int): String = s"\u001B[${n}B" @@ -108,24 +104,21 @@ object ConsoleAppender { private val showProgressHolder: AtomicBoolean = new AtomicBoolean(false) def setShowProgress(b: Boolean): Unit = showProgressHolder.set(b) def showProgress: Boolean = showProgressHolder.get - private[sbt] trait Properties { + private[sbt] trait Properties: def isAnsiSupported: Boolean def isColorEnabled: Boolean def out: ConsoleOut - } - private[sbt] object Properties { + private[sbt] object Properties: def from(terminal: Terminal): Properties = from(ConsoleOut.terminalOut(terminal), terminal.isAnsiSupported, terminal.isColorEnabled) def safelyFrom(terminal: Terminal): Properties = from(ConsoleOut.safeTerminalOut(terminal), terminal.isAnsiSupported, terminal.isColorEnabled) - def from(o: ConsoleOut, ansi: Boolean, color: Boolean): Properties = new Properties { + def from(o: ConsoleOut, ansi: Boolean, color: Boolean): Properties = new Properties: override def isAnsiSupported: Boolean = ansi override def isColorEnabled: Boolean = color override def out = o - } - } /** Hide stack trace altogether. */ val noSuppressedMessage = (_: SuppressedTraceContext) => None @@ -139,11 +132,10 @@ object ConsoleAppender { @deprecated("Use Terminal.isAnsiSupported or Terminal.isColorEnabled", "1.4.0") lazy val formatEnabledInEnv: Boolean = Terminal.isAnsiSupported - private[sbt] def parseLogOption(s: String): LogOption = Terminal.parseLogOption(s) match { + private[sbt] def parseLogOption(s: String): LogOption = Terminal.parseLogOption(s) match case Some(true) => LogOption.Always case Some(false) => LogOption.Never case _ => LogOption.Auto - } private val generateId: AtomicInteger = new AtomicInteger @@ -223,10 +215,9 @@ object ConsoleAppender { name: String, out: ConsoleOut, suppressedMessage: SuppressedTraceContext => Option[String] - ): Appender = { + ): Appender = val ansi = Terminal.isAnsiSupported apply(name, out, ansi, ansi, suppressedMessage) - } /** * A new `ConsoleAppender` identified by `name`, and that writes to `out`. @@ -253,9 +244,8 @@ object ConsoleAppender { * @return * A new `ConsoleAppender` that writes to `out`. */ - def apply(name: String, terminal: Terminal): Appender = { + def apply(name: String, terminal: Terminal): Appender = new ConsoleAppender(name, Properties.from(terminal), noSuppressedMessage) - } /** * A new `ConsoleAppender` identified by `name`, and that writes to `terminal`. @@ -265,9 +255,8 @@ object ConsoleAppender { * @param terminal The terminal to which this appender corresponds * @return A new `ConsoleAppender` that writes to `terminal`. */ - def safe(name: String, terminal: Terminal): Appender = { + def safe(name: String, terminal: Terminal): Appender = new ConsoleAppender(name, Properties.safelyFrom(terminal), noSuppressedMessage) - } /** * A new `ConsoleAppender` identified by `name`, and that writes to `out`. @@ -285,9 +274,8 @@ object ConsoleAppender { name: String, terminal: Terminal, suppressedMessage: SuppressedTraceContext => Option[String] - ): Appender = { + ): Appender = new ConsoleAppender(name, Properties.from(terminal), suppressedMessage) - } /** * A new `ConsoleAppender` identified by `name`, and that writes to `out`. @@ -309,16 +297,15 @@ object ConsoleAppender { ansiCodesSupported: Boolean, useFormat: Boolean, suppressedMessage: SuppressedTraceContext => Option[String] - ): Appender = { + ): Appender = new ConsoleAppender( name, Properties.from(out, ansiCodesSupported, useFormat), suppressedMessage ) - } private[sbt] def generateName(): String = "out-" + generateId.incrementAndGet -} +end ConsoleAppender // See http://stackoverflow.com/questions/24205093/how-to-create-a-custom-appender-in-log4j2 // for custom appender using Java. @@ -334,10 +321,9 @@ class ConsoleAppender( override private[sbt] val name: String, override private[sbt] val properties: Properties, override private[sbt] val suppressedMessage: SuppressedTraceContext => Option[String] -) extends Appender { +) extends Appender: override def close(): Unit = () -} -trait Appender extends AutoCloseable { +trait Appender extends AutoCloseable: private[sbt] def name: String private[sbt] def properties: Properties private[sbt] def suppressedMessage: SuppressedTraceContext => Option[String] @@ -373,15 +359,11 @@ trait Appender extends AutoCloseable { * @param traceLevel * How to shorten the stack trace. */ - def trace(t: => Throwable, traceLevel: Int): Unit = { - if (traceLevel >= 0) - write(StackTrace.trimmed(t, traceLevel)) - if (traceLevel <= 2) { + def trace(t: => Throwable, traceLevel: Int): Unit = + if traceLevel >= 0 then write(StackTrace.trimmed(t, traceLevel)) + if traceLevel <= 2 then val ctx = new SuppressedTraceContext(traceLevel, ansiCodesSupported && useFormat) - for (msg <- suppressedMessage(ctx)) - appendLog(NO_COLOR, "trace", NO_COLOR, msg) - } - } + for msg <- suppressedMessage(ctx) do appendLog(NO_COLOR, "trace", NO_COLOR, msg) /** * Logs a `ControlEvent` to the log. @@ -402,9 +384,8 @@ trait Appender extends AutoCloseable { * @param message * The message to log. */ - def appendLog(level: Level.Value, message: => String): Unit = { + def appendLog(level: Level.Value, message: => String): Unit = appendLog(labelColor(level), level.toString, NO_COLOR, message) - } /** * Select the right color for the label given `level`. @@ -415,11 +396,10 @@ trait Appender extends AutoCloseable { * The color to use to color the label. */ private def labelColor(level: Level.Value): String = - level match { + level match case Level.Error => RED case Level.Warn => YELLOW case _ => NO_COLOR - } /** * Appends a full message to the log. Each line is prefixed with `[$label]`, written in @@ -441,81 +421,71 @@ trait Appender extends AutoCloseable { messageColor: String, message: String ): Unit = - try { + try // according to https://github.com/sbt/sbt/issues/5608, sometimes we get a null message - if (message == null) () - else { + if message == null then () + else val len = labelColor.length + label.length + messageColor.length + reset.length * 3 val builder: StringBuilder = new StringBuilder(len) message.linesIterator.foreach { line => builder.ensureCapacity(len + line.length + 4) builder.setLength(0) - def fmted(a: String, b: String) = { - if (useFormat) builder.append(reset).append(a).append(b).append(reset) + def fmted(a: String, b: String) = + if useFormat then builder.append(reset).append(a).append(b).append(reset) else builder.append(b) - } - if (useFormat) builder.append(reset) + if useFormat then builder.append(reset) builder.append('[') fmted(labelColor, label) builder.append("] ") fmted(messageColor, line) write(builder.toString) } - } - } catch { case _: InterruptedException => } + catch + case _: InterruptedException => // success is called by ConsoleLogger. - private[sbt] def success(message: => String): Unit = { + private[sbt] def success(message: => String): Unit = appendLog(SUCCESS_LABEL_COLOR, Level.SuccessLabel, SUCCESS_MESSAGE_COLOR, message) - } - private def write(msg: String): Unit = { + private def write(msg: String): Unit = // There is no api for removing only colors but not other ansi escape sequences // so we do nothing if useFormat is false but ansiCodesSupported is true which is // a rare use case but if ansiCodesSupported is true, color codes should work so // the output may have unwanted colors but it would still be legible. This should // only be relevant if the log message string itself contains ansi escape sequences // other than color codes which is very unlikely. - val toWrite = if ((!ansiCodesSupported || !useFormat) && msg.getBytes.contains(27.toByte)) { + val toWrite = if (!ansiCodesSupported || !useFormat) && msg.getBytes.contains(27.toByte) then val (bytes, len) = EscHelpers.strip(msg.getBytes, stripAnsi = !ansiCodesSupported, stripColor = !useFormat) new String(bytes, 0, len) - } else msg + else msg out.println(toWrite) - } - private def appendTraceEvent(te: TraceEvent): Unit = { + private def appendTraceEvent(te: TraceEvent): Unit = val traceLevel = getTrace - if (traceLevel >= 0) { + if traceLevel >= 0 then val throwableShowLines: ShowLines[Throwable] = - ShowLines[Throwable]((t: Throwable) => { - List(StackTrace.trimmed(t, traceLevel)) - }) + ShowLines[Throwable]((t: Throwable) => List(StackTrace.trimmed(t, traceLevel))) val codec: ShowLines[TraceEvent] = - ShowLines[TraceEvent]((t: TraceEvent) => { - throwableShowLines.showLines(t.message) - }) + ShowLines[TraceEvent]((t: TraceEvent) => throwableShowLines.showLines(t.message)) codec.showLines(te).toVector foreach { appendLog(Level.Error, _) } - } - if (traceLevel <= 2) { + if traceLevel <= 2 then suppressedMessage( new SuppressedTraceContext(traceLevel, ansiCodesSupported && useFormat) ) foreach { appendLog(Level.Error, _) } - } - } - private def appendMessageContent(level: Level.Value, o: AnyRef): Unit = { - def appendEvent(oe: ObjectEvent[?]): Unit = { + private def appendMessageContent(level: Level.Value, o: AnyRef): Unit = + def appendEvent(oe: ObjectEvent[?]): Unit = val contentType = oe.contentType - contentType match { + contentType match case "sbt.internal.util.TraceEvent" => appendTraceEvent(oe.message.asInstanceOf[TraceEvent]) case "sbt.internal.util.ProgressEvent" => case _ => - LogExchange.stringCodec[AnyRef](contentType) match { + LogExchange.stringCodec[AnyRef](contentType) match case Some(codec) if contentType == "sbt.internal.util.SuccessEvent" => codec.showLines(oe.message.asInstanceOf[AnyRef]).toVector foreach { success(_) } case Some(codec) => @@ -524,19 +494,14 @@ trait Appender extends AutoCloseable { _ )) case _ => appendLog(level, oe.message.toString) - } - } - } - o match { + o match case x: StringEvent => Vector(x.message) foreach { appendLog(level, _) } case x: ObjectEvent[?] => appendEvent(x) case _ => Vector(o.toString) foreach { appendLog(level, _) } - } - } + end appendMessageContent private[sbt] def appendObjectEvent[T](level: Level.Value, message: => ObjectEvent[T]): Unit = appendMessageContent(level, message) - -} +end Appender final class SuppressedTraceContext(val traceLevel: Int, val useFormat: Boolean) diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala index 012a38c2e..7f56dc75d 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ConsoleOut.scala @@ -13,27 +13,25 @@ import java.nio.channels.ClosedChannelException import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicReference -sealed trait ConsoleOut { +sealed trait ConsoleOut: val lockObject: AnyRef def print(s: String): Unit def println(s: String): Unit def println(): Unit def flush(): Unit -} -object ConsoleOut { +object ConsoleOut: def systemOut: ConsoleOut = terminalOut - private[sbt] object NullConsoleOut extends ConsoleOut { + private[sbt] object NullConsoleOut extends ConsoleOut: override val lockObject: AnyRef = this override def print(s: String): Unit = {} override def println(): Unit = {} override def println(s: String): Unit = {} override def flush(): Unit = {} - } private[sbt] def globalProxy: ConsoleOut = Proxy private[sbt] def setGlobalProxy(out: ConsoleOut): Unit = Proxy.set(out) private[sbt] def getGlobalProxy: ConsoleOut = Proxy.proxy.get - private object Proxy extends ConsoleOut { + private object Proxy extends ConsoleOut: private[ConsoleOut] val proxy = new AtomicReference[ConsoleOut](systemOut) private def get: ConsoleOut = proxy.get def set(proxy: ConsoleOut): Unit = this.proxy.set(proxy) @@ -43,7 +41,6 @@ object ConsoleOut { override def println(): Unit = get.println() override def flush(): Unit = get.flush() override def toString: String = s"ProxyConsoleOut" - } def overwriteContaining(s: String): (String, String) => Boolean = (cur, prev) => cur.contains(s) && prev.contains(s) @@ -58,7 +55,7 @@ object ConsoleOut { * The ConsoleOut returned by this method assumes that the only newlines are from println calls * and not in the String arguments. */ - def systemOutOverwrite(f: (String, String) => Boolean): ConsoleOut = new ConsoleOut { + def systemOutOverwrite(f: (String, String) => Boolean): ConsoleOut = new ConsoleOut: val lockObject: PrintStream = System.out private var last: Option[String] = None private val current = new java.lang.StringBuffer @@ -66,7 +63,7 @@ object ConsoleOut { def println(s: String): Unit = synchronized { current.append(s); println() } def println(): Unit = synchronized { val s = current.toString - if (Terminal.isAnsiSupported && last.exists(lmsg => f(s, lmsg))) + if Terminal.isAnsiSupported && last.exists(lmsg => f(s, lmsg)) then lockObject.print(OverwriteLine) lockObject.println(s) last = Some(s) @@ -74,82 +71,74 @@ object ConsoleOut { } def flush(): Unit = synchronized { val s = current.toString - if (Terminal.isAnsiSupported && last.exists(lmsg => f(s, lmsg))) + if Terminal.isAnsiSupported && last.exists(lmsg => f(s, lmsg)) then lockObject.print(OverwriteLine) lockObject.print(s) last = Some(s) current.setLength(0) } override def toString: String = s"SystemOutOverwrite@${System.identityHashCode(this)}" - } - def terminalOut: ConsoleOut = new ConsoleOut { + def terminalOut: ConsoleOut = new ConsoleOut: override val lockObject: AnyRef = System.out override def print(s: String): Unit = Terminal.get.printStream.print(s) override def println(s: String): Unit = Terminal.get.printStream.println(s) override def println(): Unit = Terminal.get.printStream.println() override def flush(): Unit = Terminal.get.printStream.flush() override def toString: String = s"TerminalOut" - } /** * Same as terminalOut but it catches and ignores the ClosedChannelException */ - def safeTerminalOut(terminal: Terminal): ConsoleOut = { + def safeTerminalOut(terminal: Terminal): ConsoleOut = val out = terminalOut(terminal) - new ConsoleOut { + new ConsoleOut: override val lockObject: AnyRef = terminal override def print(s: String): Unit = catchException(out.print(s)) override def println(s: String): Unit = catchException(out.println(s)) override def println(): Unit = catchException(out.println()) override def flush(): Unit = catchException(out.flush()) override def toString: String = s"SafeTerminalOut($terminal)" - private def catchException(f: => Unit): Unit = { + private def catchException(f: => Unit): Unit = try f - catch { - case _: ClosedChannelException => () - } - } - } - } + catch case _: ClosedChannelException => () private val consoleOutPerTerminal = new ConcurrentHashMap[Terminal, ConsoleOut] - def terminalOut(terminal: Terminal): ConsoleOut = consoleOutPerTerminal.get(terminal) match { + def terminalOut(terminal: Terminal): ConsoleOut = consoleOutPerTerminal.get(terminal) match case null => - val res = new ConsoleOut { + val res = new ConsoleOut: override val lockObject: AnyRef = terminal override def print(s: String): Unit = terminal.printStream.print(s) override def println(s: String): Unit = terminal.printStream.println(s) override def println(): Unit = terminal.printStream.println() override def flush(): Unit = terminal.printStream.flush() override def toString: String = s"TerminalOut($terminal)" - } consoleOutPerTerminal.put(terminal, res) res case c => c - } - def printStreamOut(out: PrintStream): ConsoleOut = new ConsoleOut { + def printStreamOut(out: PrintStream): ConsoleOut = new ConsoleOut: val lockObject: AnyRef = out def print(s: String) = out.print(s) def println(s: String) = out.println(s) def println() = out.println() def flush() = out.flush() override def toString: String = s"PrintStreamConsoleOut($out)" - } - def printWriterOut(out: PrintWriter): ConsoleOut = new ConsoleOut { + def printWriterOut(out: PrintWriter): ConsoleOut = new ConsoleOut: val lockObject: AnyRef = out def print(s: String) = out.print(s) - def println(s: String) = { out.println(s); flush() } - def println() = { out.println(); flush() } - def flush() = { out.flush() } + def println(s: String) = + out.println(s); flush() + def println() = + out.println(); flush() + def flush() = out.flush() override def toString: String = s"PrintWriterConsoleOut($out)" - } - def bufferedWriterOut(out: BufferedWriter): ConsoleOut = new ConsoleOut { + def bufferedWriterOut(out: BufferedWriter): ConsoleOut = new ConsoleOut: val lockObject: AnyRef = out def print(s: String) = out.write(s) - def println(s: String) = { out.write(s); println() } - def println() = { out.newLine(); flush() } - def flush() = { out.flush() } + def println(s: String) = + out.write(s); println() + def println() = + out.newLine(); flush() + def flush() = out.flush() override def toString: String = s"BufferedWriterConsoleOut($out)" - } -} +end ConsoleOut diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/EscHelpers.scala b/internal/util-logging/src/main/scala/sbt/internal/util/EscHelpers.scala index 98ca4cf27..f69bdadc0 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/EscHelpers.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/EscHelpers.scala @@ -12,7 +12,7 @@ import scala.annotation.tailrec import scala.collection.mutable.ArrayBuffer import scala.util.Try -object EscHelpers { +object EscHelpers: /** Escape character, used to introduce an escape sequence. */ final val ESC = '\u001B' @@ -64,43 +64,39 @@ object EscHelpers { * isEscapeTerminator */ def removeEscapeSequences(s: String): String = - if (s.isEmpty || !hasEscapeSequence(s)) s - else { + if s.isEmpty || !hasEscapeSequence(s) then s + else val sb = new java.lang.StringBuilder nextESC(s, 0, sb) sb.toString - } @tailrec - private def nextESC(s: String, start: Int, sb: java.lang.StringBuilder): Unit = { + private def nextESC(s: String, start: Int, sb: java.lang.StringBuilder): Unit = val escIndex = s.indexOf(ESC, start) - if (escIndex < 0) { + if escIndex < 0 then sb.append(s, start, s.length) () - } else { + else sb.append(s, start, escIndex) val next: Int = - if (escIndex + 1 >= s.length) skipESC(s, escIndex + 1) + if escIndex + 1 >= s.length then skipESC(s, escIndex + 1) // If it's a CSI we skip past it and then look for a terminator. - else if (isCSI(s.charAt(escIndex + 1))) skipESC(s, escIndex + 2) - else if (isAnsiTwoCharacterTerminator(s.charAt(escIndex + 1))) escIndex + 2 - else { + else if isCSI(s.charAt(escIndex + 1)) then skipESC(s, escIndex + 2) + else if isAnsiTwoCharacterTerminator(s.charAt(escIndex + 1)) then escIndex + 2 + else // There could be non-ANSI character sequences we should make sure we handle here. skipESC(s, escIndex + 1) - } nextESC(s, next, sb) - } - } private val esc = 1 private val csi = 2 - def cursorPosition(s: String): Int = { + def cursorPosition(s: String): Int = val bytes = s.getBytes var i = 0 var index = 0 var state = 0 val digit = new ArrayBuffer[Byte] - while (i < bytes.length) { - bytes(i) match { + while i < bytes.length do + bytes(i) match case 27 => state = esc case b if (state == esc || state == csi) && b >= 48 && b < 58 => state = csi @@ -112,23 +108,22 @@ object EscHelpers { case b if state == csi => val leftDigit = Try(new String(digit.toArray).toInt).getOrElse(0) state = 0 - b.toChar match { + b.toChar match case 'D' => index = math.max(index - leftDigit, 0) case 'C' => index += leftDigit case 'K' => - case 'J' => if (leftDigit == 2) index = 0 + case 'J' => if leftDigit == 2 then index = 0 case 'm' => case ';' => state = csi case _ => - } digit.clear() case _ => index += 1 - } + end match i += 1 - } + end while index - } + end cursorPosition /** * Strips ansi escape and color codes from an input string. @@ -143,7 +138,7 @@ object EscHelpers { * a string with the escape and color codes removed depending on the input parameter along with * the length of the output string (which may be smaller than the returned array) */ - def strip(bytes: Array[Byte], stripAnsi: Boolean, stripColor: Boolean): (Array[Byte], Int) = { + def strip(bytes: Array[Byte], stripAnsi: Boolean, stripColor: Boolean): (Array[Byte], Int) = val res = new Array[Byte](bytes.length) var index = 0 var state = 0 @@ -151,11 +146,11 @@ object EscHelpers { val digit = new ArrayBuffer[Byte] var escIndex = -1 bytes.foreach { b => - if (index < res.length) res(index) = b + if index < res.length then res(index) = b index += 1 limit = math.max(limit, index) - if (state == 0) escIndex = -1 - b match { + if state == 0 then escIndex = -1 + b match case 27 => escIndex = index - 1 state = esc @@ -169,20 +164,19 @@ object EscHelpers { case b if state == csi => val leftDigit = Try(new String(digit.toArray).toInt).getOrElse(0) state = 0 - b.toChar match { + b.toChar match case 'h' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'J' | 'K' => - if (stripAnsi) index = math.max(escIndex, 0) - case 'm' => if (stripColor) index = escIndex + if stripAnsi then index = math.max(escIndex, 0) + case 'm' => if stripColor then index = escIndex case ';' | 's' | 'u' | '?' => state = csi case b => - } digit.clear() case b if state == esc => state = 0 case b => - } + end match } (res, index) - } + end strip /** * Removes the ansi escape sequences from a string and makes a best attempt at calculating any @@ -197,7 +191,7 @@ object EscHelpers { * a string containing the original characters of the input stream with the ansi escape codes * removed. */ - def stripColorsAndMoves(s: String): String = { + def stripColorsAndMoves(s: String): String = val bytes = s.getBytes val res = new Array[Byte](bytes.length) var index = 0 @@ -216,17 +210,16 @@ object EscHelpers { case b if state == csi => val leftDigit = Try(new String(digit.toArray).toInt).getOrElse(0) state = 0 - b.toChar match { + b.toChar match case 'h' => index = math.max(index - 1, 0) case 'D' => index = math.max(index - leftDigit, 0) case 'C' => index = math.min(limit, math.min(index + leftDigit, res.length - 1)) case 'K' | 'J' => - if (leftDigit > 0) (0 until index).foreach(res(_) = 32) + if leftDigit > 0 then (0 until index).foreach(res(_) = 32) else res(index) = 32 case 'm' => case ';' => state = csi case b => state = csi - } digit.clear() case b if state == esc => state = 0 case b => @@ -235,21 +228,15 @@ object EscHelpers { limit = math.max(limit, index) } new String(res, 0, limit) - } + end stripColorsAndMoves /** * Skips the escape sequence starting at `i-1`. `i` should be positioned at the character after * the ESC that starts the sequence. */ @tailrec - private def skipESC(s: String, i: Int): Int = { - if (i >= s.length) { - i - } else if (isEscapeTerminator(s.charAt(i))) { - i + 1 - } else { - skipESC(s, i + 1) - } - } - -} + private def skipESC(s: String, i: Int): Int = + if i >= s.length then i + else if isEscapeTerminator(s.charAt(i)) then i + 1 + else skipESC(s, i + 1) +end EscHelpers diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/FilterLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/FilterLogger.scala index 289860c41..4ba47b6cb 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/FilterLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/FilterLogger.scala @@ -15,26 +15,17 @@ import sbt.util.* * means that messages are logged at the higher of the two levels set by this logger and its * delegate. */ -class FilterLogger(delegate: AbstractLogger) extends BasicLogger { - def trace(t: => Throwable): Unit = { - if (traceEnabled) - delegate.trace(t) - } +class FilterLogger(delegate: AbstractLogger) extends BasicLogger: + def trace(t: => Throwable): Unit = + if traceEnabled then delegate.trace(t) override def setSuccessEnabled(flag: Boolean): Unit = delegate.setSuccessEnabled(flag) override def successEnabled = delegate.successEnabled override def setTrace(level: Int): Unit = delegate.setTrace(level) override def getTrace = delegate.getTrace - def log(level: Level.Value, message: => String): Unit = { - if (atLevel(level)) - delegate.log(level, message) - } - def success(message: => String): Unit = { - if (successEnabled) - delegate.success(message) - } - def control(event: ControlEvent.Value, message: => String): Unit = { - if (atLevel(Level.Info)) - delegate.control(event, message) - } + def log(level: Level.Value, message: => String): Unit = + if atLevel(level) then delegate.log(level, message) + def success(message: => String): Unit = + if successEnabled then delegate.success(message) + def control(event: ControlEvent.Value, message: => String): Unit = + if atLevel(Level.Info) then delegate.control(event, message) def logAll(events: Seq[LogEvent]): Unit = delegate.logAll(events) -} diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/FullLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/FullLogger.scala index 7178e33a8..319adcb2a 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/FullLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/FullLogger.scala @@ -11,26 +11,18 @@ package sbt.internal.util import sbt.util.* /** Promotes the simple Logger interface to the full AbstractLogger interface. */ -class FullLogger(delegate: Logger) extends BasicLogger { - def trace(t: => Throwable): Unit = { - if (traceEnabled) - delegate.trace(t) - } - def log(level: Level.Value, message: => String): Unit = { - if (atLevel(level)) - delegate.log(level, message) - } +class FullLogger(delegate: Logger) extends BasicLogger: + def trace(t: => Throwable): Unit = + if traceEnabled then delegate.trace(t) + def log(level: Level.Value, message: => String): Unit = + if atLevel(level) then delegate.log(level, message) def success(message: => String): Unit = - if (successEnabled) - delegate.success(message) + if successEnabled then delegate.success(message) def control(event: ControlEvent.Value, message: => String): Unit = info(message) def logAll(events: Seq[LogEvent]): Unit = events.foreach(log) -} -object FullLogger { +object FullLogger: def apply(delegate: Logger): AbstractLogger = - delegate match { + delegate match case d: AbstractLogger => d case _ => new FullLogger(delegate) - } -} diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala b/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala index 8f1cfebf5..691d85655 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/GlobalLogging.scala @@ -41,7 +41,7 @@ final case class GlobalLogging1( * the previous backing file, if there is one. `newBackingFile` creates a new temporary location for * the next backing file. */ -final case class GlobalLogBacking(file: File, last: Option[File], newBackingFile: () => File) { +final case class GlobalLogBacking(file: File, last: Option[File], newBackingFile: () => File): /** Shifts the current backing file to `last` and sets the current backing to `newFile`. */ def shift(newFile: File) = GlobalLogBacking(newFile, Some(file), newBackingFile) @@ -58,14 +58,11 @@ final case class GlobalLogBacking(file: File, last: Option[File], newBackingFile */ def unshift = GlobalLogBacking(last getOrElse file, None, newBackingFile) -} - -object GlobalLogBacking { +object GlobalLogBacking: def apply(newBackingFile: => File): GlobalLogBacking = GlobalLogBacking(newBackingFile, None, () => newBackingFile) -} -object GlobalLogging { +object GlobalLogging: import java.util.concurrent.atomic.AtomicInteger private def generateName: String = "GlobalLogging" + generateId.incrementAndGet @@ -75,10 +72,9 @@ object GlobalLogging { newLogger: (PrintWriter, GlobalLogBacking) => GlobalLogging1, newBackingFile: => File, console: ConsoleOut - ): GlobalLogging1 = { + ): GlobalLogging1 = val log = ConsoleLogger(console) GlobalLogging1(log, console, log, GlobalLogBacking(newBackingFile), newLogger) - } def initial( newAppender: (ManagedLogger, PrintWriter, GlobalLogBacking, LoggerContext) => GlobalLogging, @@ -92,11 +88,10 @@ object GlobalLogging { newBackingFile: => File, console: ConsoleOut, initialLevel: Level.Value = Level.Info - ): GlobalLogging = { + ): GlobalLogging = val loggerName = generateName val log = LoggerContext.globalContext.logger(loggerName, None, None) val appender = ConsoleAppender(ConsoleAppender.generateName(), console) LoggerContext.globalContext.addAppender(loggerName, appender -> initialLevel) GlobalLogging(log, console, appender, GlobalLogBacking(newBackingFile), newAppender) - } -} +end GlobalLogging diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala b/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala index e9c9229cd..2a7a4e0f9 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/JLine3.scala @@ -24,159 +24,135 @@ import sbt.internal.util.Terminal.hasConsole import scala.jdk.CollectionConverters.* import java.util.concurrent.LinkedBlockingQueue -private[sbt] object JLine3 { +private[sbt] object JLine3: private[util] val initialAttributes = new AtomicReference[Attributes] - private[util] def system: org.jline.terminal.Terminal = { + private[util] def system: org.jline.terminal.Terminal = val term = org.jline.terminal.TerminalBuilder .builder() .system(hasConsole) .paused(true) .build() - initialAttributes.get match { + initialAttributes.get match case null => initialAttributes.set(term.getAttributes) case _ => - } term - } - private[sbt] def apply(term: Terminal): JTerminal = { - if (System.getProperty("jline.terminal", "") == "none") + private[sbt] def apply(term: Terminal): JTerminal = + if System.getProperty("jline.terminal", "") == "none" then new DumbTerminal(term.inputStream, term.outputStream) else wrapTerminal(term) - } - private[util] def decodeInput(decoder: CharsetDecoder, inputStream: InputStream): Int = { + private[util] def decodeInput(decoder: CharsetDecoder, inputStream: InputStream): Int = val bytes = new Array[Byte](4) var i = 0 var res = -2 - while (i < 4 && res == -2) { - inputStream.read() match { + while i < 4 && res == -2 do + inputStream.read() match case -1 => res = -1 case byte => bytes(i) = byte.toByte i += 1 val bb = ByteBuffer.wrap(bytes, 0, i) - try { + try val cb = decoder.decode(bb) val it = cb.codePoints().iterator - if (it.hasNext) res = it.next - } catch { case _: CharacterCodingException => } - } - } + if it.hasNext then res = it.next + catch + case _: CharacterCodingException => res - } - private def wrapTerminal(term: Terminal): JTerminal = { + private def wrapTerminal(term: Terminal): JTerminal = new AbstractTerminal( term.name, "nocapabilities", Charset.forName("UTF-8"), SignalHandler.SIG_DFL - ) { + ): val closed = new AtomicBoolean(false) setOnClose { () => doClose() reader.close() - if (closed.compareAndSet(false, true)) { + if closed.compareAndSet(false, true) then // This is necessary to shutdown the non blocking input reader // so that it doesn't keep blocking - term.inputStream match { + term.inputStream match case w: Terminal.WriteableInputStream => w.cancel() case _ => - } - } } // returns 'null' if the terminal was created with no provider override def getProvider(): TerminalProvider = null // returns 'null' if the terminal is not bound to a system stream. override def getSystemStream(): SystemStream = null - override val input: InputStream = new InputStream { - override def read: Int = { - val res = term.inputStream match { + override val input: InputStream = new InputStream: + override def read: Int = + val res = term.inputStream match case w: Terminal.WriteableInputStream => val result = new LinkedBlockingQueue[Integer] - try { + try w.read(result) - result.poll match { + result.poll match case null => throw new ClosedException case i => i.toInt - } - } catch { + catch case _: InterruptedException => w.cancel() throw new ClosedException - } case _ => throw new ClosedException - } - res match { + res match case 3 /* ctrl+c */ => throw new ClosedException case r => r - } - } - } - override val output: OutputStream = new OutputStream { + override val output: OutputStream = new OutputStream: override def write(b: Int): Unit = write(Array[Byte](b.toByte)) - override def write(b: Array[Byte]): Unit = if (!closed.get) term.withPrintStream { ps => - ps.write(b) - term.prompt match { - case a: Prompt.AskUser => a.write(b) - case _ => + override def write(b: Array[Byte]): Unit = if !closed.get then + term.withPrintStream { ps => + ps.write(b) + term.prompt match + case a: Prompt.AskUser => a.write(b) + case _ => } - } override def write(b: Array[Byte], offset: Int, len: Int) = write(Arrays.copyOfRange(b, offset, offset + len)) override def flush(): Unit = term.withPrintStream(_.flush()) - } - override val reader = new NonBlockingReader { + override val reader = new NonBlockingReader: val buffer = new LinkedBlockingQueue[Integer] val thread = new AtomicReference[Thread] private def fillBuffer(): Unit = thread.synchronized { thread.set(Thread.currentThread) try buffer.put(decodeInput(encoding.newDecoder, term.inputStream)) - catch { case _: InterruptedException => buffer.put(-3) } + catch case _: InterruptedException => buffer.put(-3) } - override def close(): Unit = thread.get match { + override def close(): Unit = thread.get match case null => case t => t.interrupt() - } - override def read(timeout: Long, peek: Boolean) = { - if (buffer.isEmpty && !peek) fillBuffer() - (if (peek) buffer.peek else buffer.take) match { + override def read(timeout: Long, peek: Boolean) = + if buffer.isEmpty && !peek then fillBuffer() + (if peek then buffer.peek else buffer.take) match case null => -2 - case i => if (i == -3) throw new InterruptedException else i - } - } - override def peek(timeout: Long): Int = buffer.peek() match { + case i => if i == -3 then throw new InterruptedException else i + override def peek(timeout: Long): Int = buffer.peek() match case null => -1 case i => i.toInt - } - override def readBuffered(buf: Array[Char], off: Int, len: Int, timeout: Long): Int = { - if (buffer.isEmpty) fillBuffer() - buffer.take match { + override def readBuffered(buf: Array[Char], off: Int, len: Int, timeout: Long): Int = + if buffer.isEmpty then fillBuffer() + buffer.take match case i if i == -1 => -1 case i => buf(0) = i.toChar 1 - } - } - } override val writer: PrintWriter = new PrintWriter(output, true) /* * For now assume that the terminal capabilities for client and server * are the same. */ - override def getStringCapability(cap: Capability): String = { - term.getStringCapability(cap.toString) match { + override def getStringCapability(cap: Capability): String = + term.getStringCapability(cap.toString) match case null if cap == Capability.key_dc && Util.isWindows => "\\E[3~" case null if cap == Capability.key_end && Util.isWindows => "\\E[4~" case null if cap == Capability.key_ic && Util.isWindows => "\\E[2~" case c => c - } - } - override def getNumericCapability(cap: Capability): Integer = { - if (cap == Capability.max_colors && !term.isColorEnabled) 1 + override def getNumericCapability(cap: Capability): Integer = + if cap == Capability.max_colors && !term.isColorEnabled then 1 else term.getNumericCapability(cap.toString) - } override def getBooleanCapability(cap: Capability): Boolean = term.getBooleanCapability(cap.toString) def getAttributes(): Attributes = attributesFromMap(term.getAttributes) @@ -186,19 +162,15 @@ private[sbt] object JLine3 { ): Unit = {} // don't allow the jline line reader to change attributes def setSize(size: Size): Unit = term.setSize(size.getColumns, size.getRows) - override def enterRawMode(): Attributes = { + override def enterRawMode(): Attributes = // don't actually modify the term, that is handled by LineReader attributesFromMap(term.getAttributes) - } - } - } // We need to set the ENABLE_PROCESS_INPUT flag for ctrl+c to be treated as a signal in windows // https://docs.microsoft.com/en-us/windows/console/setconsolemode private val ENABLE_PROCESS_INPUT = 1 - private[util] def setEnableProcessInput(): Unit = if (Util.isWindows) { + private[util] def setEnableProcessInput(): Unit = if Util.isWindows then WindowsSupport.setConsoleMode(WindowsSupport.getConsoleMode | ENABLE_PROCESS_INPUT) - } - private[util] def enterRawMode(term: JTerminal): Unit = { + private[util] def enterRawMode(term: JTerminal): Unit = val prevAttr = initialAttributes.get val newAttr = new Attributes(prevAttr) // These flags are copied from the jline3 enterRawMode but the jline implementation @@ -207,15 +179,13 @@ private[sbt] object JLine3 { newAttr.setInputFlags(EnumSet.of(InputFlag.IXON, InputFlag.ICRNL, InputFlag.INLCR), false) term.setAttributes(newAttr) setEnableProcessInput() - } - private[util] def exitRawMode(term: JTerminal): Unit = { + private[util] def exitRawMode(term: JTerminal): Unit = val initAttr = initialAttributes.get val newAttr = new Attributes(initAttr) newAttr.setLocalFlags(EnumSet.of(LocalFlag.ICANON, LocalFlag.ECHO), true) term.setAttributes(newAttr) setEnableProcessInput() - } - private[util] def toMap(jattributes: Attributes): Map[String, String] = { + private[util] def toMap(jattributes: Attributes): Map[String, String] = val result = new java.util.LinkedHashMap[String, String] result.put( "iflag", @@ -242,7 +212,7 @@ private[sbt] object JLine3 { .mkString(" ") ) result.asScala.toMap - } + end toMap private val iflagMap: Map[String, InputFlag] = InputFlag.values.map(f => f.name.toLowerCase -> f).toMap private val oflagMap: Map[String, Attributes.OutputFlag] = @@ -253,14 +223,13 @@ private[sbt] object JLine3 { LocalFlag.values.map(f => f.name.toLowerCase -> f).toMap private val charMap: Map[String, Attributes.ControlChar] = Attributes.ControlChar.values().map(f => f.name.toLowerCase -> f).toMap - private[sbt] def setMode(term: Terminal, canonical: Boolean, echo: Boolean): Unit = { + private[sbt] def setMode(term: Terminal, canonical: Boolean, echo: Boolean): Unit = val prev = attributesFromMap(term.getAttributes) val newAttrs = new Attributes(prev) newAttrs.setLocalFlag(LocalFlag.ICANON, canonical) newAttrs.setLocalFlag(LocalFlag.ECHO, echo) term.setAttributes(toMap(newAttrs)) - } - private[util] def attributesFromMap(map: Map[String, String]): Attributes = { + private[util] def attributesFromMap(map: Map[String, String]): Attributes = val attributes = new Attributes map.get("iflag").foreach { flags => flags.split(" ").foreach(f => iflagMap.get(f).foreach(attributes.setInputFlag(_, true))) @@ -276,16 +245,14 @@ private[sbt] object JLine3 { } map.get("cchars").foreach { chars => chars.split(" ").foreach { keyValue => - keyValue.split(",") match { + keyValue.split(",") match case Array(k, v) => v.toIntOption.foreach(i => charMap.get(k).foreach(c => attributes.setControlChar(c, i))) case _ => - } } } attributes - } - private[sbt] def isEchoEnabled(map: Map[String, String]): Boolean = { + end attributesFromMap + private[sbt] def isEchoEnabled(map: Map[String, String]): Boolean = attributesFromMap(map).getLocalFlag(LocalFlag.ECHO) - } -} +end JLine3 diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/LoggerWriter.scala b/internal/util-logging/src/main/scala/sbt/internal/util/LoggerWriter.scala index 85e304da9..58205d7c5 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/LoggerWriter.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/LoggerWriter.scala @@ -20,7 +20,7 @@ class LoggerWriter( delegate: Logger, unbufferedLevel: Option[Level.Value], nl: String = System.getProperty("line.separator") -) extends java.io.Writer { +) extends java.io.Writer: def this(delegate: Logger, level: Level.Value) = this(delegate, Some(level)) def this(delegate: Logger) = this(delegate, None) @@ -31,16 +31,14 @@ class LoggerWriter( override def flush(): Unit = synchronized { - if (buffer.nonEmpty) { + if buffer.nonEmpty then log(buffer.toString) buffer.clear() - } } def flushLines(level: Level.Value): Unit = synchronized { - for (line <- lines) - delegate.log(level, line) + for line <- lines do delegate.log(level, line) lines.clear() } @@ -51,18 +49,15 @@ class LoggerWriter( } @tailrec - private def process(): Unit = { + private def process(): Unit = val i = buffer.indexOf(nl) - if (i >= 0) { + if i >= 0 then log(buffer.substring(0, i)) buffer.delete(0, i + nl.length) process() - } - } - private def log(s: String): Unit = unbufferedLevel match { + private def log(s: String): Unit = unbufferedLevel match case None => lines += s; () case Some(level) => delegate.log(level, s) - } -} +end LoggerWriter diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/MainAppender.scala b/internal/util-logging/src/main/scala/sbt/internal/util/MainAppender.scala index eec04d5d4..5bf9d1d8f 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/MainAppender.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/MainAppender.scala @@ -11,7 +11,7 @@ package sbt.internal.util import sbt.util.* import java.io.PrintWriter -object MainAppender { +object MainAppender: import java.util.concurrent.atomic.AtomicInteger private def generateGlobalBackingName: String = "GlobalBacking" + generateId.incrementAndGet @@ -21,36 +21,32 @@ object MainAppender { log: ManagedLogger, config: MainAppenderConfig, context: LoggerContext - ): ManagedLogger = { + ): ManagedLogger = import config.* // TODO // backed setTrace backingTrace // multi: Logger context.clearAppenders(log.name) - consoleOpt match { + consoleOpt match case Some(a: ConsoleAppender) => a.setTrace(screenTrace) context.addAppender(log.name, a -> screenLevel) case _ => - } context.addAppender(log.name, backed -> backingLevel) extra.foreach(a => context.addAppender(log.name, a -> Level.Info)) log - } def globalDefault( console: ConsoleOut - ): (ManagedLogger, PrintWriter, GlobalLogBacking, LoggerContext) => GlobalLogging = { + ): (ManagedLogger, PrintWriter, GlobalLogBacking, LoggerContext) => GlobalLogging = lazy val newAppender : (ManagedLogger, PrintWriter, GlobalLogBacking, LoggerContext) => GlobalLogging = - (log, writer, backing, lc) => { + (log, writer, backing, lc) => val backed: Appender = defaultBacked(generateGlobalBackingName)(writer) val full = multiLogger(log, defaultMultiConfig(Option(console), backed, Nil), lc) GlobalLogging(full, console, backed, backing, newAppender) - } newAppender - } def defaultMultiConfig( consoleOpt: Option[ConsoleOut], @@ -73,13 +69,12 @@ object MainAppender { def defaultScreen( console: ConsoleOut, suppressedMessage: SuppressedTraceContext => Option[String] - ): Appender = { + ): Appender = ConsoleAppender( ConsoleAppender.generateName(), console, suppressedMessage = suppressedMessage ) - } def defaultScreen( name: String, @@ -98,13 +93,12 @@ object MainAppender { defaultBacked(generateGlobalBackingName, useFormat) def defaultBacked(loggerName: String, useFormat: Boolean): PrintWriter => Appender = - to => { + to => ConsoleAppender( ConsoleAppender.generateName(), ConsoleOut.printWriterOut(to), useFormat = useFormat ) - } final case class MainAppenderConfig( consoleOpt: Option[Appender], @@ -115,4 +109,4 @@ object MainAppender { screenTrace: Int, backingTrace: Int ) -} +end MainAppender diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala index 6788969a2..fde04d272 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ManagedLogger.scala @@ -13,10 +13,9 @@ import sbt.util.* import sjsonnew.JsonFormat import sbt.internal.util.appmacro.StringTypeTag -private[sbt] trait MiniLogger { +private[sbt] trait MiniLogger: def log[T](level: Level.Value, message: ObjectEvent[T]): Unit def log(level: Level.Value, message: => String): Unit -} /** * Delegates log events to the associated LogExchange. @@ -28,7 +27,7 @@ class ManagedLogger( xlogger: MiniLogger, terminal: Option[Terminal], private[sbt] val context: LoggerContext, -) extends Logger { +) extends Logger: def this( name: String, channelName: Option[String], @@ -42,15 +41,11 @@ class ManagedLogger( xlogger.log(level, message) // send special event for success since it's not a real log level - override def success(message: => String): Unit = { - if (terminal.fold(true)(_.isSuccessEnabled)) { - infoEvent[SuccessEvent](SuccessEvent(message)) - } - } + override def success(message: => String): Unit = + if terminal.fold(true)(_.isSuccessEnabled) then infoEvent[SuccessEvent](SuccessEvent(message)) - def registerStringCodec[A: ShowLines: StringTypeTag]: Unit = { + def registerStringCodec[A: ShowLines: StringTypeTag]: Unit = LogExchange.registerStringCodec[A] - } final def debugEvent[A: JsonFormat: StringTypeTag](event: => A): Unit = logEvent(Level.Debug, event) @@ -60,10 +55,9 @@ class ManagedLogger( logEvent(Level.Error, event) def logEvent[A: JsonFormat](level: Level.Value, event: => A)(using tag: StringTypeTag[A] - ): Unit = { + ): Unit = val v: A = event // println("logEvent " + tag.key) val entry: ObjectEvent[A] = ObjectEvent(level, v, channelName, execId, tag.key) xlogger.log(level, entry) - } -} +end ManagedLogger diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/MultiLogger.scala b/internal/util-logging/src/main/scala/sbt/internal/util/MultiLogger.scala index 78aa73da6..6b245700f 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/MultiLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/MultiLogger.scala @@ -12,22 +12,19 @@ import sbt.util.* // note that setting the logging level on this logger has no effect on its behavior, only // on the behavior of the delegates. -class MultiLogger(delegates: List[AbstractLogger]) extends BasicLogger { +class MultiLogger(delegates: List[AbstractLogger]) extends BasicLogger: - override def setLevel(newLevel: Level.Value): Unit = { + override def setLevel(newLevel: Level.Value): Unit = super.setLevel(newLevel) dispatch(new SetLevel(newLevel)) - } - override def setTrace(level: Int): Unit = { + override def setTrace(level: Int): Unit = super.setTrace(level) dispatch(new SetTrace(level)) - } - override def setSuccessEnabled(flag: Boolean): Unit = { + override def setSuccessEnabled(flag: Boolean): Unit = super.setSuccessEnabled(flag) dispatch(new SetSuccess(flag)) - } def trace(t: => Throwable): Unit = dispatch(new Trace(t)) def log(level: Level.Value, message: => String): Unit = dispatch(new Log(level, message)) @@ -37,9 +34,6 @@ class MultiLogger(delegates: List[AbstractLogger]) extends BasicLogger { def control(event: ControlEvent.Value, message: => String): Unit = delegates.foreach(_.control(event, message)) - private def dispatch(event: LogEvent): Unit = { - for (d <- delegates) { - d.log(event) - } - } -} + private def dispatch(event: LogEvent): Unit = + for d <- delegates do d.log(event) +end MultiLogger diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ObjectEvent.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ObjectEvent.scala index dcebd5eb6..65d6ba23c 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ObjectEvent.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ObjectEvent.scala @@ -22,12 +22,11 @@ final class ObjectEvent[A]( val execId: Option[String], val contentType: String, val json: JValue -) extends Serializable { +) extends Serializable: override def toString: String = s"ObjectEvent($level, $message, $channelName, $execId, $contentType, $json)" -} -object ObjectEvent { +object ObjectEvent: def apply[A: JsonFormat]( level: Level.Value, message: A, @@ -43,4 +42,3 @@ object ObjectEvent { contentType, Converter.toJsonUnsafe(message) ) -} diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala b/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala index 45253491e..9c101c920 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/ProgressState.scala @@ -29,7 +29,7 @@ private[sbt] final class ProgressState( val blankZone: Int, val currentLineBytes: AtomicReference[ArrayBuffer[Byte]], val maxItems: Int, -) { +): def this(blankZone: Int, maxItems: Int) = this( new AtomicReference(Nil), new AtomicInteger(0), @@ -42,50 +42,43 @@ private[sbt] final class ProgressState( new String(currentLineBytes.get.toArray, "UTF-8").linesIterator.toSeq.lastOption .map(EscHelpers.stripColorsAndMoves) .filter(_.nonEmpty) - def reset(): Unit = { + def reset(): Unit = progressLines.set(Nil) padding.set(0) currentLineBytes.set(new ArrayBuffer[Byte]) - } private val lineBuffer = new ArrayBlockingQueue[String](300) private[util] def getLines: Seq[String] = lineBuffer.asScala.toVector - private def appendLine(line: String) = while (!lineBuffer.offer(line)) { lineBuffer.poll } - private[util] def clearBytes(): Unit = { + private def appendLine(line: String) = while !lineBuffer.offer(line) do lineBuffer.poll + private[util] def clearBytes(): Unit = val pad = padding.get - if (currentLineBytes.get.isEmpty && pad > 0) padding.decrementAndGet() + if currentLineBytes.get.isEmpty && pad > 0 then padding.decrementAndGet() currentLineBytes.set(new ArrayBuffer[Byte]) - } private val lineSeparatorBytes: Array[Byte] = System.lineSeparator.getBytes("UTF-8") - private[util] def addBytes(terminal: Terminal, bytes: Seq[Byte]): Unit = { + private[util] def addBytes(terminal: Terminal, bytes: Seq[Byte]): Unit = val previous: ArrayBuffer[Byte] = currentLineBytes.get val padding = this.padding.get - val prevLineCount = if (padding > 0) terminal.lineCount(new String(previous.toArray)) else 0 + val prevLineCount = if padding > 0 then terminal.lineCount(new String(previous.toArray)) else 0 previous ++= bytes - if (padding > 0) { + if padding > 0 then val newLineCount = terminal.lineCount(new String(previous.toArray)) val diff = newLineCount - prevLineCount this.padding.set(math.max(padding - diff, 0)) - } val lines = new String(previous.toArray, "UTF-8") - if (lines.contains(System.lineSeparator)) { + if lines.contains(System.lineSeparator) then currentLineBytes.set(new ArrayBuffer[Byte]) - if (!lines.endsWith(System.lineSeparator)) { + if !lines.endsWith(System.lineSeparator) then val allLines = lines.split(System.lineSeparator) allLines.dropRight(1).foreach(appendLine) allLines.lastOption.foreach(currentLineBytes.get ++= _.getBytes("UTF-8")) - } else if (lines.contains(System.lineSeparator)) { + else if lines.contains(System.lineSeparator) then lines.split(System.lineSeparator).foreach(appendLine) - } - } - } - private[util] def getPrompt(terminal: Terminal): Array[Byte] = { - if (terminal.prompt.isInstanceOf[Prompt.AskUser]) { - val prefix = if (terminal.isAnsiSupported) s"$DeleteLine$CursorLeft1000" else "" + private[util] def getPrompt(terminal: Terminal): Array[Byte] = + if terminal.prompt.isInstanceOf[Prompt.AskUser] then + val prefix = if terminal.isAnsiSupported then s"$DeleteLine$CursorLeft1000" else "" prefix.getBytes ++ terminal.prompt.render().getBytes("UTF-8") - } else Array.empty - } + else Array.empty private val cleanPrompt = (DeleteLine + ClearScreenAfterCursor + CursorLeft1000).getBytes("UTF-8") private val clearScreenBytes = ClearScreenAfterCursor.getBytes("UTF-8") @@ -94,67 +87,57 @@ private[sbt] final class ProgressState( bytes: Array[Byte], printStream: PrintStream, hasProgress: Boolean - ): Unit = { - if (hasProgress) { + ): Unit = + if hasProgress then val canClearPrompt = currentLineBytes.get.isEmpty addBytes(terminal, bytes.toSeq) val toWrite = new ArrayBuffer[Byte] - terminal.prompt match { + terminal.prompt match case a: Prompt.AskUser if a.render().nonEmpty && canClearPrompt => toWrite ++= cleanPrompt case _ => - } val endsWithNewLine = bytes.endsWith(lineSeparatorBytes) - if (endsWithNewLine || bytes.containsSlice(lineSeparatorBytes)) { + if endsWithNewLine || bytes.containsSlice(lineSeparatorBytes) then val parts = new String(bytes, "UTF-8").split(System.lineSeparator) - def appendLine(l: String, appendNewline: Boolean): Unit = { + def appendLine(l: String, appendNewline: Boolean): Unit = toWrite ++= l.getBytes("UTF-8") - if (!l.getBytes("UTF-8").endsWith("\r".getBytes)) toWrite ++= clearScreenBytes - if (appendNewline) toWrite ++= lineSeparatorBytes - } + if !l.getBytes("UTF-8").endsWith("\r".getBytes) then toWrite ++= clearScreenBytes + if appendNewline then toWrite ++= lineSeparatorBytes parts.dropRight(1).foreach(appendLine(_, true)) - parts.lastOption match { + parts.lastOption match case Some(l) => appendLine(l, bytes.endsWith(lineSeparatorBytes)) case None => toWrite ++= lineSeparatorBytes - } - } else toWrite ++= bytes + else toWrite ++= bytes toWrite ++= clearScreenBytes - if (endsWithNewLine) { - if (progressLines.get.nonEmpty) { - val lastLine = terminal.prompt match { + if endsWithNewLine then + if progressLines.get.nonEmpty then + val lastLine = terminal.prompt match case a: Prompt.AskUser => a.render() case _ => currentLine.getOrElse("") - } val lines = printProgress(terminal, lastLine) toWrite ++= lines.getBytes("UTF-8") - } toWrite ++= getPrompt(terminal) - } printStream.write(toWrite.toArray) printStream.flush() - } else printStream.write(bytes) - } + else printStream.write(bytes) - private[util] def printProgress(terminal: Terminal, lastLine: String): String = { + private[util] def printProgress(terminal: Terminal, lastLine: String): String = val previousLines = progressLines.get - if (previousLines.nonEmpty) { + if previousLines.nonEmpty then val currentLength = previousLines.foldLeft(0)(_ + terminal.lineCount(_)) val (height, width) = terminal.getLineHeightAndWidth(lastLine) val offset = width > 0 val pad = math.max(padding.get - height, 0) - val start = (if (offset) s"\n$CursorLeft1000" else "") + val start = (if offset then s"\n$CursorLeft1000" else "") val totalSize = currentLength + blankZone + pad val blank = CursorLeft1000 + s"\n$DeleteLine" * (totalSize - currentLength) val lines = previousLines.mkString(DeleteLine, s"\n$DeleteLine", s"\n$DeleteLine") - val resetCursorUp = cursorUp(totalSize + (if (offset) 1 else 0)) + val resetCursorUp = cursorUp(totalSize + (if offset then 1 else 0)) val resetCursor = resetCursorUp + CursorLeft1000 + lastLine start + blank + lines + resetCursor - } else { - ClearScreenAfterCursor - } - } -} + else ClearScreenAfterCursor +end ProgressState -private[sbt] object ProgressState { +private[sbt] object ProgressState: private val SERVER_IS_RUNNING = "sbt server is running " // the + 2 is for the quotation marks private val SERVER_IS_RUNNING_LENGTH = SERVER_IS_RUNNING.length + 3 @@ -168,36 +151,35 @@ private[sbt] object ProgressState { private[sbt] def updateProgressState( pe: ProgressEvent, terminal: Terminal - ): Unit = { + ): Unit = val state = terminal.progressState val isAskUser = terminal.prompt.isInstanceOf[Prompt.AskUser] val isRunning = terminal.prompt == Prompt.Running val isBatch = terminal.prompt == Prompt.Batch val isWatch = terminal.prompt == Prompt.Watch - if (terminal.isSupershellEnabled) { + if terminal.isSupershellEnabled then setShowProgress(true) // used by Zinc to not show "done compiling" - if (!pe.skipIfActive.getOrElse(false) || (!isRunning && !isBatch)) { + if !pe.skipIfActive.getOrElse(false) || (!isRunning && !isBatch) then terminal.withPrintStream { ps => val commandFromThisTerminal = pe.channelName.fold(true)(_ == terminal.name) - val info = if (commandFromThisTerminal) { + val info = if commandFromThisTerminal then val base = pe.items.map { item => val elapsed = item.elapsedMicros / 1000000L s" | => ${item.name} ${elapsed}s" } val limit = state.maxItems - if (base.size > limit + 1) + if base.size > limit + 1 then s" | ... (${base.size - limit} other tasks)" +: base.takeRight(limit) else base - } else { + else pe.command.toSeq.flatMap { cmd => val width = terminal.getWidth - val sanitized = if ((cmd.length + SERVER_IS_RUNNING_LENGTH) > width) { + val sanitized = if (cmd.length + SERVER_IS_RUNNING_LENGTH) > width then cmd.take(width - 3 - SERVER_IS_RUNNING_LENGTH) + "..." - } else cmd - val tail = if (isWatch) Nil else "enter 'cancel' to stop evaluation" :: Nil + else cmd + val tail = if isWatch then Nil else "enter 'cancel' to stop evaluation" :: Nil s"$SERVER_IS_RUNNING '$sanitized'" :: tail } - } val currentLength = info.foldLeft(0)(_ + terminal.lineCount(_)) val previousLines = state.progressLines.getAndSet(info) @@ -205,21 +187,21 @@ private[sbt] object ProgressState { val prevSize = prevLength + state.padding.get val lastLine = - if (isAskUser) terminal.prompt.render() else terminal.getLastLine.getOrElse("") + if isAskUser then terminal.prompt.render() else terminal.getLastLine.getOrElse("") state.padding.set(math.max(0, prevSize - currentLength)) val toWrite = state.getPrompt(terminal) ++ state.printProgress(terminal, lastLine).getBytes("UTF-8") ps.write(toWrite) ps.flush() } - } else if (state.progressLines.get.nonEmpty) { + else if state.progressLines.get.nonEmpty then state.progressLines.set(Nil) terminal.withPrintStream { ps => val lastLine = terminal.getLastLine.getOrElse("") ps.print(lastLine + ClearScreenAfterCursor) ps.flush() } - } - } - } -} + end if + end if + end updateProgressState +end ProgressState diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/Prompt.scala b/internal/util-logging/src/main/scala/sbt/internal/util/Prompt.scala index ccd45801d..f2be5324a 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/Prompt.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/Prompt.scala @@ -11,30 +11,25 @@ package sbt.internal.util import java.util.concurrent.LinkedBlockingQueue import scala.jdk.CollectionConverters.* -private[sbt] sealed trait Prompt { +private[sbt] sealed trait Prompt: def mkPrompt: () => String def render(): String def reset(): Unit -} -private[sbt] object Prompt { - private[sbt] case class AskUser(override val mkPrompt: () => String) extends Prompt { +private[sbt] object Prompt: + private[sbt] case class AskUser(override val mkPrompt: () => String) extends Prompt: private val bytes = new LinkedBlockingQueue[Byte] def write(b: Array[Byte]): Unit = b.foreach(bytes.put) - override def render(): String = { + override def render(): String = val res = new String(bytes.asScala.toArray, "UTF-8") - if (res.endsWith(System.lineSeparator)) "" else res - } + if res.endsWith(System.lineSeparator) then "" else res override def reset(): Unit = bytes.clear() - } - private[sbt] trait NoPrompt extends Prompt { + private[sbt] trait NoPrompt extends Prompt: override val mkPrompt: () => String = () => "" override def render(): String = "" override def reset(): Unit = {} - } private[sbt] case object Running extends NoPrompt private[sbt] case object Batch extends NoPrompt private[sbt] case object Watch extends NoPrompt private[sbt] case object Pending extends NoPrompt private[sbt] case object NoPrompt extends NoPrompt -} diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/StackTrace.scala b/internal/util-logging/src/main/scala/sbt/internal/util/StackTrace.scala index 5145718c2..eabe197db 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/StackTrace.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/StackTrace.scala @@ -12,7 +12,7 @@ import sbt.io.IO import scala.collection.mutable.ListBuffer import java.util.{ IdentityHashMap, Collections } -object StackTrace { +object StackTrace: def isSbtClass(name: String) = name.startsWith("sbt.") || name.startsWith("xsbt.") /** @@ -28,51 +28,47 @@ object StackTrace { * * See also ConsoleAppender where d <= 2 is treated specially by printing a prepared statement. */ - def trimmedLines(t: Throwable, d: Int): List[String] = { + def trimmedLines(t: Throwable, d: Int): List[String] = require(d >= 0) val b = new ListBuffer[String]() val seen: java.util.Set[Throwable] = Collections.newSetFromMap(new IdentityHashMap[Throwable, java.lang.Boolean]()) - def appendStackTrace(t: Throwable, first: Boolean): Unit = { + def appendStackTrace(t: Throwable, first: Boolean): Unit = val include: StackTraceElement => Boolean = - if (d == 0) element => !isSbtClass(element.getClassName) - else { + if d == 0 then element => !isSbtClass(element.getClassName) + else var count = d - 1 - (_ => { count -= 1; count >= 0 }) - } + (_ => + count -= 1; count >= 0 + ) - def appendElement(e: StackTraceElement): Unit = { + def appendElement(e: StackTraceElement): Unit = b.append("\tat " + e) () - } - if (!first) b.append("Caused by: " + t.toString) + if !first then b.append("Caused by: " + t.toString) else b.append(t.toString) val els = t.getStackTrace() var i = 0 - while ((i < els.length) && include(els(i))) { + while (i < els.length) && include(els(i)) do appendElement(els(i)) i += 1 - } - - } + end appendStackTrace appendStackTrace(t, true) seen.add(t) var c = t - while (c.getCause() != null && !seen.contains(c.getCause())) { + while c.getCause() != null && !seen.contains(c.getCause()) do c = c.getCause() seen.add(c) appendStackTrace(c, false) - } - if (c.getCause() != null && seen.contains(c.getCause())) { + if c.getCause() != null && seen.contains(c.getCause()) then b.append("[CIRCULAR REFERENCE: " + c.getCause().toString + "]") - } b.toList - } + end trimmedLines /** * Return a printable representation of the stack trace associated with t. Information about t and @@ -87,4 +83,4 @@ object StackTrace { */ def trimmed(t: Throwable, d: Int): String = trimmedLines(t, d).mkString(IO.Newline) -} +end StackTrace diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala index bcbb63698..7eb77bd6c 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/Terminal.scala @@ -20,7 +20,7 @@ import scala.concurrent.duration.* import scala.util.Try import scala.util.control.NonFatal -trait Terminal extends AutoCloseable { +trait Terminal extends AutoCloseable: /** * Gets the current width of the terminal. The implementation reads a property from the jline @@ -154,12 +154,12 @@ trait Terminal extends AutoCloseable { private[sbt] def setSize(width: Int, height: Int): Unit private[sbt] def name: String - private[sbt] final def withRawInput[T](f: => T): T = { + private[sbt] final def withRawInput[T](f: => T): T = enterRawMode() - try f - catch { case e: InterruptedIOException => throw new InterruptedException } + try + try f + catch case e: InterruptedIOException => throw new InterruptedException finally exitRawMode() - } private[sbt] def enterRawMode(): Unit private[sbt] def exitRawMode(): Unit private[sbt] def write(bytes: Int*): Unit @@ -171,7 +171,7 @@ trait Terminal extends AutoCloseable { private val promptHolder: AtomicReference[Prompt] = new AtomicReference(Prompt.Batch) private[sbt] final def prompt: Prompt = promptHolder.get private[sbt] final def setPrompt(newPrompt: Prompt): Unit = - if (prompt != Prompt.NoPrompt) promptHolder.set(newPrompt) + if prompt != Prompt.NoPrompt then promptHolder.set(newPrompt) /** * Returns the number of lines that the input string will cover given the current width of the @@ -182,16 +182,14 @@ trait Terminal extends AutoCloseable { * @return * the number of lines that the line will cover on the terminal */ - private[sbt] def lineCount(line: String): Int = { + private[sbt] def lineCount(line: String): Int = val lines = EscHelpers.stripColorsAndMoves(line).split('\n') val width = getWidth - def count(l: String): Int = { + def count(l: String): Int = val len = l.length - if (width > 0 && len > 0) (len - 1 + width) / width else 0 - } - if (lines.nonEmpty) lines.tail.foldLeft(lines.headOption.fold(0)(count))(_ + count(_)) + if width > 0 && len > 0 then (len - 1 + width) / width else 0 + if lines.nonEmpty then lines.tail.foldLeft(lines.headOption.fold(0)(count))(_ + count(_)) else 0 - } private[sbt] def flush(): Unit = printStream.flush() private[sbt] def readArrow: Int = withRawInput { @@ -199,28 +197,25 @@ trait Terminal extends AutoCloseable { val ESC = '\u001B' val EOT = '\u0004' def readBracket: Int = - in.read() match { + in.read() match case '[' => readAnsiControl case _ => 0 - } def readAnsiControl: Int = - in.read() match { + in.read() match case 'A' => Terminal.VK_UP case 'B' => Terminal.VK_DOWN case 'C' => Terminal.VK_RIGHT case 'D' => Terminal.VK_LEFT case _ => 0 - } - in.read() match { + in.read() match case ESC => readBracket // Ctrl+D to quit case EOT => -1 case c => c - } } -} +end Terminal -object Terminal { +object Terminal: private[sbt] final val VK_UP = 256 private[sbt] final val VK_DOWN = 257 private[sbt] final val VK_RIGHT = 258 @@ -228,26 +223,25 @@ object Terminal { val NO_BOOT_CLIENTS_CONNECTED: Int = -2 // Disable noisy jline log spam - if (System.getProperty("sbt.jline.verbose", "false") != "true") + if System.getProperty("sbt.jline.verbose", "false") != "true" then jline.internal.Log.setOutput(new PrintStream(_ => {}, false)) private val isCI = System.getProperty("sbt.ci", "") == "true" || sys.env.contains("BUILD_NUMBER") || sys.env.contains("CI") - def consoleLog(string: String): Unit = { + def consoleLog(string: String): Unit = try Terminal.console.printStream.println(s"[info] $string") - catch { case _: IOException => } - } + catch + case _: IOException => private val writeLock = new AnyRef - def withWriteLock[A](f: => A): A = { + def withWriteLock[A](f: => A): A = writeLock.synchronized { f } - } private[sbt] def set(terminal: Terminal): Terminal = activeTerminal.getAndSet(terminal) - extension (term: Terminal) { + extension (term: Terminal) def ansi(richString: => String, string: => String): String = - if (term.isAnsiSupported) richString else string + if term.isAnsiSupported then richString else string /* * Whenever we are dealing with JLine, which is true in sbt's ConsoleReader * as well as in the scala `console` task, we need to provide a jline.Terminal2 @@ -255,9 +249,9 @@ object Terminal { * already wraps a jline terminal, so we can just return the wrapped jline * terminal. */ - private[sbt] def toJLine: jline.Terminal & jline.Terminal2 = term match { + private[sbt] def toJLine: jline.Terminal & jline.Terminal2 = term match case _ => - new jline.Terminal with jline.Terminal2 { + new jline.Terminal with jline.Terminal2: override def init(): Unit = {} override def restore(): Unit = {} override def reset(): Unit = {} @@ -279,21 +273,17 @@ object Terminal { term.getNumericCapability(capability) override def getStringCapability(capability: String): String = term.getStringCapability(capability) - } - } - } + end extension /* * Closes the standard input and output streams for the process. This allows * the sbt client to detach from the server it launches. */ - def close(): Unit = { - if (!hasConsole) { + def close(): Unit = + if !hasConsole then originalOut.close() originalIn.close() originalErr.close() - } - } /** * Returns true if System.in is attached. When sbt is run as a subprocess, like in scripted or as @@ -305,24 +295,21 @@ object Terminal { */ def systemInIsAttached: Boolean = attached.get - def read: Int = inputStream.get match { + def read: Int = inputStream.get match case null => -1 case is => is.read - } /** * Returns an InputStream that will throw a [[ClosedChannelException]] if read returns -1. * @return * the wrapped InputStream. */ - private[sbt] def throwOnClosedSystemIn(in: InputStream): InputStream = new InputStream { + private[sbt] def throwOnClosedSystemIn(in: InputStream): InputStream = new InputStream: override def available(): Int = in.available() - override def read(): Int = in.read() match { + override def read(): Int = in.read() match case -1 => throw new ClosedChannelException case r if r >= 0 => r case _ => -1 - } - } /** * Provides a wrapper around System.in. The wrapped stream in will check if the terminal is @@ -342,14 +329,13 @@ object Terminal { private val hasProgress: AtomicBoolean = new AtomicBoolean(false) private[sbt] def parseLogOption(s: String): Option[Boolean] = - s.toLowerCase match { + s.toLowerCase match case "always" => Some(true) case "auto" => None case "never" => Some(false) case "true" => Some(true) case "false" => Some(false) case _ => None - } /** * Indicates whether formatting has been disabled in environment variables. @@ -357,27 +343,23 @@ object Terminal { * 3. -Dsbt.colour=always/auto/never/true/false 4. * -Dsbt.log.format=always/auto/never/true/false */ - private lazy val logFormatEnabled: Option[Boolean] = { - sys.props.get("sbt.log.noformat") match { + private lazy val logFormatEnabled: Option[Boolean] = + sys.props.get("sbt.log.noformat") match case Some(_) => Some(!java.lang.Boolean.getBoolean("sbt.log.noformat")) case _ => sys.props.get("sbt.log.format").flatMap(parseLogOption) - } - } private[sbt] lazy val isAnsiSupported: Boolean = logFormatEnabled.getOrElse(useColorDefault) private val isDumb = Some("dumb") == sys.env.get("TERM") private def isDumbTerminal = isDumb || System.getProperty("jline.terminal", "") == "none" - private[sbt] val hasConsole = { + private[sbt] val hasConsole = System.console != null && { - try { + try val isTerminal = System.console.getClass.getMethod("isTerminal") isTerminal.invoke(System.console).asInstanceOf[Boolean] - } catch { + catch case _: NoSuchMethodException => true - } } - } /** * Pure function that determines whether color output should be enabled. @@ -418,7 +400,7 @@ object Terminal { private[sbt] lazy val isColorEnabled = useColorDefault private[sbt] def red(str: String, doRed: Boolean): String = - if (isColorEnabled && doRed) Console.RED + str + Console.RESET + if isColorEnabled && doRed then Console.RED + str + Console.RESET else str private def hasVirtualIO = System.getProperty("sbt.io.virtual", "") == "true" || !isCI @@ -434,18 +416,18 @@ object Terminal { * @return * the result of the thunk */ - private[sbt] def withStreams[T](isServer: Boolean, isSubProcess: Boolean)(f: => T): T = { + private[sbt] def withStreams[T](isServer: Boolean, isSubProcess: Boolean)(f: => T): T = // In ci environments, don't touch the io streams unless run with -Dsbt.io.virtual=true - if ((hasConsole && !isDumbTerminal) || isSubProcess) + if (hasConsole && !isDumbTerminal) || isSubProcess then consoleTerminalHolder.set(newConsoleTerminal()) - if (hasVirtualIO) { + if hasVirtualIO then hasProgress.set(isServer && isAnsiSupported) Terminal.set(consoleTerminalHolder.get) try withOut(withIn(f)) - finally { + finally jline.TerminalFactory.reset() - if (isServer) { - console match { + if isServer then + console match case c: ConsoleTerminal if !isWindows => /* * Entering raw mode in this way causes the standard in InputStream @@ -455,37 +437,34 @@ object Terminal { * this on a background thread in case the read blocks indefinitely. */ c.system.enterRawMode() - val runnable: Runnable = () => { + val runnable: Runnable = () => try Util.ignoreResult(c.inputStream.read) - catch { case _: InterruptedException => } - } + catch + case _: InterruptedException => val thread = new Thread(runnable, "sbt-console-background-close") thread.setDaemon(true) thread.start() // The thread should exit almost instantly but give it 200ms to spin up thread.join(200) - if (thread.isAlive) thread.interrupt() + if thread.isAlive then thread.interrupt() c.close() case c => c.close() - } - } else { - console.close() - } - } - } else f - } + else console.close() + end if + end try + else f + end if + end withStreams - private object ProxyTerminal extends Terminal { - private def t: Terminal = { + private object ProxyTerminal extends Terminal: + private def t: Terminal = val current = activeTerminal.get // if the activeTerminal is yet to be initialized on use, // initialize to the conventional simple terminal for compatibility and testing - if (current ne null) current - else { + if current ne null then current + else Terminal.set(Terminal.SimpleTerminal) activeTerminal.get - } - } override private[sbt] def progressState: ProgressState = t.progressState override private[sbt] def enterRawMode(): Unit = t.enterRawMode() override private[sbt] def exitRawMode(): Unit = t.exitRawMode() @@ -522,63 +501,55 @@ object Terminal { override def getLines: Seq[String] = t.getLines override private[sbt] def name: String = t.name override def toString: String = s"ProxyTerminal(current = $t)" - } + end ProxyTerminal private[sbt] def get: Terminal = ProxyTerminal private[sbt] def current: Terminal = activeTerminal.get - private[sbt] def withIn[T](in: InputStream)(f: => T): T = { + private[sbt] def withIn[T](in: InputStream)(f: => T): T = val original = inputStream.get - try { + try inputStream.set(in) System.setIn(in) scala.Console.withIn(in)(f) - } finally { + finally inputStream.set(original) System.setIn(original) - } - } - private[sbt] def withOut[T](out: PrintStream)(f: => T): T = { + private[sbt] def withOut[T](out: PrintStream)(f: => T): T = val originalOut = System.out val originalErr = System.err val originalProxyOut = ConsoleOut.getGlobalProxy - try { + try ConsoleOut.setGlobalProxy(ConsoleOut.printStreamOut(out)) System.setOut(out) System.setErr(out) scala.Console.withErr(out)(scala.Console.withOut(out)(f)) - } finally { + finally ConsoleOut.setGlobalProxy(originalProxyOut) System.setOut(originalOut) System.setErr(originalErr) - } - } val sepBytes = System.lineSeparator.getBytes("UTF-8") - private class LinePrintStream(outputStream: OutputStream) - extends PrintStream(outputStream, true) { + private class LinePrintStream(outputStream: OutputStream) extends PrintStream(outputStream, true): override def println(s: String): Unit = withWriteLock { out.write(s.getBytes("UTF-8") ++ sepBytes) out.flush() } - } private val originalOut = new LinePrintStream(System.out) private val originalErr = System.err private val originalIn = System.in private[sbt] class WriteableInputStream(in: InputStream, name: String) extends SimpleInputStream - with AutoCloseable { + with AutoCloseable: private val isRaw = new AtomicBoolean(false) final def write(bytes: Int*): Unit = buffer.synchronized { bytes.foreach(b => buffer.put(b)) } - def setRawMode(toggle: Boolean): Unit = { + def setRawMode(toggle: Boolean): Unit = isRaw.set(toggle) - in match { + in match case win: WindowsInputStream => win.setRawMode(toggle) case _ => - } - } private val executor = Executors.newSingleThreadExecutor(r => new Thread(r, s"sbt-$name-input-reader")) private val buffer = new LinkedBlockingQueue[Integer] @@ -596,76 +567,67 @@ object Terminal { * by setting the terminal to raw mode, which the input stream makes it non blocking, * but this approach only works on posix platforms. */ - private val runnable: Runnable = () => { - @tailrec def impl(): Unit = { + private val runnable: Runnable = () => + @tailrec def impl(): Unit = val _ = readQueue.take val b = in.read buffer.synchronized(buffer.put(b)) - if (Thread.interrupted() || (b == -1 && isRaw.get)) closed.set(true) + if Thread.interrupted() || (b == -1 && isRaw.get) then closed.set(true) else impl() - } try impl() - catch { case _: InterruptedException => closed.set(true) } - } + catch case _: InterruptedException => closed.set(true) executor.submit(runnable) def read(result: LinkedBlockingQueue[Integer]): Unit = - if (!closed.get) + if !closed.get then readThread.synchronized { readThread.set(Thread.currentThread) try - buffer.poll match { + buffer.poll match case null => readQueue.put(()) result.put(buffer.take) case b if b == -1 => throw new ClosedChannelException case b => result.put(b) - } finally readThread.set(null) } - override def read(): Int = { + override def read(): Int = val result = new LinkedBlockingQueue[Integer] read(result) - result.poll match { + result.poll match case null => -1 case i => i.toInt - } - } def cancel(): Unit = readThread.synchronized { Option(readThread.getAndSet(null)).foreach(_.interrupt()) readQueue.clear() } - override def available(): Int = { + override def available(): Int = buffer.size - } - override def close(): Unit = if (closed.compareAndSet(false, true)) { + override def close(): Unit = if closed.compareAndSet(false, true) then executor.shutdownNow() buffer.synchronized(buffer.put(-1: Integer)) () - } - } - private def nonBlockingIn(term: org.jline.terminal.Terminal): WriteableInputStream = { - val in = if (Util.isNonCygwinWindows) new WindowsInputStream(term, originalIn) else originalIn + end WriteableInputStream + private def nonBlockingIn(term: org.jline.terminal.Terminal): WriteableInputStream = + val in = + if Util.isNonCygwinWindows then new WindowsInputStream(term, originalIn) else originalIn new WriteableInputStream(in, "console") - } private val inputStream = new AtomicReference[InputStream](System.in) - private def withOut[T](f: => T): T = { - try { + private def withOut[T](f: => T): T = + try System.setOut(proxyPrintStream) System.setErr(proxyErrorStream) scala.Console.withErr(proxyErrorStream)(scala.Console.withOut(proxyPrintStream)(f)) - } finally { + finally System.setOut(originalOut) System.setErr(originalErr) - } - } private def withIn[T](f: => T): T = - try { + try inputStream.set(proxyInputStream) System.setIn(proxyInputStream) scala.Console.withIn(proxyInputStream)(f) - } finally System.setIn(originalIn) + finally System.setIn(originalIn) private[sbt] def withPrintStream[T](f: PrintStream => T): T = console.withPrintStream(f) private val attached = new AtomicBoolean(true) @@ -708,23 +670,19 @@ object Terminal { private[sbt] def setBootStreams( bootInputStream: InputStream, bootOutputStream: OutputStream - ): Unit = { + ): Unit = bootInputStreamHolder.set(bootInputStream) bootOutputStreamHolder.set(bootOutputStream) - } - private[sbt] trait SimpleInputStream extends InputStream { + private[sbt] trait SimpleInputStream extends InputStream: override def read(b: Array[Byte]): Int = read(b, 0, b.length) - override def read(b: Array[Byte], off: Int, len: Int): Int = { - read() match { + override def read(b: Array[Byte], off: Int, len: Int): Int = + read() match case -1 => -1 case byte => b(off) = byte.toByte 1 - } - } - } - private object proxyInputStream extends SimpleInputStream { + private object proxyInputStream extends SimpleInputStream: private val isScripted = System.getProperty("sbt.scripted", "false") == "true" /* * This is to handle the case when a remote client starts sbt and the build fails. @@ -736,98 +694,84 @@ object Terminal { * of the input streams and periodically interrupt the thread to switch between * the two input streams. */ - private class ReadThread extends Thread with AutoCloseable { + private class ReadThread extends Thread with AutoCloseable: val result = new LinkedBlockingQueue[Integer] val running = new AtomicBoolean(true) setDaemon(true) start() - override def run(): Unit = while (running.get) { - bootInputStreamHolder.get match { + override def run(): Unit = while running.get do + bootInputStreamHolder.get match case null => case is => def readFrom(inputStream: InputStream) = - try { - if (running.get) { - inputStream.read match { + try + if running.get then + inputStream.read match case -1 => case `NO_BOOT_CLIENTS_CONNECTED` => - if (!Terminal.hasConsole) { + if !Terminal.hasConsole then result.put(-1) running.set(false) - } case i => result.put(i) running.set(false) - } - } - } catch { case _: InterruptedException => } + catch + case _: InterruptedException => readFrom(is) readFrom(activeTerminal.get().inputStream) - } - } - override def close(): Unit = if (running.compareAndSet(true, false)) this.interrupt() - } - override def read(): Int = { - if (isScripted) -1 - else if (bootInputStreamHolder.get == null) activeTerminal.get().inputStream.read() - else { + override def close(): Unit = if running.compareAndSet(true, false) then this.interrupt() + end ReadThread + override def read(): Int = + if isScripted then -1 + else if bootInputStreamHolder.get == null then activeTerminal.get().inputStream.read() + else val thread = new ReadThread - @tailrec def poll(): Int = thread.result.poll(10, TimeUnit.MILLISECONDS) match { + @tailrec def poll(): Int = thread.result.poll(10, TimeUnit.MILLISECONDS) match case null => thread.interrupt() poll() case i => i - } poll() - } - } override def available(): Int = - if (isScripted) 0 + if isScripted then 0 else - bootInputStreamHolder.get match { + bootInputStreamHolder.get match case null => activeTerminal.get().inputStream.available() case stream => stream.available() + activeTerminal.get().inputStream.available() - } - } - private object proxyOutputStream extends OutputStream { + end proxyInputStream + private object proxyOutputStream extends OutputStream: private def os: OutputStream = activeTerminal.get().outputStream - def write(byte: Int): Unit = { + def write(byte: Int): Unit = os.write(byte) os.flush() - if (byte == 10) os.flush() - } + if byte == 10 then os.flush() override def write(bytes: Array[Byte]): Unit = write(bytes, 0, bytes.length) - override def write(bytes: Array[Byte], offset: Int, len: Int): Unit = { + override def write(bytes: Array[Byte], offset: Int, len: Int): Unit = os.write(bytes, offset, len) os.flush() - } override def flush(): Unit = os.flush() - } - private val proxyPrintStream = new LinePrintStream(proxyOutputStream) { + private val proxyPrintStream = new LinePrintStream(proxyOutputStream): override def toString: String = s"proxyPrintStream($proxyOutputStream)" - } - private object proxyErrorOutputStream extends OutputStream { + private object proxyErrorOutputStream extends OutputStream: private def os: OutputStream = activeTerminal.get().errorStream def write(byte: Int): Unit = os.write(byte) override def write(bytes: Array[Byte]): Unit = write(bytes, 0, bytes.length) override def write(bytes: Array[Byte], offset: Int, len: Int): Unit = os.write(bytes, offset, len) override def flush(): Unit = os.flush() - } private object proxyErrorStream extends PrintStream(proxyErrorOutputStream, true) private lazy val isWindows = System.getProperty("os.name", "").toLowerCase(Locale.ENGLISH).indexOf("windows") >= 0 - private object WrappedSystemIn extends SimpleInputStream { + private object WrappedSystemIn extends SimpleInputStream: private val in = proxyInputStream - override def available(): Int = if (attached.get) in.available() else 0 + override def available(): Int = if attached.get then in.available() else 0 override def read(): Int = synchronized { - if (attached.get) { + if attached.get then val res = in.read() - if (res == -1) attached.set(false) + if res == -1 then attached.set(false) res - } else -1 + else -1 } - } /* * When the server is booted by a remote client, it may not be able to accurately @@ -844,7 +788,7 @@ object Terminal { ) private[sbt] val TERMINAL_PROPS = "SBT_TERMINAL_PROPS" private val props = sys.env.get(TERMINAL_PROPS) flatMap { p => - p.split(",") match { + p.split(",") match case Array(width, height, ansi, color, supershell) => Try( new Props( @@ -856,29 +800,26 @@ object Terminal { ) ).toOption case _ => None - } } private[sbt] def startedByRemoteClient = props.isDefined - private def newConsoleTerminal(): Terminal = { + private def newConsoleTerminal(): Terminal = val system = JLine3.system new ConsoleTerminal(nonBlockingIn(system), originalOut, system) - } - private[sbt] def reset(): Unit = { + private[sbt] def reset(): Unit = jline.TerminalFactory.reset() console.close() - if (hasConsole && !isDumbTerminal) consoleTerminalHolder.set(newConsoleTerminal()) - } + if hasConsole && !isDumbTerminal then consoleTerminalHolder.set(newConsoleTerminal()) // translate explicit class names to type in order to support // older Scala, since it shaded classes but not the system property - private def fixTerminalProperty(): Unit = { + private def fixTerminalProperty(): Unit = val terminalProperty = "jline.terminal" val newValue = - if (!isAnsiSupported && System.getProperty("sbt.io.virtual", "") == "false") "none" + if !isAnsiSupported && System.getProperty("sbt.io.virtual", "") == "false" then "none" else - System.getProperty(terminalProperty) match { + System.getProperty(terminalProperty) match case "jline.UnixTerminal" => "unix" case null if System.getProperty("sbt.cygwin") != null => "unix" case "jline.WindowsTerminal" => "windows" @@ -886,26 +827,20 @@ object Terminal { case "jline.UnsupportedTerminal" => "none" case null if isDumb => "none" case x => x - } - if (newValue != null) { + if newValue != null then System.setProperty(terminalProperty, newValue) () - } - } fixTerminalProperty() - private[sbt] def createReader(term: Terminal, prompt: Prompt): ConsoleReader = { - new ConsoleReader(term.inputStream, term.outputStream, term.toJLine) { + private[sbt] def createReader(term: Terminal, prompt: Prompt): ConsoleReader = + new ConsoleReader(term.inputStream, term.outputStream, term.toJLine): override def readLine(prompt: String, mask: Character): String = term.withRawInput(super.readLine(prompt, mask)) override def readLine(prompt: String): String = term.withRawInput(super.readLine(prompt)) - } - } - def console: Terminal = consoleTerminalHolder.get match { + def console: Terminal = consoleTerminalHolder.get match case null => throw new IllegalStateException("Uninitialized terminal.") case term => term - } private val capabilityMap = org.jline.utils.InfoCmp.Capability.values().map(c => c.toString -> c).toMap @@ -917,67 +852,62 @@ object Terminal { in: WriteableInputStream, out: OutputStream, private[util] val system: org.jline.terminal.Terminal, - ) extends TerminalImpl(in, out, originalErr, "console0") { + ) extends TerminalImpl(in, out, originalErr, "console0"): private val rawMode = new AtomicBoolean(false) - if (Util.isWindows && hasConsole) { + if Util.isWindows && hasConsole then // It is necessary to enter and exit raw mode in order to get the windows // console to echo input. enterRawMode() exitRawMode() - } - override private[sbt] def getSizeImpl: (Int, Int) = { + override private[sbt] def getSizeImpl: (Int, Int) = val size = system.getSize (size.getColumns, size.getRows) - } override lazy val isAnsiSupported: Boolean = !isDumbTerminal && Terminal.isAnsiSupported && !isCI override private[sbt] def progressState: ProgressState = consoleProgressState.get override def isSuccessEnabled: Boolean = true private lazy val echoEnabled: AtomicBoolean = new AtomicBoolean({ try system.echo() - catch { - case _: InterruptedIOException => false - } + catch case _: InterruptedIOException => false }) override def isEchoEnabled: Boolean = echoEnabled.get() override def setEchoEnabled(toggle: Boolean): Unit = - try { + try Util.ignoreResult(system.echo(toggle)) echoEnabled.set(toggle) - } catch { case _: InterruptedIOException => } + catch + case _: InterruptedIOException => override def getBooleanCapability(capability: String): Boolean = capabilityMap.get(capability).fold(false)(system.getBooleanCapability) override def getNumericCapability(capability: String): Integer = capabilityMap.get(capability).fold(null: Integer)(system.getNumericCapability) - override def getStringCapability(capability: String): String = { + override def getStringCapability(capability: String): String = val res = capabilityMap.get(capability).fold(null: String)(system.getStringCapability) res - } override private[sbt] def restore(): Unit = exitRawMode() override private[sbt] def getAttributes: Map[String, String] = Try(JLine3.toMap(system.getAttributes)).getOrElse(Map.empty) - override private[sbt] def setAttributes(attributes: Map[String, String]): Unit = { + override private[sbt] def setAttributes(attributes: Map[String, String]): Unit = system.setAttributes(JLine3.attributesFromMap(attributes)) JLine3.setEnableProcessInput() - } override private[sbt] def setSize(width: Int, height: Int): Unit = system.setSize(new org.jline.terminal.Size(width, height)) override def inputStream: InputStream = in override private[sbt] def enterRawMode(): Unit = - if (rawMode.compareAndSet(false, true) && hasConsole) { + if rawMode.compareAndSet(false, true) && hasConsole then in.setRawMode(true) try JLine3.enterRawMode(system) - catch { case _: java.io.IOError => } - } + catch + case _: java.io.IOError => override private[sbt] def exitRawMode(): Unit = - if (rawMode.compareAndSet(true, false) && hasConsole) { + if rawMode.compareAndSet(true, false) && hasConsole then in.setRawMode(false) try JLine3.exitRawMode(system) - catch { case _: java.io.IOError => } - } + catch + case _: java.io.IOError => override lazy val isColorEnabled: Boolean = props .map(_.color) @@ -986,65 +916,59 @@ object Terminal { override lazy val isSupershellEnabled: Boolean = props .map(_.supershell) - .getOrElse(System.getProperty("sbt.supershell") match { + .getOrElse(System.getProperty("sbt.supershell") match case null => !(sys.env.contains("BUILD_NUMBER") || sys.env .contains("CI")) && isColorEnabled && !Util.isEmacs case "true" => true - case _ => false - }) - override def close(): Unit = { - try { + case _ => false) + override def close(): Unit = + try system.setAttributes(JLine3.initialAttributes.get) system.close() in.close() - } catch { case NonFatal(_) => } + catch + case NonFatal(_) => super.close() - } - } + end ConsoleTerminal private[sbt] abstract class TerminalImpl private[sbt] ( val in: WriteableInputStream, val out: OutputStream, override val errorStream: OutputStream, override private[sbt] val name: String - ) extends Terminal { self => + ) extends Terminal: + self => private[sbt] def getSizeImpl: (Int, Int) private val sizeRefreshPeriod = 1.second private val size = new AtomicReference[((Int, Int), Deadline)](((1, 1), Deadline.now - 1.day)) private def setSize() = size.set((Try(getSizeImpl).getOrElse((1, 1)), Deadline.now)) - private def getSize = size.get match { + private def getSize = size.get match case (s, d) if (d + sizeRefreshPeriod).isOverdue() => setSize() size.get._1 case (s, _) => s - } override def getWidth: Int = getSize._1 override def getHeight: Int = getSize._2 private val rawMode = new AtomicBoolean(false) - def throwIfClosed[R](f: => R): R = if (isStopped.get) throw new ClosedChannelException else f + def throwIfClosed[R](f: => R): R = if isStopped.get then throw new ClosedChannelException else f override def getLastLine: Option[String] = progressState.currentLine override def getLines: Seq[String] = progressState.getLines - private val combinedOutputStream = new OutputStream { - override def write(b: Int): Unit = { + private val combinedOutputStream = new OutputStream: + override def write(b: Int): Unit = Option(bootOutputStreamHolder.get).foreach(_.write(b)) out.write(b) - } - override def write(b: Array[Byte]): Unit = { + override def write(b: Array[Byte]): Unit = write(b, 0, b.length) - } - override def write(b: Array[Byte], offset: Int, len: Int): Unit = { + override def write(b: Array[Byte], offset: Int, len: Int): Unit = Option(bootOutputStreamHolder.get).foreach(_.write(b, offset, len)) out.write(b, offset, len) - } - override def flush(): Unit = { + override def flush(): Unit = Option(bootOutputStreamHolder.get).foreach(_.flush()) out.flush() - } - } - override val outputStream = new OutputStream { + override val outputStream = new OutputStream: override def write(b: Int): Unit = throwIfClosed { write(Array((b & 0xff).toByte)) } @@ -1055,19 +979,18 @@ object Terminal { write(Arrays.copyOfRange(b, offset, offset + length)) } override def flush(): Unit = combinedOutputStream.flush() - } private def doWrite(rawBytes: Array[Byte]): Unit = withPrintStream { ps => val (toWrite, len) = - if (rawBytes.contains(27.toByte)) { - if (!self.isAnsiSupported || !self.isColorEnabled) + if rawBytes.contains(27.toByte) then + if !self.isAnsiSupported || !self.isColorEnabled then EscHelpers.strip( rawBytes, stripAnsi = !self.isAnsiSupported, stripColor = !self.isColorEnabled ) else (rawBytes, rawBytes.length) - } else (rawBytes, rawBytes.length) - val bytes = if (len < toWrite.length) toWrite.take(len) else toWrite + else (rawBytes, rawBytes.length) + val bytes = if len < toWrite.length then toWrite.take(len) else toWrite progressState.write(TerminalImpl.this, bytes, ps, hasProgress.get && !rawMode.get) } override private[sbt] val printStream: PrintStream = new LinePrintStream(outputStream) @@ -1076,37 +999,33 @@ object Terminal { private[sbt] def write(bytes: Int*): Unit = in.write(bytes*) private val isStopped = new AtomicBoolean(false) - override def getLineHeightAndWidth(line: String): (Int, Int) = getWidth match { + override def getLineHeightAndWidth(line: String): (Int, Int) = getWidth match case width if width > 0 => val position = EscHelpers.cursorPosition(line) val count = (position + width - 1) / width (count, position - (math.max((count - 1), 0) * width)) case _ => (0, 0) - } - private[sbt] def withRawOutput[R](f: => R): R = { + private[sbt] def withRawOutput[R](f: => R): R = rawMode.set(true) try f finally rawMode.set(false) - } private val rawPrintStream: PrintStream = new LinePrintStream(combinedOutputStream) override def withPrintStream[T](f: PrintStream => T): T = writeLock.synchronized(f(rawPrintStream)) - override def close(): Unit = if (isStopped.compareAndSet(false, true)) { - in.close() - } - } - private lazy val nullInputStream: InputStream = () => { + override def close(): Unit = if isStopped.compareAndSet(false, true) then in.close() + end TerminalImpl + private lazy val nullInputStream: InputStream = () => try this.synchronized(this.wait()) - catch { case _: InterruptedException => } + catch + case _: InterruptedException => -1 - } private[sbt] class DefaultTerminal( val inputStream: InputStream, val outputStream: OutputStream, val errorStream: OutputStream - ) extends Terminal { + ) extends Terminal: override def close(): Unit = {} override private[sbt] def progressState: ProgressState = new ProgressState(1) override private[sbt] def enterRawMode(): Unit = {} @@ -1134,5 +1053,5 @@ object Terminal { override private[sbt] def withPrintStream[T](f: PrintStream => T): T = f(printStream) override private[sbt] def write(bytes: Int*): Unit = {} override private[sbt] def withRawOutput[R](f: => R): R = f - } -} + end DefaultTerminal +end Terminal diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/WindowsInputStream.scala b/internal/util-logging/src/main/scala/sbt/internal/util/WindowsInputStream.scala index a46595aab..b60330d27 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/WindowsInputStream.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/WindowsInputStream.scala @@ -16,22 +16,18 @@ import org.jline.utils.InfoCmp.Capability import scala.annotation.tailrec import Terminal.SimpleInputStream -private object WindowsSupport { - def getConsoleMode = { +private object WindowsSupport: + def getConsoleMode = val console = Kernel32.GetStdHandle(Kernel32.STD_INPUT_HANDLE); val mode = new Array[Int](1); - if (Kernel32.GetConsoleMode(console, mode) == 0) -1 else mode.head - } - def setConsoleMode(mode: Int): Unit = { + if Kernel32.GetConsoleMode(console, mode) == 0 then -1 else mode.head + def setConsoleMode(mode: Int): Unit = val console = Kernel32.GetStdHandle(Kernel32.STD_INPUT_HANDLE); Kernel32.SetConsoleMode(console, mode) () - } - def readConsoleInput(count: Int) = { + def readConsoleInput(count: Int) = val console = Kernel32.GetStdHandle(Kernel32.STD_INPUT_HANDLE); Kernel32.readConsoleInputHelper(console, 1, false) - } -} /* * We need a special input stream for windows because special key events * like arrow keys are not reported by System.in. What makes this extra @@ -45,23 +41,22 @@ private object WindowsSupport { * raw mode. */ private[util] class WindowsInputStream(term: org.jline.terminal.Terminal, in: InputStream) - extends SimpleInputStream { + extends SimpleInputStream: private val RIGHT_ALT_PRESSED = 0x0001; private val LEFT_ALT_PRESSED = 0x0002; private val RIGHT_CTRL_PRESSED = 0x0004; private val LEFT_CTRL_PRESSED = 0x0008; private val SHIFT_PRESSED = 0x0010; - private def getCapability(cap: Capability): String = term.getStringCapability(cap) match { + private def getCapability(cap: Capability): String = term.getStringCapability(cap) match case null => null case c => c.replace("\\E", "\u001B") - } /* * This function is a hybrid of jline 2 WindowsTerminal.readConsoleInput * and jline3 AbstractTerminal.getEscapeSequence. */ - private def readConsoleInput(): Array[Byte] = { - WindowsSupport.readConsoleInput(1) match { + private def readConsoleInput(): Array[Byte] = + WindowsSupport.readConsoleInput(1) match case null => Array.empty case events => val sb = new StringBuilder(); @@ -71,26 +66,20 @@ private[util] class WindowsInputStream(term: org.jline.terminal.Terminal, in: In val isCtrl = (controlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) > 0; val isAlt = (controlKeyState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)) > 0; val isShift = (controlKeyState & SHIFT_PRESSED) > 0; - if (keyEvent.keyDown) { - if (keyEvent.uchar > 0) { - if ( - ((keyEvent.uchar >= '@' && keyEvent.uchar <= '_') || (keyEvent.uchar >= 'a' && keyEvent.uchar <= 'z')) + if keyEvent.keyDown then + if keyEvent.uchar > 0 then + if ((keyEvent.uchar >= '@' && keyEvent.uchar <= '_') || (keyEvent.uchar >= 'a' && keyEvent.uchar <= 'z')) && isAlt && !isCtrl - ) { - sb.append('\u001B') // ESC - } - if (isShift && keyEvent.keyCode == 9) { - getCapability(Capability.key_btab) match { + then sb.append('\u001B') // ESC + if isShift && keyEvent.keyCode == 9 then + getCapability(Capability.key_btab) match case null => sb.append(keyEvent.uchar) case cap => sb.append(cap) - } - } else { - sb.append(keyEvent.uchar) - } - } else { + else sb.append(keyEvent.uchar) + else // virtual keycodes: http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx // just add support for basic editing keys (no control state, no numpad keys) - val escapeSequence = keyEvent.keyCode match { + val escapeSequence = keyEvent.keyCode match case 0x21 /* VK_PRIOR PageUp*/ => getCapability(Capability.key_ppage); case 0x22 /* VK_NEXT PageDown*/ => getCapability(Capability.key_npage); case 0x24 /* VK_HOME */ => getCapability(Capability.key_home) @@ -119,36 +108,27 @@ private[util] class WindowsInputStream(term: org.jline.terminal.Terminal, in: In case 0x2e /* VK_DELETE */ => Option(getCapability(Capability.key_dc)).getOrElse("\u001B[3~") case _ => null - } - escapeSequence match { + escapeSequence match case null => case es => (0 until keyEvent.repeatCount.toInt).foreach(_ => sb.append(es)) - } - } - } else { + else // key up event // support ALT+NumPad input method - if (keyEvent.keyCode == 0x12 /*VK_MENU ALT key*/ && keyEvent.uchar > 0) { + if keyEvent.keyCode == 0x12 /*VK_MENU ALT key*/ && keyEvent.uchar > 0 then sb.append(keyEvent.uchar); - } - } + end if } sb.toString().getBytes() - } - } - private val raw: InputStream = new SimpleInputStream { + private val raw: InputStream = new SimpleInputStream: val buffer = new LinkedBlockingQueue[Integer] @tailrec - override def read(): Int = { - buffer.poll match { + override def read(): Int = + buffer.poll match case null => readConsoleInput().foreach(b => buffer.put(b & 0xff)) - if (!Thread.interrupted) read() else throw new InterruptedException + if !Thread.interrupted then read() else throw new InterruptedException case b => b - } - } - } private val isRaw = new AtomicBoolean(true) private[sbt] def setRawMode(toggle: Boolean): Unit = isRaw.set(toggle) - override def read(): Int = if (isRaw.get) raw.read() else in.read() -} + override def read(): Int = if isRaw.get then raw.read() else in.read() +end WindowsInputStream diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/JValueFormats.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/JValueFormats.scala index 443a18ff6..31a11dbfb 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/JValueFormats.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/JValueFormats.scala @@ -13,11 +13,11 @@ package util.codec import sjsonnew.{ JsonWriter as JW, JsonReader as JR, JsonFormat as JF, * } import sjsonnew.shaded.scalajson.ast.unsafe.* -trait JValueFormats { self: sjsonnew.BasicJsonProtocol => - given JNullFormat: JF[JNull.type] = new JF[JNull.type] { +trait JValueFormats: + self: sjsonnew.BasicJsonProtocol => + given JNullFormat: JF[JNull.type] = new JF[JNull.type]: def write[J](x: JNull.type, b: Builder[J]) = b.writeNull() def read[J](j: Option[J], u: Unbuilder[J]) = JNull - } given JBooleanFormat: JF[JBoolean] = projectFormat(_.get, (x: Boolean) => JBoolean(x)) given JStringFormat: JF[JString] = projectFormat(_.value, (x: String) => JString(x)) @@ -28,34 +28,28 @@ trait JValueFormats { self: sjsonnew.BasicJsonProtocol => given JArrayFormat(using JF[JValue]): JF[JArray] = projectFormat[JArray, Array[JValue]](_.value, JArray(_)) - given JObjectJsonWriter: JW[JObject] = new JW[JObject] { - def write[J](x: JObject, b: Builder[J]) = { + given JObjectJsonWriter: JW[JObject] = new JW[JObject]: + def write[J](x: JObject, b: Builder[J]) = b.beginObject() x.value foreach (jsonField => JValueFormat.addField(jsonField.field, jsonField.value, b)) b.endObject() - } - } - given JValueJsonWriter: JW[JValue] = new JW[JValue] { - def write[J](x: JValue, b: Builder[J]) = x match { + given JValueJsonWriter: JW[JValue] = new JW[JValue]: + def write[J](x: JValue, b: Builder[J]) = x match case x: JNull.type => JNullFormat.write(x, b) case x: JBoolean => JBooleanFormat.write(x, b) case x: JString => JStringFormat.write(x, b) case x: JNumber => JNumberFormat.write(x, b) case x: JArray => JArrayFormat.write(x, b) case x: JObject => JObjectJsonWriter.write(x, b) - } - } // This passes through JValue, or returns JNull instead of blowing up with unimplemented. - given JValueJsonReader: JR[JValue] = new JR[JValue] { - def read[J](j: Option[J], u: Unbuilder[J]) = j match { + given JValueJsonReader: JR[JValue] = new JR[JValue]: + def read[J](j: Option[J], u: Unbuilder[J]) = j match case Some(x: JValue) => x case Some(x) => sys.error(s"Unknown AST $x") case _ => JNull - } - } given JValueFormat: JF[JValue] = jsonFormat[JValue](JValueJsonReader, JValueJsonWriter) -} +end JValueFormats diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/PositionFormats.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/PositionFormats.scala index d1009a048..61bae400d 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/PositionFormats.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/PositionFormats.scala @@ -12,10 +12,11 @@ import sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder } import xsbti.Position import java.util.Optional -trait PositionFormats { self: sjsonnew.BasicJsonProtocol => - given PositionFormat: JsonFormat[Position] = new JsonFormat[Position] { - override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Position = { - jsOpt match { +trait PositionFormats: + self: sjsonnew.BasicJsonProtocol => + given PositionFormat: JsonFormat[Position] = new JsonFormat[Position]: + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Position = + jsOpt match case Some(js) => unbuilder.beginObject(js) val line0 = unbuilder.readField[Optional[java.lang.Integer]]("line") @@ -33,7 +34,7 @@ trait PositionFormats { self: sjsonnew.BasicJsonProtocol => val endColumn0 = unbuilder.readField[Optional[java.lang.Integer]]("endColumn") unbuilder.endObject() - new Position() { + new Position(): override val line = line0 override val lineContent = lineContent0 override val offset = offset0 @@ -48,12 +49,9 @@ trait PositionFormats { self: sjsonnew.BasicJsonProtocol => override val endLine = endLine0 override val endColumn = endColumn0 - } case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: Position, builder: Builder[J]): Unit = { + override def write[J](obj: Position, builder: Builder[J]): Unit = builder.beginObject() builder.addField("line", obj.line) builder.addField("lineContent", obj.lineContent) @@ -70,6 +68,4 @@ trait PositionFormats { self: sjsonnew.BasicJsonProtocol => builder.addField("endColumn", obj.endColumn) builder.endObject() - } - } -} +end PositionFormats diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala index d73a13952..3fcbea4fe 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/ProblemFormats.scala @@ -12,10 +12,11 @@ import xsbti.{ Problem, Severity, Position } import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder } import java.util.Optional -trait ProblemFormats { self: SeverityFormats & PositionFormats & sjsonnew.BasicJsonProtocol => - given ProblemFormat: JsonFormat[Problem] = new JsonFormat[Problem] { - override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Problem = { - jsOpt match { +trait ProblemFormats: + self: SeverityFormats & PositionFormats & sjsonnew.BasicJsonProtocol => + given ProblemFormat: JsonFormat[Problem] = new JsonFormat[Problem]: + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Problem = + jsOpt match case Some(js) => unbuilder.beginObject(js) val category0 = unbuilder.readField[String]("category") @@ -25,18 +26,15 @@ trait ProblemFormats { self: SeverityFormats & PositionFormats & sjsonnew.BasicJ val rendered0 = unbuilder.readField[Optional[String]]("rendered") unbuilder.endObject() - new Problem { + new Problem: override val category = category0 override val position = position0 override val message = message0 override val severity = severity0 override val rendered = rendered0 - } case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: Problem, builder: Builder[J]): Unit = { + override def write[J](obj: Problem, builder: Builder[J]): Unit = builder.beginObject() builder.addField("category", obj.category) builder.addField("severity", obj.severity) @@ -44,6 +42,4 @@ trait ProblemFormats { self: SeverityFormats & PositionFormats & sjsonnew.BasicJ builder.addField("position", obj.position) builder.addField("rendered", obj.rendered) builder.endObject() - } - } -} +end ProblemFormats diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/SeverityFormats.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/SeverityFormats.scala index 19d16f636..5e8d3ed37 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/SeverityFormats.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/SeverityFormats.scala @@ -11,27 +11,21 @@ package sbt.internal.util.codec import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder } import xsbti.Severity; -trait SeverityFormats { self: sjsonnew.BasicJsonProtocol => - given SeverityFormat: JsonFormat[Severity] = new JsonFormat[Severity] { - override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Severity = { - jsOpt match { +trait SeverityFormats: + self: sjsonnew.BasicJsonProtocol => + given SeverityFormat: JsonFormat[Severity] = new JsonFormat[Severity]: + override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Severity = + jsOpt match case Some(js) => - unbuilder.readString(js) match { + unbuilder.readString(js) match case "Info" => Severity.Info case "Warn" => Severity.Warn case "Error" => Severity.Error - } case None => deserializationError("Expected JsString but found None") - } - } - override def write[J](obj: Severity, builder: Builder[J]): Unit = { - val str = obj match { + override def write[J](obj: Severity, builder: Builder[J]): Unit = + val str = obj match case Severity.Info => "Info" case Severity.Warn => "Warn" case Severity.Error => "Error" - } builder.writeString(str) - } - } -} diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/SuccessEventShowLines.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/SuccessEventShowLines.scala index fdc67da39..3e52c140d 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/SuccessEventShowLines.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/SuccessEventShowLines.scala @@ -12,11 +12,8 @@ package internal.util.codec import sbt.util.ShowLines import sbt.internal.util.SuccessEvent -trait SuccessEventShowLines { +trait SuccessEventShowLines: given sbtSuccessEventShowLines: ShowLines[SuccessEvent] = - ShowLines[SuccessEvent]((e: SuccessEvent) => { - Vector(e.message) - }) -} + ShowLines[SuccessEvent]((e: SuccessEvent) => Vector(e.message)) object SuccessEventShowLines extends SuccessEventShowLines diff --git a/internal/util-logging/src/main/scala/sbt/internal/util/codec/ThrowableShowLines.scala b/internal/util-logging/src/main/scala/sbt/internal/util/codec/ThrowableShowLines.scala index e25bf55d5..d2ddd4840 100644 --- a/internal/util-logging/src/main/scala/sbt/internal/util/codec/ThrowableShowLines.scala +++ b/internal/util-logging/src/main/scala/sbt/internal/util/codec/ThrowableShowLines.scala @@ -12,22 +12,20 @@ package internal.util.codec import sbt.util.ShowLines import sbt.internal.util.{ StackTrace, TraceEvent } -trait ThrowableShowLines { +trait ThrowableShowLines: given sbtThrowableShowLines: ShowLines[Throwable] = - ShowLines[Throwable]((t: Throwable) => { + ShowLines[Throwable]((t: Throwable) => // 0 means enabled with default behavior. See StackTrace.scala. val traceLevel = 0 List(StackTrace.trimmed(t, traceLevel)) - }) -} + ) object ThrowableShowLines extends ThrowableShowLines -trait TraceEventShowLines { +trait TraceEventShowLines: given sbtTraceEventShowLines: ShowLines[TraceEvent] = - ShowLines[TraceEvent]((t: TraceEvent) => { + ShowLines[TraceEvent]((t: TraceEvent) => ThrowableShowLines.sbtThrowableShowLines.showLines(t.message) - }) -} + ) object TraceEventShowLines extends TraceEventShowLines diff --git a/internal/util-logging/src/main/scala/sbt/util/AbstractLogger.scala b/internal/util-logging/src/main/scala/sbt/util/AbstractLogger.scala index 69a3de344..8c3e4408c 100644 --- a/internal/util-logging/src/main/scala/sbt/util/AbstractLogger.scala +++ b/internal/util-logging/src/main/scala/sbt/util/AbstractLogger.scala @@ -8,7 +8,7 @@ package sbt.util -abstract class AbstractLogger extends Logger { +abstract class AbstractLogger extends Logger: def getLevel: Level.Value def setLevel(newLevel: Level.Value): Unit def setTrace(flag: Int): Unit @@ -23,8 +23,8 @@ abstract class AbstractLogger extends Logger { def logAll(events: Seq[LogEvent]): Unit /** Defined in terms of other methods in Logger and should not be called from them. */ - final def log(event: LogEvent): Unit = { - event match { + final def log(event: LogEvent): Unit = + event match case s: Success => success(s.msg) case l: Log => log(l.level, l.msg) case t: Trace => trace(t.exception) @@ -32,6 +32,4 @@ abstract class AbstractLogger extends Logger { case setT: SetTrace => setTrace(setT.level) case setS: SetSuccess => setSuccessEnabled(setS.enabled) case c: ControlEvent => control(c.event, c.msg) - } - } -} +end AbstractLogger diff --git a/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala b/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala index 1f866babb..e94950551 100644 --- a/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala +++ b/internal/util-logging/src/main/scala/sbt/util/InterfaceUtil.scala @@ -27,10 +27,9 @@ import xsbti.{ import scala.collection.mutable.ListBuffer -object InterfaceUtil { - def toSupplier[A](a: => A): Supplier[A] = new Supplier[A] { +object InterfaceUtil: + def toSupplier[A](a: => A): Supplier[A] = new Supplier[A]: override def get: A = a - } import java.util.function.Function as JavaFunction def toJavaFunction[A1, R](f: A1 => R): JavaFunction[A1, R] = @@ -39,35 +38,31 @@ object InterfaceUtil { def t2[A1, A2](x: (A1, A2)): T2[A1, A2] = new ConcreteT2(x._1, x._2) def toOption[A](m: Optional[A]): Option[A] = - if (m.isPresent) Some(m.get) else None + if m.isPresent then Some(m.get) else None def toOptional[A](o: Option[A]): Optional[A] = - o match { + o match case Some(v) => Optional.of(v) case None => Optional.empty() - } def jo2o[A](o: Optional[A]): Option[A] = - if (o.isPresent) Some(o.get) + if o.isPresent then Some(o.get) else None def o2jo[A](o: Option[A]): Optional[A] = - o match { + o match case Some(v) => Optional.ofNullable(v) case None => Optional.empty[A]() - } - def l2jl[A](l: List[A]): ju.List[A] = { + def l2jl[A](l: List[A]): ju.List[A] = val jl = new ju.ArrayList[A](l.size) l.foreach(jl.add(_)) jl - } - def jl2l[A](jl: ju.List[A]): List[A] = { + def jl2l[A](jl: ju.List[A]): List[A] = val l = ListBuffer[A]() jl.forEach(l += _) l.toList - } def position( line0: Option[Integer], @@ -144,21 +139,18 @@ object InterfaceUtil { new ConcreteDiagnosticRelatedInformation(position, message) private final class ConcreteT2[A1, A2](override val get1: A1, override val get2: A2) - extends T2[A1, A2] { + extends T2[A1, A2]: override def toString: String = s"ConcreteT2($get1, $get2)" - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case o: ConcreteT2[?, ?] => this.get1 == o.get1 && this.get2 == o.get2 case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = var hash = 1 hash = hash * 31 + this.get1.## hash = hash * 31 + this.get2.## hash - } - } private final class ConcretePosition( line0: Option[Integer], @@ -174,7 +166,7 @@ object InterfaceUtil { startColumn0: Option[Integer], endLine0: Option[Integer], endColumn0: Option[Integer] - ) extends Position { + ) extends Position: val line = o2jo(line0) val lineContent = content val offset = o2jo(offset0) @@ -188,21 +180,17 @@ object InterfaceUtil { override val startColumn = o2jo(startColumn0) override val endLine = o2jo(endLine0) override val endColumn = o2jo(endColumn0) - override def toString: String = { - val src = sourcePath0 match { + override def toString: String = + val src = sourcePath0 match case Some(x) => s"$x" case None => "none" - } - val line = line0 match { + val line = line0 match case Some(x) => s":$x" case None => "" - } - val offset = offset0 match { + val offset = offset0 match case Some(x) => s":$x" case None => "" - } s"""$src$line$offset""" - } private def toTuple(p: Position) = ( p.line, @@ -220,11 +208,10 @@ object InterfaceUtil { p.endColumn, ) override def hashCode: Int = toTuple(this).## - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case o: Position => toTuple(this) == toTuple(o) case _ => false - } - } + end ConcretePosition private final class ConcreteProblem( override val category: String, @@ -235,7 +222,7 @@ object InterfaceUtil { diagnosticCode0: Option[DiagnosticCode], diagnosticRelatedInformation0: List[DiagnosticRelatedInformation], actions0: List[Action], - ) extends Problem { + ) extends Problem: override val rendered = o2jo(rendered0) override def diagnosticCode: Optional[DiagnosticCode] = o2jo(diagnosticCode0) override def diagnosticRelatedInformation(): ju.List[DiagnosticRelatedInformation] = @@ -258,17 +245,16 @@ object InterfaceUtil { p.actions, ) override def hashCode: Int = toTuple(this).## - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case o: Problem => toTuple(this) == toTuple(o) case _ => false - } - } + end ConcreteProblem private final class ConcreteAction( override val title: String, description0: Option[String], override val edit: WorkspaceEdit, - ) extends Action { + ) extends Action: override def description(): Optional[String] = o2jo(description0) override def toString(): String = @@ -280,28 +266,24 @@ object InterfaceUtil { a.edit, ) override def hashCode: Int = toTuple(this).## - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case o: Action => toTuple(this) == toTuple(o) case _ => false - } - } - private final class ConcreteWorkspaceEdit(changes0: List[TextEdit]) extends WorkspaceEdit { + private final class ConcreteWorkspaceEdit(changes0: List[TextEdit]) extends WorkspaceEdit: override def changes(): ju.List[TextEdit] = l2jl(changes0) override def toString(): String = s"WorkspaceEdit($changes0)" private def toTuple(w: WorkspaceEdit) = jl2l(w.changes) override def hashCode: Int = toTuple(this).## - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case o: WorkspaceEdit => toTuple(this) == toTuple(o) case _ => false - } - } private final class ConcreteTextEdit( override val position: Position, override val newText: String - ) extends TextEdit { + ) extends TextEdit: override def toString(): String = s"TextEdit($position, $newText)" private def toTuple(edit: TextEdit) = @@ -310,16 +292,14 @@ object InterfaceUtil { edit.newText, ) override def hashCode: Int = toTuple(this).## - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case o: TextEdit => toTuple(this) == toTuple(o) case _ => false - } - } private final class ConcreteDiagnosticCode( override val code: String, explanation0: Option[String] - ) extends DiagnosticCode { + ) extends DiagnosticCode: val explanation: Optional[String] = o2jo(explanation0) override def toString(): String = s"DiagnosticCode($code)" private def toTuple(c: DiagnosticCode) = @@ -328,16 +308,14 @@ object InterfaceUtil { c.explanation, ) override def hashCode: Int = toTuple(this).## - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case o: DiagnosticCode => toTuple(this) == toTuple(o) case _ => false - } - } private final class ConcreteDiagnosticRelatedInformation( override val position: Position, override val message: String - ) extends DiagnosticRelatedInformation { + ) extends DiagnosticRelatedInformation: override def toString(): String = s"DiagnosticRelatedInformation($position, $message)" private def toTuple(info: DiagnosticRelatedInformation) = ( @@ -345,9 +323,7 @@ object InterfaceUtil { info.message, ) override def hashCode: Int = toTuple(this).## - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case o: DiagnosticRelatedInformation => toTuple(this) == toTuple(o) case _ => false - } - } -} +end InterfaceUtil diff --git a/internal/util-logging/src/main/scala/sbt/util/Level.scala b/internal/util-logging/src/main/scala/sbt/util/Level.scala index 5e2fa1326..69e95b2ea 100644 --- a/internal/util-logging/src/main/scala/sbt/util/Level.scala +++ b/internal/util-logging/src/main/scala/sbt/util/Level.scala @@ -12,7 +12,7 @@ package sbt.util * An enumeration defining the levels available for logging. A level includes all of the levels with * id larger than its own id. For example, Warn (id=3) includes Error (id=4). */ -object Level extends Enumeration { +object Level extends Enumeration: val Debug = Value(1, "debug") val Info = Value(2, "info") val Warn = Value(3, "warn") @@ -24,7 +24,7 @@ object Level extends Enumeration { */ val SuccessLabel = "success" - def union(a: Value, b: Value) = if (a.id < b.id) a else b + def union(a: Value, b: Value) = if a.id < b.id then a else b def unionAll(vs: Seq[Value]) = vs reduceLeft union /** @@ -35,4 +35,4 @@ object Level extends Enumeration { /** Same as apply, defined for use in pattern matching. */ private[sbt] def unapply(s: String) = apply(s) -} +end Level diff --git a/internal/util-logging/src/main/scala/sbt/util/LogEvent.scala b/internal/util-logging/src/main/scala/sbt/util/LogEvent.scala index 74068c0ff..3867296f1 100644 --- a/internal/util-logging/src/main/scala/sbt/util/LogEvent.scala +++ b/internal/util-logging/src/main/scala/sbt/util/LogEvent.scala @@ -17,10 +17,8 @@ final class SetTrace(val level: Int) extends LogEvent final class SetSuccess(val enabled: Boolean) extends LogEvent final class ControlEvent(val event: ControlEvent.Value, val msg: String) extends LogEvent -object ControlEvent { - enum Value { +object ControlEvent: + enum Value: case Start case Header case Finish - } -} diff --git a/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala b/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala index bd1e0cbac..2d7ed30e7 100644 --- a/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala +++ b/internal/util-logging/src/main/scala/sbt/util/LogExchange.scala @@ -12,26 +12,24 @@ import sbt.internal.util.{ Appender, ManagedLogger, TraceEvent, SuccessEvent } import sbt.internal.util.appmacro.StringTypeTag import scala.collection.concurrent -sealed abstract class LogExchange { +sealed abstract class LogExchange: private[sbt] val stringCodecs: concurrent.Map[String, ShowLines[?]] = concurrent.TrieMap() private[sbt] val builtInStringCodecs: Unit = initStringCodecs() def logger(name: String): ManagedLogger = logger(name, None, None) def logger(name: String, channelName: Option[String], execId: Option[String]): ManagedLogger = LoggerContext.globalContext.logger(name, channelName, execId) - def unbindLoggerAppenders(loggerName: String): Unit = { + def unbindLoggerAppenders(loggerName: String): Unit = LoggerContext.globalContext.clearAppenders(loggerName) - } def bindLoggerAppenders( loggerName: String, appenders: Seq[(Appender, Level.Value)] - ): Unit = { + ): Unit = appenders.foreach(LoggerContext.globalContext.addAppender(loggerName, _)) () - } - private[sbt] def initStringCodecs(): Unit = { + private[sbt] def initStringCodecs(): Unit = import sbt.internal.util.codec.SuccessEventShowLines.given import sbt.internal.util.codec.ThrowableShowLines.given import sbt.internal.util.codec.TraceEventShowLines.given @@ -39,7 +37,6 @@ sealed abstract class LogExchange { registerStringCodec[Throwable] registerStringCodec[TraceEvent] registerStringCodec[SuccessEvent] - } def stringCodec[A](tag: String): Option[ShowLines[A]] = stringCodecs.get(tag) map { _.asInstanceOf[ShowLines[A]] } @@ -48,10 +45,9 @@ sealed abstract class LogExchange { def getOrElseUpdateStringCodec[A](tag: String, v: ShowLines[A]): ShowLines[A] = stringCodecs.getOrElseUpdate(tag, v).asInstanceOf[ShowLines[A]] - private[sbt] def registerStringCodec[A: ShowLines: StringTypeTag]: Unit = { + private[sbt] def registerStringCodec[A: ShowLines: StringTypeTag]: Unit = val ev = implicitly[ShowLines[A]] val tag = implicitly[StringTypeTag[A]] val _ = getOrElseUpdateStringCodec(tag.key, ev) - } -} +end LogExchange object LogExchange extends LogExchange diff --git a/internal/util-logging/src/main/scala/sbt/util/Logger.scala b/internal/util-logging/src/main/scala/sbt/util/Logger.scala index 2c4253dcc..6d61fa3be 100644 --- a/internal/util-logging/src/main/scala/sbt/util/Logger.scala +++ b/internal/util-logging/src/main/scala/sbt/util/Logger.scala @@ -19,7 +19,7 @@ import java.util.function.Supplier * This is intended to be the simplest logging interface for use by code that wants to log. It does * not include configuring the logger. */ -abstract class Logger extends xLogger { +abstract class Logger extends xLogger: final def verbose(message: => String): Unit = debug(message) final def debug(message: => String): Unit = log(Level.Debug, message) final def info(message: => String): Unit = log(Level.Info, message) @@ -43,15 +43,14 @@ abstract class Logger extends xLogger { def trace(msg: Supplier[Throwable]): Unit = trace(msg.get()) def success(msg: Supplier[String]): Unit = success(msg.get()) def log(level: Level.Value, msg: Supplier[String]): Unit = log(level, msg.get) -} +end Logger -object Logger { - def transferLevels(oldLog: AbstractLogger, newLog: AbstractLogger): Unit = { +object Logger: + def transferLevels(oldLog: AbstractLogger, newLog: AbstractLogger): Unit = newLog.setLevel(oldLog.getLevel) newLog.setTrace(oldLog.getTrace) - } - val Null: AbstractLogger = new AbstractLogger { + val Null: AbstractLogger = new AbstractLogger: def getLevel: Level.Value = Level.Error def setLevel(newLevel: Level.Value): Unit = () def getTrace: Int = 0 @@ -63,19 +62,17 @@ object Logger { def trace(t: => Throwable): Unit = () def success(message: => String): Unit = () def log(level: Level.Value, message: => String): Unit = () - } implicit def absLog2PLog(log: AbstractLogger): ProcessLogger = new BufferedLogger(log) with ProcessLogger implicit def log2PLog(log: Logger): ProcessLogger = absLog2PLog(new FullLogger(log)) - implicit def xlog2Log(lg: xLogger): Logger = lg match { + implicit def xlog2Log(lg: xLogger): Logger = lg match case l: Logger => l case _ => wrapXLogger(lg) - } - private def wrapXLogger(lg: xLogger): Logger = new Logger { + private def wrapXLogger(lg: xLogger): Logger = new Logger: import InterfaceUtil.toSupplier override def debug(msg: Supplier[String]): Unit = lg.debug(msg) override def warn(msg: Supplier[String]): Unit = lg.warn(msg) @@ -85,17 +82,14 @@ object Logger { override def log(level: Level.Value, msg: Supplier[String]): Unit = lg.log(level, msg) def trace(t: => Throwable): Unit = trace(toSupplier(t)) def success(s: => String): Unit = info(toSupplier(s)) - def log(level: Level.Value, msg: => String): Unit = { + def log(level: Level.Value, msg: => String): Unit = val fmsg = toSupplier(msg) - level match { + level match case Level.Debug => lg.debug(fmsg) case Level.Info => lg.info(fmsg) case Level.Warn => lg.warn(fmsg) case Level.Error => lg.error(fmsg) - } - } - } def jo2o[A](o: Optional[A]): Option[A] = InterfaceUtil.jo2o(o) def o2jo[A](o: Option[A]): Optional[A] = InterfaceUtil.o2jo(o) -} +end Logger diff --git a/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala b/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala index b21784c31..a2fd902c4 100644 --- a/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala +++ b/internal/util-logging/src/main/scala/sbt/util/LoggerContext.scala @@ -21,7 +21,7 @@ import java.util.concurrent.atomic.{ AtomicReference, AtomicBoolean } * when it became clear that it was very difficult to manage the loggers and appenders without * introducing memory leaks. */ -sealed trait LoggerContext extends AutoCloseable { +sealed trait LoggerContext extends AutoCloseable: def logger(name: String, channelName: Option[String], execId: Option[String]): ManagedLogger def clearAppenders(loggerName: String): Unit def addAppender( @@ -30,83 +30,65 @@ sealed trait LoggerContext extends AutoCloseable { ): Unit def appenders(loggerName: String): Seq[Appender] def remove(name: String): Unit -} -object LoggerContext { +object LoggerContext: private[sbt] lazy val globalContext: LoggerContext = new LoggerContext.LoggerContextImpl - private[util] class LoggerContextImpl extends LoggerContext { - private class Log extends MiniLogger { + private[util] class LoggerContextImpl extends LoggerContext: + private class Log extends MiniLogger: private val consoleAppenders: AtomicReference[Vector[(Appender, Level.Value)]] = new AtomicReference(Vector.empty) - def log(level: Level.Value, message: => String): Unit = { + def log(level: Level.Value, message: => String): Unit = val toAppend = consoleAppenders.get.filter { (a, l) => level.compare(l) >= 0 } - if (toAppend.nonEmpty) { + if toAppend.nonEmpty then val m = message toAppend.foreach { (a, l) => a.appendLog(level, m) } - } - } - def log[T](level: Level.Value, message: ObjectEvent[T]): Unit = { + def log[T](level: Level.Value, message: ObjectEvent[T]): Unit = consoleAppenders.get.foreach { (a, l) => - if (level.compare(l) >= 0) a.appendObjectEvent(level, message) + if level.compare(l) >= 0 then a.appendObjectEvent(level, message) } - } def addAppender(newAppender: (Appender, Level.Value)): Unit = Util.ignoreResult(consoleAppenders.updateAndGet(_ :+ newAppender)) - def clearAppenders(): Unit = { + def clearAppenders(): Unit = consoleAppenders.get.foreach { case (a, _) => a.close() } consoleAppenders.set(Vector.empty) - } def appenders: Seq[Appender] = consoleAppenders.get.map(_._1) - } private val loggers = new ConcurrentHashMap[String, Log] private val closed = new AtomicBoolean(false) override def logger( name: String, channelName: Option[String], execId: Option[String] - ): ManagedLogger = { - if (closed.get) { + ): ManagedLogger = + if closed.get then throw new IllegalStateException("Tried to create logger for closed LoggerContext") - } val xlogger = new Log loggers.put(name, xlogger) new ManagedLogger(name, channelName, execId, xlogger, Some(Terminal.get), this) - } - override def clearAppenders(loggerName: String): Unit = { - loggers.get(loggerName) match { + override def clearAppenders(loggerName: String): Unit = + loggers.get(loggerName) match case null => case l => l.clearAppenders() - } - } override def addAppender( loggerName: String, appender: (Appender, Level.Value) - ): Unit = { - if (closed.get) { + ): Unit = + if closed.get then throw new IllegalStateException("Tried to add appender for closed LoggerContext") - } - loggers.get(loggerName) match { + loggers.get(loggerName) match case null => case l => l.addAppender(appender) - } - } - override def appenders(loggerName: String): Seq[Appender] = { - loggers.get(loggerName) match { + override def appenders(loggerName: String): Seq[Appender] = + loggers.get(loggerName) match case null => Nil case l => l.appenders - } - } - override def remove(name: String): Unit = { - loggers.remove(name) match { + override def remove(name: String): Unit = + loggers.remove(name) match case null => case l => l.clearAppenders() - } - } - def close(): Unit = { + def close(): Unit = closed.set(true) loggers.forEach((_, l) => l.clearAppenders()) loggers.clear() - } - } + end LoggerContextImpl private[sbt] def apply() = new LoggerContextImpl -} +end LoggerContext diff --git a/internal/util-logging/src/main/scala/sbt/util/ShowLines.scala b/internal/util-logging/src/main/scala/sbt/util/ShowLines.scala index dc6417ad6..737c7dca7 100644 --- a/internal/util-logging/src/main/scala/sbt/util/ShowLines.scala +++ b/internal/util-logging/src/main/scala/sbt/util/ShowLines.scala @@ -8,14 +8,10 @@ package sbt.util -trait ShowLines[A] { +trait ShowLines[A]: def showLines(a: A): Seq[String] -} -object ShowLines { +object ShowLines: def apply[A](f: A => Seq[String]): ShowLines[A] = (a: A) => f(a) - extension [A: ShowLines](a: A) { - def lines: Seq[String] = implicitly[ShowLines[A]].showLines(a) - } -} + extension [A: ShowLines](a: A) def lines: Seq[String] = implicitly[ShowLines[A]].showLines(a) diff --git a/internal/util-logging/src/test/scala/Escapes.scala b/internal/util-logging/src/test/scala/Escapes.scala index ead9e9c42..ad871dc5b 100644 --- a/internal/util-logging/src/test/scala/Escapes.scala +++ b/internal/util-logging/src/test/scala/Escapes.scala @@ -16,7 +16,7 @@ import EscHelpers.{ ESC, hasEscapeSequence, isEscapeTerminator, removeEscapeSequ import scala.annotation.tailrec -object Escapes extends Properties("Escapes") { +object Escapes extends Properties("Escapes"): property("genTerminator only generates terminators") = forAllNoShrink(genTerminator)((c: Char) => isEscapeTerminator(c)) @@ -69,39 +69,29 @@ object Escapes extends Properties("Escapes") { (original == removed) } - def diffIndex(expect: String, original: String): String = { + def diffIndex(expect: String, original: String): String = @tailrec - def loop(i: Int): Option[String] = { - if (i < expect.length && i < original.length) { - if (expect.charAt(i) != original.charAt(i)) { + def loop(i: Int): Option[String] = + if i < expect.length && i < original.length then + if expect.charAt(i) != original.charAt(i) then Some( "Differing character, idx: " + i + ", char: " + original.charAt(i) + ", expected: " + expect.charAt(i) ) - } else { - loop(i + 1) - } - } else { - None - } - } + else loop(i + 1) + else None loop(0).getOrElse( - if (expect.length != original.length) { - "Strings are different lengths!" - } else { - "No differences found" - } + if expect.length != original.length then "Strings are different lengths!" + else "No differences found" ) - } - final case class EscapeAndNot(escape: EscapeSequence, notEscape: String) { + final case class EscapeAndNot(escape: EscapeSequence, notEscape: String): override def toString = s"EscapeAntNot(escape = [$escape], notEscape = [${notEscape.map(_.toInt)}])" - } // 2.10.5 warns on "implicit numeric widening" but it looks like a bug: https://issues.scala-lang.org/browse/SI-8450 - final case class EscapeSequence(content: String, terminator: Char) { - if (!content.isEmpty) { + final case class EscapeSequence(content: String, terminator: Char): + if !content.isEmpty then assert( content.tail.forall(c => !isEscapeTerminator(c)), "Escape sequence content contains an escape terminator: '" + content + "'" @@ -110,14 +100,12 @@ object Escapes extends Properties("Escapes") { (content.head == '[') || !isEscapeTerminator(content.head), "Escape sequence content contains an escape terminator: '" + content.headOption + "'" ) - } assert(isEscapeTerminator(terminator)) def makeString: String = s"$ESC$content$terminator" override def toString = - if (content.isEmpty) s"ESC (${terminator.toInt})" + if content.isEmpty then s"ESC (${terminator.toInt})" else s"ESC ($content) (${terminator.toInt})" - } private def noEscape(s: String): String = s.replace(ESC, ' ') @@ -125,13 +113,13 @@ object Escapes extends Properties("Escapes") { oneOf(genKnownSequence, genTwoCharacterSequence, genArbitraryEscapeSequence) lazy val genEscapePair: Gen[EscapeAndNot] = - for (esc <- genEscapeSequence; not <- genWithoutEscape) yield EscapeAndNot(esc, not) + for esc <- genEscapeSequence; not <- genWithoutEscape yield EscapeAndNot(esc, not) lazy val genEscapePairs: Gen[List[EscapeAndNot]] = listOf(genEscapePair) lazy val genArbitraryEscapeSequence: Gen[EscapeSequence] = - for (content <- genWithoutTerminator if !content.isEmpty; term <- genTerminator) - yield new EscapeSequence("[" + content, term) + for content <- genWithoutTerminator if !content.isEmpty; term <- genTerminator + yield new EscapeSequence("[" + content, term) lazy val genKnownSequence: Gen[EscapeSequence] = oneOf((misc ++ setGraphicsMode ++ setMode ++ resetMode).map(toEscapeSequence)) @@ -141,8 +129,8 @@ object Escapes extends Properties("Escapes") { lazy val misc = Seq("14;23H", "5;3f", "2A", "94B", "19C", "85D", "s", "u", "2J", "K") lazy val setGraphicsMode: Seq[String] = - for (txt <- 0 to 8; fg <- 30 to 37; bg <- 40 to 47) - yield txt.toString + ";" + fg.toString + ";" + bg.toString + "m" + for txt <- 0 to 8; fg <- 30 to 37; bg <- 40 to 47 + yield txt.toString + ";" + fg.toString + ";" + bg.toString + "m" lazy val resetMode = setModeLike('I') lazy val setMode = setModeLike('h') @@ -159,8 +147,8 @@ object Escapes extends Properties("Escapes") { lazy val genWithoutEscape: Gen[String] = genRawString.map(noEscape) def genWithRandomEscapes: Gen[String] = - for (ls <- listOf(genRawString); end <- genRawString) - yield ls.mkString("", ESC.toString, ESC.toString + end) + for ls <- listOf(genRawString); end <- genRawString + yield ls.mkString("", ESC.toString, ESC.toString + end) private def genRawString = Arbitrary.arbString.arbitrary -} +end Escapes diff --git a/internal/util-logging/src/test/scala/LogWriterTest.scala b/internal/util-logging/src/test/scala/LogWriterTest.scala index b5f0eec4d..f8ce77e97 100644 --- a/internal/util-logging/src/test/scala/LogWriterTest.scala +++ b/internal/util-logging/src/test/scala/LogWriterTest.scala @@ -16,7 +16,7 @@ import Prop.* import java.io.Writer -object LogWriterTest extends Properties("Log Writer") { +object LogWriterTest extends Properties("Log Writer"): final val MaxLines = 100 final val MaxSegments = 10 @@ -37,27 +37,22 @@ object LogWriterTest extends Properties("Log Writer") { * `Log` types and non-printable characters should be escaped */ def show(event: LogEvent): String = - event match { + event match case l: Log => "Log('" + Escape(l.msg) + "', " + l.level + ")" case _ => "Not Log" - } /** * Writes the given lines to the Writer. `lines` is taken to be a list of lines, which are * represented as separately written segments (ToLog instances). ToLog.`byCharacter` indicates * whether to write the segment by character (true) or all at once (false) */ - def logLines(writer: Writer, lines: List[List[ToLog]], newLine: String): Unit = { - for (line <- lines; section <- line) { + def logLines(writer: Writer, lines: List[List[ToLog]], newLine: String): Unit = + for line <- lines; section <- line do val content = section.content val normalized = Escape.newline(content, newLine) - if (section.byCharacter) - normalized.foreach(c => writer.write(c.toInt)) - else - writer.write(normalized) - } + if section.byCharacter then normalized.foreach(c => writer.write(c.toInt)) + else writer.write(normalized) writer.flush() - } /** * Converts the given lines in segments to lines as Strings for checking the results of the test. @@ -85,23 +80,22 @@ object LogWriterTest extends Properties("Log Writer") { implicit lazy val arbLevel: Arbitrary[Level.Value] = Arbitrary(genLevel) implicit def genLine(using logG: Gen[ToLog]): Gen[List[ToLog]] = - for (l <- listOf[ToLog](MaxSegments); last <- logG) - yield (addNewline(last) :: l.filter(!_.content.isEmpty)).reverse + for l <- listOf[ToLog](MaxSegments); last <- logG + yield (addNewline(last) :: l.filter(!_.content.isEmpty)).reverse implicit def genLog(using content: Arbitrary[String], byChar: Arbitrary[Boolean]): Gen[ToLog] = - for (c <- content.arbitrary; by <- byChar.arbitrary) yield { + for c <- content.arbitrary; by <- byChar.arbitrary yield assert(c != null) new ToLog(removeNewlines(c), by) - } given genNewLine: Gen[NewLine] = - for (str <- oneOf("\n", "\r", "\r\n")) yield new NewLine(str) + for str <- oneOf("\n", "\r", "\r\n") yield new NewLine(str) given genLevel: Gen[Level.Value] = oneOf(Level.values.toSeq) given genOutput: Gen[Output] = - for (ls <- listOf[List[ToLog]](MaxLines); lv <- genLevel) yield new Output(ls, lv) + for ls <- listOf[List[ToLog]](MaxLines); lv <- genLevel yield new Output(ls, lv) def removeNewlines(s: String) = s.replaceAll("""[\n\r]+""", "") def addNewline(l: ToLog): ToLog = @@ -112,63 +106,54 @@ object LogWriterTest extends Properties("Log Writer") { def listOf[T](max: Int)(using content: Arbitrary[T]): Gen[List[T]] = Gen.choose(0, max) flatMap (sz => listOfN(sz, content.arbitrary)) -} +end LogWriterTest /* Helper classes*/ -final class Output(val lines: List[List[ToLog]], val level: Level.Value) { +final class Output(val lines: List[List[ToLog]], val level: Level.Value): override def toString = "Level: " + level + "\n" + lines.map(_.mkString).mkString("\n") -} -final class NewLine(val str: String) { +final class NewLine(val str: String): override def toString = Escape(str) -} -final class ToLog(val content: String, val byCharacter: Boolean) { +final class ToLog(val content: String, val byCharacter: Boolean): def contentOnly = Escape.newline(content, "") override def toString = - if (content.isEmpty) "" else "ToLog('" + Escape(contentOnly) + "', " + byCharacter + ")" -} + if content.isEmpty then "" else "ToLog('" + Escape(contentOnly) + "', " + byCharacter + ")" /** Defines some utility methods for escaping unprintable characters. */ -object Escape { +object Escape: /** Escapes characters with code less than 20 by printing them as unicode escapes. */ - def apply(s: String): String = { + def apply(s: String): String = val builder = new StringBuilder(s.length) - for (c <- s) { + for c <- s do val char = c.toInt def escaped = pad(char.toHexString.toUpperCase, 4, '0') - if (c < 20) builder.append("\\u").append(escaped) else builder.append(c) - } + if c < 20 then builder.append("\\u").append(escaped) else builder.append(c) builder.toString - } - def pad(s: String, minLength: Int, extra: Char) = { + def pad(s: String, minLength: Int, extra: Char) = val diff = minLength - s.length - if (diff <= 0) s else List.fill(diff)(extra).mkString("", "", s) - } + if diff <= 0 then s else List.fill(diff)(extra).mkString("", "", s) /** Replaces a \n character at the end of a string `s` with `nl`. */ def newline(s: String, nl: String): String = - if (s.endsWith("\n")) s.substring(0, s.length - 1) + nl else s - -} + if s.endsWith("\n") then s.substring(0, s.length - 1) + nl else s /** Records logging events for later retrieval. */ -final class RecordingLogger extends BasicLogger { +final class RecordingLogger extends BasicLogger: private var events: List[LogEvent] = Nil def getEvents = events.reverse def ansiCodesSupported = true - def trace(t: => Throwable): Unit = { events ::= new Trace(t) } - def log(level: Level.Value, message: => String): Unit = { events ::= new Log(level, message) } - def success(message: => String): Unit = { events ::= new Success(message) } - def logAll(es: Seq[LogEvent]): Unit = { events :::= es.toList } + def trace(t: => Throwable): Unit = events ::= new Trace(t) + def log(level: Level.Value, message: => String): Unit = events ::= new Log(level, message) + def success(message: => String): Unit = events ::= new Success(message) + def logAll(es: Seq[LogEvent]): Unit = events :::= es.toList def control(event: ControlEvent.Value, message: => String): Unit = events ::= new ControlEvent(event, message) -} diff --git a/internal/util-logging/src/test/scala/ProblemTest.scala b/internal/util-logging/src/test/scala/ProblemTest.scala index 2164fb393..a57dda41f 100644 --- a/internal/util-logging/src/test/scala/ProblemTest.scala +++ b/internal/util-logging/src/test/scala/ProblemTest.scala @@ -15,7 +15,7 @@ import _root_.sbt.util.InterfaceUtil import InterfaceUtil.{ jl2l, jo2o, l2jl } import xsbti.* -object ProblemTest extends Properties { +object ProblemTest extends Properties: override def tests: List[Test] = List( property( "All problems can toString", @@ -42,7 +42,7 @@ object ProblemTest extends Properties { def toStringCheck(p: Problem): Result = Result.assert(p.toString() != "") - def equalityCheck(p: Problem): Result = { + def equalityCheck(p: Problem): Result = val other = InterfaceUtil.problem( p.category, p.position, @@ -56,25 +56,21 @@ object ProblemTest extends Properties { Result .assert(p == other) .log(s"$p == $other") - } - def equalityCheck(c: DiagnosticCode): Result = { + def equalityCheck(c: DiagnosticCode): Result = val other = copy(c) Result.assert(c == other) - } - def equalityCheck(info: DiagnosticRelatedInformation): Result = { + def equalityCheck(info: DiagnosticRelatedInformation): Result = val other = copy(info) Result.assert(info == other) - } - def equalityCheck(a: Action): Result = { + def equalityCheck(a: Action): Result = val other = copy(a) Result.assert(a == other) - } lazy val genProblem: Gen[Problem] = - for { + for cat <- genString pos <- genPosition msg <- genString @@ -83,7 +79,7 @@ object ProblemTest extends Properties { code <- optDiagnosticCode info <- listDiagnosticRelatedInformation actions <- listAction - } yield InterfaceUtil.problem( + yield InterfaceUtil.problem( cat, pos, msg, @@ -98,19 +94,18 @@ object ProblemTest extends Properties { Gen.choice1(genDiagnosticCode.map(Some(_)), Gen.constant(None)) lazy val genDiagnosticCode: Gen[DiagnosticCode] = - for { - code <- Gen.int(Range.linear(0, 1024)) - } yield InterfaceUtil.diagnosticCode("E" + code.toString, None) + for code <- Gen.int(Range.linear(0, 1024)) + yield InterfaceUtil.diagnosticCode("E" + code.toString, None) lazy val genSeverity: Gen[Severity] = Gen.element(Severity.Info, List(Severity.Warn, Severity.Error)) lazy val genPosition: Gen[Position] = - for { + for line <- optIntGen content <- genString offset <- optIntGen - } yield InterfaceUtil.position( + yield InterfaceUtil.position( line, content, offset, @@ -130,39 +125,37 @@ object ProblemTest extends Properties { Gen.list(genDiagnosticRelatedInformation, Range.linear(0, 2)) lazy val genDiagnosticRelatedInformation: Gen[DiagnosticRelatedInformation] = - for { + for pos <- genPosition message <- genString - } yield InterfaceUtil.diagnosticRelatedInformation(pos, message) + yield InterfaceUtil.diagnosticRelatedInformation(pos, message) lazy val listAction: Gen[List[Action]] = Gen.list(genAction, Range.linear(0, 2)) lazy val genAction: Gen[Action] = - for { + for title <- genString description <- optString edit <- genWorkspaceEdit - } yield InterfaceUtil.action(title, description, edit) + yield InterfaceUtil.action(title, description, edit) lazy val genWorkspaceEdit: Gen[WorkspaceEdit] = - for { - changes <- listTextEdit - } yield InterfaceUtil.workspaceEdit(changes) + for changes <- listTextEdit + yield InterfaceUtil.workspaceEdit(changes) lazy val listTextEdit: Gen[List[TextEdit]] = Gen.list(genTextEdit, Range.linear(0, 2)) lazy val genTextEdit: Gen[TextEdit] = - for { + for pos <- genPosition newText <- genString - } yield InterfaceUtil.textEdit(pos, newText) + yield InterfaceUtil.textEdit(pos, newText) lazy val genUri: Gen[URI] = - for { - ssp <- genString - } yield new URI("file", "///" + ssp, null) + for ssp <- genString + yield new URI("file", "///" + ssp, null) lazy val optString: Gen[Option[String]] = Gen.choice1(genString.map(Some(_)), Gen.constant(None)) @@ -173,30 +166,26 @@ object ProblemTest extends Properties { Gen.choice1(Gen.int(Range.linear(0, 1024)).map(Some(_)), Gen.constant(None)) private def copy(c: DiagnosticCode): DiagnosticCode = - new DiagnosticCode() { + new DiagnosticCode(): val code = c.code override def explanation = c.explanation - } private def copy(info: DiagnosticRelatedInformation): DiagnosticRelatedInformation = - new DiagnosticRelatedInformation() { + new DiagnosticRelatedInformation(): override def position = info.position override def message = info.message - } private def copy(a: Action): Action = - new Action { + new Action: override def title = a.title override def description = a.description override def edit = copy(a.edit) - } private def copy(edit: WorkspaceEdit): WorkspaceEdit = () => l2jl(jl2l(edit.changes).map(copy)) private def copy(edit: TextEdit): TextEdit = - new TextEdit { + new TextEdit: override val position = edit.position override val newText = edit.newText - } -} +end ProblemTest diff --git a/internal/util-logging/src/test/scala/TestLogger.scala b/internal/util-logging/src/test/scala/TestLogger.scala index 80e46c8b6..7403c4061 100644 --- a/internal/util-logging/src/test/scala/TestLogger.scala +++ b/internal/util-logging/src/test/scala/TestLogger.scala @@ -10,10 +10,8 @@ package sbt.internal.util import sbt.util.* -object TestLogger { - def apply[T](f: Logger => T): T = { +object TestLogger: + def apply[T](f: Logger => T): T = val log = new BufferedLogger(ConsoleLogger()) log.setLevel(Level.Debug) log.bufferQuietly(f(log)) - } -} diff --git a/internal/util-logging/src/test/scala/sbt/internal/util/CleanStringSpec.scala b/internal/util-logging/src/test/scala/sbt/internal/util/CleanStringSpec.scala index 9d459fc9a..0e54aa84a 100644 --- a/internal/util-logging/src/test/scala/sbt/internal/util/CleanStringSpec.scala +++ b/internal/util-logging/src/test/scala/sbt/internal/util/CleanStringSpec.scala @@ -10,7 +10,7 @@ package sbt.internal.util import verify.BasicTestSuite -object CleanStringSpec extends BasicTestSuite { +object CleanStringSpec extends BasicTestSuite: test("EscHelpers should not modify normal strings") { val cleanString = s"1234" assert(EscHelpers.stripColorsAndMoves(cleanString) == cleanString) @@ -108,4 +108,4 @@ object CleanStringSpec extends BasicTestSuite { val expected2 = "[info] + com.acme.CoyoteTest.makeTrap 9ms \n" assert(new String(bytes2, 0, len2) == expected2) } -} +end CleanStringSpec diff --git a/internal/util-logging/src/test/scala/sbt/internal/util/TerminalColorSpec.scala b/internal/util-logging/src/test/scala/sbt/internal/util/TerminalColorSpec.scala index 69adc8d93..72e2c966c 100644 --- a/internal/util-logging/src/test/scala/sbt/internal/util/TerminalColorSpec.scala +++ b/internal/util-logging/src/test/scala/sbt/internal/util/TerminalColorSpec.scala @@ -18,7 +18,12 @@ object TerminalColorSpec extends BasicTestSuite: out: ByteArrayOutputStream ): Terminal.TerminalImpl = new Terminal.TerminalImpl( - new Terminal.WriteableInputStream(new InputStream { def read() = -1 }, "test"), + new Terminal.WriteableInputStream( + new InputStream: + def read() = -1 + , + "test" + ), out, new ByteArrayOutputStream(), "test" diff --git a/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala b/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala index 8000a88d9..dfb15e325 100644 --- a/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala +++ b/internal/util-logic/src/main/scala/sbt/internal/util/logic/Logic.scala @@ -36,17 +36,16 @@ as is this: */ /** Disjunction (or) of the list of clauses. */ -final case class Clauses(clauses: List[Clause]) { +final case class Clauses(clauses: List[Clause]): assert(clauses.nonEmpty, "At least one clause is required.") override def toString: String = s"Clauses(${clauses.mkString("\n")})" -} /** When the `body` Formula succeeds, atoms in `head` are true. */ final case class Clause(body: Formula, head: Set[Atom]) /** A literal is an [[Atom]] or its negation ([[Negated]]). */ -sealed abstract class Literal extends Formula { +sealed abstract class Literal extends Formula: /** The underlying (positive) atom. */ def atom: Atom @@ -54,56 +53,46 @@ sealed abstract class Literal extends Formula { /** Negates this literal. */ def unary_! : Literal -} - /** A variable with name `label`. */ -final case class Atom(label: String) extends Literal { +final case class Atom(label: String) extends Literal: def atom = this def unary_! : Negated = Negated(this) -} /** * A negated atom, in the sense of negation as failure, not logical negation. That is, it is true if * `atom` is not known/defined. */ -final case class Negated(atom: Atom) extends Literal { +final case class Negated(atom: Atom) extends Literal: def unary_! : Atom = atom -} /** * A formula consists of variables, negation, and conjunction (and). (Disjunction is not currently * included- it is modeled at the level of a sequence of clauses. This is less convenient when * defining clauses, but is not less powerful.) */ -sealed abstract class Formula { +sealed abstract class Formula: /** Constructs a clause that proves `atoms` when this formula is true. */ def proves(atom: Atom, atoms: Atom*): Clause = Clause(this, (atom +: atoms).toSet) /** Constructs a formula that is true iff this formula and `f` are both true. */ - def &&(f: Formula): Formula = (this, f) match { + def &&(f: Formula): Formula = (this, f) match case (True, x) => x case (x, True) => x case (And(as), And(bs)) => And(as ++ bs) case (And(as), b: Literal) => And(as + b) case (a: Literal, And(bs)) => And(bs + a) case (a: Literal, b: Literal) => And(Set(a, b)) - } -} - -object Formula { +object Formula: /** A conjunction of literals. */ - final case class And(literals: Set[Literal]) extends Formula { + final case class And(literals: Set[Literal]) extends Formula: assert(literals.nonEmpty, "'And' requires at least one literal.") - } case object True extends Formula -} - -object Logic { +object Logic: def reduceAll( clauses: List[Clause], initialFacts: Set[Literal] @@ -116,7 +105,7 @@ object Logic { * `initialFacts` cannot be in the head of any clauses in `clause`. These restrictions ensure that * the logic program has a unique minimal model. */ - def reduce(clauses: Clauses, initialFacts: Set[Literal]): Either[LogicException, Matched] = { + def reduce(clauses: Clauses, initialFacts: Set[Literal]): Either[LogicException, Matched] = val (posSeq, negSeq) = separate(initialFacts.toSeq) val (pos, neg) = (posSeq.toSet, negSeq.toSet) @@ -128,7 +117,6 @@ object Logic { problem.toLeft( reduce0(clauses, initialFacts, Matched.empty) ) - } /** * Verifies `initialFacts` are not in the head of any `clauses`. This avoids the situation where @@ -138,19 +126,17 @@ object Logic { private def checkOverlap( clauses: Clauses, initialFacts: Set[Atom] - ): Option[InitialOverlap] = { + ): Option[InitialOverlap] = val as = atoms(clauses) val initialOverlap = initialFacts.filter(as.inHead) - if (initialOverlap.nonEmpty) Some(new InitialOverlap(initialOverlap)) else None - } + if initialOverlap.nonEmpty then Some(new InitialOverlap(initialOverlap)) else None private def checkContradictions( pos: Set[Atom], neg: Set[Atom] - ): Option[InitialContradictions] = { + ): Option[InitialContradictions] = val contradictions = pos intersect neg - if (contradictions.nonEmpty) Some(new InitialContradictions(contradictions)) else None - } + if contradictions.nonEmpty then Some(new InitialContradictions(contradictions)) else None sealed abstract class LogicException(override val toString: String) @@ -170,21 +156,18 @@ object Logic { ) /** Tracks proven atoms in the reverse order they were proved. */ - final class Matched private (val provenSet: Set[Atom], reverseOrdered: List[Atom]) { + final class Matched private (val provenSet: Set[Atom], reverseOrdered: List[Atom]): def add(atoms: Set[Atom]): Matched = add(atoms.toList) - def add(atoms: List[Atom]): Matched = { + def add(atoms: List[Atom]): Matched = val newOnly = atoms.filterNot(provenSet) new Matched(provenSet ++ newOnly.toSet, newOnly ::: reverseOrdered) - } def ordered: List[Atom] = reverseOrdered.reverse override def toString = ordered.map(_.label).mkString("Matched(", ",", ")") - } - object Matched { + object Matched: val empty = new Matched(Set.empty, Nil) - } /** Separates a sequence of literals into `(pos, neg)` atom sequences. */ private def separate(lits: Seq[Literal]): (Seq[Atom], Seq[Atom]) = @@ -197,10 +180,9 @@ object Logic { * Finds clauses that have no body and thus prove their head. Returns `(, )`. */ - private def findProven(c: Clauses): (Set[Atom], List[Clause]) = { + private def findProven(c: Clauses): (Set[Atom], List[Clause]) = val (proven, unproven) = c.clauses.partition(_.body == True) (proven.flatMap(_.head).toSet, unproven) - } private def keepPositive(lits: Set[Literal]): Set[Atom] = lits.collect { case a: Atom => a } @@ -211,28 +193,26 @@ object Logic { factsToProcess: Set[Literal], state: Matched ): Matched = - applyAll(clauses, factsToProcess) match { + applyAll(clauses, factsToProcess) match case None => state // all of the remaining clauses failed on the new facts case Some(applied) => val (proven, unprovenClauses) = findProven(applied) val processedFacts = state.add(keepPositive(factsToProcess)) val newlyProven = proven -- processedFacts.provenSet val newState = processedFacts.add(newlyProven) - if (unprovenClauses.isEmpty) newState // no remaining clauses, done. - else { + if unprovenClauses.isEmpty then newState // no remaining clauses, done. + else val unproven = Clauses(unprovenClauses) val nextFacts: Set[Literal] = - if (newlyProven.nonEmpty) newlyProven.toSet[Literal] + if newlyProven.nonEmpty then newlyProven.toSet[Literal] else inferFailure(unproven) reduce0(unproven, nextFacts, newState) - } - } /** * Finds negated atoms under the negation as failure rule and returns them. This should be called * only after there are no more known atoms to be substituted. */ - private def inferFailure(clauses: Clauses): Set[Literal] = { + private def inferFailure(clauses: Clauses): Set[Literal] = /* At this point, there is at least one clause and one of the following is the case as the result of the acyclic negation rule: i. there is at least one variable that occurs in a clause body but not in the head of a @@ -245,16 +225,13 @@ object Logic { */ val allAtoms = atoms(clauses) val newFacts: Set[Literal] = negated(allAtoms.triviallyFalse) - if (newFacts.nonEmpty) newFacts - else { + if newFacts.nonEmpty then newFacts + else val possiblyTrue = hasNegatedDependency(clauses.clauses, Relation.empty, Relation.empty) val newlyFalse: Set[Literal] = negated(allAtoms.inHead -- possiblyTrue) - if (newlyFalse.nonEmpty) - newlyFalse + if newlyFalse.nonEmpty then newlyFalse else // should never happen due to the acyclic negation rule sys.error(s"No progress:\n\tclauses: $clauses\n\tpossibly true: $possiblyTrue") - } - } private def negated(atoms: Set[Atom]): Set[Literal] = atoms.map(a => (Negated(a): Literal)) @@ -269,7 +246,7 @@ object Logic { posDeps: Relation[Atom, Atom], negDeps: Relation[Atom, Atom] ): List[Atom] = - clauses match { + clauses match case Seq() => // because cycles between positive literals are allowed, this isn't strictly a topological sort Dag.topologicalSortUnchecked(negDeps._1s)(posDeps.reverse) @@ -281,7 +258,6 @@ object Logic { (pdeps.+(d, pos), ndeps.+(d, neg)) } hasNegatedDependency(tail, newPos, newNeg) - } /** Computes the `(positive, negative)` literals in `formula`. */ private def directDeps(formula: Formula): (Seq[Atom], Seq[Atom]) = @@ -290,27 +266,25 @@ object Logic { case a: Atom => Left(a) } - private def literals(formula: Formula): Set[Literal] = formula match { + private def literals(formula: Formula): Set[Literal] = formula match case And(lits) => lits case l: Literal => Set(l) case True => Set.empty - } /** Computes the atoms in the heads and bodies of the clauses in `clause`. */ def atoms(cs: Clauses): Atoms = cs.clauses.map(c => Atoms(c.head, atoms(c.body))).reduce(_ ++ _) /** Computes the set of all atoms in `formula`. */ - def atoms(formula: Formula): Set[Atom] = formula match { + def atoms(formula: Formula): Set[Atom] = formula match case And(lits) => lits.map(_.atom) case Negated(lit) => Set(lit) case a: Atom => Set(a) case True => Set() - } /** * Represents the set of atoms in the heads of clauses and in the bodies (formulas) of clauses. */ - final case class Atoms(inHead: Set[Atom], inFormula: Set[Atom]) { + final case class Atoms(inHead: Set[Atom], inFormula: Set[Atom]): /** Concatenates this with `as`. */ def ++(as: Atoms): Atoms = Atoms(inHead ++ as.inHead, inFormula ++ as.inFormula) @@ -318,8 +292,6 @@ object Logic { /** Atoms that cannot be true because they do not occur in a head. */ def triviallyFalse: Set[Atom] = inFormula -- inHead - } - /** * Applies known facts to `clause`s, deriving a new, possibly empty list of clauses. * 1. If a fact is in the body of a clause, the derived clause has that fact removed from the @@ -333,39 +305,33 @@ object Logic { * Postcondition: no atom in `facts` is present in the result Postcondition: No clauses have an * empty head */ - def applyAll(cs: Clauses, facts: Set[Literal]): Option[Clauses] = { + def applyAll(cs: Clauses, facts: Set[Literal]): Option[Clauses] = val newClauses = - if (facts.isEmpty) + if facts.isEmpty then cs.clauses.filter(_.head.nonEmpty) // still need to drop clauses with an empty head - else - cs.clauses.map(c => applyAll(c, facts)).flatMap(_.toList) - if (newClauses.isEmpty) None else Some(Clauses(newClauses)) - } + else cs.clauses.map(c => applyAll(c, facts)).flatMap(_.toList) + if newClauses.isEmpty then None else Some(Clauses(newClauses)) - def applyAll(c: Clause, facts: Set[Literal]): Option[Clause] = { + def applyAll(c: Clause, facts: Set[Literal]): Option[Clause] = val atoms = facts.map(_.atom) val newHead = c.head -- atoms // 3. - if (newHead.isEmpty) // 4. empty head + if newHead.isEmpty then // 4. empty head None - else - substitute(c.body, facts).map(f => Clause(f, newHead)) // 1, 2 - } + else substitute(c.body, facts).map(f => Clause(f, newHead)) // 1, 2 /** Derives the formula that results from substituting `facts` into `formula`. */ - @tailrec def substitute(formula: Formula, facts: Set[Literal]): Option[Formula] = formula match { + @tailrec def substitute(formula: Formula, facts: Set[Literal]): Option[Formula] = formula match case And(lits) => def negated(lits: Set[Literal]): Set[Literal] = lits.map(a => !a) - if (lits.exists(negated(facts))) // 2. + if lits.exists(negated(facts)) then // 2. None - else { + else val newLits = lits -- facts val newF = - if (newLits.isEmpty) (True: Formula) + if newLits.isEmpty then (True: Formula) else (And(newLits): Formula) Some(newF) // 1. - } case True => Some(True) case lit: Literal => // define in terms of And substitute(And(Set(lit)), facts) - } -} +end Logic diff --git a/internal/util-logic/src/test/scala/sbt/logic/Test.scala b/internal/util-logic/src/test/scala/sbt/logic/Test.scala index 5649ce7ee..5ed9e2ac0 100644 --- a/internal/util-logic/src/test/scala/sbt/logic/Test.scala +++ b/internal/util-logic/src/test/scala/sbt/logic/Test.scala @@ -13,7 +13,7 @@ import org.scalacheck.* import Prop.secure import Logic.{ LogicException, Matched } -object LogicTest extends Properties("Logic") { +object LogicTest extends Properties("Logic"): import TestClauses.* property("Handles trivial resolution.") = secure(expect(trivial, Set(A))) @@ -34,18 +34,16 @@ object LogicTest extends Properties("Logic") { ) */ - def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match { + def expect(result: Either[LogicException, Matched], expected: Set[Atom]) = result match case Left(_) => false case Right(res) => val actual = res.provenSet - if (actual != expected) + if actual != expected then sys.error(s"Expected to prove $expected, but actually proved $actual") - else - true - } -} + else true +end LogicTest -object TestClauses { +object TestClauses: val A = Atom("A") val B = Atom("B") @@ -71,7 +69,7 @@ object TestClauses { A.proves(D) :: clauses - val excludedNeg = { + val excludedNeg = val cs = (!A).proves(B) :: Nil @@ -80,9 +78,8 @@ object TestClauses { (!B) :: Nil Logic.reduceAll(cs, init.toSet) - } - val excludedPos = { + val excludedPos = val cs = A.proves(B) :: Nil @@ -91,39 +88,34 @@ object TestClauses { (!B) :: Nil Logic.reduceAll(cs, init.toSet) - } - val trivial = { + val trivial = val cs = Formula.True.proves(A) :: Nil Logic.reduceAll(cs, Set.empty) - } - val lessTrivial = { + val lessTrivial = val cs = Formula.True.proves(A) :: Formula.True.proves(B) :: (A && B && (!C)).proves(D) :: Nil Logic.reduceAll(cs, Set()) - } - val ordering = { + val ordering = val cs = E.proves(F) :: (C && !D).proves(E) :: (A && B).proves(C) :: Nil Logic.reduceAll(cs, Set(A, B)) - } - def all(): Unit = { + def all(): Unit = println(s"Cycles: $cycles") println(s"xNeg: $excludedNeg") println(s"xPos: $excludedPos") println(s"trivial: $trivial") println(s"lessTrivial: $lessTrivial") println(s"ordering: $ordering") - } -} +end TestClauses diff --git a/internal/util-position/src/main/scala-3/sbt/internal/util/SourcePositionMacro.scala b/internal/util-position/src/main/scala-3/sbt/internal/util/SourcePositionMacro.scala index 130c7d753..ab8605059 100644 --- a/internal/util-position/src/main/scala-3/sbt/internal/util/SourcePositionMacro.scala +++ b/internal/util-position/src/main/scala-3/sbt/internal/util/SourcePositionMacro.scala @@ -9,7 +9,7 @@ package sbt.internal.util import scala.quoted.{ Expr, Quotes, quotes } -abstract class SourcePositionImpl { +abstract class SourcePositionImpl: /** * Creates a SourcePosition by using the enclosing position of the invocation of this method. @@ -18,11 +18,10 @@ abstract class SourcePositionImpl { */ inline def fromEnclosing(): SourcePosition = ${ SourcePositionImpl.fromEnclosingImpl } -} -object SourcePositionImpl { +object SourcePositionImpl: - def fromEnclosingImpl(using Quotes): Expr[SourcePosition] = { + def fromEnclosingImpl(using Quotes): Expr[SourcePosition] = val pos = quotes.reflect.Position.ofMacroExpansion if pos.startLine >= 0 then '{ @@ -32,5 +31,3 @@ object SourcePositionImpl { ).withSourceCode(${ Expr(pos.sourceCode) }) } else '{ NoPosition } - } -} diff --git a/internal/util-position/src/main/scala/sbt/internal/util/Positions.scala b/internal/util-position/src/main/scala/sbt/internal/util/Positions.scala index ad4e611a6..c50e9c1d3 100644 --- a/internal/util-position/src/main/scala/sbt/internal/util/Positions.scala +++ b/internal/util-position/src/main/scala/sbt/internal/util/Positions.scala @@ -10,15 +10,14 @@ package sbt.internal.util sealed trait SourcePosition -sealed trait FilePosition extends SourcePosition { +sealed trait FilePosition extends SourcePosition: def path: String def startLine: Int def sourceCode: Option[String] -} case object NoPosition extends SourcePosition -final case class LinePosition(path: String, startLine: Int) extends FilePosition { +final case class LinePosition(path: String, startLine: Int) extends FilePosition: private var _sourceCode: Option[String] = None def sourceCode: Option[String] = _sourceCode def withSourceCode(code: String): LinePosition = @@ -29,13 +28,11 @@ final case class LinePosition(path: String, startLine: Int) extends FilePosition c match case Some(code) => this.withSourceCode(code) case None => this -} -final case class LineRange(start: Int, end: Int) { +final case class LineRange(start: Int, end: Int): def shift(n: Int) = new LineRange(start + n, end + n) -} -final case class RangePosition(path: String, range: LineRange) extends FilePosition { +final case class RangePosition(path: String, range: LineRange) extends FilePosition: private var _sourceCode: Option[String] = None def startLine = range.start def sourceCode: Option[String] = _sourceCode @@ -47,6 +44,5 @@ final case class RangePosition(path: String, range: LineRange) extends FilePosit c match case Some(code) => this.withSourceCode(code) case None => this -} object SourcePosition extends SourcePositionImpl diff --git a/internal/util-position/src/test/scala/sbt/internal/util/SourcePositionSpec.scala b/internal/util-position/src/test/scala/sbt/internal/util/SourcePositionSpec.scala index 8d4ecabea..bad346364 100644 --- a/internal/util-position/src/test/scala/sbt/internal/util/SourcePositionSpec.scala +++ b/internal/util-position/src/test/scala/sbt/internal/util/SourcePositionSpec.scala @@ -17,7 +17,7 @@ object SourcePositionSpec extends Properties: "SourcePosition() should return a SourcePosition", { val filename = "SourcePositionSpec.scala" val lineNumber = 19 - SourcePosition.fromEnclosing() match { + SourcePosition.fromEnclosing() match case pos @ LinePosition(path, startLine) => Result.assert(path == filename && startLine == lineNumber).log(pos.toString()) Result @@ -26,7 +26,6 @@ object SourcePositionSpec extends Properties: case pos @ RangePosition(path, range) => Result.assert(path == filename && inRange(range, lineNumber)).log(pos.toString()) case NoPosition => Result.assert(false).log("No source position found") - } } ) ) diff --git a/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala b/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala index b6e9179e1..eeb1fd1e9 100644 --- a/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala +++ b/internal/util-relation/src/main/scala/sbt/internal/util/Relation.scala @@ -10,7 +10,7 @@ package sbt.internal.util import Relation.* -object Relation { +object Relation: /** Constructs a new immutable, finite relation that is initially empty. */ def empty[A, B]: Relation[A, B] = make(Map.empty, Map.empty) @@ -27,24 +27,22 @@ object Relation { * Constructs a relation such that for every entry `_1 -> _2s` in `forward` and every `_2` in * `_2s`, `(_1, _2)` is in the relation. */ - def reconstruct[A, B](forward: Map[A, Set[B]]): Relation[A, B] = { - val reversePairs = for ((a, bs) <- forward.view; b <- bs.view) yield (b, a) + def reconstruct[A, B](forward: Map[A, Set[B]]): Relation[A, B] = + val reversePairs = for (a, bs) <- forward.view; b <- bs.view yield (b, a) val reverse = reversePairs.foldLeft(Map.empty[B, Set[A]]) { case (m, (b, a)) => add(m, b, a :: Nil) } make(forward filter { (a, bs) => bs.nonEmpty }, reverse) - } def merge[A, B](rels: Iterable[Relation[A, B]]): Relation[A, B] = rels.foldLeft(Relation.empty[A, B])(_ ++ _) private[sbt] def remove[X, Y](map: M[X, Y], from: X, to: Y): M[X, Y] = - map.get(from) match { + map.get(from) match case Some(tos) => val newSet = tos - to - if (newSet.isEmpty) map - from else map.updated(from, newSet) + if newSet.isEmpty then map - from else map.updated(from, newSet) case None => map - } private[sbt] def combine[X, Y](a: M[X, Y], b: M[X, Y]): M[X, Y] = b.foldLeft(a)((map, mapping) => add(map, mapping._1, mapping._2)) @@ -57,7 +55,7 @@ object Relation { private[sbt] type M[X, Y] = Map[X, Set[Y]] /** when both parameters taken by relation are the same type, switch calls a function on them. */ - private[sbt] def switch[X, Y](relation: Relation[X, X], f: X => Y): Relation[Y, Y] = { + private[sbt] def switch[X, Y](relation: Relation[X, X], f: X => Y): Relation[Y, Y] = val forward = relation.forwardMap.map { (first, second) => f(first) -> second.map(f) } @@ -65,11 +63,10 @@ object Relation { f(first) -> second.map(f) } make(forward, reverse) - } -} +end Relation /** Binary relation between A and B. It is a set of pairs (_1, _2) for _1 in A, _2 in B. */ -trait Relation[A, B] { +trait Relation[A, B]: /** Returns the set of all `_2`s such that `(_1, _2)` is in this relation. */ def forward(_1: A): Set[B] @@ -153,11 +150,11 @@ trait Relation[A, B] { * is in this relation. */ def reverseMap: Map[B, Set[A]] -} +end Relation // Note that we assume without checking that fwd and rev are consistent. private final class MRelation[A, B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) - extends Relation[A, B] { + extends Relation[A, B]: def forwardMap = fwd def reverseMap = rev @@ -175,7 +172,7 @@ private final class MRelation[A, B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) def +(pair: (A, B)) = this + (pair._1, Set(pair._2)) def +(from: A, to: B) = this + (from, to :: Nil) def +(from: A, to: Iterable[B]) = - if (to.isEmpty) this + if to.isEmpty then this else new MRelation(add(fwd, from, to), to.foldLeft(rev)((map, t) => add(map, t, from :: Nil))) def ++(rs: Iterable[(A, B)]) = rs.foldLeft(this: Relation[A, B]) { _ + _ } @@ -191,35 +188,32 @@ private final class MRelation[A, B](fwd: Map[A, Set[B]], rev: Map[B, Set[A]]) new MRelation(remove(fwd, pair._1, pair._2), remove(rev, pair._2, pair._1)) def -(t: A): Relation[A, B] = - fwd.get(t) match { + fwd.get(t) match case Some(rs) => val upRev = rs.foldLeft(rev)((map, r) => remove(map, r, t)) new MRelation(fwd - t, upRev) case None => this - } def filter(f: (A, B) => Boolean): Relation[A, B] = Relation.empty[A, B] ++ all.filter(f.tupled) - def partition(f: (A, B) => Boolean): (Relation[A, B], Relation[A, B]) = { + def partition(f: (A, B) => Boolean): (Relation[A, B], Relation[A, B]) = val (y, n) = all.partition(f.tupled) (Relation.empty[A, B] ++ y, Relation.empty[A, B] ++ n) - } def groupBy[K](discriminator: ((A, B)) => K): Map[K, Relation[A, B]] = all.groupBy(discriminator).view.mapValues { Relation.empty[A, B] ++ _ }.toMap def contains(a: A, b: B): Boolean = forward(a)(b) - override def equals(other: Any) = other match { + override def equals(other: Any) = other match // We assume that the forward and reverse maps are consistent, so we only use the forward map // for equality. Note that key -> Empty is semantically the same as key not existing. case o: MRelation[?, ?] => forwardMap.filterNot(_._2.isEmpty) == o.forwardMap.filterNot(_._2.isEmpty) case _ => false - } override def hashCode = fwd.filterNot(_._2.isEmpty).hashCode() override def toString = all.map { (a, b) => s"$a -> $b" }.mkString("Relation [", ", ", "]") -} +end MRelation diff --git a/internal/util-relation/src/test/scala/RelationTest.scala b/internal/util-relation/src/test/scala/RelationTest.scala index d280c4c5e..886216274 100644 --- a/internal/util-relation/src/test/scala/RelationTest.scala +++ b/internal/util-relation/src/test/scala/RelationTest.scala @@ -11,12 +11,12 @@ package sbt.internal.util import org.scalacheck.* import Prop.* -object RelationTest extends Properties("Relation") { +object RelationTest extends Properties("Relation"): property("Added entry check") = forAll { (pairs: List[(Int, Double)]) => val r = Relation.empty[Int, Double] ++ pairs check(r, pairs) } - def check(r: Relation[Int, Double], pairs: Seq[(Int, Double)]) = { + def check(r: Relation[Int, Double], pairs: Seq[(Int, Double)]) = val _1s = pairs.map(_._1).toSet val _2s = pairs.map(_._2).toSet @@ -28,7 +28,6 @@ object RelationTest extends Properties("Relation") { (r.forwardMap(a) contains b) && (r.reverseMap(b) contains a) } - } property("Does not contain removed entries") = forAll { (pairs: List[(Int, Double, Boolean)]) => val add = pairs.map { (a, b, c) => (a, b) } @@ -71,10 +70,10 @@ object RelationTest extends Properties("Relation") { } def all[T](s: Seq[T])(p: T => Prop): Prop = - if (s.isEmpty) true else s.map(p).reduceLeft(_ && _) -} + if s.isEmpty then true else s.map(p).reduceLeft(_ && _) +end RelationTest -object EmptyRelationTest extends Properties("Empty relation") { +object EmptyRelationTest extends Properties("Empty relation"): lazy val e = Relation.empty[Int, Double] property("Forward empty") = forAll((i: Int) => e.forward(i).isEmpty) @@ -83,4 +82,3 @@ object EmptyRelationTest extends Properties("Empty relation") { property("Reverse map empty") = e.reverseMap.isEmpty property("_1 empty") = e._1s.isEmpty property("_2 empty") = e._2s.isEmpty -} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/CommentHandler.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/CommentHandler.scala index d832536d0..2dffec766 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/CommentHandler.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/CommentHandler.scala @@ -10,6 +10,5 @@ package sbt package internal package scripted -object CommentHandler extends BasicStatementHandler { +object CommentHandler extends BasicStatementHandler: def apply(command: String, args: List[String]) = () -} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala index 8a5c34412..195608ede 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/FileCommands.scala @@ -16,7 +16,7 @@ import sbt.io.{ IO, Path } import sbt.io.syntax.* import Path.* -class FileCommands(baseDirectory: File) extends BasicStatementHandler { +class FileCommands(baseDirectory: File) extends BasicStatementHandler: final val OR = "||" lazy val view = FileTreeView.Ops(FileTreeView.default) val baseGlob = Glob(baseDirectory) @@ -46,10 +46,9 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { ) def apply(command: String, arguments: List[String]): Unit = - commands.get(command).map(_(arguments)) match { + commands.get(command).map(_(arguments)) match case Some(_) => () case _ => scriptError("Unknown command " + command); () - } def scriptError(message: String): Unit = sys.error("Test script error: " + message) def spaced[T](l: Seq[T]) = l.mkString(" ") @@ -77,6 +76,7 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { List(combined) if exprs.contains("||") then orGlobs else globs(exprs) + end filterFromStrings def touch(paths: List[String]): Unit = IO.touch(fromStrings(paths)) def delete(paths: List[String]): Unit = @@ -92,47 +92,37 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { IO.copyFile(fromString(from), fromString(to)) def makeDirectories(paths: List[String]) = IO.createDirectories(fromStrings(paths)) - def diffFiles(file1: String, file2: String): Unit = { + def diffFiles(file1: String, file2: String): Unit = val lines1 = IO.readLines(fromString(file1)) val lines2 = IO.readLines(fromString(file2)) - if (lines1 != lines2) + if lines1 != lines2 then scriptError( "File contents are different:\n" + lines1.mkString("\n") + "\nAnd:\n" + lines2.mkString("\n") ) - } - def newer(a: String, b: String): Unit = { + def newer(a: String, b: String): Unit = val pathA = fromString(a) val pathB = fromString(b) val isNewer = pathA.exists && (!pathB.exists || IO.getModifiedTimeOrZero(pathA) > IO.getModifiedTimeOrZero(pathB)) - if (!isNewer) { - scriptError(s"$pathA is not newer than $pathB") - } - } + if !isNewer then scriptError(s"$pathA is not newer than $pathB") // use FileTreeView to test if a file with the given filter exists def exists0(filter: PathFilter): Boolean = view.list(baseGlob / RecursiveGlob, filter).nonEmpty - def exists(paths: List[String]): Unit = { + def exists(paths: List[String]): Unit = val notPresent = filterFromStrings(paths).filter(!exists0(_)) - if (notPresent.nonEmpty) + if notPresent.nonEmpty then scriptError("File(s) did not exist: " + notPresent.mkString("[ ", " , ", " ]")) - } - def absent(paths: List[String]): Unit = { + def absent(paths: List[String]): Unit = val present = filterFromStrings(paths).filter(exists0) - if (present.nonEmpty) - scriptError("File(s) existed: " + present.mkString("[ ", " , ", " ]")) - } + if present.nonEmpty then scriptError("File(s) existed: " + present.mkString("[ ", " , ", " ]")) def execute(command: List[String]): Unit = execute0(command.head, command.tail) - def execute0(command: String, args: List[String]): Unit = { - if (command.trim.isEmpty) scriptError("Command was empty.") - else { + def execute0(command: String, args: List[String]): Unit = + if command.trim.isEmpty then scriptError("Command was empty.") + else val exitValue = sys.process.Process(command :: args, baseDirectory).! - if (exitValue != 0) - sys.error("Nonzero exit value (" + exitValue + ")") - } - } + if exitValue != 0 then sys.error("Nonzero exit value (" + exitValue + ")") type NamedCommand = (String, List[String] => Unit) @@ -140,10 +130,8 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { extension (commandName: String) def nonEmpty(action: List[String] => Unit): NamedCommand = commandName -> { paths => - if (paths.isEmpty) - scriptError("No arguments specified for " + commandName + " command.") - else - action(paths) + if paths.isEmpty then scriptError("No arguments specified for " + commandName + " command.") + else action(paths) } def twoArg(requiredArgs: String, action: (String, String) => Unit): NamedCommand = commandName -> { @@ -181,4 +169,5 @@ class FileCommands(baseDirectory: File) extends BasicStatementHandler { "Wrong number of arguments to " + commandName + " command. " + requiredArgs + " required, found: '" + spaced(args) + "'." ) -} + end extension +end FileCommands diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/FilteredLoader.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/FilteredLoader.scala index efaa8a6d8..fc4729d3d 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/FilteredLoader.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/FilteredLoader.scala @@ -13,14 +13,11 @@ package scripted import java.util as ju import java.net.URL -final class FilteredLoader(parent: ClassLoader) extends ClassLoader(parent) { +final class FilteredLoader(parent: ClassLoader) extends ClassLoader(parent): @throws(classOf[ClassNotFoundException]) - override final def loadClass(className: String, resolve: Boolean): Class[?] = { - if (className.startsWith("java.") || className.startsWith("javax.")) + override final def loadClass(className: String, resolve: Boolean): Class[?] = + if className.startsWith("java.") || className.startsWith("javax.") then super.loadClass(className, resolve) - else - throw new ClassNotFoundException(className) - } + else throw new ClassNotFoundException(className) override def getResources(name: String): ju.Enumeration[URL] = null override def getResource(name: String): URL = null -} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/HandlersProvider.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/HandlersProvider.scala index 06ccdf023..5b9c76fa6 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/HandlersProvider.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/HandlersProvider.scala @@ -8,6 +8,5 @@ package sbt.internal.scripted -trait HandlersProvider { +trait HandlersProvider: def getHandlers(config: ScriptConfig): Map[Char, StatementHandler] -} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptRunner.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptRunner.scala index 85b61ad59..1e3b65d17 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptRunner.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptRunner.scala @@ -13,46 +13,35 @@ package scripted final class TestException(statement: Statement, msg: String, exception: Throwable) extends RuntimeException(statement.linePrefix + " " + msg, exception) -class ScriptRunner { +class ScriptRunner: import scala.collection.mutable.HashMap - def apply(statements: List[(StatementHandler, Statement)]): Unit = { + def apply(statements: List[(StatementHandler, Statement)]): Unit = val states = new HashMap[StatementHandler, Any] - def processStatement(handler: StatementHandler, statement: Statement): Unit = { + def processStatement(handler: StatementHandler, statement: Statement): Unit = val state = states(handler).asInstanceOf[handler.State] val nextState = - try { - Right(handler(statement.command, statement.arguments, state)) - } catch { - case e: Exception => Left(e) - } - nextState match { + try Right(handler(statement.command, statement.arguments, state)) + catch case e: Exception => Left(e) + nextState match case Left(err) => - if (statement.successExpected) { - err match { + if statement.successExpected then + err match case t: TestFailed => throw new TestException(statement, "Command failed: " + t.getMessage, null) case _ => throw new TestException(statement, "Command failed", err) - } - } else - () + else () case Right(s) => - if (statement.successExpected) - states(handler) = s + if statement.successExpected then states(handler) = s else throw new TestException(statement, "Command succeeded but failure was expected", null) - } - } val handlers = Set() ++ statements.map(_._1) - try { + try handlers.foreach(handler => states(handler) = handler.initialState) statements foreach (Function.tupled(processStatement)) - } finally { - for (handler <- handlers; state <- states.get(handler)) { - try { - handler.finish(state.asInstanceOf[handler.State]) - } catch { case e: Exception => () } - } - } - } -} + finally + for handler <- handlers; state <- states.get(handler) do + try handler.finish(state.asInstanceOf[handler.State]) + catch case e: Exception => () + end apply +end ScriptRunner diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala index c9db381c5..9cfc856c6 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/ScriptedTests.scala @@ -19,13 +19,13 @@ import sbt.io.syntax.* import sbt.internal.io.Resources import java.util.concurrent.atomic.AtomicInteger -object ScriptedRunnerImpl { +object ScriptedRunnerImpl: def run( resourceBaseDirectory: File, bufferLog: Boolean, tests: Array[String], handlersProvider: HandlersProvider - ): Unit = { + ): Unit = val context = LoggerContext() val runner = new ScriptedTests(resourceBaseDirectory, bufferLog, handlersProvider) val logger = newLogger(context) @@ -33,38 +33,32 @@ object ScriptedRunnerImpl { case ScriptedTest(group, name) => runner.scriptedTest(group, name, logger, context) } - if (tests.nonEmpty && allTests.isEmpty) { + if tests.nonEmpty && allTests.isEmpty then sys.error(s"No tests found matching: ${tests.mkString(", ")}") - } runAll(allTests) - } - def runAll(tests: Seq[() => Option[String]]): Unit = { - val errors = for (test <- tests; err <- test()) yield err - if (errors.nonEmpty) - sys.error(errors.mkString("Failed tests:\n\t", "\n\t", "\n")) - } + def runAll(tests: Seq[() => Option[String]]): Unit = + val errors = for test <- tests; err <- test() yield err + if errors.nonEmpty then sys.error(errors.mkString("Failed tests:\n\t", "\n\t", "\n")) def get(tests: Seq[String], baseDirectory: File, log: ManagedLogger): Seq[ScriptedTest] = - if (tests.isEmpty) listTests(baseDirectory, log) else parseTests(tests) + if tests.isEmpty then listTests(baseDirectory, log) else parseTests(tests) def listTests(baseDirectory: File, log: ManagedLogger): Seq[ScriptedTest] = (new ListTests(baseDirectory, _ => true, log)).listTests def parseTests(in: Seq[String]): Seq[ScriptedTest] = - for (testString <- in) yield { + for testString <- in yield val Array(group, name) = testString.split("/").map(_.trim) ScriptedTest(group, name) - } private[sbt] val generateId: AtomicInteger = new AtomicInteger - private[sbt] def newLogger(context: LoggerContext): ManagedLogger = { + private[sbt] def newLogger(context: LoggerContext): ManagedLogger = val loggerName = "scripted-" + generateId.incrementAndGet context.logger(loggerName, None, None) - } -} +end ScriptedRunnerImpl final class ScriptedTests( resourceBaseDirectory: File, bufferLog: Boolean, handlersProvider: HandlersProvider, stripQuotes: Boolean -) { +): def this(resourceBaseDirectory: File, bufferLog: Boolean, handlersProvider: HandlersProvider) = this(resourceBaseDirectory, bufferLog, handlersProvider, true) private val testResources = new Resources(resourceBaseDirectory) @@ -105,29 +99,23 @@ final class ScriptedTests( prescripted: File => Unit, log: ManagedLogger, context: LoggerContext, - ): Seq[() => Option[String]] = { - for (groupDir <- (resourceBaseDirectory * group).get(); nme <- (groupDir * name).get()) yield { + ): Seq[() => Option[String]] = + for groupDir <- (resourceBaseDirectory * group).get(); nme <- (groupDir * name).get() yield val g = groupDir.getName val n = nme.getName val str = s"$g / $n" - () => { + () => println("Running " + str) testResources.readWriteResourceDirectory(g, n) { testDirectory => val disabled = new File(testDirectory, "disabled").isFile - if (disabled) { + if disabled then log.info("D " + str + " [DISABLED]") None - } else { - try { + else + try scriptedTest(str, testDirectory, prescripted, log, context); None - } catch { - case _: TestException | _: PendingTestSuccessException => Some(str) - } - } + catch case _: TestException | _: PendingTestSuccessException => Some(str) } - } - } - } private def scriptedTest( label: String, @@ -135,104 +123,88 @@ final class ScriptedTests( prescripted: File => Unit, log: ManagedLogger, context: LoggerContext, - ): Unit = { + ): Unit = val buffered = BufferedAppender(appender) context.clearAppenders(log.name) context.addAppender(log.name, (buffered -> Level.Debug)) - if (bufferLog) { - buffered.record() - } - def createParser() = { + if bufferLog then buffered.record() + def createParser() = // val fileHandler = new FileCommands(testDirectory) // // val sbtHandler = new SbtHandler(testDirectory, launcher, buffered, launchOpts) // new TestScriptParser(Map('$' -> fileHandler, /* '>' -> sbtHandler, */ '#' -> CommentHandler)) val scriptConfig = new ScriptConfig(label, testDirectory, log) new TestScriptParser(handlersProvider.getHandlers(scriptConfig)) - } - val (file, pending) = { + val (file, pending) = val normal = new File(testDirectory, ScriptFilename) val pending = new File(testDirectory, PendingScriptFilename) - if (pending.isFile) (pending, true) else (normal, false) - } - val pendingString = if (pending) " [PENDING]" else "" + if pending.isFile then (pending, true) else (normal, false) + val pendingString = if pending then " [PENDING]" else "" - def runTest(): Unit = { + def runTest(): Unit = val run = new ScriptRunner val parser = createParser() run(parser.parse(file, stripQuotes)) - } - def testFailed(): Unit = { - if (pending) buffered.clearBuffer() else buffered.stopBuffer() + def testFailed(): Unit = + if pending then buffered.clearBuffer() else buffered.stopBuffer() log.error("x " + label + pendingString) - } - try { + try prescripted(testDirectory) runTest() log.info("+ " + label + pendingString) - if (pending) throw new PendingTestSuccessException(label) - } catch { + if pending then throw new PendingTestSuccessException(label) + catch case e: TestException => testFailed() - e.getCause match { + e.getCause match case null | _: java.net.SocketException => log.error(" " + e.getMessage) - case _ => if (!pending) e.printStackTrace - } - if (!pending) throw e + case _ => if !pending then e.printStackTrace + if !pending then throw e case e: PendingTestSuccessException => testFailed() log.error(" Mark as passing to remove this failure.") throw e case e: Exception => testFailed() - if (!pending) throw e - } finally { - buffered.clearBuffer() - } - } -} + if !pending then throw e + finally buffered.clearBuffer() + end scriptedTest +end ScriptedTests // object ScriptedTests extends ScriptedRunner { // val emptyCallback: File => Unit = { _ => () } // } -final case class ScriptedTest(group: String, name: String) { +final case class ScriptedTest(group: String, name: String): override def toString = group + "/" + name -} -object ListTests { +object ListTests: def list(directory: File, filter: java.io.FileFilter): Seq[File] = wrapNull(directory.listFiles(filter)).toSeq -} import ListTests.* -final class ListTests(baseDirectory: File, accept: ScriptedTest => Boolean, log: Logger) { +final class ListTests(baseDirectory: File, accept: ScriptedTest => Boolean, log: Logger): def filter = DirectoryFilter -- HiddenFileFilter - def listTests: Seq[ScriptedTest] = { + def listTests: Seq[ScriptedTest] = list(baseDirectory, filter) flatMap { group => val groupName = group.getName listTests(group).map(ScriptedTest(groupName, _)) } - } - private def listTests(group: File): Seq[String] = { + private def listTests(group: File): Seq[String] = val groupName = group.getName val allTests = list(group, filter).sortBy(_.getName) - if (allTests.isEmpty) { + if allTests.isEmpty then log.warn("No tests in test group " + groupName) Seq.empty - } else { + else val (included, skipped) = allTests.toList.partition(test => accept(ScriptedTest(groupName, test.getName))) - if (included.isEmpty) log.warn("Test group " + groupName + " skipped.") - else if (skipped.nonEmpty) { + if included.isEmpty then log.warn("Test group " + groupName + " skipped.") + else if skipped.nonEmpty then log.warn("Tests skipped in group " + group.getName + ":") skipped.foreach(testName => log.warn(" " + testName.getName)) - } Seq(included.map(_.getName)*) - } - } -} +end ListTests -class PendingTestSuccessException(label: String) extends Exception { +class PendingTestSuccessException(label: String) extends Exception: override def getMessage: String = s"The pending test $label succeeded. Mark this test as passing to remove this failure." -} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/StatementHandler.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/StatementHandler.scala index 872ecbb1c..9d22a94fa 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/StatementHandler.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/StatementHandler.scala @@ -10,14 +10,13 @@ package sbt package internal package scripted -trait StatementHandler { +trait StatementHandler: type State def initialState: State def apply(command: String, arguments: List[String], state: State): State def finish(state: State): Unit -} -trait BasicStatementHandler extends StatementHandler { +trait BasicStatementHandler extends StatementHandler: final type State = Unit final def initialState: State = () @@ -26,9 +25,7 @@ trait BasicStatementHandler extends StatementHandler { def apply(command: String, arguments: List[String]): Unit def finish(state: Unit) = () -} /** Use when a stack trace is not useful */ -final class TestFailed(msg: String) extends RuntimeException(msg) { +final class TestFailed(msg: String) extends RuntimeException(msg): override def fillInStackTrace: Throwable = this -} diff --git a/internal/util-scripted/src/main/scala/sbt/internal/scripted/TestScriptParser.scala b/internal/util-scripted/src/main/scala/sbt/internal/scripted/TestScriptParser.scala index faa04cb44..c0124ee2b 100644 --- a/internal/util-scripted/src/main/scala/sbt/internal/scripted/TestScriptParser.scala +++ b/internal/util-scripted/src/main/scala/sbt/internal/scripted/TestScriptParser.scala @@ -30,25 +30,22 @@ final case class Statement( arguments: List[String], successExpected: Boolean, line: Int -) { +): def linePrefix = "{line " + line + "} " -} -private object TestScriptParser { +private object TestScriptParser: val SuccessLiteral = "success" val FailureLiteral = "failure" val WordRegex = """[^ \[\]\s'\"][^ \[\]\s]*""".r -} import TestScriptParser.* -class TestScriptParser(handlers: Map[Char, StatementHandler]) extends RegexParsers { +class TestScriptParser(handlers: Map[Char, StatementHandler]) extends RegexParsers: require(handlers.nonEmpty) override def skipWhitespace = false import IO.read - if (handlers.keys.exists(isWhitespace)) - sys.error("Start characters cannot be whitespace") - if (handlers.keys.exists(key => key == '+' || key == '-')) + if handlers.keys.exists(isWhitespace) then sys.error("Start characters cannot be whitespace") + if handlers.keys.exists(key => key == '+' || key == '-') then sys.error("Start characters cannot be '+' or '-'") @deprecated("Use variant that specifies whether to strip quotes or not", "1.4.0") @@ -65,15 +62,12 @@ class TestScriptParser(handlers: Map[Char, StatementHandler]) extends RegexParse script: String, label: Option[String], stripQuotes: Boolean - ): List[(StatementHandler, Statement)] = { - parseAll(statements(stripQuotes), script) match { + ): List[(StatementHandler, Statement)] = + parseAll(statements(stripQuotes), script) match case Success(result, next) => result - case err: NoSuccess => { + case err: NoSuccess => val labelString = label.map("'" + _ + "' ").getOrElse("") sys.error("Could not parse test script, " + labelString + err.toString) - } - } - } @deprecated("Use variant that specifies whether to strip quotes or not", "1.4.0") lazy val statements = rep1(space ~> statement <~ newline) @@ -82,23 +76,20 @@ class TestScriptParser(handlers: Map[Char, StatementHandler]) extends RegexParse @deprecated("Use variant that specifies whether to strip quotes or not", "1.4.0") def statement: Parser[(StatementHandler, Statement)] = statement(stripQuotes = true) - def statement(stripQuotes: Boolean): Parser[(StatementHandler, Statement)] = { - trait PositionalStatement extends Positional { + def statement(stripQuotes: Boolean): Parser[(StatementHandler, Statement)] = + trait PositionalStatement extends Positional: def tuple: (StatementHandler, Statement) - } positioned { - val w = if (stripQuotes) word else rawWord + val w = if stripQuotes then word else rawWord val command = w | err("expected command") val arguments = rep(space ~> w | failure("expected argument")) (successParser ~ (space ~> startCharacterParser <~ space) ~! command ~! arguments) ^^ { case successExpected ~ start ~ command ~ arguments => - new PositionalStatement { + new PositionalStatement: def tuple = (handlers(start), new Statement(command, arguments, successExpected, pos.line)) - } } } ^^ (_.tuple) - } def successParser: Parser[Boolean] = ('+' ^^^ true) | ('-' ^^^ false) | success(true) def space: Parser[String] = """[ \t]*""".r @@ -116,4 +107,4 @@ class TestScriptParser(handlers: Map[Char, StatementHandler]) extends RegexParse ) def newline = """\s*([\n\r]|$)""".r -} +end TestScriptParser diff --git a/launcher-package/build.sbt b/launcher-package/build.sbt index f9d710038..8a492c7c7 100755 --- a/launcher-package/build.sbt +++ b/launcher-package/build.sbt @@ -6,48 +6,43 @@ import DebianConstants.* import Dependencies.* lazy val sbtOfflineInstall = - sys.props.getOrElse("sbt.build.offline", sys.env.getOrElse("sbt.build.offline", "false")) match { + sys.props.getOrElse("sbt.build.offline", sys.env.getOrElse("sbt.build.offline", "false")) match case "true" | "1" => true case "false" | "0" => false case _ => false - } lazy val sbtIncludeSbtn = sys.props.getOrElse( "sbt.build.includesbtn", sys.env.getOrElse("sbt.build.includesbtn", "true") - ) match { + ) match case "true" | "1" => true case "false" | "0" => false case _ => false - } lazy val sbtIncludeSbtLaunch = sys.props.getOrElse( "sbt.build.includesbtlaunch", sys.env.getOrElse("sbt.build.includesbtlaunch", "true") - ) match { + ) match case "true" | "1" => true case "false" | "0" => false case _ => false - } lazy val sbtVersionToRelease = sys.props .getOrElse("sbt.build.version", sys.env.getOrElse("sbt.build.version", "1.12.0")) lazy val scala210 = "2.10.7" lazy val scala210Jline = "org.scala-lang" % "jline" % scala210 -lazy val jansi = { - if (sbtVersionToRelease.startsWith("1.")) "org.fusesource.jansi" % "jansi" % "1.12" +lazy val jansi = + if sbtVersionToRelease.startsWith("1.") then "org.fusesource.jansi" % "jansi" % "1.12" else "org.fusesource.jansi" % "jansi" % "1.4" -} lazy val scala212Compiler = "org.scala-lang" % "scala-compiler" % scala212 lazy val scala212Jline = "jline" % "jline" % "2.14.6" // use the scala-xml version used by the compiler not the latest: https://github.com/scala/scala/blob/v2.12.21/versions.properties lazy val scala212Xml = "org.scala-lang.modules" % "scala-xml_2.12" % "2.3.0" lazy val sbtActual = "org.scala-sbt" % "sbt" % sbtVersionToRelease -lazy val sbt013ExtraDeps = { - if (sbtVersionToRelease.startsWith("0.13.")) Seq(scala210Jline) +lazy val sbt013ExtraDeps = + if sbtVersionToRelease.startsWith("0.13.") then Seq(scala210Jline) else Seq() -} lazy val isWindows: Boolean = sys.props("os.name").toLowerCase(java.util.Locale.ENGLISH).contains("windows") @@ -115,10 +110,9 @@ val launcherPackage = (project in file(".")) clean.value }, credentials ++= { - (sys.env.get("BINTRAY_USER"), sys.env.get("BINTRAY_PASS")) match { + (sys.env.get("BINTRAY_USER"), sys.env.get("BINTRAY_PASS")) match case (Some(u), Some(p)) => Seq(Credentials("Bintray API Realm", "api.bintray.com", u, p)) case _ => Nil - } }, pgpSecretRing := file(s"""${sys.props("user.home")}""") / ".ssh" / "scalasbt.key", pgpPublicRing := file(s"""${sys.props("user.home")}""") / ".ssh" / "scalasbt.pub", @@ -128,20 +122,17 @@ val launcherPackage = (project in file(".")) sbtLaunchJar := { val uri = sbtLaunchJarUrl.value val file = sbtLaunchJarLocation.value - if (!file.exists) { + if !file.exists then // oddly, some places require us to create the file before writing... IO.touch(file) val url = new URI(uri).toURL val connection = url.openConnection() val input = connection.getInputStream val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(file)) - try { - input.transferTo(writer) - } finally { + try input.transferTo(writer) + finally input.close() writer.close() - } - } // TODO - GPG Trust validation. fileConverter.value.toVirtualFile(file.toPath) }, @@ -158,76 +149,64 @@ val launcherPackage = (project in file(".")) val linuxX86Tar = t / linuxX86ImageTar val linuxAarch64Tar = t / linuxAarch64ImageTar val windowsZip = t / windowsImageZip - if (!macosUniversalTar.exists && !isWindows && sbtIncludeSbtn) { + if !macosUniversalTar.exists && !isWindows && sbtIncludeSbtn then IO.touch(macosUniversalTar) val url = new URI(s"$baseUrl/v$v/$macosUniversalImageTar").toURL val connection = url.openConnection() val input = connection.getInputStream val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(macosUniversalTar)) - try { - input.transferTo(writer) - } finally { + try input.transferTo(writer) + finally input.close() writer.close() - } val platformDir = t / universalMacPlatform IO.createDirectory(platformDir) s"tar zxvf $macosUniversalTar --directory $platformDir".! IO.move(platformDir / "sbtn", t / universalMacImageName) - } - if (!linuxX86Tar.exists && !isWindows && sbtIncludeSbtn) { + if !linuxX86Tar.exists && !isWindows && sbtIncludeSbtn then IO.touch(linuxX86Tar) val url = new URI(s"$baseUrl/v$v/$linuxX86ImageTar").toURL val connection = url.openConnection() val input = connection.getInputStream val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(linuxX86Tar)) - try { - input.transferTo(writer) - } finally { + try input.transferTo(writer) + finally input.close() writer.close() - } val platformDir = t / x86LinuxPlatform IO.createDirectory(platformDir) s"""tar zxvf $linuxX86Tar --directory $platformDir""".! IO.move(platformDir / "sbtn", t / x86LinuxImageName) - } - if (!linuxAarch64Tar.exists && !isWindows && sbtIncludeSbtn) { + if !linuxAarch64Tar.exists && !isWindows && sbtIncludeSbtn then IO.touch(linuxAarch64Tar) val url = new URI(s"$baseUrl/v$v/$linuxAarch64ImageTar").toURL val connection = url.openConnection() val input = connection.getInputStream val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(linuxAarch64Tar)) - try { - input.transferTo(writer) - } finally { + try input.transferTo(writer) + finally input.close() writer.close() - } val platformDir = t / aarch64LinuxPlatform IO.createDirectory(platformDir) s"""tar zxvf $linuxAarch64Tar --directory $platformDir""".! IO.move(platformDir / "sbtn", t / aarch64LinuxImageName) - } - if (!windowsZip.exists && sbtIncludeSbtn) { + if !windowsZip.exists && sbtIncludeSbtn then IO.touch(windowsZip) val url = new URI(s"$baseUrl/v$v/$windowsImageZip").toURL val connection = url.openConnection() val input = connection.getInputStream val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(windowsZip)) - try { - input.transferTo(writer) - } finally { + try input.transferTo(writer) + finally input.close() writer.close() - } val platformDir = t / x86WindowsPlatform IO.unzip(windowsZip, platformDir) IO.move(platformDir / "sbtn.exe", t / x86WindowsImageName) - } - if (!sbtIncludeSbtn) Seq() - else if (isWindows) + if !sbtIncludeSbtn then Seq() + else if isWindows then Seq( fileConverter.value .toVirtualFile((t / x86WindowsImageName).toPath) -> s"bin/$x86WindowsImageName" @@ -251,10 +230,10 @@ val launcherPackage = (project in file(".")) // Here we remove the jar file and launch lib from the symlinks: linuxPackageSymlinks := { val links = linuxPackageSymlinks.value - for { + for link <- links if !link.destination.endsWith("sbt-launch.jar") - } yield link + yield link }, // DEBIAN SPECIFIC @@ -262,7 +241,7 @@ val launcherPackage = (project in file(".")) .getOrElse("sbt.build.patch", sys.env.getOrElse("DIST_PATCHVER", "0")) .toInt, Debian / version := { - if (debianBuildId.value == 0) sbtVersionToRelease + if debianBuildId.value == 0 then sbtVersionToRelease else sbtVersionToRelease + "." + debianBuildId.value }, // Used to have "openjdk-8-jdk" but that doesn't work on Ubuntu 14.04 https://github.com/sbt/sbt/issues/3105 @@ -287,11 +266,12 @@ val launcherPackage = (project in file(".")) Rpm / version := { val stable0 = (sbtVersionToRelease.split("[^\\d]") filterNot (_.isEmpty) mkString ".") val stable = - if (rpmRelease.value == "0") stable0 + if rpmRelease.value == "0" then stable0 else stable0 + "." + rpmRelease.value - if (isExperimental) (sbtVersionToRelease.split("[^\\d]").filterNot(_.isEmpty).toList match { - case List(_, _, c, d) => List(0, 99, c, d).mkString(".") - }) + if isExperimental then + (sbtVersionToRelease.split("[^\\d]").filterNot(_.isEmpty).toList match + case List(_, _, c, d) => List(0, 99, c, d).mkString(".") + ) else stable }, // remove sbtn from RPM because it complains about it being noarch @@ -319,12 +299,11 @@ val launcherPackage = (project in file(".")) Windows / version := { val bid = windowsBuildId.value val sv = sbtVersionToRelease - sv.split("[^\\d]").filterNot(_.isEmpty) match { + sv.split("[^\\d]").filterNot(_.isEmpty) match case Array(major, minor, bugfix, _*) if bid == 0 => Seq(major, minor, bugfix) mkString "." case Array(major, minor, bugfix, _*) => Seq(major, minor, bugfix, bid.toString) mkString "." case Array(major, minor) => Seq(major, minor, "0", bid.toString) mkString "." case Array(major) => Seq(major, "0", "0", bid.toString) mkString "." - } }, Windows / maintainer := "Scala Center", Windows / packageSummary := "sbt " + (Windows / version).value, @@ -360,10 +339,9 @@ val launcherPackage = (project in file(".")) ) ) - if (FileSystems.getDefault.supportedFileAttributeViews.contains("posix")) { + if FileSystems.getDefault.supportedFileAttributeViews.contains("posix") then val perms = Files.getPosixFilePermissions(fileConverter.value.toPath(k)) Files.setPosixFilePermissions((t / "sbt").toPath, perms) - } (fileConverter.value.toVirtualFile((t / "sbt").toPath), BinSbt) case (k, BinBat) => @@ -380,7 +358,7 @@ val launcherPackage = (project in file(".")) } }, Universal / mappings ++= (Def.taskDyn { - if (sbtIncludeSbtLaunch) + if sbtIncludeSbtLaunch then Def.task { Seq( sbtLaunchJar.value -> "bin/sbt-launch.jar" @@ -390,14 +368,14 @@ val launcherPackage = (project in file(".")) }).value, Universal / mappings ++= sbtnJarsMappings.value, Universal / mappings ++= (Def.taskDyn { - if (sbtOfflineInstall && sbtVersionToRelease.startsWith("1.")) + if sbtOfflineInstall && sbtVersionToRelease.startsWith("1.") then Def.task { val _ = ((dist / exportRepoUsingCoursier)).value directory(((dist / target)).value / "lib").map { (k, v) => fileConverter.value.toVirtualFile(k.toPath) -> v } } - else if (sbtOfflineInstall) + else if sbtOfflineInstall then Def.task { val _ = ((dist / exportRepo)).value directory(((dist / target)).value / "lib").map { (k, v) => @@ -409,7 +387,7 @@ val launcherPackage = (project in file(".")) Universal / mappings ++= { val base = baseDirectory.value val converter = fileConverter.value - if (sbtVersionToRelease.startsWith("0.13.")) Nil + if sbtVersionToRelease.startsWith("0.13.") then Nil else Seq[(HashedVirtualFileRef, String)]( converter.toVirtualFile((base.getParentFile / "LICENSE").toPath) -> "LICENSE", @@ -437,7 +415,7 @@ val launcherPackage = (project in file(".")) ) def downloadUrlForVersion(v: String) = - (v.split("[^\\d]") flatMap (i => catching(classOf[Exception]) opt (i.toInt))) match { + (v.split("[^\\d]") flatMap (i => catching(classOf[Exception]) opt (i.toInt))) match case Array(0, 11, 3, _*) => "https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/sbt-launch/0.11.3-2/sbt-launch.jar" case Array(0, 11, x, _*) if x >= 3 => @@ -448,9 +426,8 @@ def downloadUrlForVersion(v: String) = "https://repo.scala-sbt.org/scalasbt/maven-snapshots/org/scala-sbt/sbt-launch/" + v + "/sbt-launch.jar" case _ => "https://repo1.maven.org/maven2/org/scala-sbt/sbt-launch/" + v + "/sbt-launch-" + v + ".jar" - } -def makePublishToForConfig(config: Configuration) = { +def makePublishToForConfig(config: Configuration) = // Add the publish to and ensure global resolvers has the resolver we just configured. inConfig(config)( Seq( @@ -461,14 +438,13 @@ def makePublishToForConfig(config: Configuration) = { bintrayRpmExperimentalUrl := s"https://scala.jfrog.io/artifactory/rpm-experimental/", bintrayGenericPackagesUrl := s"https://scala.jfrog.io/artifactory/native-packages/", bintrayTripple := { - config.name match { + config.name match case Debian.name if isExperimental => ("debian-experimental", bintrayDebianExperimentalUrl.value, artifactoryDebianPattern) case Debian.name => ("debian", bintrayDebianUrl.value, artifactoryDebianPattern) case Rpm.name if isExperimental => ("rpm-experimental", bintrayRpmExperimentalUrl.value, artifactoryLinuxPattern) case Rpm.name => ("rpm", bintrayRpmUrl.value, artifactoryLinuxPattern) - } }, publishTo := { val (id, url, pattern) = bintrayTripple.value @@ -477,26 +453,21 @@ def makePublishToForConfig(config: Configuration) = { } ) ) -} def publishToSettings = Seq[Configuration](Debian, Rpm) flatMap makePublishToForConfig -def downloadUrl(uri: URI, out: File): Unit = { - if (!out.exists) { +def downloadUrl(uri: URI, out: File): Unit = + if !out.exists then IO.touch(out) val url = new URI(uri.toString).toURL val connection = url.openConnection() val input = connection.getInputStream val writer = new java.io.BufferedOutputStream(new java.io.FileOutputStream(out)) - try { - input.transferTo(writer) - } finally { + try input.transferTo(writer) + finally input.close() writer.close() - } - } -} def colonName(m: ModuleID): String = s"${m.organization}:${m.name}:${m.revision}" @@ -504,7 +475,7 @@ lazy val dist = (project in file("dist")) .settings( name := "dist", scalaVersion := { - if (sbtVersionToRelease.startsWith("0.13.")) scala210 + if sbtVersionToRelease.startsWith("0.13.") then scala210 else scala212 }, libraryDependencies ++= Seq( @@ -516,7 +487,7 @@ lazy val dist = (project in file("dist")) ) ++ sbt013ExtraDeps, exportRepo := { val outDir = exportRepoDirectory.value - sbtVersionToRelease match { + sbtVersionToRelease match case v if v.startsWith("1.") => sys.error("sbt 1.x should use coursier") case v if v.startsWith("0.13.") => @@ -533,7 +504,6 @@ lazy val dist = (project in file("dist")) outbase / "srcs" / "compiler-interface-sources.jar" ) case _ => - } outDir }, exportRepoDirectory := target.value / "lib" / "local-preloaded", @@ -541,7 +511,7 @@ lazy val dist = (project in file("dist")) exportRepoUsingCoursier := { val outDirectory = exportRepoCsrDirectory.value val csr = - if (isWindows) (LocalRootProject / baseDirectory).value / "bin" / "coursier.bat" + if isWindows then (LocalRootProject / baseDirectory).value / "bin" / "coursier.bat" else (LocalRootProject / baseDirectory).value / "bin" / "coursier" val cache = target.value / "coursier" IO.delete(cache) @@ -555,7 +525,7 @@ lazy val dist = (project in file("dist")) .listFiles(mavenCache / "org" / "scala-sbt" / "compiler-bridge_2.12", DirectoryFilter) .toList .headOption - compilerBridgeVer match { + compilerBridgeVer match case Some(bridgeDir) => val bridgeVer = bridgeDir.getName s"$csr fetch --cache $cache --sources org.scala-sbt:compiler-bridge_2.10:$bridgeVer".! @@ -564,7 +534,6 @@ lazy val dist = (project in file("dist")) s"$csr fetch --cache $cache --sources org.scala-sbt:compiler-bridge_2.13:$bridgeVer".! case _ => sys.error("bridge not found") - } IO.copyDirectory(mavenCache, outDirectory, true, true) outDirectory }, diff --git a/launcher-package/integration-test/src/test/scala/BspConfigTest.scala b/launcher-package/integration-test/src/test/scala/BspConfigTest.scala index da6d8369d..c7e24f6c7 100644 --- a/launcher-package/integration-test/src/test/scala/BspConfigTest.scala +++ b/launcher-package/integration-test/src/test/scala/BspConfigTest.scala @@ -61,7 +61,7 @@ object BspConfigTest extends BasicTestSuite: // Test execution of the generated argv // Run the BSP command with a very short timeout to verify it starts correctly // We just need to verify the command doesn't fail immediately on startup - if (!isWindows) { + if !isWindows then // On Unix, we can test the argv execution // Create a process and check if it starts (will timeout waiting for BSP input) val process = Process(argv.toSeq, tmp) @@ -76,14 +76,13 @@ object BspConfigTest extends BasicTestSuite: // The process should either still be alive (waiting for BSP messages) // or have exited with code 0 (graceful) - if (!isAlive) { + if !isAlive then val exitCode = processBuilder.exitValue() assert( exitCode == 0 || exitCode == 143, // 143 = SIGTERM from destroy() s"BSP process failed with exit code $exitCode" ) - } - } + end if } () } diff --git a/launcher-package/integration-test/src/test/scala/ExtendedRunnerTest.scala b/launcher-package/integration-test/src/test/scala/ExtendedRunnerTest.scala index f001182aa..106b592c8 100755 --- a/launcher-package/integration-test/src/test/scala/ExtendedRunnerTest.scala +++ b/launcher-package/integration-test/src/test/scala/ExtendedRunnerTest.scala @@ -53,13 +53,12 @@ object ExtendedRunnerTest extends BasicTestSuite: assert(!out.linesIterator.exists(_.startsWith("sbt runner version:"))) } - def testVersion(lines: List[String]): Unit = { + def testVersion(lines: List[String]): Unit = assert(lines.size >= 2) val expected0 = s"(?m)^sbt version in this project: $versionRegEx(\\r)?" assert(lines(0).matches(expected0)) val expected1 = s"sbt runner version: $versionRegEx$$" assert(lines(1).matches(expected1)) - } /* TODO: The lines seems to return List([0Jsbt runner version: 1.11.4) on CI test("sbt -V|-version|--version should print sbtVersion") { @@ -113,12 +112,11 @@ object ExtendedRunnerTest extends BasicTestSuite: } test("sbt \"testOnly *\"") { - if (isMac) () - else { + if isMac then () + else val out = sbtProcess("testOnly *", "--no-colors", "-v").!!.linesIterator.toList assert(out.contains[String]("[info] HelloTest")) () - } } test("sbt in empty directory") { @@ -143,24 +141,17 @@ object ExtendedRunnerTest extends BasicTestSuite: } test("sbt --jvm-client") { - if (isMac) { + if isMac then // `--jvm-client` is flaky in macOS CI due to intermittent startup/connection failures. // Keep coverage on Linux/Windows where the behavior is stable. () - } else { + else val out = sbtProcess("--jvm-client", "--no-colors", "compile").!!.linesIterator.toList - if (isWindows) { - println(out) - } else { - assert(out.exists { _.contains("server was not detected") }) - } + if isWindows then println(out) + else assert(out.exists { _.contains("server was not detected") }) val out2 = sbtProcess("--jvm-client", "--no-colors", "shutdown").!!.linesIterator.toList - if (isWindows) { - println(out2) - } else { - assert(out2.exists { _.contains("disconnected") }) - } - } + if isWindows then println(out2) + else assert(out2.exists { _.contains("disconnected") }) () } @@ -199,10 +190,10 @@ object ExtendedRunnerTest extends BasicTestSuite: // Test for issue #6485: Test `sbt --client` startup // https://github.com/sbt/sbt/issues/6485 test("sbt --client startup time") { - if (isWindows || isMac) { + if isWindows || isMac then // Skip on Windows (sbtn behavior differs) and macOS CI (slow hostname resolution) () - } else { + else // First call starts the server if not running (warmup) val warmup = sbtProcess("--client", "version").! assert(warmup == 0, "Warmup sbt --client version failed") @@ -235,17 +226,17 @@ object ExtendedRunnerTest extends BasicTestSuite: // Cleanup: shutdown the server val shutdown = sbtProcess("--client", "shutdown").! assert(shutdown == 0, "Failed to shutdown sbt server") - } + end if () } // Test for issue #8644: sbt.bat fails when project path contains parentheses // https://github.com/sbt/sbt/issues/8644 test("sbt.bat handles paths with parentheses") { - if (!isWindows) { + if !isWindows then // This test is Windows-specific, skip on other platforms () - } else { + else IO.withTemporaryDirectory { baseDir => // Create a temporary directory with parentheses in the name val testDir = new File(baseDir, "test(parentheses)") @@ -288,7 +279,7 @@ object ExtendedRunnerTest extends BasicTestSuite: s"Error message should not contain parsing error when path has parentheses. Error output: $errorOutput" ) } - } + end if () } end ExtendedRunnerTest diff --git a/launcher-package/integration-test/src/test/scala/IntegrationTestPaths.scala b/launcher-package/integration-test/src/test/scala/IntegrationTestPaths.scala index 1045e3844..b3339616c 100644 --- a/launcher-package/integration-test/src/test/scala/IntegrationTestPaths.scala +++ b/launcher-package/integration-test/src/test/scala/IntegrationTestPaths.scala @@ -8,30 +8,29 @@ import java.io.File * sbt.test.integrationtest.basedir when Test/fork is true so paths are * correct regardless of the forked JVM's working directory (e.g. on Windows). */ -object IntegrationTestPaths { +object IntegrationTestPaths: private val baseDir: Option[File] = sys.props.get("sbt.test.integrationtest.basedir").map(new File(_)) def sbtScript(isWindows: Boolean): File = - baseDir match { + baseDir match case Some(b) => - val name = if (isWindows) "sbt.bat" else "sbt" + val name = if isWindows then "sbt.bat" else "sbt" new File( b.getParentFile.getParentFile, s"target/out/jvm/u/sbt-launcher-packaging/universal/stage/bin/$name" ).getAbsoluteFile case None => val rel = - if (isWindows) "../../target/out/jvm/u/sbt-launcher-packaging/universal/stage/bin/sbt.bat" + if isWindows then + "../../target/out/jvm/u/sbt-launcher-packaging/universal/stage/bin/sbt.bat" else "../../target/out/jvm/u/sbt-launcher-packaging/universal/stage/bin/sbt" new File(rel).getAbsoluteFile - } def citestDir(citestVariant: String = "citest"): File = - baseDir match { + baseDir match case Some(b) => new File(b.getParentFile, citestVariant).getAbsoluteFile case None => new File("..", citestVariant).getAbsoluteFile - } -} +end IntegrationTestPaths diff --git a/launcher-package/integration-test/src/test/scala/LauncherTestHelper.scala b/launcher-package/integration-test/src/test/scala/LauncherTestHelper.scala index 324b5e67c..59feac95a 100644 --- a/launcher-package/integration-test/src/test/scala/LauncherTestHelper.scala +++ b/launcher-package/integration-test/src/test/scala/LauncherTestHelper.scala @@ -6,7 +6,7 @@ import java.util.Locale * Shared helper for launcher integration tests. When sbt.test.useSbtw=true on Windows, * tests use sbtw (JVM) as the runner instead of sbt.bat, to validate sbtw as a drop-in. */ -object LauncherTestHelper { +object LauncherTestHelper: def isWindows: Boolean = sys.props("os.name").toLowerCase(Locale.ENGLISH).contains("windows") @@ -21,4 +21,3 @@ object LauncherTestHelper { val cp = sys.props.get("sbt.test.classpath").getOrElse(System.getProperty("java.class.path")) Seq("java", "-cp", cp, "sbtw.Main") else Seq(scriptPath) -} diff --git a/launcher-package/integration-test/src/test/scala/RunnerScriptTest.scala b/launcher-package/integration-test/src/test/scala/RunnerScriptTest.scala index 548ea1b7d..7214cd8ac 100644 --- a/launcher-package/integration-test/src/test/scala/RunnerScriptTest.scala +++ b/launcher-package/integration-test/src/test/scala/RunnerScriptTest.scala @@ -29,7 +29,7 @@ abstract class RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUt testOutput("sbt --no-colors in SBT_OPTS", sbtOpts = "--no-colors")("compile", "-v"): (out: List[String]) => - if (isWindows) cancel("Test not supported on windows") + if isWindows then cancel("Test not supported on windows") assert(out.contains[String]("-Dsbt.log.noformat=true")) testOutput("sbt --no-server")("compile", "--no-server", "-v"): (out: List[String]) => @@ -92,7 +92,7 @@ abstract class RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUt javaOpts = "", sbtOpts = "-debug" )("compile", "-v"): (out: List[String]) => - if (isWindows) cancel("Test not supported on windows") + if isWindows then cancel("Test not supported on windows") // Debug argument must appear in the 'commands' section (after the sbt-launch.jar argument) to work val sbtLaunchMatcher = """^.+sbt-launch.jar["]{0,1}$""".r @@ -138,7 +138,7 @@ abstract class RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUt testOutput("accept `--ivy` in `SBT_OPTS`", sbtOpts = "--ivy /ivy/dir")("-v"): (out: List[String]) => - if (isWindows) cancel("Test not supported on windows") + if isWindows then cancel("Test not supported on windows") else assert(out.contains[String]("-Dsbt.ivy.home=/ivy/dir")) testOutput( @@ -270,7 +270,7 @@ abstract class RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUt distSbtoptsContents = "-Dsbt.test.config=dist-default", sbtOptsFileContents = "-Dsbt.test.config=project-local" )("-d", "-v"): (out: List[String]) => - if (isWindows) cancel("Test not supported on windows") + if isWindows then cancel("Test not supported on windows") else // Find the command line section val cmdLineStart = out.indexWhere(_.contains("Executing command line")) @@ -292,7 +292,7 @@ abstract class RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUt machineSbtoptsContents = "-Dsbt.test.config=machine-config", sbtOptsFileContents = "-Dsbt.test.config=project-local" )("-d", "-v"): (out: List[String]) => - if (isWindows) cancel("Test not supported on windows") + if isWindows then cancel("Test not supported on windows") else // Find the command line section val cmdLineStart = out.indexWhere(_.contains("Executing command line")) @@ -315,7 +315,7 @@ abstract class RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUt machineSbtoptsContents = "-Dsbt.test.config=machine-config", sbtOptsFileContents = "-Dsbt.test.config=project-local" )("-d", "-v"): (out: List[String]) => - if (isWindows) cancel("Test not supported on windows") + if isWindows then cancel("Test not supported on windows") else // Find the command line section val cmdLineStart = out.indexWhere(_.contains("Executing command line")) @@ -341,7 +341,7 @@ abstract class RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUt sbtOptsFileContents = "-J-Xmx2g\n-J-XX:ReservedCodeCacheSize=1g\n-J-XX:MaxMetaspaceSize=2g\n-J-Xss512m\n-J-XX:+UseG1GC" )("-d", "-v", "-mem", "12288"): (out: List[String]) => - if (isWindows) cancel("Test not supported on windows") + if isWindows then cancel("Test not supported on windows") else val cmdLineStart = out.indexWhere(_.contains("Executing command line")) assert(cmdLineStart >= 0, "Command line section not found") diff --git a/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala b/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala index 9661a3464..1eb5986e5 100644 --- a/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala +++ b/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala @@ -8,21 +8,19 @@ import scala.collection.mutable import scala.sys.process.{ BasicIO, Process, ProcessIO } import verify.BasicTestSuite -trait ShellScriptUtil extends BasicTestSuite { +trait ShellScriptUtil extends BasicTestSuite: val isWindows: Boolean = sys.props("os.name").toLowerCase(java.util.Locale.ENGLISH).contains("windows") protected val javaBinDir = new File("bin").getAbsolutePath protected def retry[A1](f: () => A1, maxAttempt: Int = 10): A1 = - try { - f() - } catch { + try f() + catch case e: Exception if maxAttempt > 1 => Thread.sleep(100) retry(f, maxAttempt - 1) case e: Exception => throw e - } def isGitBashTest: Boolean = false lazy val sbtScript = IntegrationTestPaths.sbtScript(isWindows && !isGitBashTest) @@ -56,31 +54,23 @@ trait ShellScriptUtil extends BasicTestSuite { val citestDir = IntegrationTestPaths.citestDir(citestVariant) // Clean target directory if it exists to avoid copying temporary files that may be deleted during copy val targetDir = new File(citestDir, "target") - if (targetDir.exists()) { - try { - IO.delete(targetDir) - } catch { + if targetDir.exists() then + try IO.delete(targetDir) + catch case _: Exception => // Ignore deletion errors, will retry copy - } - } // Retry copy operation to handle race conditions with temporary files - retry(() => { - try { - IO.copyDirectory(citestDir, workingDirectory) - } catch { + retry(() => + try IO.copyDirectory(citestDir, workingDirectory) + catch case e: java.io.IOException if e.getMessage.contains("does not exist") => // If a file doesn't exist during copy, clean target and retry val targetInCitest = new File(citestDir, "target") - if (targetInCitest.exists()) { - try { - IO.delete(targetInCitest) - } catch { + if targetInCitest.exists() then + try IO.delete(targetInCitest) + catch case _: Exception => // Ignore - } - } throw e // Re-throw to trigger retry - } - }) + ) var sbtHome: Option[File] = None var configHome: Option[File] = None @@ -90,36 +80,32 @@ trait ShellScriptUtil extends BasicTestSuite { val sbtOptsFile = new File(workingDirectory, ".sbtopts") sbtOptsFile.createNewFile() val writer = new PrintWriter(sbtOptsFile) - try { + try writer.write(sbtOptsFileContents) - } finally { + finally writer.close() - } // Create .jvmopts file if contents provided - if (jvmoptsFileContents.nonEmpty) { + if jvmoptsFileContents.nonEmpty then val jvmoptsFile = new File(workingDirectory, ".jvmopts") jvmoptsFile.createNewFile() val jvmoptsWriter = new PrintWriter(jvmoptsFile) - try { + try jvmoptsWriter.write(jvmoptsFileContents) - } finally { + finally jvmoptsWriter.close() - } - } - if (buildPropsContents.nonEmpty) { + if buildPropsContents.nonEmpty then val projectDir = new File(workingDirectory, "project") projectDir.mkdirs() IO.write(new File(projectDir, "build.properties"), buildPropsContents) - } val envVars = scala.collection.mutable.Map[String, String]() // Set up dist sbtopts if provided // Note: sbt script derives sbt_home from script location, not SBT_HOME env var // Copy the sbt staging directory to a temp location to avoid modifying the staging directory - if (distSbtoptsContents.nonEmpty || stagedRunnerVersionOverride.nonEmpty) { + if distSbtoptsContents.nonEmpty || stagedRunnerVersionOverride.nonEmpty then val originalSbtHome = sbtScript.getParentFile.getParentFile val tempSbtHomeDir = Files.createTempDirectory("sbt-home-test").toFile tempSbtHome = Some(tempSbtHomeDir) @@ -128,18 +114,17 @@ trait ShellScriptUtil extends BasicTestSuite { // Get the script from the copied directory val binDir = new File(tempSbtHomeDir, "bin") testSbtScript = new File(binDir, sbtScript.getName) - if (distSbtoptsContents.nonEmpty) { + if distSbtoptsContents.nonEmpty then // Create dist sbtopts in the copied directory val distSbtoptsDir = new File(tempSbtHomeDir, "conf") distSbtoptsDir.mkdirs() IO.write(new File(distSbtoptsDir, "sbtopts"), distSbtoptsContents) - } - if (stagedRunnerVersionOverride.nonEmpty) { + if stagedRunnerVersionOverride.nonEmpty then val isBat = testSbtScript.getName.endsWith(".bat") val prefix = - if (isBat) "set init_sbt_version=" else "declare init_sbt_version=" + if isBat then "set init_sbt_version=" else "declare init_sbt_version=" val pattern = - if (isBat) "(?m)^set init_sbt_version=.*$" + if isBat then "(?m)^set init_sbt_version=.*$" else "(?m)^declare init_sbt_version=.*$" val original = IO.read(testSbtScript) val regex = pattern.r @@ -152,17 +137,15 @@ trait ShellScriptUtil extends BasicTestSuite { val updated = regex.replaceAllIn(original, replacement) assert(updated.contains(prefix + stagedRunnerVersionOverride)) IO.write(testSbtScript, updated) - if (!isBat) testSbtScript.setExecutable(true) - } + if !isBat then testSbtScript.setExecutable(true) // Store reference for cleanup sbtHome = Some(tempSbtHomeDir) - } + end if // Ensure no machine sbtopts exists when testing dist-only (unless explicitly provided) // The script only loads dist if machine doesn't exist - if ( - distSbtoptsContents.nonEmpty && machineSbtoptsContents.isEmpty && configHome.isEmpty - ) { + if distSbtoptsContents.nonEmpty && machineSbtoptsContents.isEmpty && configHome.isEmpty + then // Set XDG_CONFIG_HOME to a temp directory without sbtopts to prevent default machine sbtopts from being found val emptyConfigHome = Files.createTempDirectory("empty-config-home").toFile envVars("XDG_CONFIG_HOME") = emptyConfigHome.getAbsolutePath @@ -170,10 +153,9 @@ trait ShellScriptUtil extends BasicTestSuite { sys.env.get("SBT_ETC_FILE").foreach(_ => envVars("SBT_ETC_FILE") = "") // Store for cleanup configHome = Some(emptyConfigHome) - } // Set up machine sbtopts if provided - if (machineSbtoptsContents.nonEmpty) { + if machineSbtoptsContents.nonEmpty then val configHomeDir = Files.createTempDirectory("config-home").toFile configHome = Some(configHomeDir) val machineSbtoptsDir = new File(configHomeDir, "sbt") @@ -181,7 +163,6 @@ trait ShellScriptUtil extends BasicTestSuite { val machineSbtoptsFile = new File(machineSbtoptsDir, "sbtopts") IO.write(machineSbtoptsFile, machineSbtoptsContents) envVars("XDG_CONFIG_HOME") = configHomeDir.getAbsolutePath - } val path = sys.env.getOrElse("PATH", sys.env.getOrElse("Path", "")) val javaHomeEnv = sys.env.getOrElse("JAVA_HOME", System.getProperty("java.home")) @@ -220,5 +201,6 @@ trait ShellScriptUtil extends BasicTestSuite { // Clean up temporary sbt home directory if we created one tempSbtHome.foreach(IO.delete) configHome.foreach(IO.delete) + end try } -} +end ShellScriptUtil diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/CompatibilityWarning.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/CompatibilityWarning.scala index 5f7550a4a..be9121093 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/CompatibilityWarning.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/CompatibilityWarning.scala @@ -10,7 +10,7 @@ final class CompatibilityWarningOptions private[sbt] ( val level: Level.Value ) -object CompatibilityWarningOptions { +object CompatibilityWarningOptions: def default: CompatibilityWarningOptions = apply(configurations = List(Compile, Runtime), level = Level.Warn) def apply( @@ -21,39 +21,33 @@ object CompatibilityWarningOptions { configurations = configurations, level = level ) -} -private[sbt] object CompatibilityWarning { +private[sbt] object CompatibilityWarning: def run( config: CompatibilityWarningOptions, module: ModuleDescriptor, mavenStyle: Boolean, log: Logger - ): Unit = { - if (mavenStyle) { - processIntransitive(config, module, log) - } - } + ): Unit = + if mavenStyle then processIntransitive(config, module, log) def processIntransitive( config: CompatibilityWarningOptions, module: ModuleDescriptor, log: Logger - ): Unit = { + ): Unit = val monitoredConfigsStr: Set[String] = (config.configurations map { _.name }).toSet def inMonitoredConfigs(configOpt: Option[String]): Boolean = - configOpt match { + configOpt match case Some(c) => (c.split(",").toSet intersect monitoredConfigsStr).nonEmpty case None => monitoredConfigsStr contains "compile" - } module.directDependencies foreach { m => - if (!m.isTransitive && inMonitoredConfigs(m.configurations)) { + if !m.isTransitive && inMonitoredConfigs(m.configurations) then log.warn( s"""Found intransitive dependency ($m) while publishMavenStyle is true, but Maven repositories | do not support intransitive dependencies. Use exclusions instead so transitive dependencies | will be correctly excluded in dependent projects. """.stripMargin ) - } else () + else () } - } -} +end CompatibilityWarning diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/InternalDefaults.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/InternalDefaults.scala index 3a13b5722..eccc6ce00 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/InternalDefaults.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/InternalDefaults.scala @@ -8,7 +8,7 @@ import sbt.io.syntax.* /** * This is a list of functions with default values. */ -object InternalDefaults { +object InternalDefaults: val sbtOrgTemp = JsonUtil.sbtOrgTemp val modulePrefixTemp = "temp-module-" @@ -26,4 +26,3 @@ object InternalDefaults { def getDeliverStatus(opt: Option[String]): String = opt.getOrElse("release") -} diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/IvyConfigurations.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/IvyConfigurations.scala index 766ee55d4..2d0a2caac 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/IvyConfigurations.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/IvyConfigurations.scala @@ -3,18 +3,15 @@ */ package sbt.librarymanagement -private[librarymanagement] abstract class InlineConfigurationFunctions { +private[librarymanagement] abstract class InlineConfigurationFunctions: def configurations( explicitConfigurations: Iterable[Configuration], defaultConfiguration: Option[Configuration] ) = - if (explicitConfigurations.isEmpty) { - defaultConfiguration match { + if explicitConfigurations.isEmpty then + defaultConfiguration match case Some(Configurations.DefaultIvyConfiguration) => Configurations.Default :: Nil case Some(Configurations.DefaultMavenConfiguration) => Configurations.defaultMavenConfigurations case _ => Nil - } - } else - explicitConfigurations -} + else explicitConfigurations diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/IvySbt.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/IvySbt.scala index c4f5650be..430fec7a0 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/IvySbt.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/IvySbt.scala @@ -7,6 +7,5 @@ import sbt.librarymanagement.ModuleSettings * [[https://github.com/sbt/sbt/issues/9676]] */ @deprecated("will be removed", "2.1.0") -private[librarymanagement] final class IvySbt { +private[librarymanagement] final class IvySbt: final class Module(rawModuleSettings: ModuleSettings) -} diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/JsonUtil.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/JsonUtil.scala index eea16125e..28e497c67 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/JsonUtil.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/JsonUtil.scala @@ -5,7 +5,7 @@ import sbt.util.{ CacheStore, Logger } import sbt.librarymanagement.*, LibraryManagementCodec.given import sbt.io.IO -private[sbt] object JsonUtil { +private[sbt] object JsonUtil: def sbtOrgTemp = "org.scala-sbt.temp" def fakeCallerOrganization = "org.scala-sbt.temp-callers" @@ -13,28 +13,25 @@ private[sbt] object JsonUtil { path: File, cachedDescriptor: File, log: Logger - ): UpdateReport = { - try { + ): UpdateReport = + try val lite = CacheStore(path).read[UpdateReportLite]() fromLite(lite, cachedDescriptor) - } catch { + catch case e: Throwable => log.error(s"Unable to parse mini graph: $path") throw e - } - } - def writeUpdateReport(ur: UpdateReport, graphPath: File): Unit = { + def writeUpdateReport(ur: UpdateReport, graphPath: File): Unit = val updateReportLite = toLite(ur) IO.createDirectory(graphPath.getParentFile) CacheStore(graphPath).write(updateReportLite) - } /** The per-module normalization `toLite` applies; anything stored alongside it must match. */ - private[sbt] def withFilteredCallers(mr: ModuleReport): ModuleReport = { + private[sbt] def withFilteredCallers(mr: ModuleReport): ModuleReport = val callers = filterOutArtificialCallers(mr.callers) // Reuse the instance when filtering changed nothing, so interning is not undone here. - if (callers eq mr.callers) mr + if callers eq mr.callers then mr else ModuleReport( mr.module, @@ -56,11 +53,12 @@ private[sbt] object JsonUtil { mr.licenses, callers ) - } + end if + end withFilteredCallers def toLite(ur: UpdateReport): UpdateReportLite = UpdateReportLite(ur.configurations map { cr => - val details0 = if (cr.details.nonEmpty) cr.details else modulesToDetails(cr.modules) + val details0 = if cr.details.nonEmpty then cr.details else modulesToDetails(cr.modules) ConfigurationReportLite( cr.configuration.name, details0 map { oar => @@ -74,20 +72,19 @@ private[sbt] object JsonUtil { }) private def modulesToDetails(modules: Vector[ModuleReport]): Vector[OrganizationArtifactReport] = - if (modules.isEmpty) Vector.empty - else { + if modules.isEmpty then Vector.empty + else val grouped = modules.groupBy(m => (m.module.organization, m.module.name)) val orderedKeys = modules.map(m => (m.module.organization, m.module.name)).distinct orderedKeys.map { case (organization, name) => OrganizationArtifactReport(organization, name, grouped((organization, name))) } - } // #1763/#2030. Caller takes up 97% of space, so we need to shrink it down, // but there are semantics associated with some of them. def filterOutArtificialCallers(callers: Vector[Caller]): Vector[Caller] = - if (callers.isEmpty) callers - else { + if callers.isEmpty then callers + else val nonArtificial = callers filter { c => (c.caller.organization != sbtOrgTemp) && (c.caller.organization != fakeCallerOrganization) @@ -96,9 +93,8 @@ private[sbt] object JsonUtil { c.caller.organization == sbtOrgTemp }).toVector interProj ++ nonArtificial - } - def fromLite(lite: UpdateReportLite, cachedDescriptor: File): UpdateReport = { + def fromLite(lite: UpdateReportLite, cachedDescriptor: File): UpdateReport = val stats = UpdateStats(0L, 0L, 0L, false) val configReports = lite.configurations map { cr => val details = cr.details @@ -110,9 +106,8 @@ private[sbt] object JsonUtil { ConfigurationReport(ConfigRef(cr.configuration), modules, details) } UpdateReport(cachedDescriptor, configReports, stats, Map.empty) - } - def fromLiteFull(lite: UpdateReportLite, cachedDescriptor: File): UpdateReport = { + def fromLiteFull(lite: UpdateReportLite, cachedDescriptor: File): UpdateReport = val stats = UpdateStats(0L, 0L, 0L, false) val configReports = lite.configurations map { cr => val details = cr.details @@ -120,5 +115,4 @@ private[sbt] object JsonUtil { ConfigurationReport(ConfigRef(cr.configuration), modules, details) } UpdateReport(cachedDescriptor, configReports, stats, Map.empty) - } -} +end JsonUtil diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/LMSysProp.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/LMSysProp.scala index 209e0cd11..95c2cbace 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/LMSysProp.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/LMSysProp.scala @@ -12,15 +12,14 @@ package librarymanagement import java.util.Locale import scala.util.control.NonFatal -object LMSysProp { +object LMSysProp: def booleanOpt(name: String): Option[Boolean] = sys.props.get(name).flatMap { x => - x.toLowerCase(Locale.ENGLISH) match { + x.toLowerCase(Locale.ENGLISH) match case "1" | "always" | "true" => Some(true) case "0" | "never" | "false" => Some(false) case "auto" => None case _ => None - } } def getOrFalse(name: String): Boolean = booleanOpt(name).getOrElse(false) @@ -44,23 +43,18 @@ object LMSysProp { lazy val modifyVersionRange: Boolean = getOrTrue("sbt.modversionrange") lazy val isJavaVersion9Plus: Boolean = javaVersion > 8 - lazy val javaVersion: Int = { - try { + lazy val javaVersion: Int = + try // See Oracle section 1.5.3 at: // https://docs.oracle.com/javase/8/docs/technotes/guides/versioning/spec/versioning2.html val version = sys.props("java.specification.version").split("\\.").toList.map(_.toInt) - version match { + version match case 1 :: minor :: _ => minor case major :: _ => major case _ => 0 - } - } catch { - case NonFatal(_) => 0 - } - } + catch case NonFatal(_) => 0 lazy val useGigahorse: Boolean = getOrFalse("sbt.gigahorse") lazy val maxPublishAttempts: Int = java.lang.Integer.getInteger("sbt.repository.publish.attempts", 3) - -} +end LMSysProp diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/SemSelOperator.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/SemSelOperator.scala index 942672269..9fd9294c3 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/SemSelOperator.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/SemSelOperator.scala @@ -1,18 +1,15 @@ package sbt.internal.librarymanagement -sealed abstract class SemSelOperator { - override def toString: String = this match { +sealed abstract class SemSelOperator: + override def toString: String = this match case SemSelOperator.Lte => "<=" case SemSelOperator.Lt => "<" case SemSelOperator.Gte => ">=" case SemSelOperator.Gt => ">" case SemSelOperator.Eq => "=" - } -} -object SemSelOperator { +object SemSelOperator: case object Lte extends SemSelOperator case object Lt extends SemSelOperator case object Gte extends SemSelOperator case object Gt extends SemSelOperator case object Eq extends SemSelOperator -} diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/SemanticSelectorExtra.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/SemanticSelectorExtra.scala index 036fa3483..cfd09f30e 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/SemanticSelectorExtra.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/SemanticSelectorExtra.scala @@ -6,68 +6,61 @@ import sbt.internal.librarymanagement.SemSelOperator.{ Lt, Lte, Gt, Gte, Eq } import scala.annotation.tailrec import java.util.Locale -private[librarymanagement] abstract class SemSelAndChunkFunctions { - protected def parse(andClauseToken: String): SemSelAndChunk = { +private[librarymanagement] abstract class SemSelAndChunkFunctions: + protected def parse(andClauseToken: String): SemSelAndChunk = val comparatorTokens = scala.collection.immutable.ArraySeq.unsafeWrapArray(andClauseToken.split("\\s+")) val hyphenIndex = comparatorTokens.indexWhere(_ == "-") - val comparators = if (hyphenIndex == -1) { - comparatorTokens.map(SemComparator.apply) - } else { + val comparators = if hyphenIndex == -1 then comparatorTokens.map(SemComparator.apply) + else // interpret `A.B.C - D.E.F` to `>=A.B.C <=D.E.F` val (before, after) = comparatorTokens.splitAt(hyphenIndex) - (before.lastOption, after.drop(1).headOption) match { + (before.lastOption, after.drop(1).headOption) match case (Some(fromStr), Some(toStr)) => // from and to cannot have an operator. - if (hasOperator(fromStr) || hasOperator(toStr)) { + if hasOperator(fromStr) || hasOperator(toStr) then throw new IllegalArgumentException( s"Invalid ' - ' range, both side of comparators cannot have an operator: $fromStr - $toStr" ) - } val from = SemComparator(fromStr) val to = SemComparator(toStr) val comparatorsBefore = before.dropRight(1).map(SemComparator.apply) - val comparatorsAfter = after.drop(2) match { + val comparatorsAfter = after.drop(2) match case tokens if !tokens.isEmpty => parse(tokens.mkString(" ")).comparators case _ => Seq.empty - } from.withOp(Gte) +: to.withOp(Lte) +: (comparatorsBefore ++ comparatorsAfter) case _ => throw new IllegalArgumentException( s"Invalid ' - ' range position, both side of versions must be specified: $andClauseToken" ) - } - } SemSelAndChunk(comparators.flatMap(_.expandWildcard)) - } + end parse - private def hasOperator(comparator: String): Boolean = { + private def hasOperator(comparator: String): Boolean = comparator.startsWith("<") || - comparator.startsWith(">") || - comparator.startsWith("=") - } -} + comparator.startsWith(">") || + comparator.startsWith("=") +end SemSelAndChunkFunctions -private[librarymanagement] abstract class SemComparatorExtra { +private[librarymanagement] abstract class SemComparatorExtra: val op: SemSelOperator val major: Option[Long] val minor: Option[Long] val patch: Option[Long] val tags: Seq[String] - protected def toStringImpl: String = { + protected def toStringImpl: String = val versionStr = Seq(major, minor, patch) .collect { case Some(v) => v.toString } .mkString(".") - val tagsStr = if (tags.nonEmpty) s"-${tags.mkString("-")}" else "" + val tagsStr = if tags.nonEmpty then s"-${tags.mkString("-")}" else "" s"$op$versionStr$tagsStr" - } - protected def matchesImpl(version: VersionNumber): Boolean = { + protected def matchesImpl(version: VersionNumber): Boolean = // Fill empty fields of version specifier with 0 or max value of Long. // By filling them, SemComparator realize the properties below // `<=1.0` is equivalent to `<1.1.0` (`<=1.0.${Long.MaxValue}`) @@ -78,13 +71,12 @@ private[librarymanagement] abstract class SemComparatorExtra { // However this fills 0 for a comparator that have `=` operator, // a comparator that have empty part of version and `=` operator won't appear // because of expanding it to and clause of comparators. - val assumed = op match { + val assumed = op match case Lte => Long.MaxValue case Lt => 0L case Gte => 0L case Gt => Long.MaxValue case Eq => 0L - } // empty fields of the version number are assumed to be 0. val versionNumber = (version._1.getOrElse(0L), version._2.getOrElse(0L), version._3.getOrElse(0L)) @@ -92,36 +84,34 @@ private[librarymanagement] abstract class SemComparatorExtra { val normalVersionCmp = implicitly[Ordering[(Long, Long, Long)]].compare(versionNumber, selector) val cmp = - if (normalVersionCmp == 0) comparePreReleaseTags(version.tags, tags) + if normalVersionCmp == 0 then comparePreReleaseTags(version.tags, tags) else normalVersionCmp - op match { + op match case Lte if cmp <= 0 => true case Lt if cmp < 0 => true case Gte if cmp >= 0 => true case Gt if cmp > 0 => true case Eq if cmp == 0 => true case _ => false - } - } - private def comparePreReleaseTags(ts1: Seq[String], ts2: Seq[String]): Int = { + end matchesImpl + private def comparePreReleaseTags(ts1: Seq[String], ts2: Seq[String]): Int = // > When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version. - if (ts1.isEmpty && ts2.isEmpty) 0 - else if (ts1.nonEmpty && ts2.isEmpty) -1 // ts1 is pre-release version - else if (ts1.isEmpty && ts2.nonEmpty) 1 // ts2 is pre-release version + if ts1.isEmpty && ts2.isEmpty then 0 + else if ts1.nonEmpty && ts2.isEmpty then -1 // ts1 is pre-release version + else if ts1.isEmpty && ts2.nonEmpty then 1 // ts2 is pre-release version else compareTags(ts1, ts2) - } @tailrec - private def compareTags(ts1: Seq[String], ts2: Seq[String]): Int = { + private def compareTags(ts1: Seq[String], ts2: Seq[String]): Int = // > A larger set of pre-release fields has a higher precedence than a smaller set, // > if all of the preceding identifiers are equal. - if (ts1.isEmpty && ts2.isEmpty) 0 - else if (ts1.nonEmpty && ts2.isEmpty) 1 - else if (ts1.isEmpty && ts2.nonEmpty) -1 - else { + if ts1.isEmpty && ts2.isEmpty then 0 + else if ts1.nonEmpty && ts2.isEmpty then 1 + else if ts1.isEmpty && ts2.nonEmpty then -1 + else val ts1head = ts1.head val ts2head = ts2.head - val cmp = (ts1head.matches("\\d+"), ts2head.matches("\\d+")) match { + val cmp = (ts1head.matches("\\d+"), ts2head.matches("\\d+")) match // Identifiers consisting of only digits are compared numerically. // Numeric identifiers always have lower precedence than non-numeric identifiers. // Identifiers with letters are compared case-insensitive lexical order. @@ -130,19 +120,16 @@ private[librarymanagement] abstract class SemComparatorExtra { case (true, false) => -1 case (false, false) => ts1head.toLowerCase(Locale.ENGLISH).compareTo(ts2head.toLowerCase(Locale.ENGLISH)) - } - if (cmp == 0) compareTags(ts1.tail, ts2.tail) + if cmp == 0 then compareTags(ts1.tail, ts2.tail) else cmp - } - } // Expand wildcard with `=` operator to and clause of comparators. // `=1.0` is equivalent to `>=1.0 <=1.0` protected def allFieldsSpecified: Boolean = major.isDefined && minor.isDefined && patch.isDefined -} +end SemComparatorExtra -private[librarymanagement] abstract class SemComparatorFunctions { +private[librarymanagement] abstract class SemComparatorFunctions: private val ComparatorRegex = """(?x)^ ([<>]=?|=)? (?:(\d+|[xX*]) @@ -151,8 +138,8 @@ private[librarymanagement] abstract class SemComparatorFunctions { )? )((?:-\w+(?:\.\w+)*)*)$ """.r - protected def parse(comparator: String): SemComparator = { - comparator match { + protected def parse(comparator: String): SemComparator = + comparator match case ComparatorRegex(rawOp, rawMajor, rawMinor, rawPatch, ts) => val opStr = Option(rawOp) val major = Option(rawMajor) @@ -167,8 +154,8 @@ private[librarymanagement] abstract class SemComparatorFunctions { case Some(str) => str.matches("[xX*]") case None => false } - if (hasXrangeSelector) { - if (tags.nonEmpty) + if hasXrangeSelector then + if tags.nonEmpty then throw new IllegalArgumentException( s"Pre-release version requires major, minor, patch versions to be specified: $comparator" ) @@ -183,12 +170,12 @@ private[librarymanagement] abstract class SemComparatorFunctions { } .mkString(".") ) - } else { - if (tags.nonEmpty && (major.isEmpty || minor.isEmpty || patch.isEmpty)) + else + if tags.nonEmpty && (major.isEmpty || minor.isEmpty || patch.isEmpty) then throw new IllegalArgumentException( s"Pre-release version requires major, minor, patch versions to be specified: $comparator" ) - val operator = opStr match { + val operator = opStr match case Some("<") => Lt case Some("<=") => Lte case Some(">") => Gt @@ -197,7 +184,6 @@ private[librarymanagement] abstract class SemComparatorFunctions { case None => Eq case Some(_) => throw new IllegalArgumentException(s"Invalid operator: $opStr") - } SemComparator( operator, major.map(_.toLong), @@ -205,12 +191,10 @@ private[librarymanagement] abstract class SemComparatorFunctions { patch.map(_.toLong), tags ) - } + end if case _ => throw new IllegalArgumentException(s"Invalid comparator: $comparator") - } - } private def splitOn(s: String, sep: Char): Vector[String] = - if (s eq null) Vector() + if s eq null then Vector() else s.split(sep).filterNot(_ == "").toVector private def splitDash(s: String) = splitOn(s, '-') -} +end SemComparatorFunctions diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/StringUtilities.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/StringUtilities.scala index e5d878cec..650c29468 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/StringUtilities.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/StringUtilities.scala @@ -3,8 +3,7 @@ */ package sbt.internal.librarymanagement -object StringUtilities { +object StringUtilities: def nonEmpty(s: String, label: String): Unit = require(s.trim.length > 0, label + " cannot be empty.") - def appendable(s: String) = if (s.isEmpty) "" else "_" + s -} + def appendable(s: String) = if s.isEmpty then "" else "_" + s diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateClassifiersUtil.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateClassifiersUtil.scala index 239a49c03..2094768b7 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateClassifiersUtil.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateClassifiersUtil.scala @@ -4,13 +4,13 @@ import java.io.File import sbt.librarymanagement.* import sbt.librarymanagement.syntax.* -object UpdateClassifiersUtil { +object UpdateClassifiersUtil: def restrictedCopy(m: ModuleID, confs: Boolean) = ModuleID(m.organization, m.name, m.revision) .withCrossVersion(m.crossVersion) .withExtraAttributes(m.extraAttributes) - .withConfigurations(if (confs) m.configurations else None) + .withConfigurations(if confs then m.configurations else None) .branch(m.branchName) // This version adds explicit artifact @@ -18,37 +18,33 @@ object UpdateClassifiersUtil { classifiers: Vector[String], exclude: Map[ModuleID, Set[ConfigRef]], artifacts: Vector[(String, ModuleID, Artifact, File)] - )(m: ModuleID): Option[ModuleID] = { + )(m: ModuleID): Option[ModuleID] = def sameModule(m1: ModuleID, m2: ModuleID): Boolean = m1.organization == m2.organization && m1.name == m2.name && m1.revision == m2.revision - def explicitArtifacts = { + def explicitArtifacts = val arts = (artifacts collect { case (_, x, art, _) if sameModule(m, x) && art.classifier.isDefined => art }).distinct - if (arts.isEmpty) None + if arts.isEmpty then None else Some(intransitiveModuleWithExplicitArts(m, arts)) - } def hardcodedArtifacts = classifiedArtifacts(classifiers, exclude)(m) explicitArtifacts orElse hardcodedArtifacts - } def classifiedArtifacts( classifiers: Vector[String], exclude: Map[ModuleID, Set[ConfigRef]] - )(m: ModuleID): Option[ModuleID] = { + )(m: ModuleID): Option[ModuleID] = val excluded: Set[ConfigRef] = exclude.getOrElse(restrictedCopy(m, false), Set.empty) val exls = excluded map { _.name } val included = classifiers filterNot exls - if (included.isEmpty) None - else { + if included.isEmpty then None + else Some( intransitiveModuleWithExplicitArts( module = m, arts = classifiedArtifacts(m.name, included) ) ) - } - } def classifiedArtifacts(name: String, classifiers: Vector[String]): Vector[Artifact] = classifiers map { c => @@ -98,5 +94,4 @@ object UpdateClassifiersUtil { private def getExcluded(id: ModuleID, exclude: Map[ModuleID, Set[String]]): Set[String] = exclude.getOrElse(restrictedCopy(id, false), Set.empty[String]) - -} +end UpdateClassifiersUtil diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateReportInterner.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateReportInterner.scala index 3da755a08..ffa65fd95 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateReportInterner.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateReportInterner.scala @@ -16,7 +16,7 @@ import sbt.librarymanagement.* * * The pools are weak, so an entry lives only as long as some report references it. */ -object UpdateReportInterner { +object UpdateReportInterner: private val configRefs = new WeakInterner[ConfigRef] private val rules = new WeakInterner[InclExclRule] @@ -36,16 +36,15 @@ object UpdateReportInterner { artifacts.internWith(a) { artifact => // Canonicalize the nested vectors so value-equal artifacts share their pieces even when only // encountered once. - if (artifact.configurations.isEmpty) artifact + if artifact.configurations.isEmpty then artifact else artifact.withConfigurations(artifact.configurations.map(intern)) } def intern(m: ModuleID): ModuleID = moduleIds.internWith(m) { moduleId => - if ( - moduleId.inclusions.isEmpty && moduleId.exclusions.isEmpty && + if moduleId.inclusions.isEmpty && moduleId.exclusions.isEmpty && moduleId.explicitArtifacts.isEmpty - ) moduleId + then moduleId else moduleId .withInclusions(moduleId.inclusions.map(intern)) @@ -62,7 +61,7 @@ object UpdateReportInterner { def intern(mr: ModuleReport): ModuleReport = // A publicationDate is a mutable Calendar, so canonicalize such a report but never pool it. - if (mr.publicationDate.isDefined) canonicalize(mr) + if mr.publicationDate.isDefined then canonicalize(mr) else moduleReports.internWith(mr)(canonicalize) private def canonicalize(mr: ModuleReport): ModuleReport = @@ -71,4 +70,4 @@ object UpdateReportInterner { .withMissingArtifacts(mr.missingArtifacts.map(intern)) .withConfigurations(mr.configurations.map(intern)) .withCallers(mr.callers.map(intern)) -} +end UpdateReportInterner diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateReportPersistence.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateReportPersistence.scala index 70ddf5470..e4f518133 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateReportPersistence.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/UpdateReportPersistence.scala @@ -141,6 +141,7 @@ object UpdateReportPersistence: case hit => hit.intValue def result(): Vector[ModuleReport] = table.result() + end ModuleTable private def writeV1[J](obj: UpdateReportCache, builder: Builder[J]): Unit = val modules = new ModuleTable @@ -196,6 +197,7 @@ object UpdateReportPersistence: cachedDescriptor, configurations.map(cfg => cfg.modules.getOrElse(cfg.details.flatMap(_.modules)).map(modules)) ) + end readV1 /** * `lookupField` consumes nothing, so the branch readers below still see every field. Each branch diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/VersionRange.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/VersionRange.scala index bf5c62bba..bbf9f2823 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/VersionRange.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/VersionRange.scala @@ -4,18 +4,17 @@ package librarymanagement import sbt.librarymanagement.VersionNumber -object VersionRange { +object VersionRange: /** True if the revision is an ivy-range, not a complete revision. */ - def isVersionRange(revision: String): Boolean = { + def isVersionRange(revision: String): Boolean = (revision.endsWith("+")) || - (revision.contains("[")) || - (revision.contains("]")) || - (revision.contains("(")) || - (revision.contains(")")) || - // Comma-separated range e.g. "1.3.1,2.3" (fixes #6244 when Coursier passes range without brackets) - (revision.contains(",") && revision.exists(_.isDigit)) - } + (revision.contains("[")) || + (revision.contains("]")) || + (revision.contains("(")) || + (revision.contains(")")) || + // Comma-separated range e.g. "1.3.1,2.3" (fixes #6244 when Coursier passes range without brackets) + (revision.contains(",") && revision.exists(_.isDigit)) /** * Checks if a version satisfies a version range. @@ -23,46 +22,40 @@ object VersionRange { * @param range The version range (e.g., "[4.1.0,5)" or "[1.0,2.0]") * @return true if version is within the range, false otherwise */ - def versionSatisfiesRange(version: String, range: String): Boolean = { - if (!isVersionRange(range)) { + def versionSatisfiesRange(version: String, range: String): Boolean = + if !isVersionRange(range) then // Not a range, just compare directly version == range - } else if (range.contains(",") && !hasMavenVersionRange(range)) { + else if range.contains(",") && !hasMavenVersionRange(range) then // Comma-separated range without brackets e.g. "1.3.1,2.3" (fixes #6244) val parts = range.split(",", 2) - if (parts.length == 2) { + if parts.length == 2 then val lower = parts(0).trim val upper = parts(1).trim lower.nonEmpty && upper.nonEmpty && compareVersions(version, lower) >= 0 && compareVersions(version, upper) <= 0 - } else false - } else if (range.endsWith("+")) { + else false + else if range.endsWith("+") then // Handle plus ranges like "1.0+" meaning >= 1.0 val base = range.dropRight(1) compareVersions(version, base) >= 0 - } else if (hasMavenVersionRange(range)) { + else if hasMavenVersionRange(range) then // Parse Maven-style range like [1.0,2.0) or (1.0,2.0] - parseMavenRange(range) match { + parseMavenRange(range) match case Some((lowerBound, lowerInclusive, upperBound, upperInclusive)) => - val lowerOk = lowerBound match { + val lowerOk = lowerBound match case Some(lb) => val cmp = compareVersions(version, lb) - if (lowerInclusive) cmp >= 0 else cmp > 0 + if lowerInclusive then cmp >= 0 else cmp > 0 case None => true - } - val upperOk = upperBound match { + val upperOk = upperBound match case Some(ub) => val cmp = compareVersions(version, ub) - if (upperInclusive) cmp <= 0 else cmp < 0 + if upperInclusive then cmp <= 0 else cmp < 0 case None => true - } lowerOk && upperOk case None => false - } - } else { - false - } - } + else false /** * Parses a Maven-style version range. @@ -70,48 +63,46 @@ object VersionRange { */ private def parseMavenRange( range: String - ): Option[(Option[String], Boolean, Option[String], Boolean)] = { + ): Option[(Option[String], Boolean, Option[String], Boolean)] = val trimmed = range.trim - if (trimmed.length < 2) None - else { + if trimmed.length < 2 then None + else val startChar = trimmed.head val endChar = trimmed.last val lowerInclusive = startChar == '[' val upperInclusive = endChar == ']' - if (!Set('[', '(').contains(startChar) || !Set(']', ')').contains(endChar)) { - None - } else { + if !Set('[', '(').contains(startChar) || !Set(']', ')').contains(endChar) then None + else val inner = trimmed.substring(1, trimmed.length - 1) val commaIdx = inner.indexOf(',') - if (commaIdx < 0) { + if commaIdx < 0 then // Single version constraint like [1.0] means exactly 1.0 val v = inner.trim - if (v.nonEmpty) Some((Some(v), true, Some(v), true)) + if v.nonEmpty then Some((Some(v), true, Some(v), true)) else None - } else { + else val lower = inner.substring(0, commaIdx).trim val upper = inner.substring(commaIdx + 1).trim Some( ( - if (lower.nonEmpty) Some(lower) else None, + if lower.nonEmpty then Some(lower) else None, lowerInclusive, - if (upper.nonEmpty) Some(upper) else None, + if upper.nonEmpty then Some(upper) else None, upperInclusive ) ) - } - } - } - } + end if + end if + end parseMavenRange /** * Compares two version strings. * @return negative if v1 < v2, 0 if v1 == v2, positive if v1 > v2 */ - private def compareVersions(v1: String, v2: String): Int = { + private def compareVersions(v1: String, v2: String): Int = val vn1 = VersionNumber(v1) val vn2 = VersionNumber(v2) @@ -122,13 +113,13 @@ object VersionRange { val numericComparison = (0 until maxLen).iterator .map { i => - val n1 = if (i < nums1.length) nums1(i) else 0L - val n2 = if (i < nums2.length) nums2(i) else 0L + val n1 = if i < nums1.length then nums1(i) else 0L + val n2 = if i < nums2.length then nums2(i) else 0L n1.compare(n2) } .find(_ != 0) - numericComparison match { + numericComparison match case Some(cmp) => cmp case None => // If numeric parts are equal, compare tags (versions with tags are usually pre-releases) @@ -136,61 +127,56 @@ object VersionRange { val tags2 = vn2.tags // No tags means release version, which is higher than any pre-release - if (tags1.isEmpty && tags2.nonEmpty) 1 - else if (tags1.nonEmpty && tags2.isEmpty) -1 - else { + if tags1.isEmpty && tags2.nonEmpty then 1 + else if tags1.nonEmpty && tags2.isEmpty then -1 + else // Compare tags lexicographically val tagMaxLen = math.max(tags1.length, tags2.length) val tagComparison = (0 until tagMaxLen).iterator .map { i => - val t1 = if (i < tags1.length) tags1(i) else "" - val t2 = if (i < tags2.length) tags2(i) else "" + val t1 = if i < tags1.length then tags1(i) else "" + val t2 = if i < tags2.length then tags2(i) else "" t1.compare(t2) } .find(_ != 0) tagComparison.getOrElse(0) - } - } - } + end match + end compareVersions // Assuming Ivy is used to resolve conflict, this removes the version range // when it is open-ended to avoid dependency resolution hitting the Internet to get the latest. // See https://github.com/sbt/sbt/issues/2954 def stripMavenVersionRange(version: String): Option[String] = - if (isVersionRange(version)) { + if isVersionRange(version) then val noSpace = version.replace(" ", "") - noSpace match { + noSpace match case MavenVersionSetPattern(open1, x1, comma, x2, close1, _) => // http://maven.apache.org/components/enforcer/enforcer-rules/versionRanges.html - (open1, Option(x1), Option(comma), Option(x2), close1) match { + (open1, Option(x1), Option(comma), Option(x2), close1) match case (_, None, _, Some(x2), "]") => Some(x2) // a good upper bound is unknown case (_, None, _, Some(_), ")") => None case (_, Some(x1), _, None, _) => Some(x1) case _ => None - } case _ => None - } - } else None + else None /** Converts Ivy revision ranges to that of Maven POM */ - def fromIvyToMavenVersion(revision: String): String = { - def plusRange(s: String, shift: Int = 0) = { - def pow(i: Int): Int = if (i > 0) 10 * pow(i - 1) else 1 - val (prefixVersion, lastVersion) = (s + "0" * shift).reverse.split("\\.", 2) match { + def fromIvyToMavenVersion(revision: String): String = + def plusRange(s: String, shift: Int = 0) = + def pow(i: Int): Int = if i > 0 then 10 * pow(i - 1) else 1 + val (prefixVersion, lastVersion) = (s + "0" * shift).reverse.split("\\.", 2) match case Array(revLast, revRest) => (revRest.reverse + ".", revLast.reverse) case Array(revLast) => ("", revLast.reverse) - } val lastVersionInt = lastVersion.toInt s"[${prefixVersion}${lastVersion},${prefixVersion}${lastVersionInt + pow(shift)})" - } val DotPlusPattern = """(.+)\.\+""".r val DotNumPlusPattern = """(.+)\.(\d+)\+""".r val NumPlusPattern = """(\d+)\+""".r val maxDigit = 5 - try { - revision match { + try + revision match case "+" => "[0,)" case DotPlusPattern(base) => plusRange(base) // This is a heuristic. Maven just doesn't support Ivy's notions of 1+, so @@ -204,24 +190,23 @@ object VersionRange { val start = rev(0) val stop = rev(rev.length - 1) val mid = rev.substring(1, rev.length - 1) - (if (start == ']') "(" else start.toString) + mid + (if (stop == '[') ")" else stop) + (if start == ']' then "(" else start.toString) + mid + (if stop == '[' then ")" else stop) case _ => revision - } - } catch { + catch case _: NumberFormatException => // TODO - if the version doesn't meet our expectations, maybe we just issue a hard // error instead of softly ignoring the attempt to rewrite. // sys.error(s"Could not fix version [$revision] into maven style version") revision - } - } + end try + end fromIvyToMavenVersion def hasMavenVersionRange(version: String): Boolean = - if (version.length <= 1) false + if version.length <= 1 then false else startSym(version(0)) && stopSym(version(version.length - 1)) private val startSym = Set(']', '[', '(') private val stopSym = Set(']', '[', ')') private val MavenVersionSetPattern = """([\]\[\(])([\w\.\-]+)?(,)?([\w\.\-]+)?([\]\[\)])(,.+)?""".r -} +end VersionRange diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/VersionSchemes.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/VersionSchemes.scala index 685894312..d44bc4cc9 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/VersionSchemes.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/VersionSchemes.scala @@ -13,7 +13,7 @@ import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties import sbt.librarymanagement.{ EvictionWarningOptions, ModuleID, ScalaModuleInfo } // See APIMappings.scala -private[sbt] object VersionSchemes { +private[sbt] object VersionSchemes: final val EarlySemVer = "early-semver" final val SemVerSpec = "semver-spec" final val PackVer = "pvp" @@ -21,7 +21,7 @@ private[sbt] object VersionSchemes { final val Always = "always" def validateScheme(value: String): Unit = - value match { + value match case EarlySemVer | SemVerSpec | PackVer | Strict | Always => () case "semver" => sys.error( @@ -34,7 +34,6 @@ private[sbt] object VersionSchemes { |Specify 'semver-spec' for the spec-correct SemVer.""".stripMargin ) case x => sys.error(s"unknown version scheme: $x") - } /** * info.versionScheme property will be included into POM after sbt 1.4.0. @@ -47,11 +46,10 @@ private[sbt] object VersionSchemes { def evalFunc( scheme: String ): Function1[(ModuleID, Option[ModuleID], Option[ScalaModuleInfo]), Boolean] = - scheme match { + scheme match case EarlySemVer => EvictionWarningOptions.guessEarlySemVer case SemVerSpec => EvictionWarningOptions.guessSemVer case PackVer => EvictionWarningOptions.evalPvp case Strict => EvictionWarningOptions.guessStrict case Always => EvictionWarningOptions.guessTrue - } -} +end VersionSchemes diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/WeakPools.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/WeakPools.scala index 91108dc53..8b76f54f5 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/WeakPools.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/WeakPools.scala @@ -18,56 +18,48 @@ import scala.annotation.tailrec * * Ported from zinc's `sbt.internal.inc.WeakInterner` to avoid a new dependency, plus `internWith`. */ -private[librarymanagement] final class WeakInterner[A <: AnyRef] { +private[librarymanagement] final class WeakInterner[A <: AnyRef]: private val stale = new ReferenceQueue[A] private val pool = new ConcurrentHashMap[WeakValue[A], WeakValue[A]] def intern(a: A): A = internWith(a)(identity) /** Like `intern`, but applies `canonicalize` only on a miss, since equality is structural. */ - def internWith(a: A)(canonicalize: A => A): A = { + def internWith(a: A)(canonicalize: A => A): A = expunge() - lookup(a) match { + lookup(a) match case null => publish(canonicalize(a)) case hit => hit - } - } /** The pooled instance value-equal to `a`, or null if there is none. */ - private def lookup(a: A): A = { + private def lookup(a: A): A = val probe = new WeakValue(a, stale) try - pool.get(probe) match { + pool.get(probe) match case null => null.asInstanceOf[A] case existing => existing.get // null if it was collected since it matched - } finally probe.clear() // never enqueue a reference that was not pooled - } - private def publish(a: A): A = { + private def publish(a: A): A = val candidate = new WeakValue(a, stale) - @tailrec def attempt(): A = pool.putIfAbsent(candidate, candidate) match { + @tailrec def attempt(): A = pool.putIfAbsent(candidate, candidate) match case null => a case existing => - existing.get match { + existing.get match case null => // collected since it matched: drop the dead entry and retry pool.remove(existing, existing) attempt() case canonical => candidate.clear() canonical - } - } attempt() - } - @tailrec private def expunge(): Unit = stale.poll() match { + @tailrec private def expunge(): Unit = stale.poll() match case null => () case dead => pool.remove(dead, dead) expunge() - } -} +end WeakInterner /** * Weak reference that hashes and compares by its referent's value. @@ -76,16 +68,14 @@ private[librarymanagement] final class WeakInterner[A <: AnyRef] { * could never be found and removed. */ private final class WeakValue[A <: AnyRef](a: A, stale: ReferenceQueue[A]) - extends WeakReference[A](a, stale) { + extends WeakReference[A](a, stale): private val hash: Int = a.hashCode override def hashCode(): Int = hash - override def equals(other: Any): Boolean = other match { + override def equals(other: Any): Boolean = other match case that: WeakValue[?] => (this `eq` that) || { val value = get value != null && value == that.get } case _ => false - } -} diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala index 6e6630e01..4416e9d7c 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala @@ -3,7 +3,7 @@ package cross import scala.annotation.tailrec -object CrossVersionUtil { +object CrossVersionUtil: val trueString = "true" val falseString = "false" val fullString = "full" @@ -41,7 +41,7 @@ object CrossVersionUtil { * RCs for x.y.0 are considered API compatible. * Compatible versions include 0.12.0-1 and 0.12.0-RC1 for Some(0, 12). */ - private[sbt] def sbtApiVersion(v: String): Option[(Long, Long)] = v match { + private[sbt] def sbtApiVersion(v: String): Option[(Long, Long)] = v match case ReleaseV(x, y, _, _) => Some(sbtApiVersion(x.toLong, y.toLong)) case CandidateV(x, y, _, _) => Some(sbtApiVersion(x.toLong, y.toLong)) case BinCompatV(x, y, _, _, _) => Some(sbtApiVersion(x.toLong, y.toLong)) @@ -50,15 +50,13 @@ object CrossVersionUtil { case NonReleaseV_n(x, y, z, _) if x.toLong > 0 && (y.toLong > 0 || z.toLong > 0) => Some(sbtApiVersion(x.toLong, y.toLong)) case _ => None - } - private def sbtApiVersion(x: Long, y: Long) = { + private def sbtApiVersion(x: Long, y: Long) = // Prior to sbt 1 the "sbt api version" was the X.Y in the X.Y.Z version. // For example for sbt 0.13.x releases, the sbt api version is 0.13 // As of sbt 1 it is now X.0. // This means, for example, that all versions of sbt 1.x have sbt api version 1.0 - if (x > 0) (x, 0L) else (x, y) - } + if x > 0 then (x, 0L) else (x, y) private[sbt] def isScalaApiCompatible(v: String): Boolean = scalaApiVersion(v).isDefined @@ -66,29 +64,26 @@ object CrossVersionUtil { * Returns Scala binary interface x.y API compatible with the given version string v. * Compatible versions include 2.10.0-1 and 2.10.1-M1 for Some(2, 10), but not 2.10.0-RC1. */ - private[sbt] def scalaApiVersion(v: String): Option[(Long, Long)] = v match { + private[sbt] def scalaApiVersion(v: String): Option[(Long, Long)] = v match case ReleaseV(x, y, _, _) => Some((x.toLong, y.toLong)) case BinCompatV(x, y, _, _, _) => Some((x.toLong, y.toLong)) case NonReleaseV_1(x, y, z, _) if z.toLong > 0 => Some((x.toLong, y.toLong)) case _ => None - } private[sbt] def partialVersion(s: String): Option[(Long, Long)] = - s match { + s match case PartialVersion(major, minor) => Some((major.toLong, minor.toLong)) case MajorV(major) => Some((major.toLong, 0L)) case _ => None - } @tailrec - private[sbt] def binaryScala3Version(full: String): String = full match { + private[sbt] def binaryScala3Version(full: String): String = full match case ReleaseV(maj, _, _, _) => maj case NonReleaseV_n(maj, min, patch, _) if min.toLong > 0 || patch.toLong > 0 => maj case BinCompatV(maj, min, patch, stageOrNull, _) => - val stage = if (stageOrNull != null) stageOrNull else "" + val stage = if stageOrNull != null then stageOrNull else "" binaryScala3Version(s"$maj.$min.$patch$stage") case _ => full - } // Uses the following rules: // @@ -98,8 +93,8 @@ object CrossVersionUtil { // // - For non-stable Scala 3 versions, compiler versions can read TASTy in an older stable format but their TASTY versions are not compatible between each other even if the compilers have the same minor version (https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html) // - private[sbt] def isScalaBinaryCompatibleWith(newVersion: String, origVersion: String): Boolean = { - (newVersion, origVersion) match { + private[sbt] def isScalaBinaryCompatibleWith(newVersion: String, origVersion: String): Boolean = + (newVersion, origVersion) match case (NonReleaseV_n("2", _, _, _), NonReleaseV_n("2", _, _, _)) => val api1 = scalaApiVersion(newVersion) val api2 = scalaApiVersion(origVersion) @@ -112,11 +107,9 @@ object CrossVersionUtil { nMin.toInt > oMin.toInt case _ => newVersion == origVersion - } - } def binaryScalaVersion(full: String): String = - full match { + full match // Handle dynamic Scala 3 version patterns like "3-latest.candidate" case DynamicScala3V(maj) => maj case _ if full.startsWith("2.") => @@ -124,7 +117,6 @@ object CrossVersionUtil { scalaApiVersion ) // Scala 2 binary version case _ => binaryScala3Version(full) - } /** * Returns the binary version of the Scala, except for @@ -133,7 +125,7 @@ object CrossVersionUtil { * In Scala 3 onwards, it would be the major version. */ def earlyScalaVersion(full: String): String = - full match { + full match // Handle dynamic Scala 3 version patterns like "3-latest.candidate" case DynamicScala3V(maj) => maj case _ if full.startsWith("2.") => @@ -144,29 +136,25 @@ object CrossVersionUtil { partialVersion(full) match case Some((major, minor)) => major.toString case None => full - } def binarySbtVersion(full: String): String = - sbtApiVersion(full) match { + sbtApiVersion(full) match case Some((0, minor)) if minor < 12 => full case Some((0, minor)) => s"0.$minor" case Some((1, minor)) => s"1.$minor" case Some((major, _)) => major.toString case _ => full - } private def isNewer(major: Long, minor: Long, minMajor: Long, minMinor: Long): Boolean = major > minMajor || (major == minMajor && minor >= minMinor) private def binaryVersionWithApi(full: String, cutoff: String)( apiVersion: String => Option[(Long, Long)] - ): String = { - (apiVersion(full), partialVersion(cutoff)) match { + ): String = + (apiVersion(full), partialVersion(cutoff)) match case (Some((major, minor)), None) => s"$major.$minor" case (Some((major, minor)), Some((minMajor, minMinor))) if isNewer(major, minor, minMajor, minMinor) => s"$major.$minor" case _ => full - } - } -} +end CrossVersionUtil diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/GlobalLockFormat.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/GlobalLockFormat.scala index 4fe27fb77..3eda27612 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/GlobalLockFormat.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/GlobalLockFormat.scala @@ -9,7 +9,8 @@ import java.util.concurrent.Callable * A fake JsonFormat for xsbti.GlobalLock. * This is mostly for making IvyConfiguration serializable to JSON. */ -trait GlobalLockFormat { self: BasicJsonProtocol => +trait GlobalLockFormat: + self: BasicJsonProtocol => import GlobalLockFormats.* given globalLockIsoString: IsoString[GlobalLock] = @@ -18,10 +19,7 @@ trait GlobalLockFormat { self: BasicJsonProtocol => given GlobalLockFormat: JsonFormat[GlobalLock] = isoStringFormat(using globalLockIsoString ) -} -private[sbt] object GlobalLockFormats { - object NoGlobalLock extends GlobalLock { +private[sbt] object GlobalLockFormats: + object NoGlobalLock extends GlobalLock: def apply[T](lockFile: File, run: Callable[T]) = run.call() - } -} diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/LoggerFormat.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/LoggerFormat.scala index 7364c3c3e..bc6d17fbf 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/LoggerFormat.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/LoggerFormat.scala @@ -8,9 +8,9 @@ import sbt.util.Logger.Null * A fake JsonFormat for xsbti.Logger. * This is mostly for making IvyConfiguration serializable to JSON. */ -trait LoggerFormat { self: BasicJsonProtocol => +trait LoggerFormat: + self: BasicJsonProtocol => given xsbtiLoggerIsoString: IsoString[Logger] = IsoString.iso(_ => "", _ => Null) given LoggerFormat: JsonFormat[Logger] = isoStringFormat(using implicitly) -} diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/LogicalClockFormats.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/LogicalClockFormats.scala index 518539e23..d47b7b9cf 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/LogicalClockFormats.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/LogicalClockFormats.scala @@ -4,10 +4,10 @@ import sjsonnew.* import sbt.librarymanagement.LogicalClock -trait LogicalClockFormats { self: BasicJsonProtocol => +trait LogicalClockFormats: + self: BasicJsonProtocol => given LogicalClockFormat: JsonFormat[LogicalClock] = projectFormat[LogicalClock, String]( cl => cl.toString, str => LogicalClock(str) ) -} diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/NodeSeqFormat.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/NodeSeqFormat.scala index 3a1834aad..86a0500cf 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/NodeSeqFormat.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/formats/NodeSeqFormat.scala @@ -3,9 +3,9 @@ package sbt.internal.librarymanagement.formats import sjsonnew.* import scala.xml.* -trait NodeSeqFormat { self: BasicJsonProtocol => +trait NodeSeqFormat: + self: BasicJsonProtocol => given NodeSeqFormat: JsonFormat[NodeSeq] = projectFormat[NodeSeq, String]( xml => {xml}.toString, str => XML.loadString(str).child ) -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala b/lm-core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala index 000f96354..9126e8c01 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ArtifactExtra.scala @@ -6,18 +6,17 @@ package sbt.librarymanagement import java.io.File import java.net.URI -private[librarymanagement] abstract class ArtifactExtra { +private[librarymanagement] abstract class ArtifactExtra: def extraAttributes: Map[String, String] def withExtraAttributes(extraAttributes: Map[String, String]): Artifact def extra(attributes: (String, String)*) = withExtraAttributes(extraAttributes ++ ModuleID.checkE(attributes)) -} import Configurations.{ Optional, Pom, Test } -private[librarymanagement] abstract class ArtifactFunctions { +private[librarymanagement] abstract class ArtifactFunctions: def apply(name: String, extra: Map[String, String]): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Vector.empty, None, extra, None) def apply(name: String, classifier: String): Artifact = @@ -80,17 +79,14 @@ private[librarymanagement] abstract class ArtifactFunctions { assert(DefaultSourceTypes contains SourceType) def extract(uri: URI, default: String): String = extract(uri.toString, default) - def extract(name: String, default: String): String = { + def extract(name: String, default: String): String = val i = name.lastIndexOf('.') - if (i >= 0) - name.substring(i + 1) - else - default - } - def defaultArtifact(file: File) = { + if i >= 0 then name.substring(i + 1) + else default + def defaultArtifact(file: File) = val name = file.getName val i = name.lastIndexOf('.') - val base = if (i >= 0) name.substring(0, i) else name + val base = if i >= 0 then name.substring(0, i) else name Artifact( base, extract(name, DefaultType), @@ -99,18 +95,17 @@ private[librarymanagement] abstract class ArtifactFunctions { Vector.empty, Some(file.toURI) ) - } - def artifactName(scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact): String = { + def artifactName(scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact): String = import artifact.* - val classifierStr = classifier match { case None => ""; case Some(c) => "-" + c } + val classifierStr = classifier match + case None => ""; + case Some(c) => "-" + c val cross = CrossVersion(module.crossVersion, scalaVersion.full, scalaVersion.binary) - val withPlatform = module.crossVersion match { + val withPlatform = module.crossVersion match case _: Disabled => artifact.name case _ => CrossVersion.addPlatformSuffix(artifact.name, module.platformOpt, None) - } val base = CrossVersion.applyCross(withPlatform, cross) base + "-" + module.revision + classifierStr + "." + artifact.extension - } /** * Like `artifactName`, but omits the module's version. @@ -134,10 +129,8 @@ private[librarymanagement] abstract class ArtifactFunctions { val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType) @deprecated("Configuration should not be decided from the classifier.", "1.0") def classifierConf(classifier: String): Configuration = - if (classifier.startsWith(TestsClassifier)) - Test - else - Optional + if classifier.startsWith(TestsClassifier) then Test + else Optional def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType) @@ -155,4 +148,4 @@ private[librarymanagement] abstract class ArtifactFunctions { Vector.empty, None ) -} +end ArtifactFunctions diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ConfigRef.scala b/lm-core/src/main/scala/sbt/librarymanagement/ConfigRef.scala index 9dceb8526..47085e963 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ConfigRef.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ConfigRef.scala @@ -9,29 +9,24 @@ import scala.collection.concurrent.TrieMap * A reference to Configuration. * @param name The name of the configuration that eventually get used by Maven. */ -final class ConfigRef private (val name: String) extends Serializable { +final class ConfigRef private (val name: String) extends Serializable: override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) - override val hashCode: Int = { + override val hashCode: Int = 37 * (37 * (17 + "sbt.librarymanagement.ConfigRef".##) + name.##) - } - override def toString: String = { + override def toString: String = name - } - private def copy(name: String = name): ConfigRef = { + private def copy(name: String = name): ConfigRef = ConfigRef(name) - } - def withName(name: String): ConfigRef = { + def withName(name: String): ConfigRef = copy(name = name) - } -} -object ConfigRef extends sbt.librarymanagement.ConfigRefFunctions { +object ConfigRef extends sbt.librarymanagement.ConfigRefFunctions: // cache the reference to ConfigRefs private val cache = new TrieMap[String, ConfigRef] private lazy val Default = new ConfigRef("default") @@ -52,7 +47,7 @@ object ConfigRef extends sbt.librarymanagement.ConfigRefFunctions { private lazy val CompileInternal = new ConfigRef("compile-internal") private lazy val ZincTool = new ConfigRef("zinc-tool") - def apply(name: String): ConfigRef = name match { + def apply(name: String): ConfigRef = name match case "default" => Default case "compile" => Compile case "provided" => Provided @@ -71,5 +66,4 @@ object ConfigRef extends sbt.librarymanagement.ConfigRefFunctions { case "compile-internal" => CompileInternal case "zinc-tool" => ZincTool case _ => cache.getOrElseUpdate(name, new ConfigRef(name)) - } -} +end ConfigRef diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ConfigRefFormats.scala b/lm-core/src/main/scala/sbt/librarymanagement/ConfigRefFormats.scala index f18ebc73e..5bc53a9ad 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ConfigRefFormats.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ConfigRefFormats.scala @@ -5,14 +5,15 @@ package sbt.librarymanagement import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } -trait ConfigRefFormats { self: sjsonnew.BasicJsonProtocol => +trait ConfigRefFormats: + self: sjsonnew.BasicJsonProtocol => given ConfigRefFormat: JsonFormat[sbt.librarymanagement.ConfigRef] = - new JsonFormat[sbt.librarymanagement.ConfigRef] { + new JsonFormat[sbt.librarymanagement.ConfigRef]: override def read[J]( __jsOpt: Option[J], unbuilder: Unbuilder[J] - ): sbt.librarymanagement.ConfigRef = { - __jsOpt match { + ): sbt.librarymanagement.ConfigRef = + __jsOpt match case Some(__js) => unbuilder.beginObject(__js) val name = unbuilder.readField[String]("name") @@ -20,12 +21,7 @@ trait ConfigRefFormats { self: sjsonnew.BasicJsonProtocol => sbt.librarymanagement.ConfigRef(name) case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.librarymanagement.ConfigRef, builder: Builder[J]): Unit = { + override def write[J](obj: sbt.librarymanagement.ConfigRef, builder: Builder[J]): Unit = builder.beginObject() builder.addField("name", obj.name) builder.endObject() - } - } -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/Configuration.scala b/lm-core/src/main/scala/sbt/librarymanagement/Configuration.scala index 9ffbd7047..6a1d8571b 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/Configuration.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/Configuration.scala @@ -10,7 +10,7 @@ final class Configuration private[sbt] ( val extendsConfigs: Vector[Configuration], val transitive: Boolean ) extends ConfigurationExtra - with Serializable { + with Serializable: require(name != null, "name cannot be null") require(name.nonEmpty, "name cannot be empty") @@ -18,7 +18,7 @@ final class Configuration private[sbt] ( require(id.nonEmpty, "id cannot be empty") require(id.head.isUpper, s"id must be capitalized: $id") - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case x: Configuration => (this.id == x.id) && (this.name == x.name) && @@ -27,7 +27,6 @@ final class Configuration private[sbt] ( (this.extendsConfigs == x.extendsConfigs) && (this.transitive == x.transitive) case _ => false - } override val hashCode: Int = 37 * (37 * (37 * (37 * (37 * (37 * (17 + @@ -56,9 +55,9 @@ final class Configuration private[sbt] ( copy(transitive = transitive) def toConfigRef: ConfigRef = ConfigRef(name) -} +end Configuration -object Configuration { +object Configuration: // Don't call this directly. It's intended to be used from config macro. def of(id: String, name: String): Configuration = new Configuration(id, name, "", true, Vector.empty, true) @@ -72,7 +71,6 @@ object Configuration { transitive: Boolean ): Configuration = new Configuration(id, name, description, isPublic, extendsConfigs, transitive) -} /* Configuration isn't defined with Contraband. diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala b/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala index 966df7c9d..4a3fbca50 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala @@ -6,7 +6,7 @@ package sbt.librarymanagement import scala.annotation.tailrec import scala.quoted.* -object Configurations { +object Configurations: inline def config(name: String): Configuration = ${ ConfigurationMacro.configMacroImpl('{ name }) } @@ -23,12 +23,11 @@ object Configurations { lazy val TestInternal = fullInternal(Test) lazy val CompileInternal = fullInternal(Compile) - def internalMap(c: Configuration) = c match { + def internalMap(c: Configuration) = c match case Compile => CompileInternal case Test => TestInternal case Runtime => RuntimeInternal case _ => c - } private[sbt] def internal(base: Configuration, ext: Configuration*) = Configuration.of(base.id + "Internal", base.name + "-internal").extend(ext*).hide @@ -56,24 +55,23 @@ object Configurations { private[sbt] val DefaultMavenConfiguration = defaultConfiguration(true) private[sbt] val DefaultIvyConfiguration = defaultConfiguration(false) private[sbt] def DefaultConfiguration(mavenStyle: Boolean) = - if (mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration + if mavenStyle then DefaultMavenConfiguration else DefaultIvyConfiguration private[sbt] def defaultConfiguration(mavenStyle: Boolean) = - if (mavenStyle) Configurations.Compile else Configurations.Default + if mavenStyle then Configurations.Compile else Configurations.Default private[sbt] def removeDuplicates(configs: Iterable[Configuration]) = configs.map(config => (config.name, config)).toMap.values.toSet /** Returns true if the configuration should be under the influence of scalaVersion. */ private[sbt] def underScalaVersion(c: Configuration): Boolean = - c match { + c match case Default | Compile | Provided | Runtime | Test | Optional | CompilerPlugin | CompileInternal | RuntimeInternal | TestInternal => true case config => config.extendsConfigs.exists(underScalaVersion) - } -} +end Configurations -private[librarymanagement] abstract class ConfigurationExtra { +private[librarymanagement] abstract class ConfigurationExtra: def id: String def name: String def description: String @@ -98,7 +96,7 @@ private[librarymanagement] abstract class ConfigurationExtra { def notTransitive = intransitive def intransitive = Configuration.of(id, name, description, isPublic, extendsConfigs, false) def hide = Configuration.of(id, name, description, false, extendsConfigs, transitive) -} +end ConfigurationExtra private[sbt] object ConfigurationMacro: def configMacroImpl(name: Expr[String])(using Quotes): Expr[Configuration] = @@ -122,7 +120,6 @@ private[sbt] object ConfigurationMacro: '{ Configuration.of($id, $name) } end ConfigurationMacro -private[librarymanagement] abstract class ConfigRefFunctions { +private[librarymanagement] abstract class ConfigRefFunctions: implicit def configToConfigRef(c: Configuration): ConfigRef = c.toConfigRef -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationFormats.scala b/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationFormats.scala index 0fc97c39a..1be7a9848 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationFormats.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationFormats.scala @@ -6,15 +6,15 @@ package librarymanagement import _root_.sjsonnew.{ deserializationError, Builder, JsonFormat, Unbuilder } -trait ConfigurationFormats { +trait ConfigurationFormats: self: sbt.librarymanagement.ConfigurationFormats & sjsonnew.BasicJsonProtocol => given ConfigurationFormat: JsonFormat[sbt.librarymanagement.Configuration] = - new JsonFormat[sbt.librarymanagement.Configuration] { + new JsonFormat[sbt.librarymanagement.Configuration]: override def read[J]( jsOpt: Option[J], unbuilder: Unbuilder[J] - ): sbt.librarymanagement.Configuration = { - jsOpt match { + ): sbt.librarymanagement.Configuration = + jsOpt match case Some(js) => unbuilder.beginObject(js) val id = unbuilder.readField[String]("id") @@ -35,9 +35,7 @@ trait ConfigurationFormats { ) case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.librarymanagement.Configuration, builder: Builder[J]): Unit = { + override def write[J](obj: sbt.librarymanagement.Configuration, builder: Builder[J]): Unit = builder.beginObject() builder.addField("id", obj.id) builder.addField("name", obj.name) @@ -46,6 +44,4 @@ trait ConfigurationFormats { builder.addField("extendsConfigs", obj.extendsConfigs) builder.addField("transitive", obj.transitive) builder.endObject() - } - } -} +end ConfigurationFormats diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ConflictWarning.scala b/lm-core/src/main/scala/sbt/librarymanagement/ConflictWarning.scala index 010f51bfd..be173db57 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ConflictWarning.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ConflictWarning.scala @@ -8,65 +8,55 @@ import sbt.util.{ Logger, Level } * library dependency graph by mistake, but it won't be caught by eviction. */ final case class ConflictWarning(label: String, level: Level.Value, failOnConflict: Boolean) {} -object ConflictWarning { +object ConflictWarning: def disable: ConflictWarning = ConflictWarning("", Level.Debug, false) private def idString(org: String, name: String) = s"$org:$name" def default(label: String): ConflictWarning = ConflictWarning(label, Level.Error, true) - def apply(config: ConflictWarning, report: UpdateReport, log: Logger): Unit = { + def apply(config: ConflictWarning, report: UpdateReport, log: Logger): Unit = processCrossVersioned(config, report, log) - } private def processCrossVersioned( config: ConflictWarning, report: UpdateReport, log: Logger - ): Unit = { + ): Unit = val crossMismatches = crossVersionMismatches(report) - if (crossMismatches.nonEmpty) { + if crossMismatches.nonEmpty then val pre = s"Modules were resolved with conflicting cross-version suffixes in ${config.label}:\n " val conflictMsgs = - for (((org, rawName), fullNames) <- crossMismatches) yield { + for ((org, rawName), fullNames) <- crossMismatches yield val suffixes = fullNames.map(getCrossSuffix).mkString(", ") s"${idString(org, rawName)} $suffixes" - } log.log(config.level, conflictMsgs.mkString(pre, "\n ", "")) - if (config.failOnConflict) { + if config.failOnConflict then val summary = crossMismatches.map { case ((org, raw), _) => idString(org, raw) }.mkString(", ") sys.error("Conflicting cross-version suffixes in: " + summary) - } - } - } /** Map from (organization, rawName) to set of multiple full names. */ - def crossVersionMismatches(report: UpdateReport): Map[(String, String), Set[String]] = { + def crossVersionMismatches(report: UpdateReport): Map[(String, String), Set[String]] = val mismatches = report.configurations.flatMap { confReport => groupByRawName(confReport.allModules).view.mapValues { modules => val differentFullNames = modules.map(_.name).toSet - if (differentFullNames.size > 1) differentFullNames else Set.empty[String] + if differentFullNames.size > 1 then differentFullNames else Set.empty[String] } } mismatches.foldLeft(Map.empty[(String, String), Set[String]])(merge) - } private def merge[A, B](m: Map[A, Set[B]], b: (A, Set[B])): Map[A, Set[B]] = - if (b._2.isEmpty) m - else - m.updated(b._1, m.getOrElse(b._1, Set.empty) ++ b._2) + if b._2.isEmpty then m + else m.updated(b._1, m.getOrElse(b._1, Set.empty) ++ b._2) private def groupByRawName(ms: Seq[ModuleID]): Map[(String, String), Seq[ModuleID]] = ms.groupBy(m => (m.organization, dropCrossSuffix(m.name))) private val CrossSuffixPattern = """(.+)_(\d+(?:\.\d+)?(?:\.\d+)?(?:-.+)?)""".r - private def dropCrossSuffix(s: String): String = s match { + private def dropCrossSuffix(s: String): String = s match case CrossSuffixPattern(raw, _) => raw case _ => s - } - private def getCrossSuffix(s: String): String = s match { + private def getCrossSuffix(s: String): String = s match case CrossSuffixPattern(_, v) => "_" + v case _ => "" - } - -} +end ConflictWarning diff --git a/lm-core/src/main/scala/sbt/librarymanagement/Credentials.scala b/lm-core/src/main/scala/sbt/librarymanagement/Credentials.scala index 38ed45f32..e28202a7f 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/Credentials.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/Credentials.scala @@ -19,22 +19,19 @@ object Credentials: def apply(file: File): Credentials = new FileCredentials(file) - final class FileCredentials(val path: File) extends Credentials { + final class FileCredentials(val path: File) extends Credentials: override def toString = s"""FileCredentials("$path")""" - } final class DirectCredentials( val realm: String, val host: String, val userName: String, val passwd: String - ) extends Credentials { - override def toString = { + ) extends Credentials: + override def toString = val dq = '"' val r = - if (realm == null) "null" + if realm == null then "null" else s"$dq$realm$dq" s"""DirectCredentials($r, "$host", "$userName", ****)""" - } - } end Credentials diff --git a/lm-core/src/main/scala/sbt/librarymanagement/CrossVersion.scala b/lm-core/src/main/scala/sbt/librarymanagement/CrossVersion.scala index aea152e22..f50200d4e 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/CrossVersion.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/CrossVersion.scala @@ -6,37 +6,28 @@ package sbt.librarymanagement import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } /** Configures how a module will be cross-versioned. */ -abstract class CrossVersion() extends Serializable { - override def equals(o: Any): Boolean = o match { +abstract class CrossVersion() extends Serializable: + override def equals(o: Any): Boolean = o match case _: CrossVersion => true case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = 37 * (17 + "sbt.librarymanagement.CrossVersion".##) - } - override def toString: String = { + override def toString: String = "CrossVersion()" - } -} object CrossVersion extends sbt.librarymanagement.CrossVersionFunctions {} /** Disables cross versioning for a module. */ -sealed class Disabled private () extends sbt.librarymanagement.CrossVersion() with Serializable { +sealed class Disabled private () extends sbt.librarymanagement.CrossVersion() with Serializable: - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case _: Disabled => true case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = 37 * (17 + "sbt.librarymanagement.Disabled".##) - } - override def toString: String = { + override def toString: String = "Disabled()" - } -} -object Disabled extends sbt.librarymanagement.Disabled { +object Disabled extends sbt.librarymanagement.Disabled: def apply(): Disabled = Disabled -} /** * Cross-versions a module using the result of @@ -46,86 +37,66 @@ object Disabled extends sbt.librarymanagement.Disabled { */ final class Binary private (val prefix: String, val suffix: String) extends sbt.librarymanagement.CrossVersion() - with Serializable { + with Serializable: private def this() = this("", "") - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case x: Binary => (this.prefix == x.prefix) && (this.suffix == x.suffix) case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = 37 * (37 * (37 * (17 + "sbt.librarymanagement.Binary".##) + prefix.##) + suffix.##) - } - override def toString: String = { + override def toString: String = "Binary(" + prefix + ", " + suffix + ")" - } - private def copy(prefix: String = prefix, suffix: String = suffix): Binary = { + private def copy(prefix: String = prefix, suffix: String = suffix): Binary = new Binary(prefix, suffix) - } - def withPrefix(prefix: String): Binary = { + def withPrefix(prefix: String): Binary = copy(prefix = prefix) - } - def withSuffix(suffix: String): Binary = { + def withSuffix(suffix: String): Binary = copy(suffix = suffix) - } -} -object Binary { +object Binary: def apply(): Binary = new Binary() def apply(prefix: String, suffix: String): Binary = new Binary(prefix, suffix) -} /** Cross-versions a module using the string `value`. */ final class Constant private (val value: String) extends sbt.librarymanagement.CrossVersion() - with Serializable { + with Serializable: private def this() = this("") - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case x: Constant => (this.value == x.value) case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = 37 * (37 * (17 + "sbt.librarymanagement.Constant".##) + value.##) - } - override def toString: String = { + override def toString: String = "Constant(" + value + ")" - } - private def copy(value: String): Constant = { + private def copy(value: String): Constant = new Constant(value) - } - def withValue(value: String): Constant = { + def withValue(value: String): Constant = copy(value = value) - } -} -object Constant { +object Constant: def apply(): Constant = new Constant() def apply(value: String): Constant = new Constant(value) -} /** * Cross-versions a module by stripping off -bin-suffix. * This is intended for patch-version compatible alternative replacements. */ -final class Patch private () extends sbt.librarymanagement.CrossVersion() with Serializable { +final class Patch private () extends sbt.librarymanagement.CrossVersion() with Serializable: - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case _: Patch => true case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = 37 * (17 + "sbt.librarymanagement.Patch".##) - } - override def toString: String = { + override def toString: String = "Patch()" - } -} -object Patch { +object Patch: def apply(): Patch = new Patch() -} /** * Cross-versions a module with the result of @@ -135,35 +106,27 @@ object Patch { */ final class Full private (val prefix: String, val suffix: String) extends sbt.librarymanagement.CrossVersion() - with Serializable { + with Serializable: private def this() = this("", "") - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case x: Full => (this.prefix == x.prefix) && (this.suffix == x.suffix) case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = 37 * (37 * (37 * (17 + "sbt.librarymanagement.Full".##) + prefix.##) + suffix.##) - } - override def toString: String = { + override def toString: String = "Full(" + prefix + ", " + suffix + ")" - } - private def copy(prefix: String = prefix, suffix: String = suffix): Full = { + private def copy(prefix: String = prefix, suffix: String = suffix): Full = new Full(prefix, suffix) - } - def withPrefix(prefix: String): Full = { + def withPrefix(prefix: String): Full = copy(prefix = prefix) - } - def withSuffix(suffix: String): Full = { + def withSuffix(suffix: String): Full = copy(suffix = suffix) - } -} -object Full { +object Full: def apply(): Full = new Full() def apply(prefix: String, suffix: String): Full = new Full(prefix, suffix) -} /** * Similar to Binary except that if the binary version is 3 @@ -173,35 +136,27 @@ object Full { */ final class For3Use2_13 private (val prefix: String, val suffix: String) extends sbt.librarymanagement.CrossVersion() - with Serializable { + with Serializable: private def this() = this("", "") - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case x: For3Use2_13 => (this.prefix == x.prefix) && (this.suffix == x.suffix) case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = 37 * (37 * (37 * (17 + "sbt.librarymanagement.For3Use2_13".##) + prefix.##) + suffix.##) - } - override def toString: String = { + override def toString: String = "For3Use2_13(" + prefix + ", " + suffix + ")" - } - private def copy(prefix: String = prefix, suffix: String = suffix): For3Use2_13 = { + private def copy(prefix: String = prefix, suffix: String = suffix): For3Use2_13 = new For3Use2_13(prefix, suffix) - } - def withPrefix(prefix: String): For3Use2_13 = { + def withPrefix(prefix: String): For3Use2_13 = copy(prefix = prefix) - } - def withSuffix(suffix: String): For3Use2_13 = { + def withSuffix(suffix: String): For3Use2_13 = copy(suffix = suffix) - } -} -object For3Use2_13 { +object For3Use2_13: def apply(): For3Use2_13 = new For3Use2_13() def apply(prefix: String, suffix: String): For3Use2_13 = new For3Use2_13(prefix, suffix) -} /** * Similar to Binary except that if the binary version is 2.13 @@ -211,44 +166,37 @@ object For3Use2_13 { */ final class For2_13Use3 private (val prefix: String, val suffix: String) extends sbt.librarymanagement.CrossVersion() - with Serializable { + with Serializable: private def this() = this("", "") - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case x: For2_13Use3 => (this.prefix == x.prefix) && (this.suffix == x.suffix) case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = 37 * (37 * (37 * (17 + "sbt.librarymanagement.For3Use2_13".##) + prefix.##) + suffix.##) - } - override def toString: String = { + override def toString: String = "For3Use2_13(" + prefix + ", " + suffix + ")" - } - private def copy(prefix: String = prefix, suffix: String = suffix): For2_13Use3 = { + private def copy(prefix: String = prefix, suffix: String = suffix): For2_13Use3 = new For2_13Use3(prefix, suffix) - } - def withPrefix(prefix: String): For2_13Use3 = { + def withPrefix(prefix: String): For2_13Use3 = copy(prefix = prefix) - } - def withSuffix(suffix: String): For2_13Use3 = { + def withSuffix(suffix: String): For2_13Use3 = copy(suffix = suffix) - } -} -object For2_13Use3 { +object For2_13Use3: def apply(): For2_13Use3 = new For2_13Use3() def apply(prefix: String, suffix: String): For2_13Use3 = new For2_13Use3(prefix, suffix) -} -trait DisabledFormats { self: sjsonnew.BasicJsonProtocol => +trait DisabledFormats: + self: sjsonnew.BasicJsonProtocol => given DisabledFormat: JsonFormat[sbt.librarymanagement.Disabled] = - new JsonFormat[sbt.librarymanagement.Disabled] { + new JsonFormat[sbt.librarymanagement.Disabled]: override def read[J]( jsOpt: Option[J], unbuilder: Unbuilder[J] - ): sbt.librarymanagement.Disabled = { - jsOpt match { + ): sbt.librarymanagement.Disabled = + jsOpt match case Some(js) => unbuilder.beginObject(js) @@ -256,22 +204,18 @@ trait DisabledFormats { self: sjsonnew.BasicJsonProtocol => sbt.librarymanagement.Disabled() case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.librarymanagement.Disabled, builder: Builder[J]): Unit = { + override def write[J](obj: sbt.librarymanagement.Disabled, builder: Builder[J]): Unit = builder.beginObject() builder.endObject() - } - } given DisabledObjectFormat: JsonFormat[sbt.librarymanagement.Disabled.type] = - new JsonFormat[sbt.librarymanagement.Disabled.type] { + new JsonFormat[sbt.librarymanagement.Disabled.type]: override def read[J]( jsOpt: Option[J], unbuilder: Unbuilder[J] - ): sbt.librarymanagement.Disabled.type = { - jsOpt match { + ): sbt.librarymanagement.Disabled.type = + jsOpt match case Some(js) => unbuilder.beginObject(js) @@ -279,24 +223,21 @@ trait DisabledFormats { self: sjsonnew.BasicJsonProtocol => sbt.librarymanagement.Disabled case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.librarymanagement.Disabled.type, builder: Builder[J]): Unit = { + override def write[J](obj: sbt.librarymanagement.Disabled.type, builder: Builder[J]): Unit = builder.beginObject() builder.endObject() - } - } -} +end DisabledFormats -trait BinaryFormats { self: sjsonnew.BasicJsonProtocol => +trait BinaryFormats: + self: sjsonnew.BasicJsonProtocol => given BinaryFormat: JsonFormat[sbt.librarymanagement.Binary] = - new JsonFormat[sbt.librarymanagement.Binary] { + new JsonFormat[sbt.librarymanagement.Binary]: override def read[J]( jsOpt: Option[J], unbuilder: Unbuilder[J] - ): sbt.librarymanagement.Binary = { - jsOpt match { + ): sbt.librarymanagement.Binary = + jsOpt match case Some(js) => unbuilder.beginObject(js) val prefix = unbuilder.readField[String]("prefix") @@ -305,25 +246,22 @@ trait BinaryFormats { self: sjsonnew.BasicJsonProtocol => sbt.librarymanagement.Binary(prefix, suffix) case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.librarymanagement.Binary, builder: Builder[J]): Unit = { + override def write[J](obj: sbt.librarymanagement.Binary, builder: Builder[J]): Unit = builder.beginObject() builder.addField("prefix", obj.prefix) builder.addField("suffix", obj.suffix) builder.endObject() - } - } -} +end BinaryFormats -trait ConstantFormats { self: sjsonnew.BasicJsonProtocol => +trait ConstantFormats: + self: sjsonnew.BasicJsonProtocol => given ConstantFormat: JsonFormat[sbt.librarymanagement.Constant] = - new JsonFormat[sbt.librarymanagement.Constant] { + new JsonFormat[sbt.librarymanagement.Constant]: override def read[J]( jsOpt: Option[J], unbuilder: Unbuilder[J] - ): sbt.librarymanagement.Constant = { - jsOpt match { + ): sbt.librarymanagement.Constant = + jsOpt match case Some(js) => unbuilder.beginObject(js) val value = unbuilder.readField[String]("value") @@ -331,24 +269,20 @@ trait ConstantFormats { self: sjsonnew.BasicJsonProtocol => sbt.librarymanagement.Constant(value) case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.librarymanagement.Constant, builder: Builder[J]): Unit = { + override def write[J](obj: sbt.librarymanagement.Constant, builder: Builder[J]): Unit = builder.beginObject() builder.addField("value", obj.value) builder.endObject() - } - } -} -trait PatchFormats { self: sjsonnew.BasicJsonProtocol => +trait PatchFormats: + self: sjsonnew.BasicJsonProtocol => given PatchFormat: JsonFormat[sbt.librarymanagement.Patch] = - new JsonFormat[sbt.librarymanagement.Patch] { + new JsonFormat[sbt.librarymanagement.Patch]: override def read[J]( jsOpt: Option[J], unbuilder: Unbuilder[J] - ): sbt.librarymanagement.Patch = { - jsOpt match { + ): sbt.librarymanagement.Patch = + jsOpt match case Some(js) => unbuilder.beginObject(js) @@ -356,24 +290,20 @@ trait PatchFormats { self: sjsonnew.BasicJsonProtocol => sbt.librarymanagement.Patch() case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.librarymanagement.Patch, builder: Builder[J]): Unit = { + override def write[J](obj: sbt.librarymanagement.Patch, builder: Builder[J]): Unit = builder.beginObject() builder.endObject() - } - } -} -trait FullFormats { self: sjsonnew.BasicJsonProtocol => +trait FullFormats: + self: sjsonnew.BasicJsonProtocol => given FullFormat: JsonFormat[sbt.librarymanagement.Full] = - new JsonFormat[sbt.librarymanagement.Full] { + new JsonFormat[sbt.librarymanagement.Full]: override def read[J]( jsOpt: Option[J], unbuilder: Unbuilder[J] - ): sbt.librarymanagement.Full = { - jsOpt match { + ): sbt.librarymanagement.Full = + jsOpt match case Some(js) => unbuilder.beginObject(js) val prefix = unbuilder.readField[String]("prefix") @@ -382,25 +312,22 @@ trait FullFormats { self: sjsonnew.BasicJsonProtocol => sbt.librarymanagement.Full(prefix, suffix) case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.librarymanagement.Full, builder: Builder[J]): Unit = { + override def write[J](obj: sbt.librarymanagement.Full, builder: Builder[J]): Unit = builder.beginObject() builder.addField("prefix", obj.prefix) builder.addField("suffix", obj.suffix) builder.endObject() - } - } -} +end FullFormats -trait For3Use2_13Formats { self: sjsonnew.BasicJsonProtocol => +trait For3Use2_13Formats: + self: sjsonnew.BasicJsonProtocol => given For3Use2_13Format: JsonFormat[sbt.librarymanagement.For3Use2_13] = - new JsonFormat[sbt.librarymanagement.For3Use2_13] { + new JsonFormat[sbt.librarymanagement.For3Use2_13]: override def read[J]( jsOpt: Option[J], unbuilder: Unbuilder[J] - ): sbt.librarymanagement.For3Use2_13 = { - jsOpt match { + ): sbt.librarymanagement.For3Use2_13 = + jsOpt match case Some(js) => unbuilder.beginObject(js) val prefix = unbuilder.readField[String]("prefix") @@ -409,25 +336,22 @@ trait For3Use2_13Formats { self: sjsonnew.BasicJsonProtocol => sbt.librarymanagement.For3Use2_13(prefix, suffix) case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.librarymanagement.For3Use2_13, builder: Builder[J]): Unit = { + override def write[J](obj: sbt.librarymanagement.For3Use2_13, builder: Builder[J]): Unit = builder.beginObject() builder.addField("prefix", obj.prefix) builder.addField("suffix", obj.suffix) builder.endObject() - } - } -} +end For3Use2_13Formats -trait For2_13Use3Formats { self: sjsonnew.BasicJsonProtocol => +trait For2_13Use3Formats: + self: sjsonnew.BasicJsonProtocol => given For2_13Use3Format: JsonFormat[sbt.librarymanagement.For2_13Use3] = - new JsonFormat[sbt.librarymanagement.For2_13Use3] { + new JsonFormat[sbt.librarymanagement.For2_13Use3]: override def read[J]( jsOpt: Option[J], unbuilder: Unbuilder[J] - ): sbt.librarymanagement.For2_13Use3 = { - jsOpt match { + ): sbt.librarymanagement.For2_13Use3 = + jsOpt match case Some(js) => unbuilder.beginObject(js) val prefix = unbuilder.readField[String]("prefix") @@ -436,23 +360,19 @@ trait For2_13Use3Formats { self: sjsonnew.BasicJsonProtocol => sbt.librarymanagement.For2_13Use3(prefix, suffix) case None => deserializationError("Expected JsObject but found None") - } - } - override def write[J](obj: sbt.librarymanagement.For2_13Use3, builder: Builder[J]): Unit = { + override def write[J](obj: sbt.librarymanagement.For2_13Use3, builder: Builder[J]): Unit = builder.beginObject() builder.addField("prefix", obj.prefix) builder.addField("suffix", obj.suffix) builder.endObject() - } - } -} +end For2_13Use3Formats -trait CrossVersionFormats { +trait CrossVersionFormats: self: sjsonnew.BasicJsonProtocol & sbt.librarymanagement.DisabledFormats & sbt.librarymanagement.BinaryFormats & sbt.librarymanagement.ConstantFormats & sbt.librarymanagement.PatchFormats & sbt.librarymanagement.FullFormats & sbt.librarymanagement.For3Use2_13Formats & sbt.librarymanagement.For2_13Use3Formats => - given CrossVersionFormat: JsonFormat[CrossVersion] = { + given CrossVersionFormat: JsonFormat[CrossVersion] = val format = flatUnionFormat8[ CrossVersion, Disabled, @@ -465,19 +385,17 @@ trait CrossVersionFormats { For2_13Use3 ]("type") // This is a hand-crafted formatter to avoid Disabled$ showing up in JSON - new JsonFormat[CrossVersion] { + new JsonFormat[CrossVersion]: override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): CrossVersion = format.read(jsOpt, unbuilder) - override def write[J](obj: CrossVersion, builder: Builder[J]): Unit = { - if (obj == Disabled) { + override def write[J](obj: CrossVersion, builder: Builder[J]): Unit = + if obj == Disabled then builder.beginPreObject() builder.addFieldName("type") builder.writeString("Disabled") builder.endPreObject() builder.beginObject() builder.endObject() - } else format.write(obj, builder) - } - } - } -} + else format.write(obj, builder) + end CrossVersionFormat +end CrossVersionFormats diff --git a/lm-core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala b/lm-core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala index 7fe5ed64b..96476fd53 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala @@ -5,7 +5,7 @@ import sbt.librarymanagement.syntax.* final case class ScalaVersion(full: String, binary: String) -private[librarymanagement] abstract class CrossVersionFunctions { +private[librarymanagement] abstract class CrossVersionFunctions: /** Compatibility with 0.13 */ @deprecated( @@ -84,30 +84,26 @@ private[librarymanagement] abstract class CrossVersionFunctions { def for2_13Use3With(prefix: String, suffix: String): CrossVersion = For2_13Use3(prefix, suffix) private[sbt] def getPrefixSuffix(value: CrossVersion): (String, String) = - value match { + value match case (_: Disabled | _: Constant | _: Patch) => ("", "") case b: Binary => (b.prefix, b.suffix) case f: Full => (f.prefix, f.suffix) case c: For3Use2_13 => (c.prefix, c.suffix) case c: For2_13Use3 => (c.prefix, c.suffix) - } private[sbt] def setPrefixSuffix(value: CrossVersion, p: String, s: String): CrossVersion = - value match { + value match case (_: Disabled | _: Constant | _: Patch) => value case b: Binary => b.withPrefix(p).withSuffix(s) case f: Full => f.withPrefix(p).withSuffix(s) case c: For3Use2_13 => c.withPrefix(p).withSuffix(s) case c: For2_13Use3 => c.withPrefix(p).withSuffix(s) - } - private[sbt] def patchFun(fullVersion: String): String = { + private[sbt] def patchFun(fullVersion: String): String = import sbt.internal.librarymanagement.cross.CrossVersionUtil.BinCompatV - fullVersion match { - case BinCompatV(x, y, z, w, _) => s"""$x.$y.$z${if (w == null) "" else w}""" + fullVersion match + case BinCompatV(x, y, z, w, _) => s"""$x.$y.$z${if w == null then "" else w}""" case other => other - } - } private[sbt] def append(s: String): Option[String => String] = Some(x => crossName(x, s)) @@ -121,7 +117,7 @@ private[librarymanagement] abstract class CrossVersionFunctions { fullVersion: String, binaryVersion: String ): Option[String => String] = - cross match { + cross match case _: Disabled => None case b: Binary => append(b.prefix + binaryVersion + b.suffix) case c: Constant => append(c.value) @@ -129,15 +125,14 @@ private[librarymanagement] abstract class CrossVersionFunctions { case f: Full => append(f.prefix + fullVersion + f.suffix) case c: For3Use2_13 => val compat = - if (binaryVersion == "3" || binaryVersion.startsWith("3.0.0")) "2.13" + if binaryVersion == "3" || binaryVersion.startsWith("3.0.0") then "2.13" else binaryVersion append(c.prefix + compat + c.suffix) case c: For2_13Use3 => val compat = - if (binaryVersion == "2.13") "3" + if binaryVersion == "2.13" then "3" else binaryVersion append(c.prefix + compat + c.suffix) - } /** * Constructs the cross-version function defined by `module` and `is`, if one is configured. @@ -160,16 +155,14 @@ private[librarymanagement] abstract class CrossVersionFunctions { artifacts: Vector[Artifact], cross: Option[String => String] ): Vector[Artifact] = - cross match { + cross match case None => artifacts case Some(_) => substituteCrossA(artifacts, cross) - } private[sbt] def applyCross(s: String, fopt: Option[String => String]): String = - fopt match { + fopt match case None => s case Some(fopt) => fopt(s) - } private[sbt] def crossName(name: String, cross: String): String = name + "_" + cross @@ -192,13 +185,12 @@ private[librarymanagement] abstract class CrossVersionFunctions { private[sbt] def substituteCross( exclude: ExclusionRule, is: Option[ScalaModuleInfo] - ): ExclusionRule = { + ): ExclusionRule = val fopt: Option[String => String] = is flatMap { i => CrossVersion(exclude.crossVersion, i.scalaFullVersion, i.scalaBinaryVersion) } exclude.withName(applyCross(exclude.name, fopt)) - } /** Cross-versions `a` according to cross-version function `cross`. */ def substituteCross(a: Artifact, cross: Option[String => String]): Artifact = @@ -214,14 +206,12 @@ private[librarymanagement] abstract class CrossVersionFunctions { * for the given full and binary Scala versions `scalaFullVersion` and `scalaBinaryVersion` * according to the ModuleID's cross-versioning setting. */ - def apply(scalaFullVersion: String, scalaBinaryVersion: String): ModuleID => ModuleID = m => { + def apply(scalaFullVersion: String, scalaBinaryVersion: String): ModuleID => ModuleID = m => val cross = apply(m.crossVersion, scalaFullVersion, scalaBinaryVersion) - if (cross.isDefined) + if cross.isDefined then m.withName(applyCross(m.name, cross)) .withExplicitArtifacts(substituteCrossA(m.explicitArtifacts, cross)) - else - m - } + else m def isSbtApiCompatible(v: String): Boolean = CrossVersionUtil.isSbtApiCompatible(v) @@ -271,4 +261,4 @@ private[librarymanagement] abstract class CrossVersionFunctions { */ def isScalaBinaryCompatibleWith(newVersion: String, origVersion: String): Boolean = CrossVersionUtil.isScalaBinaryCompatibleWith(newVersion, origVersion) -} +end CrossVersionFunctions diff --git a/lm-core/src/main/scala/sbt/librarymanagement/DependencyBuilders.scala b/lm-core/src/main/scala/sbt/librarymanagement/DependencyBuilders.scala index 0080ab372..08388731f 100755 --- a/lm-core/src/main/scala/sbt/librarymanagement/DependencyBuilders.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/DependencyBuilders.scala @@ -8,7 +8,7 @@ import sbt.internal.librarymanagement.StringUtilities.nonEmpty /** * DependencyBuilders implements the implicits for % and %% DSL. */ -trait DependencyBuilders { +trait DependencyBuilders: // See http://www.scala-lang.org/news/2.12.0#traits-compile-to-interfaces // Avoid defining fields (val or var, but a constant is ok – final val without result type) // Avoid calling super @@ -16,60 +16,48 @@ trait DependencyBuilders { import DependencyBuilders.* - implicit def stringToOrganization(organization: String): Organization = { + implicit def stringToOrganization(organization: String): Organization = nonEmpty(organization, "Organization") new Organization(organization) - } - implicit def toRepositoryName(name: String): RepositoryName = { + implicit def toRepositoryName(name: String): RepositoryName = nonEmpty(name, "Repository name") new RepositoryName(name) - } - implicit def moduleIDConfigurable(m: ModuleID): ModuleIDConfigurable = { + implicit def moduleIDConfigurable(m: ModuleID): ModuleIDConfigurable = require(m.configurations.isEmpty, "Configurations already specified for module " + m) new ModuleIDConfigurable(m) - } -} -object DependencyBuilders { - final class Organization private[sbt] (private[sbt] val organization: String) { +object DependencyBuilders: + final class Organization private[sbt] (private[sbt] val organization: String): def %(name: String) = organizationArtifact(name, Disabled()) def %%(name: String): OrganizationArtifactName = organizationArtifact(name, CrossVersion.binary) - private def organizationArtifact(name: String, cross: CrossVersion) = { + private def organizationArtifact(name: String, cross: CrossVersion) = nonEmpty(name, "Name") new OrganizationArtifactName(organization, name, cross) - } - } final class OrganizationArtifactName private[sbt] ( private[sbt] val organization: String, private[sbt] val name: String, private[sbt] val crossVersion: CrossVersion - ) { - def %(revision: String): ModuleID = { + ): + def %(revision: String): ModuleID = nonEmpty(revision, "Revision") ModuleID(organization, name, revision).cross(crossVersion) - } - } - final class ModuleIDConfigurable private[sbt] (moduleID: ModuleID) { + final class ModuleIDConfigurable private[sbt] (moduleID: ModuleID): def %(configuration: Configuration): ModuleID = %(configuration.name) def %(configuration: ConfigRef): ModuleID = %(configuration.name) - def %(configurations: String): ModuleID = { + def %(configurations: String): ModuleID = nonEmpty(configurations, "Configurations") val c = configurations moduleID.withConfigurations(configurations = Some(c)) - } - } - final class RepositoryName private[sbt] (name: String) { - infix def at(location: String): MavenRepository = { + final class RepositoryName private[sbt] (name: String): + infix def at(location: String): MavenRepository = nonEmpty(location, "Repository location") MavenRepository(name, location) - } - } -} +end DependencyBuilders diff --git a/lm-core/src/main/scala/sbt/librarymanagement/DependencyFilter.scala b/lm-core/src/main/scala/sbt/librarymanagement/DependencyFilter.scala index f17cd2066..d4425fdd3 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/DependencyFilter.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/DependencyFilter.scala @@ -5,7 +5,7 @@ package sbt.librarymanagement import sbt.io.{ AllPassFilter, NameFilter } -trait DependencyFilterExtra { +trait DependencyFilterExtra: // See http://www.scala-lang.org/news/2.12.0#traits-compile-to-interfaces // Avoid defining fields (val or var, but a constant is ok – final val without result type) // Avoid calling super @@ -33,9 +33,9 @@ trait DependencyFilterExtra { def configurationFilter(name: NameFilter = AllPassFilter): ConfigurationFilter = (c: ConfigRef) => name.accept(c.name) -} +end DependencyFilterExtra -object DependencyFilter extends DependencyFilterExtra { +object DependencyFilter extends DependencyFilterExtra: def make( configuration: ConfigurationFilter = configurationFilter(), module: ModuleFilter = moduleFilter(), @@ -56,14 +56,13 @@ object DependencyFilter extends DependencyFilterExtra { implicit def fnToConfigurationFilter(f: ConfigRef => Boolean): ConfigurationFilter = (c: ConfigRef) => f(c) implicit def subDepFilterToFn[Arg](f: SubDepFilter[Arg, ?]): Arg => Boolean = f.apply(_) -} -trait DependencyFilter { +end DependencyFilter +trait DependencyFilter: def apply(configuration: ConfigRef, module: ModuleID, artifact: Artifact): Boolean final def &&(o: DependencyFilter) = DependencyFilter(this, o, _ && _) final def ||(o: DependencyFilter) = DependencyFilter(this, o, _ || _) final def --(o: DependencyFilter) = DependencyFilter(this, o, _ && !_) -} -sealed trait SubDepFilter[Arg, Self <: SubDepFilter[Arg, Self]] extends DependencyFilter { +sealed trait SubDepFilter[Arg, Self <: SubDepFilter[Arg, Self]] extends DependencyFilter: self: Self => def apply(a: Arg): Boolean protected def make(f: Arg => Boolean): Self @@ -72,22 +71,18 @@ sealed trait SubDepFilter[Arg, Self <: SubDepFilter[Arg, Self]] extends Dependen final def -(o: Self): Self = combine(o, _ && !_) private def combine(o: Self, f: (Boolean, Boolean) => Boolean): Self = make((m: Arg) => f(this(m), o(m))) -} -trait ModuleFilter extends SubDepFilter[ModuleID, ModuleFilter] { +trait ModuleFilter extends SubDepFilter[ModuleID, ModuleFilter]: protected final def make(f: ModuleID => Boolean) = (m: ModuleID) => f(m) final def apply(configuration: ConfigRef, module: ModuleID, artifact: Artifact): Boolean = apply(module) -} -trait ArtifactFilter extends SubDepFilter[Artifact, ArtifactFilter] { +trait ArtifactFilter extends SubDepFilter[Artifact, ArtifactFilter]: protected final def make(f: Artifact => Boolean) = (m: Artifact) => f(m) final def apply(configuration: ConfigRef, module: ModuleID, artifact: Artifact): Boolean = apply(artifact) -} -trait ConfigurationFilter extends SubDepFilter[ConfigRef, ConfigurationFilter] { +trait ConfigurationFilter extends SubDepFilter[ConfigRef, ConfigurationFilter]: protected final def make(f: ConfigRef => Boolean) = (m: ConfigRef) => f(m) final def apply(configuration: ConfigRef, module: ModuleID, artifact: Artifact): Boolean = apply(configuration) -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/DependencyResolution.scala b/lm-core/src/main/scala/sbt/librarymanagement/DependencyResolution.scala index e015f1c03..f5b4ea09c 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/DependencyResolution.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/DependencyResolution.scala @@ -8,7 +8,7 @@ import sbt.librarymanagement.syntax.* /** * Library management API to resolve dependencies. */ -class DependencyResolution private[sbt] (lmEngine: DependencyResolutionInterface) { +class DependencyResolution private[sbt] (lmEngine: DependencyResolutionInterface): import sbt.internal.librarymanagement.InternalDefaults.* import sbt.internal.librarymanagement.UpdateClassifiersUtil.* @@ -33,12 +33,11 @@ class DependencyResolution private[sbt] (lmEngine: DependencyResolutionInterface moduleId: ModuleID, directDependencies: Vector[ModuleID], scalaModuleInfo: Option[ScalaModuleInfo] - ): ModuleDescriptor = { + ): ModuleDescriptor = val moduleSetting = ModuleDescriptorConfiguration(moduleId, ModuleInfo(moduleId.name)) .withScalaModuleInfo(scalaModuleInfo) .withDependencies(directDependencies) moduleDescriptor(moduleSetting) - } /** * Resolves the given module's dependencies performing a retrieval. @@ -77,12 +76,11 @@ class DependencyResolution private[sbt] (lmEngine: DependencyResolutionInterface def wrapDependencyInModule( dependencyId: ModuleID, scalaModuleInfo: Option[ScalaModuleInfo] - ): ModuleDescriptor = { + ): ModuleDescriptor = val sha1 = Hash.toHex(Hash(dependencyId.name)) val dummyID = ModuleID(sbtOrgTemp, modulePrefixTemp + sha1, dependencyId.revision) .withConfigurations(dependencyId.configurations) moduleDescriptor(dummyID, Vector(dependencyId), scalaModuleInfo) - } /** * Resolves the given dependency, and retrieves the artifacts to a directory. @@ -113,7 +111,7 @@ class DependencyResolution private[sbt] (lmEngine: DependencyResolutionInterface module: ModuleDescriptor, retrieveDirectory: File, log: Logger - ): Either[UnresolvedWarning, Vector[File]] = { + ): Either[UnresolvedWarning, Vector[File]] = // Using the default artifact type filter here, so sources and docs are excluded. val retrieveConfiguration = RetrieveConfiguration() .withRetrieveDirectory(retrieveDirectory) @@ -126,15 +124,15 @@ class DependencyResolution private[sbt] (lmEngine: DependencyResolutionInterface updateConfiguration, UnresolvedWarningConfiguration(), log - ) match { + ) match case Left(unresolvedWarning) => Left(unresolvedWarning) case Right(updateReport) => val allFiles = - for { + for conf <- updateReport.configurations m <- conf.modules (_, f) <- m.artifacts - } yield f + yield f log.debug(s"Files retrieved for ${directDependenciesNames(module)}:") log.debug(allFiles mkString ", ") // allFiles filter predicate match { @@ -142,8 +140,8 @@ class DependencyResolution private[sbt] (lmEngine: DependencyResolutionInterface // case files => Some(files) // } Right(allFiles) - } - } + end match + end retrieve /** * Creates explicit artifacts for each classifier in `config.module`, and then attempts to resolve them directly. This @@ -159,7 +157,7 @@ class DependencyResolution private[sbt] (lmEngine: DependencyResolutionInterface uwconfig: UnresolvedWarningConfiguration, artifacts: Vector[(String, ModuleID, Artifact, File)], log: Logger - ): Either[UnresolvedWarning, UpdateReport] = { + ): Either[UnresolvedWarning, UpdateReport] = import config.module.* val artifactFilter = getArtifactTypeFilter(config.updateConfiguration.artifactFilter) assert(classifiers.nonEmpty, "classifiers cannot be empty") @@ -179,7 +177,7 @@ class DependencyResolution private[sbt] (lmEngine: DependencyResolutionInterface // c.copy ensures c.types is preserved too val upConf = config.updateConfiguration.withMissingOk(true) - update(module, upConf, uwconfig, log) match { + update(module, upConf, uwconfig, log) match case Right(r) => // The artifacts that came from Ivy don't have their classifier set, let's set it according to // FIXME: this is only done because IDE plugins depend on `classifier` to determine type. They @@ -193,17 +191,15 @@ class DependencyResolution private[sbt] (lmEngine: DependencyResolutionInterface } }) case Left(w) => Left(w) - } - } + end updateClassifiers protected def directDependenciesNames(module: ModuleDescriptor): String = (module.directDependencies map { case mID: ModuleID => import mID.* s"$organization % $name % $revision" }).mkString(", ") -} +end DependencyResolution -object DependencyResolution { +object DependencyResolution: def apply(lmEngine: DependencyResolutionInterface): DependencyResolution = new DependencyResolution(lmEngine) -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/EvictionError.scala b/lm-core/src/main/scala/sbt/librarymanagement/EvictionError.scala index 5b7bfc8ba..44481bf54 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/EvictionError.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/EvictionError.scala @@ -6,14 +6,13 @@ import sbt.internal.librarymanagement.VersionSchemes import sbt.util.{ Level, ShowLines } import EvictionWarningOptions.isNameScalaSuffixed -object EvictionError { +object EvictionError: def apply( report: UpdateReport, module: ModuleDescriptor, schemes: Seq[ModuleID], - ): EvictionError = { + ): EvictionError = apply(report, module, schemes, "always", "always", Level.Debug) - } def apply( report: UpdateReport, @@ -22,7 +21,7 @@ object EvictionError { assumedVersionScheme: String, assumedVersionSchemeJava: String, assumedEvictionErrorLevel: Level.Value, - ): EvictionError = { + ): EvictionError = apply( report, module, @@ -32,7 +31,6 @@ object EvictionError { assumedEvictionErrorLevel, EvictionWarningOptions.default.configurations, ) - } def apply( report: UpdateReport, @@ -42,7 +40,7 @@ object EvictionError { assumedVersionSchemeJava: String, assumedEvictionErrorLevel: Level.Value, configurations: Seq[ConfigRef], - ): EvictionError = { + ): EvictionError = val evictions = EvictionWarning .buildEvictions(configurations, report) processEvictions( @@ -53,7 +51,6 @@ object EvictionError { assumedVersionSchemeJava, assumedEvictionErrorLevel, ) - } private[sbt] def processEvictions( module: ModuleDescriptor, @@ -62,14 +59,14 @@ object EvictionError { assumedVersionScheme: String, assumedVersionSchemeJava: String, assumedEvictionErrorLevel: Level.Value, - ): EvictionError = { + ): EvictionError = val directDependencies = module.directDependencies val sbvOpt = module.scalaModuleInfo.map(_.scalaBinaryVersion) val userDefinedSchemes: Map[(String, String), String] = Map(schemes flatMap { s => val organization = s.organization VersionSchemes.validateScheme(s.revision) val versionScheme = s.revision - (s.crossVersion, sbvOpt) match { + (s.crossVersion, sbvOpt) match case (b: Binary, Some("2.13")) => List( (s.organization, s"${s.name}${b.suffix}_2.13") -> versionScheme, @@ -84,7 +81,6 @@ object EvictionError { List((s.organization, s"${s.name}${b.suffix}_$sbv") -> versionScheme) case _ => List((s.organization, s.name) -> versionScheme) - } }*) val pairs = reports .flatMap { case (config, detail) => @@ -92,10 +88,10 @@ object EvictionError { val winner = (detail.modules filterNot { _.evicted }).headOption // don't report on a transitive eviction that does not have a winner // https://github.com/sbt/sbt/issues/4946 - winner match { + winner match case Some(winner) => // from libraryDependencyScheme or defined in the pom using the `info.versionScheme` attribute - val userDefinedSchemeOrFromPom = { + val userDefinedSchemeOrFromPom = def fromLibraryDependencySchemes(org: String = "*", mod: String = "*") = userDefinedSchemes.get((org, mod)) def fromWinnerPom = VersionSchemes.extractFromExtraAttributes( @@ -106,9 +102,8 @@ object EvictionError { .orElse(fromLibraryDependencySchemes(detail.organization)) // for whole org .orElse(fromWinnerPom) // from pom .orElse(fromLibraryDependencySchemes()) // global - } val assumedScheme = - if (isNameScalaSuffixed(detail.name)) assumedVersionScheme + if isNameScalaSuffixed(detail.name) then assumedVersionScheme else assumedVersionSchemeJava // We want the user to be able to suppress eviction errors for a specific library, @@ -118,14 +113,13 @@ object EvictionError { .map(scheme => (scheme, false)) .getOrElse((assumedScheme, true)) - val hasIncompatibleVersionForScheme = { + val hasIncompatibleVersionForScheme = val isCompat = VersionSchemes.evalFunc(scheme) evicteds.exists { r => !isCompat((r.module, Some(winner.module), module.scalaModuleInfo)) } - } - if (hasIncompatibleVersionForScheme) + if hasIncompatibleVersionForScheme then Some( ( EvictionErrorPair( @@ -143,7 +137,7 @@ object EvictionError { ) else None case None => None - } + end match } // Deduplicate eviction pairs by configuration. .groupMap(_._1)(_._2) @@ -157,29 +151,27 @@ object EvictionError { incompatibleEvictions.toList, assumedIncompatibleEvictions.toList, ) - } + end processEvictions private def callers( winner: ModuleReport, evicteds: Vector[ModuleReport], - ): List[(ModuleID, String)] = { + ): List[(ModuleID, String)] = val seen: mutable.Set[ModuleID] = mutable.Set() (evicteds.toList :+ winner).flatMap { r => val rev = r.module.revision r.callers.toList flatMap { caller => - if (seen(caller.caller)) Nil - else { + if seen(caller.caller) then Nil + else seen += caller.caller List((caller.caller, rev)) - } } } - } given evictionErrorLines: ShowLines[EvictionError] = ShowLines { (a: EvictionError) => a.toLines } -} +end EvictionError private final case class EvictionErrorPair( name: String, @@ -195,11 +187,9 @@ private final case class EvictionErrorPair( final class EvictionError private[sbt] ( val incompatibleEvictions: Seq[EvictionErrorPair], val assumedIncompatibleEvictions: Seq[EvictionErrorPair], -) { +): def run(): Unit = - if (incompatibleEvictions.nonEmpty) { - sys.error(toLines.mkString("\n")) - } + if incompatibleEvictions.nonEmpty then sys.error(toLines.mkString("\n")) def toLines: List[String] = toLines(incompatibleEvictions, false) @@ -208,7 +198,7 @@ final class EvictionError private[sbt] ( def toLines( evictions: Seq[EvictionErrorPair], assumed: Boolean - ): List[String] = { + ): List[String] = val out: mutable.ListBuffer[String] = mutable.ListBuffer() out += "found version conflict(s) in library dependencies; some are suspected to be binary incompatible:" out += "" @@ -216,21 +206,21 @@ final class EvictionError private[sbt] ( val callers: List[String] = a.callers.map { case (caller, rev) => f"\t +- ${caller}%-50s (depends on $rev)" } - val que = if (assumed) "?" else "" + val que = if assumed then "?" else "" val evictedRevs = a.evicted.map(_.revision) val evictedRevsTitle = - if (evictedRevs.size <= 1) evictedRevs.mkString + if evictedRevs.size <= 1 then evictedRevs.mkString else evictedRevs.mkString("{", ", ", "}") val winnerRev = s":${a.winner.revision} (${a.scheme}$que) is selected over ${evictedRevsTitle}" val configurationTitle = - if (a.configurations.size <= 1) a.configurations.mkString + if a.configurations.size <= 1 then a.configurations.mkString else a.configurations.mkString("{", ", ", "}") val title = s"\t* ${a.organization}:${a.name}$winnerRev for $configurationTitle" val lines = title :: callers.reverse ::: List("") out ++= lines }) out.toList - } -} + end toLines +end EvictionError diff --git a/lm-core/src/main/scala/sbt/librarymanagement/EvictionWarning.scala b/lm-core/src/main/scala/sbt/librarymanagement/EvictionWarning.scala index 5f668165a..865449c23 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/EvictionWarning.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/EvictionWarning.scala @@ -17,7 +17,7 @@ final class EvictionWarningOptions private[sbt] ( val infoAllEvictions: Boolean, val showCallers: Boolean, val guessCompatible: Function1[(ModuleID, Option[ModuleID], Option[ScalaModuleInfo]), Boolean] -) { +): def withConfigurations(configurations: Seq[ConfigRef]): EvictionWarningOptions = copy(configurations = configurations) def withWarnScalaVersionEviction(warnScalaVersionEviction: Boolean): EvictionWarningOptions = @@ -58,9 +58,9 @@ final class EvictionWarningOptions private[sbt] ( showCallers = showCallers, guessCompatible = guessCompatible ) -} +end EvictionWarningOptions -object EvictionWarningOptions { +object EvictionWarningOptions: def empty: EvictionWarningOptions = new EvictionWarningOptions( Vector(), @@ -106,24 +106,22 @@ object EvictionWarningOptions { lazy val guessSecondSegment : PartialFunction[(ModuleID, Option[ModuleID], Option[ScalaModuleInfo]), Boolean] = { case (m1, Some(m2), Some(_)) if isNameScalaSuffixed(m2.name) => - (m1.revision, m2.revision) match { + (m1.revision, m2.revision) match case (VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) => VersionNumber.SecondSegment .isCompatible(VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) case _ => false - } } /** A partial function that checks two versions match pvp. */ private[sbt] lazy val evalPvp : PartialFunction[(ModuleID, Option[ModuleID], Option[ScalaModuleInfo]), Boolean] = { case (m1, Some(m2), _) => - (m1.revision, m2.revision) match { + (m1.revision, m2.revision) match case (VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) => VersionNumber.SecondSegment .isCompatible(VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) case _ => false - } } lazy val guessSbtOne @@ -132,45 +130,41 @@ object EvictionWarningOptions { if (m2.organization == "org.scala-sbt") && (m2.name.endsWith("_" + scalaModuleInfo.scalaFullVersion) || m2.name.endsWith("_" + scalaModuleInfo.scalaBinaryVersion)) => - (m1.revision, m2.revision) match { + (m1.revision, m2.revision) match case (VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) => VersionNumber.SemVer .isCompatible(VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) case _ => false - } } lazy val guessSemVer : PartialFunction[(ModuleID, Option[ModuleID], Option[ScalaModuleInfo]), Boolean] = { case (m1, Some(m2), _) => - (m1.revision, m2.revision) match { + (m1.revision, m2.revision) match case (VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) => VersionNumber.SemVer .isCompatible(VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) case _ => false - } } lazy val guessEarlySemVer : PartialFunction[(ModuleID, Option[ModuleID], Option[ScalaModuleInfo]), Boolean] = { case (m1, Some(m2), _) => - (m1.revision, m2.revision) match { + (m1.revision, m2.revision) match case (VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) => VersionNumber.EarlySemVer .isCompatible(VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) case _ => false - } } lazy val guessStrict : PartialFunction[(ModuleID, Option[ModuleID], Option[ScalaModuleInfo]), Boolean] = { case (m1, Some(m2), _) => - (m1.revision, m2.revision) match { + (m1.revision, m2.revision) match case (VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) => VersionNumber.Strict .isCompatible(VersionNumber(ns1, ts1, es1), VersionNumber(ns2, ts2, es2)) case _ => false - } } lazy val guessFalse @@ -182,7 +176,7 @@ object EvictionWarningOptions { : PartialFunction[(ModuleID, Option[ModuleID], Option[ScalaModuleInfo]), Boolean] = { case (_, _, _) => true } -} +end EvictionWarningOptions final class EvictionPair private[sbt] ( val organization: String, @@ -191,49 +185,43 @@ final class EvictionPair private[sbt] ( val evicteds: Vector[ModuleReport], val includesDirect: Boolean, val showCallers: Boolean -) { - val evictedRevs: String = { +): + val evictedRevs: String = val revs = evicteds map { _.module.revision } - if (revs.size <= 1) revs.mkString else revs.distinct.mkString("{", ", ", "}") - } + if revs.size <= 1 then revs.mkString else revs.distinct.mkString("{", ", ", "}") override def toString: String = EvictionPair.evictionPairLines.showLines(this).mkString - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case o: EvictionPair => (this.organization == o.organization) && (this.name == o.name) case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = var hash = 1 hash = hash * 31 + this.organization.## hash = hash * 31 + this.name.## hash - } -} +end EvictionPair -object EvictionPair { +object EvictionPair: given evictionPairLines: ShowLines[EvictionPair] = ShowLines { (a: EvictionPair) => val seen: mutable.Set[ModuleID] = mutable.Set() val callers: List[String] = (a.evicteds.toList ::: a.winner.toList) flatMap { r => val rev = r.module.revision r.callers.toList flatMap { caller => - if (seen(caller.caller)) Nil - else { + if seen(caller.caller) then Nil + else seen += caller.caller List(f"\t +- ${caller}%-50s (depends on $rev)") - } } } - val winnerRev = a.winner match { + val winnerRev = a.winner match case Some(r) => s":${r.module.revision} is selected over ${a.evictedRevs}" case _ => " is evicted for all versions" - } val title = s"\t* ${a.organization}:${a.name}$winnerRev" - title :: (if (a.showCallers) callers.reverse else Nil) ::: List("") + title :: (if a.showCallers then callers.reverse else Nil) ::: List("") } -} final class EvictionWarning private[sbt] ( val options: EvictionWarningOptions, @@ -242,7 +230,7 @@ final class EvictionWarning private[sbt] ( val transitiveEvictions: Seq[EvictionPair], val allEvictions: Seq[EvictionPair], val binaryIncompatibleEvictionExists: Boolean -) { +): private[sbt] def this( options: EvictionWarningOptions, scalaEvictions: Seq[EvictionPair], @@ -253,9 +241,8 @@ final class EvictionWarning private[sbt] ( def reportedEvictions: Seq[EvictionPair] = scalaEvictions ++ directEvictions ++ transitiveEvictions private[sbt] def infoAllTheThings: List[String] = EvictionWarning.infoAllTheThings(this) -} -object EvictionWarning { +object EvictionWarning: @deprecated("Use variant that doesn't take an unused logger", "1.2.0") def apply( module: ModuleDescriptor, @@ -268,51 +255,45 @@ object EvictionWarning { module: ModuleDescriptor, options: EvictionWarningOptions, report: UpdateReport - ): EvictionWarning = { + ): EvictionWarning = val evictions = buildEvictions(options.configurations, report) processEvictions(module, options, evictions) - } private[sbt] def buildEvictions( configurations: Seq[ConfigRef], report: UpdateReport - ): Seq[(ConfigRef, OrganizationArtifactReport)] = { + ): Seq[(ConfigRef, OrganizationArtifactReport)] = val confs = report.configurations filter { x => configurations.contains[ConfigRef](x.configuration) } confs.flatMap { confReport => confReport.details.map(report => (confReport.configuration, report)) }.toVector - } private[sbt] def isScalaArtifact( module: ModuleDescriptor, organization: String, name: String ): Boolean = - module.scalaModuleInfo match { + module.scalaModuleInfo match case Some(s) => organization == s.scalaOrganization && (name == LibraryID) || (name == CompilerID) case _ => false - } private[sbt] def processEvictions( module: ModuleDescriptor, options: EvictionWarningOptions, configsAndReports: Seq[(ConfigRef, OrganizationArtifactReport)] - ): EvictionWarning = { + ): EvictionWarning = val directDependencies = module.directDependencies val buffer: mutable.ListBuffer[OrganizationArtifactReport] = mutable.ListBuffer() configsAndReports.foreach { case (_, detail) => - if ( - (detail.modules exists { _.evicted }) && + if (detail.modules exists { _.evicted }) && !(buffer exists { x => (x.organization == detail.organization) && (x.name == detail.name) }) - ) { - buffer += detail - } + then buffer += detail } val reports = buffer.toList.toVector val pairs = reports map { detail => @@ -345,23 +326,20 @@ object EvictionWarning { // Check if the evicted module's revision is a version range and if the winner satisfies it // This handles cases like [4.1.0,5) where 4.2.1 would be within range (fixes #3978) // and [1.3.1,2.3] where 2.3 is valid (fixes #6244) - val winnerSatisfiesRange: Boolean = winnerOpt match { + val winnerSatisfiesRange: Boolean = winnerOpt match case Some(winner) if VersionRange.isVersionRange(evictedRev) => VersionRange.versionSatisfiesRange(winner.revision, evictedRev) case _ => false - } - if (sameVersion || winnerSatisfiesRange) { - true - } else { - val extraAttributes = ((p.winner match { + if sameVersion || winnerSatisfiesRange then true + else + val extraAttributes = ((p.winner match case Some(r) => r.extraAttributes case _ => Map.empty - }): collection.immutable.Map[String, String]) ++ (winnerOpt match { + ): collection.immutable.Map[String, String]) ++ (winnerOpt match case Some(w) => w.extraAttributes - case _ => Map.empty - }) + case _ => Map.empty) val schemeOpt = VersionSchemes.extractFromExtraAttributes(extraAttributes) - val f = (winnerOpt, schemeOpt) match { + val f = (winnerOpt, schemeOpt) match case (Some(_), Some(VersionSchemes.Always)) => EvictionWarningOptions.guessTrue case (Some(_), Some(VersionSchemes.Strict)) => @@ -373,36 +351,26 @@ object EvictionWarning { case (Some(_), Some(VersionSchemes.PackVer)) => EvictionWarningOptions.evalPvp case _ => options.guessCompatible(_) - } f((r.module, winnerOpt, module.scalaModuleInfo)) - } + end if } pairs foreach { case p if isScalaArtifact(module, p.organization, p.name) => - (module.scalaModuleInfo, p.winner) match { + (module.scalaModuleInfo, p.winner) match case (Some(s), Some(winner)) if (s.scalaFullVersion != winner.module.revision) => - if (options.warnScalaVersionEviction) - scalaEvictions += p - if (options.warnEvictionSummary) - binaryIncompatibleEvictionExists = true + if options.warnScalaVersionEviction then scalaEvictions += p + if options.warnEvictionSummary then binaryIncompatibleEvictionExists = true case _ => - } case p if p.includesDirect => - if (!guessCompatible(p)) { - if (options.warnDirectEvictions) - directEvictions += p - if (options.warnEvictionSummary) - binaryIncompatibleEvictionExists = true - } + if !guessCompatible(p) then + if options.warnDirectEvictions then directEvictions += p + if options.warnEvictionSummary then binaryIncompatibleEvictionExists = true case p => // don't report on a transitive eviction that does not have a winner // https://github.com/sbt/sbt/issues/4946 - if (!guessCompatible(p) && p.winner.isDefined) { - if (options.warnTransitiveEvictions) - transitiveEvictions += p - if (options.warnEvictionSummary) - binaryIncompatibleEvictionExists = true - } + if !guessCompatible(p) && p.winner.isDefined then + if options.warnTransitiveEvictions then transitiveEvictions += p + if options.warnEvictionSummary then binaryIncompatibleEvictionExists = true } new EvictionWarning( options, @@ -412,47 +380,42 @@ object EvictionWarning { pairs, binaryIncompatibleEvictionExists ) - } + end processEvictions given evictionWarningLines: ShowLines[EvictionWarning] = ShowLines { (a: EvictionWarning) => import ShowLines.* val out: mutable.ListBuffer[String] = mutable.ListBuffer() - if (a.options.warnEvictionSummary && a.binaryIncompatibleEvictionExists) { + if a.options.warnEvictionSummary && a.binaryIncompatibleEvictionExists then out += "There may be incompatibilities among your library dependencies; run 'evicted' to see detailed eviction warnings." - } - if (a.scalaEvictions.nonEmpty) { + if a.scalaEvictions.nonEmpty then out += "Scala version was updated by one of library dependencies:" out ++= (a.scalaEvictions flatMap { _.lines }) out += "To force scalaVersion, add the following:" out += "\tscalaModuleInfo ~= (_.map(_.withOverrideScalaVersion(true)))" - } - if (a.directEvictions.nonEmpty || a.transitiveEvictions.nonEmpty) { + if a.directEvictions.nonEmpty || a.transitiveEvictions.nonEmpty then out += "Found version conflict(s) in library dependencies; some are suspected to be binary incompatible:" out += "" out ++= (a.directEvictions flatMap { _.lines }) out ++= (a.transitiveEvictions flatMap { _.lines }) - } out.toList } private[sbt] def infoAllTheThings(a: EvictionWarning): List[String] = - if (a.options.infoAllEvictions) { + if a.options.infoAllEvictions then import ShowLines.* val evo = a.options val out: mutable.ListBuffer[String] = mutable.ListBuffer() a.allEvictions foreach { ev => - if ((a.scalaEvictions.contains[EvictionPair](ev)) && evo.warnScalaVersionEviction) () - else if ((a.directEvictions.contains[EvictionPair](ev)) && evo.warnDirectEvictions) () - else if ((a.transitiveEvictions.contains[EvictionPair](ev)) && evo.warnTransitiveEvictions) - () - else { - out ++= ev.lines - } + if (a.scalaEvictions.contains[EvictionPair](ev)) && evo.warnScalaVersionEviction then () + else if (a.directEvictions.contains[EvictionPair](ev)) && evo.warnDirectEvictions then () + else if (a.transitiveEvictions.contains[EvictionPair](ev)) && evo.warnTransitiveEvictions + then () + else out ++= ev.lines } - if (out.isEmpty) Nil + if out.isEmpty then Nil else List("Here are other dependency conflicts that were resolved:", "") ::: out.toList - } else Nil -} + else Nil +end EvictionWarning diff --git a/lm-core/src/main/scala/sbt/librarymanagement/Extra.scala b/lm-core/src/main/scala/sbt/librarymanagement/Extra.scala index 421a40505..1c861d4c9 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/Extra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/Extra.scala @@ -5,7 +5,7 @@ package sbt.librarymanagement import sbt.librarymanagement.DependencyBuilders.{ Organization, OrganizationArtifactName } -private[librarymanagement] abstract class InclExclRuleFunctions { +private[librarymanagement] abstract class InclExclRuleFunctions: def everything = InclExclRule("*", "*", "*", Vector.empty, Disabled()) def apply(organization: String, name: String): InclExclRule = @@ -20,29 +20,26 @@ private[librarymanagement] abstract class InclExclRuleFunctions { implicit def organizationArtifactNameToExclusionRule(oa: OrganizationArtifactName): InclExclRule = InclExclRule(oa.organization, oa.name, "*", Vector.empty, oa.crossVersion) - implicit def moduleIDToExclusionRule(moduleID: ModuleID): InclExclRule = { + implicit def moduleIDToExclusionRule(moduleID: ModuleID): InclExclRule = val org = moduleID.organization val name = moduleID.name val version = moduleID.revision val crossVersion = moduleID.crossVersion InclExclRule(org, name, version, Vector.empty, crossVersion) - } -} +end InclExclRuleFunctions -private[librarymanagement] abstract class ArtifactTypeFilterExtra { +private[librarymanagement] abstract class ArtifactTypeFilterExtra: def inverted: Boolean def withInverted(inverted: Boolean): ArtifactTypeFilter def invert = withInverted(!inverted) -} -private[librarymanagement] abstract class ArtifactTypeFilterFunctions { +private[librarymanagement] abstract class ArtifactTypeFilterFunctions: def allow(types: Set[String]) = ArtifactTypeFilter(types, false) def forbid(types: Set[String]) = ArtifactTypeFilter(types, true) -} -private[librarymanagement] abstract class ConflictManagerFunctions { +private[librarymanagement] abstract class ConflictManagerFunctions: // To avoid NPE (or making the val's below lazy) // For case classes refchecks rewrites apply calls to constructor calls, we have to do it manually def apply(name: String, organization: String = "*", module: String = "*"): ConflictManager @@ -54,4 +51,3 @@ private[librarymanagement] abstract class ConflictManagerFunctions { val latestCompatible = ConflictManager("latest-compatible") val strict = ConflictManager("strict") val default = latestRevision -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/Http.scala b/lm-core/src/main/scala/sbt/librarymanagement/Http.scala index 0ac5f5dc9..ad464b030 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/Http.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/Http.scala @@ -3,6 +3,5 @@ package sbt.librarymanagement import gigahorse.*, support.apachehttp.Gigahorse import scala.concurrent.duration.DurationInt -object Http { +object Http: lazy val http: HttpClient = Gigahorse.http(gigahorse.Config().withReadTimeout(60.minutes)) -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/LibraryManagementInterface.scala b/lm-core/src/main/scala/sbt/librarymanagement/LibraryManagementInterface.scala index 7ae9ce20f..e828f1ab4 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/LibraryManagementInterface.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/LibraryManagementInterface.scala @@ -6,7 +6,7 @@ import sbt.util.Logger /** * Interface for dependency resolution intended for engine authors. */ -trait DependencyResolutionInterface { +trait DependencyResolutionInterface: /** * Builds a ModuleDescriptor that describes a subproject with dependencies. @@ -32,12 +32,12 @@ trait DependencyResolutionInterface { uwconfig: UnresolvedWarningConfiguration, log: Logger ): Either[UnresolvedWarning, UpdateReport] -} +end DependencyResolutionInterface /** * Interface for publishing modules. */ -trait PublisherInterface { +trait PublisherInterface: /** * Builds a ModuleDescriptor that describes a subproject with dependencies. @@ -65,13 +65,13 @@ trait PublisherInterface { * @return The `File` containing the POM descriptor. */ def makePomFile(module: ModuleDescriptor, configuration: MakePomConfiguration, log: Logger): File -} +end PublisherInterface /** * Describes the representation of a module, including its dependencies * and the version of Scala it uses, if any. */ -trait ModuleDescriptor { +trait ModuleDescriptor: /** * The direct dependencies of this module. @@ -93,4 +93,4 @@ trait ModuleDescriptor { * Hash for extra parameter that were not captured as `moduleSettings`. */ def extraInputHash: Long -} +end ModuleDescriptor diff --git a/lm-core/src/main/scala/sbt/librarymanagement/LibraryManagementSyntax.scala b/lm-core/src/main/scala/sbt/librarymanagement/LibraryManagementSyntax.scala index d37764b3e..3b60818da 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/LibraryManagementSyntax.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/LibraryManagementSyntax.scala @@ -2,19 +2,18 @@ package sbt.librarymanagement import java.net.URI -trait LibraryManagementSyntax0 { +trait LibraryManagementSyntax0: // See http://www.scala-lang.org/news/2.12.0#traits-compile-to-interfaces // Avoid defining fields (val or var, but a constant is ok – final val without result type) // Avoid calling super // Avoid initializer statements in the body implicit def richUpdateReport(ur: UpdateReport): RichUpdateReport = new RichUpdateReport(ur) -} trait LibraryManagementSyntax extends LibraryManagementSyntax0 with DependencyBuilders - with DependencyFilterExtra { + with DependencyFilterExtra: // See http://www.scala-lang.org/news/2.12.0#traits-compile-to-interfaces // Avoid defining fields (val or var, but a constant is ok – final val without result type) // Avoid calling super @@ -38,6 +37,6 @@ trait LibraryManagementSyntax given Conversion[(String, URI), License] with inline def apply(x: (String, URI)): License = License(x._1, x._2) -} +end LibraryManagementSyntax object syntax extends LibraryManagementSyntax diff --git a/lm-core/src/main/scala/sbt/librarymanagement/LogicalClock.scala b/lm-core/src/main/scala/sbt/librarymanagement/LogicalClock.scala index f55a61b82..aa5d4fc49 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/LogicalClock.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/LogicalClock.scala @@ -4,18 +4,14 @@ package sbt.librarymanagement * Represents a logical time point for dependency resolution. * This is used to cache dependencies across subproject resolution which may change over time. */ -trait LogicalClock { +trait LogicalClock: def toString: String -} -object LogicalClock { - def apply(hashCode: Int): LogicalClock = { +object LogicalClock: + def apply(hashCode: Int): LogicalClock = def intToByteArray(x: Int): Array[Byte] = Array((x >>> 24).toByte, (x >> 16 & 0xff).toByte, (x >> 8 & 0xff).toByte, (x & 0xff).toByte) apply(sbt.io.Hash.toHex(intToByteArray(hashCode))) - } - def apply(x: String): LogicalClock = new LogicalClock { + def apply(x: String): LogicalClock = new LogicalClock: override def toString: String = x - } def unknown: LogicalClock = apply("unknown") -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/MakePomConfigurationExtra.scala b/lm-core/src/main/scala/sbt/librarymanagement/MakePomConfigurationExtra.scala index 1b5edbb9c..a8115a448 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/MakePomConfigurationExtra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/MakePomConfigurationExtra.scala @@ -1,6 +1,6 @@ package sbt.librarymanagement -private[librarymanagement] abstract class MakePomConfigurationFunctions { +private[librarymanagement] abstract class MakePomConfigurationFunctions: private[sbt] lazy val constTrue: MavenRepository => Boolean = _ => true def apply(): MakePomConfiguration = @@ -14,4 +14,3 @@ private[librarymanagement] abstract class MakePomConfigurationFunctions { true, Set(Artifact.DefaultType, Artifact.PomType) ) -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/MemoizedFileFormats.scala b/lm-core/src/main/scala/sbt/librarymanagement/MemoizedFileFormats.scala index f0ee3c36d..1a67fbccc 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/MemoizedFileFormats.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/MemoizedFileFormats.scala @@ -27,17 +27,16 @@ import sjsonnew.{ FileIsoStringLongs, HashUtil, IsoStringLong } * Mix in after `BasicJsonProtocol` (via `contrabandCodecParents`) so these overrides win in * linearization. */ -trait MemoizedFileFormats extends FileIsoStringLongs { +trait MemoizedFileFormats extends FileIsoStringLongs: import MemoizedFileFormats.{ Entry, hashCache } // sjsonnew.HashUtil.sha256ToLong is private[sjsonnew]; same semantics via the public sha256 - private def hashFile(path: Path): Long = { + private def hashFile(path: Path): Long = val buf = HashUtil.sha256(path.toFile()) if buf.length < 8 then 0L else ByteBuffer.wrap(buf).getLong() - } private def memoizedHash(path: Path): Long = - Try(Files.readAttributes(path, classOf[BasicFileAttributes])).toOption match { + Try(Files.readAttributes(path, classOf[BasicFileAttributes])).toOption match case None => // no size/mtime to validate a cache entry against, so hash uncached if Files.isRegularFile(path) then hashFile(path) else 0L @@ -45,15 +44,13 @@ trait MemoizedFileFormats extends FileIsoStringLongs { val key = path.toString val mtime = attrs.lastModifiedTime.toMillis val cached = hashCache.getIfPresent(key) - if (cached != null && cached.size == attrs.size && cached.mtime == mtime) cached.hash - else { - val h = if (attrs.isDirectory) 0L else hashFile(path) + if cached != null && cached.size == attrs.size && cached.mtime == mtime then cached.hash + else + val h = if attrs.isDirectory then 0L else hashFile(path) // 0L is the sentinel for directories and files that vanish mid-hash, not a // content hash; caching it would keep serving 0 for the key - if (h != 0L) hashCache.put(key, new Entry(attrs.size, mtime, h)) + if h != 0L then hashCache.put(key, new Entry(attrs.size, mtime, h)) h - } - } override implicit lazy val fileStringLongIso: IsoStringLong[File] = IsoStringLong.iso[File]( file => (IO.toURI(file).toASCIIString, memoizedHash(file.toPath())), @@ -64,13 +61,12 @@ trait MemoizedFileFormats extends FileIsoStringLongs { path => (path.toString, memoizedHash(path)), p => Paths.get(p._1) ) -} +end MemoizedFileFormats -private[librarymanagement] object MemoizedFileFormats { +private[librarymanagement] object MemoizedFileFormats: private final class Entry(val size: Long, val mtime: Long, val hash: Long) // shared by every mix-in of the trait; a per-instance cache would re-hash the same // files for each codec object that extends MemoizedFileFormats private val hashCache: Cache[String, Entry] = Caffeine.newBuilder().maximumSize(65536).build[String, Entry]() -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ModuleIDExtra.scala b/lm-core/src/main/scala/sbt/librarymanagement/ModuleIDExtra.scala index cc517d0f6..2929beabe 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ModuleIDExtra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ModuleIDExtra.scala @@ -11,7 +11,7 @@ import sbt.librarymanagement.syntax.* import sbt.util.Logger import sbt.librarymanagement.DependencyBuilders.OrganizationArtifactName -private[librarymanagement] abstract class ModuleIDExtra { +private[librarymanagement] abstract class ModuleIDExtra: def organization: String def name: String def revision: String @@ -38,26 +38,21 @@ private[librarymanagement] abstract class ModuleIDExtra { protected def toStringImpl: String = s"""$organization:$name:$revision""" + - (configurations match { case Some(s) => ":" + s; case None => "" }) + { + (configurations match + case Some(s) => ":" + s; + case None => "") + { val attr = attributeString - if (attr == "") "" + if attr == "" then "" else " " + attr } + - (if (extraAttributes.isEmpty) "" else " " + extraString) + (if extraAttributes.isEmpty then "" else " " + extraString) - protected def attributeString: String = { + protected def attributeString: String = val buffer = ListBuffer.empty[String] - if (isChanging) { - buffer += "changing" - } - if (!isTransitive) { - buffer += "intransitive" - } - if (isForce) { - buffer += "force" - } + if isChanging then buffer += "changing" + if !isTransitive then buffer += "intransitive" + if isForce then buffer += "force" buffer.toList.mkString(";") - } /** String representation of the extra attributes, excluding any information only attributes. */ def extraString: String = @@ -71,7 +66,7 @@ private[librarymanagement] abstract class ModuleIDExtra { "Use `cross(CrossVersion)`, the variant accepting a CrossVersion value constructed by a member of the CrossVersion object instead.", "0.12.0" ) - def cross(v: Boolean): ModuleID = cross(if (v) CrossVersion.binary else Disabled()) + def cross(v: Boolean): ModuleID = cross(if v then CrossVersion.binary else Disabled()) /** * Specifies the cross-version behavior for this module. See [CrossVersion] for details. @@ -87,14 +82,12 @@ private[librarymanagement] abstract class ModuleIDExtra { * This allows `.cross(...)` to play well with `%%%` operator provided by sbt-platform-deps. */ def cross(v: CrossVersion): ModuleID = - withCrossVersion(CrossVersion.getPrefixSuffix(this.crossVersion) match { + withCrossVersion(CrossVersion.getPrefixSuffix(this.crossVersion) match case ("", "") => v case (prefix, suffix) => - CrossVersion.getPrefixSuffix(v) match { + CrossVersion.getPrefixSuffix(v) match case ("", "") => CrossVersion.setPrefixSuffix(v, prefix, suffix) - case _ => v - } - }) + case _ => v) // () required for chaining /** Do not follow dependencies of this module. Synonym for `intransitive`. */ @@ -117,9 +110,8 @@ private[librarymanagement] abstract class ModuleIDExtra { */ def force(): ModuleID = withIsForce(true) - private[sbt] def validateProtocol(logger: Logger): Unit = { + private[sbt] def validateProtocol(logger: Logger): Unit = explicitArtifacts foreach { _.validateProtocol(logger) } - } /** * Specifies a URL from which the main artifact for this dependency can be downloaded. @@ -161,9 +153,8 @@ private[librarymanagement] abstract class ModuleIDExtra { excludeAll(ExclusionRule().withOrganization(org).withName(name)) /** Excludes the dependency from being introduced by this dependency during resolution. */ - def exclude(rule: OrganizationArtifactName): ModuleID = { + def exclude(rule: OrganizationArtifactName): ModuleID = excludeAll(rule) - } /** * Adds extra attributes for this module. All keys are prefixed with `e:` if they are not already so prefixed. @@ -202,7 +193,7 @@ private[librarymanagement] abstract class ModuleIDExtra { */ def withJavadoc(): ModuleID = jarIfEmpty.javadoc() - private def jarIfEmpty = if (explicitArtifacts.isEmpty) jar() else this + private def jarIfEmpty = if explicitArtifacts.isEmpty then jar() else this /** * Declares a dependency on the main artifact. This is implied by default unless artifacts are explicitly declared, such @@ -218,12 +209,11 @@ private[librarymanagement] abstract class ModuleIDExtra { def branch(branchName: Option[String]): ModuleID = withBranchName(branchName) def platform(platform: String): ModuleID = withPlatformOpt(Some(platform)) -} +end ModuleIDExtra -private[librarymanagement] abstract class ModuleIDFunctions { +private[librarymanagement] abstract class ModuleIDFunctions: /** Prefixes all keys with `e:` if they are not already so prefixed. */ def checkE(attributes: Seq[(String, String)]) = - for ((key, value) <- attributes) - yield if (key.startsWith("e:")) (key, value) else ("e:" + key, value) -} + for (key, value) <- attributes + yield if key.startsWith("e:") then (key, value) else ("e:" + key, value) diff --git a/lm-core/src/main/scala/sbt/librarymanagement/Publisher.scala b/lm-core/src/main/scala/sbt/librarymanagement/Publisher.scala index c071240f9..c6dcd976c 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/Publisher.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/Publisher.scala @@ -6,7 +6,7 @@ import sbt.util.Logger /** * Library management API to publish artifacts. */ -class Publisher private[sbt] (publisherEngine: PublisherInterface) { +class Publisher private[sbt] (publisherEngine: PublisherInterface): /** * Builds a ModuleDescriptor that describes a subproject with dependencies. @@ -41,9 +41,8 @@ class Publisher private[sbt] (publisherEngine: PublisherInterface) { log: Logger ): File = publisherEngine.makePomFile(module, configuration, log) -} +end Publisher -object Publisher { +object Publisher: def apply(publisherEngine: PublisherInterface): Publisher = new Publisher(publisherEngine) -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala b/lm-core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala index 702554fc5..25e409284 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ResolverExtra.scala @@ -12,41 +12,35 @@ import sbt.util.Logger import scala.util.matching.Regex -final class RawRepository(val resolver: AnyRef, name: String) extends Resolver(name) { +final class RawRepository(val resolver: AnyRef, name: String) extends Resolver(name): override def toString = "Raw(" + resolver.toString + ")" - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case o: RawRepository => this.name == o.name case _ => false - } - override def hashCode: Int = { + override def hashCode: Int = var hash = 1 hash = hash * 31 + this.name.## hash - } -} -private[librarymanagement] abstract class MavenRepositoryFunctions { +private[librarymanagement] abstract class MavenRepositoryFunctions: def apply(name: String, root: String, localIfFile: Boolean = true): MavenRepository = MavenRepo(name, root, localIfFile) -} -private[librarymanagement] abstract class PatternsFunctions { +private[librarymanagement] abstract class PatternsFunctions: implicit def defaultPatterns: Patterns = Resolver.defaultPatterns def apply(artifactPatterns: String*): Patterns = Patterns(false, artifactPatterns*) - def apply(isMavenCompatible: Boolean, artifactPatterns: String*): Patterns = { + def apply(isMavenCompatible: Boolean, artifactPatterns: String*): Patterns = val patterns = artifactPatterns.toVector Patterns() .withIvyPatterns(patterns) .withArtifactPatterns(patterns) .withIsMavenCompatible(isMavenCompatible) - } -} -private[librarymanagement] trait SshBasedRepositoryExtra { +private[librarymanagement] trait SshBasedRepositoryExtra: /** The object representing the configured ssh connection for this repository. */ def connection: SshConnection @@ -69,9 +63,9 @@ private[librarymanagement] trait SshBasedRepositoryExtra { def as(user: String, keyfile: File, password: Option[String]): RepositoryType = copy(KeyFileAuthentication(user, keyfile, password)) -} +end SshBasedRepositoryExtra -private[librarymanagement] trait SshRepositoryExtra extends SshBasedRepositoryExtra { +private[librarymanagement] trait SshRepositoryExtra extends SshBasedRepositoryExtra: def name: String def patterns: sbt.librarymanagement.Patterns def publishPermissions: Option[String] @@ -80,9 +74,8 @@ private[librarymanagement] trait SshRepositoryExtra extends SshBasedRepositoryEx protected def copy(connection: SshConnection): SshRepository = SshRepository(name, connection, patterns, publishPermissions) -} -private[librarymanagement] trait SftpRepositoryExtra extends SshBasedRepositoryExtra { +private[librarymanagement] trait SftpRepositoryExtra extends SshBasedRepositoryExtra: def name: String def patterns: sbt.librarymanagement.Patterns @@ -90,14 +83,12 @@ private[librarymanagement] trait SftpRepositoryExtra extends SshBasedRepositoryE protected def copy(connection: SshConnection): SftpRepository = SftpRepository(name, connection, patterns) -} /** A repository that conforms to sbt launcher's interface */ -private[sbt] class FakeRepository(resolver: AnyRef, name: String) extends xsbti.Repository { +private[sbt] class FakeRepository(resolver: AnyRef, name: String) extends xsbti.Repository: def rawRepository = new RawRepository(resolver, name) -} -private[librarymanagement] abstract class ResolverFunctions { +private[librarymanagement] abstract class ResolverFunctions: val TypesafeRepositoryRoot = "https://repo.typesafe.com/typesafe" val SbtRepositoryRoot = "https://repo.scala-sbt.org/scalasbt" @deprecated("Renamed to SbtRepositoryRoot.", "1.0.0") @@ -152,7 +143,7 @@ private[librarymanagement] abstract class ResolverFunctions { def sonatypeRepo(status: String) = MavenRepository( "sonatype-" + status, - if (status == "releases") SonatypeReleasesRepository + if status == "releases" then SonatypeReleasesRepository else SonatypeRepositoryRoot + "/" + status ) private def sonatypeS01Repo(status: String) = @@ -212,23 +203,22 @@ private[librarymanagement] abstract class ResolverFunctions { appResolvers: Vector[Resolver], mavenCentral: Boolean ): Vector[Resolver] = - appResolvers.partition(_ == Resolver.defaultLocal) match { + appResolvers.partition(_ == Resolver.defaultLocal) match case (locals, xs) => locals ++ - (xs.partition(_ == DefaultMavenRepository) match { - case (_, xs) => - single( - DefaultMavenRepository, - mavenCentral - ) ++ xs // TODO - Do we need to filter out duplicates? - }) - } + (xs.partition(_ == DefaultMavenRepository) match + case (_, xs) => + single( + DefaultMavenRepository, + mavenCentral + ) ++ xs // TODO - Do we need to filter out duplicates? + ) private def single[T](value: T, nonEmpty: Boolean): Vector[T] = - if (nonEmpty) Vector(value) else Vector.empty + if nonEmpty then Vector(value) else Vector.empty /** A base class for defining factories for interfaces to Ivy repositories that require a hostname , port, and patterns. */ - sealed abstract class Define[RepositoryType <: SshBasedRepository] { + sealed abstract class Define[RepositoryType <: SshBasedRepository]: /** Subclasses should implement this method to */ protected def construct( @@ -289,7 +279,7 @@ private[librarymanagement] abstract class ResolverFunctions { implicit basePatterns: Patterns ): RepositoryType = construct(name, SshConnection(None, hostname, port), resolvePatterns(basePath, basePatterns)) - } + end Define /** * A factory to construct an interface to an Ivy SSH resolver. @@ -299,10 +289,9 @@ private[librarymanagement] abstract class ResolverFunctions { * literal (e.g. `org.example`), supply Ivy-style patterns such as a [[Patterns]] built with * `isMavenCompatible = false` (the [[Patterns]] default). See issue #535. */ - object ssh extends Define[SshRepository] { + object ssh extends Define[SshRepository]: protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SshRepository(name, connection, patterns, None) - } /** * A factory to construct an interface to an Ivy SFTP resolver. @@ -312,13 +301,12 @@ private[librarymanagement] abstract class ResolverFunctions { * literal (e.g. `org.example`), supply Ivy-style patterns such as a [[Patterns]] built with * `isMavenCompatible = false` (the [[Patterns]] default). See issue #535. */ - object sftp extends Define[SftpRepository] { + object sftp extends Define[SftpRepository]: protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SftpRepository(name, connection, patterns) - } /** A factory to construct an interface to an Ivy filesystem resolver. */ - object file { + object file: /** * Constructs a file resolver with the given name. The patterns to use must be explicitly specified @@ -336,7 +324,6 @@ private[librarymanagement] abstract class ResolverFunctions { ) private def toUri(dir: File): URI = dir.toPath.toUri - } object url: /** @@ -379,13 +366,12 @@ private[librarymanagement] abstract class ResolverFunctions { * Otherwise, the ivy file and artifact patterns in `patterns` are resolved against the given base. */ private def resolvePatterns(base: Option[String], patterns: Patterns): Patterns = - base match { + base match case Some(path) => resolvePatterns(path, patterns) case None => patterns - } /** Resolves the ivy file and artifact patterns in `patterns` against the given base. */ - private def resolvePatterns(base: String, basePatterns: Patterns): Patterns = { + private def resolvePatterns(base: String, basePatterns: Patterns): Patterns = def resolveAll(patterns: Vector[String]) = patterns.map(p => resolvePattern(base, p)) Patterns( resolveAll(basePatterns.ivyPatterns), @@ -394,12 +380,10 @@ private[librarymanagement] abstract class ResolverFunctions { basePatterns.descriptorOptional, basePatterns.skipConsistencyCheck ) - } - private[sbt] def resolvePattern(base: String, pattern: String): String = { + private[sbt] def resolvePattern(base: String, pattern: String): String = val normBase = base.replace('\\', '/') - if (normBase.endsWith("/") || pattern.startsWith("/")) normBase + pattern + if normBase.endsWith("/") || pattern.startsWith("/") then normBase + pattern else normBase + "/" + pattern - } def defaultFileConfiguration = FileConfiguration(true, None) /** @@ -428,7 +412,7 @@ private[librarymanagement] abstract class ResolverFunctions { str: String, envVars: Map[String, String] = sys.env, props: Map[String, String] = sys.props.toMap - ): String = { + ): String = // Aren't regular expressions beautifully clear and concise. // This means "find all ${...}" blocks, with the first group of each being the text between curly brackets. val findQuoted = "\\$\\{([^}]*)}".r @@ -438,25 +422,23 @@ private[librarymanagement] abstract class ResolverFunctions { str, regexMatch => Regex.quoteReplacement { - regexMatch.group(1) match { + regexMatch.group(1) match case env(variable) => envVars.getOrElse(variable, "") case property => props.getOrElse(property, "") - } } ) - } - private def mavenLocalDir: File = { + private def mavenLocalDir: File = def loadHomeFromSettings(f: () => File): Option[File] = - try { + try val file = f() - if (!file.exists) None + if !file.exists then None else - ((XML.loadFile(file) \ "localRepository").text match { + ((XML.loadFile(file) \ "localRepository").text match case "" => None case e @ _ => Some(new File(expandMavenSettings(e))) - }) - } catch { + ) + catch // Occurs inside File constructor when property or environment variable does not exist case _: NullPointerException => None // Occurs when File does not exist @@ -464,82 +446,73 @@ private[librarymanagement] abstract class ResolverFunctions { case e: SAXParseException => System.err.println(s"WARNING: Problem parsing ${f().getAbsolutePath}, ${e.getMessage}"); None - } sys.props.get("maven.repo.local").map(new File(_)) orElse loadHomeFromSettings(() => new File(sbt.io.Path.userHome, ".m2/settings.xml")) orElse loadHomeFromSettings(() => new File(new File(System.getenv("M2_HOME")), "conf/settings.xml") ) getOrElse new File(sbt.io.Path.userHome, ".m2/repository") - } + end mavenLocalDir // TODO - should this just be the *exact* same as mavenLocal? probably... def publishMavenLocal: MavenCache = new MavenCache("publish-m2-local", mavenLocalDir) def mavenLocal: MavenRepository = new MavenCache("Maven2 Local", mavenLocalDir) def defaultLocal = defaultUserFileRepository("local") def defaultShared = defaultUserFileRepository("shared") - def defaultUserFileRepository(id: String) = { + def defaultUserFileRepository(id: String) = val pList = Vector(s"$${ivy.home}/$id/$localBasePattern") FileRepository( id, defaultFileConfiguration, Patterns().withIvyPatterns(pList).withArtifactPatterns(pList).withIsMavenCompatible(false) ) - } - def defaultIvyPatterns = { + def defaultIvyPatterns = val pList = Vector(localBasePattern) Patterns().withIvyPatterns(pList).withArtifactPatterns(pList).withIsMavenCompatible(false) - } // to display all error messages at once, just log here don't throw - private[sbt] def warnHttp(value: String, suggestion: String, logger: Logger): Unit = { + private[sbt] def warnHttp(value: String, suggestion: String, logger: Logger): Unit = logger.error(s"insecure HTTP request is unsupported '$value'; switch to HTTPS$suggestion") - } - private[sbt] def isInsecureUrl(str: String): Boolean = { + private[sbt] def isInsecureUrl(str: String): Boolean = // don't try to parse str as URL because it could contain $variable from Ivy pattern str.startsWith("http:") && - !(str.startsWith("http://localhost/") - || str.startsWith("http://localhost:") - || str.startsWith("http://127.0.0.1/") - || str.startsWith("http://127.0.0.1:")) - } - private[sbt] def validateURLRepository(repo: URLRepository, logger: Logger): Boolean = { - if (repo.allowInsecureProtocol) false - else { + !(str.startsWith("http://localhost/") + || str.startsWith("http://localhost:") + || str.startsWith("http://127.0.0.1/") + || str.startsWith("http://127.0.0.1:")) + private[sbt] def validateURLRepository(repo: URLRepository, logger: Logger): Boolean = + if repo.allowInsecureProtocol then false + else val patterns = repo.patterns - val ivy = patterns.ivyPatterns.headOption match { + val ivy = patterns.ivyPatterns.headOption match case Some(x) => isInsecureUrl(x) case _ => false - } - val art = patterns.artifactPatterns.headOption match { + val art = patterns.artifactPatterns.headOption match case Some(x) => isInsecureUrl(x) case _ => false - } - if (ivy || art) { + if ivy || art then warnHttp( patterns.toString, s""" or opt-in as Resolver.uri("${repo.name}", url(...)).withAllowInsecureProtocol(true), or by using allowInsecureProtocol in repositories file""", logger ) true - } else false - } - } + else false private[sbt] def validateMavenRepo(repo: MavenRepo, logger: Logger): Boolean = - if (repo.allowInsecureProtocol) false - else if (isInsecureUrl(repo.root)) { + if repo.allowInsecureProtocol then false + else if isInsecureUrl(repo.root) then warnHttp( repo.root, s""" or opt-in as ("${repo.name}" at "${repo.root}").withAllowInsecureProtocol(true), or by using allowInsecureProtocol in repositories file""", logger ) true - } else false + else false private[sbt] def validateArtifact(art: Artifact, logger: Logger): Boolean = - if (art.allowInsecureProtocol) false + if art.allowInsecureProtocol then false else - art.url match { + art.url match case Some(url) if isInsecureUrl(url.toString) => warnHttp( art.toString, @@ -548,5 +521,4 @@ private[librarymanagement] abstract class ResolverFunctions { ) true case _ => false - } -} +end ResolverFunctions diff --git a/lm-core/src/main/scala/sbt/librarymanagement/RichUpdateReport.scala b/lm-core/src/main/scala/sbt/librarymanagement/RichUpdateReport.scala index 66bf8dadf..0bed38730 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/RichUpdateReport.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/RichUpdateReport.scala @@ -8,8 +8,8 @@ import sbt.io.IO * Provides extra methods for filtering the contents of an `UpdateReport` * and for obtaining references to a selected subset of the underlying files. */ -final class RichUpdateReport(report: UpdateReport) { - private[sbt] def recomputeStamps(): UpdateReport = { +final class RichUpdateReport(report: UpdateReport): + private[sbt] def recomputeStamps(): UpdateReport = val files = report.cachedDescriptor +: allFiles val stamps = files .map(f => @@ -24,7 +24,6 @@ final class RichUpdateReport(report: UpdateReport) { ) .toMap UpdateReport(report.cachedDescriptor, report.configurations, report.stats, stamps) - } import DependencyFilter.* @@ -50,19 +49,17 @@ final class RichUpdateReport(report: UpdateReport) { select(configurationFilter(), moduleFilter(), artifact) private def select0(f: DependencyFilter): Vector[File] = - for { + for cReport <- report.configurations mReport <- cReport.modules (artifact, file) <- mReport.artifacts if f(cReport.configuration, mReport.module, artifact) - } yield { - if (file == null) { + yield + if file == null then sys.error( s"Null file: conf=${cReport.configuration}, module=${mReport.module}, art: $artifact" ) - } file - } /** Constructs a new report that only contains files matching the specified filter. */ def filter(f: DependencyFilter): UpdateReport = @@ -92,18 +89,18 @@ final class RichUpdateReport(report: UpdateReport) { def toSeq: Seq[(ConfigRef, ModuleID, Artifact, File)] = toVector def toVector: Vector[(ConfigRef, ModuleID, Artifact, File)] = - for { + for confReport <- report.configurations modReport <- confReport.modules (artifact, file) <- modReport.artifacts - } yield (confReport.configuration, modReport.module, artifact, file) + yield (confReport.configuration, modReport.module, artifact, file) def allMissing: Vector[(ConfigRef, ModuleID, Artifact)] = - for { + for confReport <- report.configurations modReport <- confReport.modules artifact <- modReport.missingArtifacts - } yield (confReport.configuration, modReport.module, artifact) + yield (confReport.configuration, modReport.module, artifact) private[sbt] def addMissing(f: ModuleID => Seq[Artifact]): UpdateReport = moduleReportMap { (configuration, modReport) => @@ -111,7 +108,7 @@ final class RichUpdateReport(report: UpdateReport) { .withMissingArtifacts((modReport.missingArtifacts ++ f(modReport.module)).distinct) } - private[sbt] def moduleReportMap(f: (ConfigRef, ModuleReport) => ModuleReport): UpdateReport = { + private[sbt] def moduleReportMap(f: (ConfigRef, ModuleReport) => ModuleReport): UpdateReport = val newConfigurations = report.configurations.map { confReport => import confReport.* val newModules = modules map { modReport => @@ -120,5 +117,4 @@ final class RichUpdateReport(report: UpdateReport) { ConfigurationReport(configuration, newModules, details) } UpdateReport(report.cachedDescriptor, newConfigurations, report.stats, report.stamps) - } -} +end RichUpdateReport diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala b/lm-core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala index 4ccb7255d..3a0549e7c 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala @@ -1,6 +1,6 @@ package sbt.librarymanagement -object ScalaArtifacts { +object ScalaArtifacts: final val Organization = "org.scala-lang" final val LibraryID = "scala-library" final val CompilerID = "scala-compiler" @@ -28,19 +28,17 @@ object ScalaArtifacts { private[sbt] final val Scala3TastyInspectorPrefix = Scala3TastyInspectorID + "_" private[sbt] final val Scala3ReplPrefix = Scala3ReplID + "_" - def isScala2Artifact(name: String): Boolean = { + def isScala2Artifact(name: String): Boolean = name == LibraryID || name == CompilerID || name == ReflectID || name == ActorsID || name == ScalapID - } - def isScala3Artifact(name: String): Boolean = { + def isScala3Artifact(name: String): Boolean = name.startsWith(Scala3LibraryPrefix) || - name.startsWith(Scala3CompilerPrefix) || - name.startsWith(TastyCorePrefix) || - name == Scala3InterfacesID || - name.startsWith(ScaladocPrefix) || - name.startsWith(Scala3DocPrefix) || - name.startsWith(Scala3TastyInspectorPrefix) || - name.startsWith(Scala3ReplPrefix) - } + name.startsWith(Scala3CompilerPrefix) || + name.startsWith(TastyCorePrefix) || + name == Scala3InterfacesID || + name.startsWith(ScaladocPrefix) || + name.startsWith(Scala3DocPrefix) || + name.startsWith(Scala3TastyInspectorPrefix) || + name.startsWith(Scala3ReplPrefix) def isScala3(scalaVersion: String): Boolean = scalaVersion.startsWith("3.") || scalaVersion.startsWith("3-latest.") @@ -49,26 +47,22 @@ object ScalaArtifacts { * Returns true for pre-release nightlies intentionally. */ def isScala3_8Plus(scalaVersion: String): Boolean = - isScala3(scalaVersion) && (scalaVersion match { + isScala3(scalaVersion) && (scalaVersion match case VersionNumber(numbers, _, _) if numbers.size > 2 && numbers(1) >= 8 => true - case _ => false - }) + case _ => false) private[sbt] def isScala3M123(scalaVersion: String): Boolean = (scalaVersion == "3.0.0-M1") || (scalaVersion == "3.0.0-M2") || (scalaVersion == "3.0.0-M3") - def libraryIds(version: String): Array[String] = { - if (isScala3(version)) - Array(Scala3LibraryID, LibraryID) + def libraryIds(version: String): Array[String] = + if isScala3(version) then Array(Scala3LibraryID, LibraryID) else Array(LibraryID) - } - def compilerId(version: String): String = { - if (isScala3(version)) Scala3CompilerID + def compilerId(version: String): String = + if isScala3(version) then Scala3CompilerID else CompilerID - } def libraryDependency(version: String): ModuleID = libraryDependency(Organization, version) @@ -83,7 +77,7 @@ object ScalaArtifacts { org: String, version: String ): Seq[ModuleID] = - if (isScala3_8Plus(version)) + if isScala3_8Plus(version) then Seq( ModuleID(org, Scala3ReplID, version) .withConfigurations(Some(Configurations.ScalaReplTool.name + "->default(compile)")) @@ -95,13 +89,13 @@ object ScalaArtifacts { org: String, version: String ): Seq[ModuleID] = - if (isScala3M123(version)) + if isScala3M123(version) then Seq( ModuleID(org, Scala3DocID, version) .withConfigurations(Some(Configurations.ScalaDocTool.name + "->default(compile)")) .withCrossVersion(CrossVersion.binary) ) - else if (isScala3(version)) + else if isScala3(version) then Seq( ModuleID(org, ScaladocID, version) .withConfigurations(Some(Configurations.ScalaDocTool.name + "->default(compile)")) @@ -114,7 +108,7 @@ object ScalaArtifacts { org: String, version: String ): Seq[ModuleID] = - if (isScala3(version)) + if isScala3(version) then Seq( ModuleID(org, Scala3CompilerID, version) .withConfigurations(Some(Configurations.ScalaTool.name + "->default(compile)")) @@ -156,8 +150,7 @@ object ScalaArtifacts { .platform(Platform.jvm) ) else Nil -} +end ScalaArtifacts -object SbtArtifacts { +object SbtArtifacts: val Organization = "org.scala-sbt" -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/TrackLevel.scala b/lm-core/src/main/scala/sbt/librarymanagement/TrackLevel.scala index e92b37d68..e4ea90cd2 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/TrackLevel.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/TrackLevel.scala @@ -4,30 +4,24 @@ package sbt.librarymanagement * An enumeration defining the tracking of dependencies. A level includes all of the levels * with id larger than its own id. For example, Warn (id=3) includes Error (id=4). */ -object TrackLevel { - case object NoTracking extends TrackLevel { +object TrackLevel: + case object NoTracking extends TrackLevel: override def id: Int = 0 - } - case object TrackIfMissing extends TrackLevel { + case object TrackIfMissing extends TrackLevel: override def id: Int = 1 - } - case object TrackAlways extends TrackLevel { + case object TrackAlways extends TrackLevel: override def id: Int = 10 - } private[sbt] def apply(x: Int): TrackLevel = - x match { + x match case 0 => NoTracking case 1 => TrackIfMissing case 10 => TrackAlways - } def intersection(a: TrackLevel, b: TrackLevel): TrackLevel = - if (a.id < b.id) a + if a.id < b.id then a else b def intersectionAll(vs: List[TrackLevel]): TrackLevel = vs reduceLeft intersection -} -sealed trait TrackLevel { +sealed trait TrackLevel: def id: Int -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/UnresolvedWarning.scala b/lm-core/src/main/scala/sbt/librarymanagement/UnresolvedWarning.scala index 22a807d1c..46cdf6640 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/UnresolvedWarning.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/UnresolvedWarning.scala @@ -8,7 +8,7 @@ final class ResolveException( val messages: Seq[String], val failed: Seq[ModuleID], val failedPaths: Map[ModuleID, Seq[ModuleID]] -) extends RuntimeException(messages.mkString("\n")) { +) extends RuntimeException(messages.mkString("\n")): def this(messages: Seq[String], failed: Seq[ModuleID]) = this( messages, @@ -17,7 +17,6 @@ final class ResolveException( m -> Nil }*) ) -} /** * Represents unresolved dependency warning, which displays reconstructed dependency tree @@ -28,11 +27,11 @@ final class UnresolvedWarning( val failedPaths: Seq[Seq[(ModuleID, Option[SourcePosition])]] ) -object UnresolvedWarning { +object UnresolvedWarning: def apply( err: ResolveException, config: UnresolvedWarningConfiguration - ): UnresolvedWarning = { + ): UnresolvedWarning = def modulePosition(m0: ModuleID): Option[SourcePosition] = config.modulePositions.find { case (m, _) => (m.organization == m0.organization) && @@ -47,44 +46,38 @@ object UnresolvedWarning { } } new UnresolvedWarning(err, failedPaths) - } private[sbt] def sourcePosStr(posOpt: Option[SourcePosition]): String = - posOpt match { + posOpt match case Some(LinePosition(path, start)) => s" ($path#L$start)" case Some(RangePosition(path, LineRange(start, end))) => s" ($path#L$start-$end)" case _ => "" - } given unresolvedWarningLines: ShowLines[UnresolvedWarning] = ShowLines { a => val withExtra = a.resolveException.failed.filter(_.extraDependencyAttributes.nonEmpty) val buffer = mutable.ListBuffer[String]() - if (withExtra.nonEmpty) { + if withExtra.nonEmpty then buffer += "\n\tNote: Some unresolved dependencies have extra attributes. Check that these dependencies exist with the requested attributes." withExtra foreach { id => buffer += "\t\t" + id } - } - if (a.failedPaths.nonEmpty) { + if a.failedPaths.nonEmpty then buffer += "\n\tNote: Unresolved dependencies path:" a.failedPaths foreach { path => - if (path.nonEmpty) { + if path.nonEmpty then val head = path.head buffer += "\t\t" + head._1.toString + sourcePosStr(head._2) path.tail foreach { (m, pos) => buffer += "\t\t +- " + m.toString + sourcePosStr(pos) } - } } - } buffer.toList } -} +end UnresolvedWarning final class UnresolvedWarningConfiguration private[sbt] ( val modulePositions: Map[ModuleID, SourcePosition] ) -object UnresolvedWarningConfiguration { +object UnresolvedWarningConfiguration: def apply(): UnresolvedWarningConfiguration = apply(Map()) def apply(modulePositions: Map[ModuleID, SourcePosition]): UnresolvedWarningConfiguration = new UnresolvedWarningConfiguration(modulePositions) -} diff --git a/lm-core/src/main/scala/sbt/librarymanagement/UpdateReportExtra.scala b/lm-core/src/main/scala/sbt/librarymanagement/UpdateReportExtra.scala index 83f30a58b..555dd33a3 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/UpdateReportExtra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/UpdateReportExtra.scala @@ -6,7 +6,7 @@ package sbt.librarymanagement import java.io.File import java.util as ju -private[librarymanagement] abstract class ConfigurationReportExtra { +private[librarymanagement] abstract class ConfigurationReportExtra: def configuration: ConfigRef def modules: Vector[ModuleReport] def details: Vector[OrganizationArtifactReport] @@ -20,13 +20,12 @@ private[librarymanagement] abstract class ConfigurationReportExtra { * For a given organization and module name, there is only one revision/`ModuleID` in this sequence. */ def allModules: Seq[ModuleID] = modules map addConfiguration - private def addConfiguration(mr: ModuleReport): ModuleID = { + private def addConfiguration(mr: ModuleReport): ModuleID = val module = mr.module - if (module.configurations.isEmpty) { + if module.configurations.isEmpty then val conf = mr.configurations map (c => s"$configuration->$c") mkString ";" module.withConfigurations(Some(conf)) - } else module - } + else module def retrieve(f: (ConfigRef, ModuleID, Artifact, File) => File): ConfigurationReport = ConfigurationReport( @@ -36,9 +35,9 @@ private[librarymanagement] abstract class ConfigurationReportExtra { }, details ) -} +end ConfigurationReportExtra -private[librarymanagement] abstract class ModuleReportExtra { +private[librarymanagement] abstract class ModuleReportExtra: def module: ModuleID def artifacts: Vector[(Artifact, File)] def missingArtifacts: Vector[Artifact] @@ -65,7 +64,7 @@ private[librarymanagement] abstract class ModuleReportExtra { def detailReport: String = s"\t\t- ${module.revision}\n" + - (if (arts.size <= 1) "" else arts.mkString("\t\t\t", "\n\t\t\t", "\n")) + + (if arts.size <= 1 then "" else arts.mkString("\t\t\t", "\n\t\t\t", "\n")) + reportStr("status", status) + reportStr("publicationDate", publicationDate map calendarToString) + reportStr("resolver", resolver) + @@ -77,49 +76,40 @@ private[librarymanagement] abstract class ModuleReportExtra { reportStr("homepage", homepage) + reportStr( "extraAttributes", - if (extraAttributes.isEmpty) None - else { - Some(extraAttributes.toString) - } + if extraAttributes.isEmpty then None + else Some(extraAttributes.toString) ) + reportStr("isDefault", isDefault map { _.toString }) + reportStr("branch", branch) + reportStr( "configurations", - if (configurations.isEmpty) None - else { - Some(configurations.mkString(", ")) - } + if configurations.isEmpty then None + else Some(configurations.mkString(", ")) ) + reportStr( "licenses", - if (licenses.isEmpty) None - else { - Some(licenses.mkString(", ")) - } + if licenses.isEmpty then None + else Some(licenses.mkString(", ")) ) + reportStr( "callers", - if (callers.isEmpty) None - else { - Some(callers.mkString(", ")) - } + if callers.isEmpty then None + else Some(callers.mkString(", ")) ) private[sbt] def reportStr(key: String, value: Option[String]): String = value map { x => s"\t\t\t$key: $x\n" } getOrElse "" - private def calendarToString(c: ju.Calendar): String = { + private def calendarToString(c: ju.Calendar): String = import sjsonnew.*, BasicJsonProtocol.* implicitly[IsoString[ju.Calendar]].to(c) - } def retrieve(f: (ModuleID, Artifact, File) => File): ModuleReport = withArtifacts(artifacts.map { (art, file) => (art, f(module, art, file)) }) -} +end ModuleReportExtra -private[librarymanagement] abstract class UpdateReportExtra { +private[librarymanagement] abstract class UpdateReportExtra: def cachedDescriptor: File def configurations: Vector[ConfigurationReport] def stats: UpdateStats @@ -128,34 +118,30 @@ private[librarymanagement] abstract class UpdateReportExtra { private[sbt] def moduleKey(m: ModuleID) = (m.organization, m.name, m.revision) /** All resolved modules in all configurations. */ - def allModules: Vector[ModuleID] = { + def allModules: Vector[ModuleID] = configurations.flatMap(_.allModules).groupBy(moduleKey).toVector map { case (_, v) => v reduceLeft { (agg, x) => agg.withConfigurations( - (agg.configurations, x.configurations) match { + (agg.configurations, x.configurations) match case (None, _) => x.configurations case (Some(ac), None) => Some(ac) case (Some(ac), Some(xc)) => Some(s"$ac;$xc") - } ) } } - } - def allModuleReports: Vector[ModuleReport] = { + def allModuleReports: Vector[ModuleReport] = configurations.flatMap(_.modules).groupBy(mR => moduleKey(mR.module)).toVector map { case (_, v) => v reduceLeft { (agg, x) => agg.withConfigurations( - (agg.configurations, x.configurations) match { + (agg.configurations, x.configurations) match case (v, _) if v.isEmpty => x.configurations case (ac, v) if v.isEmpty => ac case (ac, xc) => ac ++ xc - } ) } } - } def retrieve(f: (ConfigRef, ModuleID, Artifact, File) => File): UpdateReport = UpdateReport(cachedDescriptor, configurations.map { _.retrieve(f) }, stats, stamps) @@ -165,4 +151,4 @@ private[librarymanagement] abstract class UpdateReportExtra { /** Gets the names of all resolved configurations. This `UpdateReport` contains one `ConfigurationReport` for each configuration in this list. */ def allConfigurations: Vector[ConfigRef] = configurations.map(_.configuration) -} +end UpdateReportExtra diff --git a/lm-core/src/main/scala/sbt/librarymanagement/VersionNumber.scala b/lm-core/src/main/scala/sbt/librarymanagement/VersionNumber.scala index e4af203db..1d28f8f4f 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/VersionNumber.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/VersionNumber.scala @@ -4,7 +4,7 @@ final class VersionNumber private[sbt] ( val numbers: Seq[Long], val tags: Seq[String], val extras: Seq[String] -) { +): def _1: Option[Long] = get(0) def _2: Option[Long] = get(1) @@ -22,21 +22,19 @@ final class VersionNumber private[sbt] ( override def hashCode: Int = numbers.## * 41 * 41 + tags.## * 41 + extras.## - override def equals(that: Any): Boolean = that match { + override def equals(that: Any): Boolean = that match case v: VersionNumber => (numbers == v.numbers) && (tags == v.tags) && (extras == v.extras) case _ => false - } - def matchesSemVer(selsem: SemanticSelector): Boolean = { + def matchesSemVer(selsem: SemanticSelector): Boolean = selsem.matches(this) - } /** A variant of mkString that returns the empty string if the sequence is empty. */ private def mkString1[A](xs: Seq[A], start: String, sep: String, end: String): String = - if (xs.isEmpty) "" else xs.mkString(start, sep, end) -} + if xs.isEmpty then "" else xs.mkString(start, sep, end) +end VersionNumber -object VersionNumber { +object VersionNumber: /** * @param numbers numbers delimited by a dot. @@ -47,19 +45,18 @@ object VersionNumber { new VersionNumber(numbers, tags, extras) def apply(s: String): VersionNumber = - unapply(s) match { + unapply(s) match case Some((ns, ts, es)) => VersionNumber(ns, ts, es) case _ => throw new IllegalArgumentException(s"Invalid version number: $s") - } def unapply(v: VersionNumber): Option[(Seq[Long], Seq[String], Seq[String])] = Some((v.numbers, v.tags, v.extras)) - def unapply(s: String): Option[(Seq[Long], Seq[String], Seq[String])] = { + def unapply(s: String): Option[(Seq[Long], Seq[String], Seq[String])] = // null safe, empty string safe def splitOn(s: String, sep: Char): Vector[String] = - if (s eq null) Vector() + if s eq null then Vector() else s.split(sep).filterNot(_ == "").toVector def splitDot(s: String) = splitOn(s, '.') map (_.toLong) @@ -69,7 +66,7 @@ object VersionNumber { val TaggedVersion = """(\d{1,14})([\.\d{1,14}]*)((?:-\w+(?:\.\w+)*)*)((?:\+.+)*)""".r val NonSpaceString = """(\S+)""".r - s match { + s match case TaggedVersion(m, ns, ts, es) => val numbers = Vector(m.toLong) ++ splitDot(ns) val tags = splitDash(ts) @@ -78,17 +75,15 @@ object VersionNumber { case "" => None case NonSpaceString(s) => Some((Vector.empty, Vector.empty, Vector(s))) case _ => None - } - } + end unapply /** Strict. Checks everything. */ - object Strict extends VersionNumberCompatibility { + object Strict extends VersionNumberCompatibility: def name: String = "Strict" def isCompatible(v1: VersionNumber, v2: VersionNumber): Boolean = v1 == v2 - } /** Semantic Versioning. See http://semver.org/spec/v2.0.0.html */ - object SemVer extends VersionNumberCompatibility { + object SemVer extends VersionNumberCompatibility: def name: String = "Semantic Versioning" /* Quotes of parts of the rules in the SemVer Spec relevant to compatibility checking: @@ -141,44 +136,41 @@ object VersionNumber { doIsCompat(dropBuildMetadata(v1), dropBuildMetadata(v2)) private def doIsCompat(v1: VersionNumber, v2: VersionNumber): Boolean = - (v1, v2) match { + (v1, v2) match case (NormalVersion(0, _, _), NormalVersion(0, _, _)) => v1 == v2 // R4 case (NormalVersion(_, 0, 0), NormalVersion(_, 0, 0)) => v1 == v2 // R9 maybe? case (NormalVersion(x1, _, _), NormalVersion(x2, _, _)) => x1 == x2 // R6, R7 & R8 case _ => false - } // SemVer Spec Rule 10 (above) private[VersionNumber] def dropBuildMetadata(v: VersionNumber) = - if (v.extras.isEmpty) v else VersionNumber(v.numbers, v.tags, Nil) + if v.extras.isEmpty then v else VersionNumber(v.numbers, v.tags, Nil) // An extractor for SemVer's "normal version number" - SemVer Spec Rule 2 & Rule 9 (above) - private[VersionNumber] object NormalVersion { + private[VersionNumber] object NormalVersion: def unapply(v: VersionNumber): Option[(Long, Long, Long)] = PartialFunction.condOpt(v.numbers) { // NOTE! We allow the z to be missing, because of legacy like commons-io 1.3 case Seq(x, y, _*) => (x, y, v._3 getOrElse 0) } - } - } + end SemVer /** * A variant of SemVar that seems to be common among the Scala libraries. * The second segment (y in x.y.z) increments breaks the binary compatibility even when x > 0. * Also API compatibility is expected even when the first segment is zero. */ - object SecondSegment extends VersionNumberCompatibility { + object SecondSegment extends VersionNumberCompatibility: def name: String = "Second Segment Variant" def isCompatible(v1: VersionNumber, v2: VersionNumber): Boolean = PackVer.isCompatible(v1, v2) - } /** * A variant of SemVar that seems to be common among the Scala libraries. * The second segment (y in x.y.z) increments breaks the binary compatibility even when x > 0. * Also API compatibility is expected even when the first segment is zero. */ - object PackVer extends VersionNumberCompatibility { + object PackVer extends VersionNumberCompatibility: import SemVer.* def name: String = "Package Versioning Policy" @@ -186,19 +178,16 @@ object VersionNumber { def isCompatible(v1: VersionNumber, v2: VersionNumber): Boolean = doIsCompat(dropBuildMetadata(v1), dropBuildMetadata(v2)) - private def doIsCompat(v1: VersionNumber, v2: VersionNumber): Boolean = { - (v1, v2) match { + private def doIsCompat(v1: VersionNumber, v2: VersionNumber): Boolean = + (v1, v2) match case (NormalVersion(_, _, 0), NormalVersion(_, _, 0)) => v1 == v2 // R9 maybe? case (NormalVersion(x1, y1, _), NormalVersion(x2, y2, _)) => (x1 == x2) && (y1 == y2) case _ => false - } - } - } /** * A variant of SemVar that enforces API compatibility when the first segment is zero. */ - object EarlySemVer extends VersionNumberCompatibility { + object EarlySemVer extends VersionNumberCompatibility: import SemVer.* def name: String = "Early Semantic Versioning" @@ -250,17 +239,15 @@ object VersionNumber { doIsCompat(dropBuildMetadata(v1), dropBuildMetadata(v2)) private def doIsCompat(v1: VersionNumber, v2: VersionNumber): Boolean = - (v1, v2) match { + (v1, v2) match case (NormalVersion(0, _, 0), NormalVersion(0, _, 0)) => v1 == v2 case (NormalVersion(0, y1, _), NormalVersion(0, y2, _)) => y1 == y2 case (NormalVersion(_, 0, 0), NormalVersion(_, 0, 0)) => v1 == v2 // R9 maybe? case (NormalVersion(x1, _, _), NormalVersion(x2, _, _)) => x1 == x2 // R6, R7 & R8 case _ => false - } - } -} + end EarlySemVer +end VersionNumber -trait VersionNumberCompatibility { +trait VersionNumberCompatibility: def name: String def isCompatible(v1: VersionNumber, v2: VersionNumber): Boolean -} diff --git a/lm-core/src/test/scala/ConfigMacroSpec.scala b/lm-core/src/test/scala/ConfigMacroSpec.scala index 1be735466..a060ed62b 100644 --- a/lm-core/src/test/scala/ConfigMacroSpec.scala +++ b/lm-core/src/test/scala/ConfigMacroSpec.scala @@ -6,26 +6,23 @@ import scala.util.control.NonFatal import org.scalacheck.* import Prop.* -class ConfigDefs { +class ConfigDefs: lazy val Kompile = config("kompile") val X = config("x") val Z = config("z").hide val A: Configuration = config("a") lazy val Aa: Configuration = config("aa") -} -object ConfigMacroSpec extends Properties("ConfigMacroSpec") { +object ConfigMacroSpec extends Properties("ConfigMacroSpec"): lazy val cd = new ConfigDefs import cd.* def secure(f: => Prop): Prop = - try { - Prop.secure(f) - } catch { + try Prop.secure(f) + catch case NonFatal(e) => e.printStackTrace throw e - } property("Explicit type on lazy val supported") = secure { check(Aa, "Aa", "aa", true) @@ -47,7 +44,7 @@ object ConfigMacroSpec extends Properties("ConfigMacroSpec") { check(Z, "Z", "z", false) } - def check(c: Configuration, id: String, name: String, isPublic: Boolean): Prop = { + def check(c: Configuration, id: String, name: String, isPublic: Boolean): Prop = s"Expected id: $id" |: s"Expected name: $name" |: s"Expected isPublic: $isPublic" |: @@ -57,5 +54,4 @@ object ConfigMacroSpec extends Properties("ConfigMacroSpec") { (c.id == id) && (c.name == name) && (c.isPublic == isPublic) - } -} +end ConfigMacroSpec diff --git a/lm-core/src/test/scala/example/tests/CrossVersionCompatTest.scala b/lm-core/src/test/scala/example/tests/CrossVersionCompatTest.scala index ab5f47633..c7981f04e 100644 --- a/lm-core/src/test/scala/example/tests/CrossVersionCompatTest.scala +++ b/lm-core/src/test/scala/example/tests/CrossVersionCompatTest.scala @@ -5,12 +5,11 @@ import verify.BasicTestSuite import scala.annotation.nowarn @nowarn -object CrossVersionCompatTest extends BasicTestSuite { +object CrossVersionCompatTest extends BasicTestSuite: test("CrossVersion.Disabled is typed to be Disabled") { - assert(CrossVersion.Disabled match { + assert(CrossVersion.Disabled match case _: Disabled => true - case _ => false - }) + case _ => false) } test("CrossVersion.Disabled functions as disabled") { @@ -19,10 +18,9 @@ object CrossVersionCompatTest extends BasicTestSuite { } test("CrossVersion.Disabled() is typed to be Disabled") { - assert(CrossVersion.Disabled() match { + assert(CrossVersion.Disabled() match case _: Disabled => true - case _ => false - }) + case _ => false) } test("CrossVersion.Disabled() functions as disabled") { @@ -31,31 +29,27 @@ object CrossVersionCompatTest extends BasicTestSuite { } test("CrossVersion.Disabled is stable") { - assert(CrossVersion.Disabled match { + assert(CrossVersion.Disabled match case CrossVersion.Disabled => true - case _ => false - }) + case _ => false) } test("sbt.librarymanagement.Disabled is typed to be Disabled") { - assert(Disabled match { + assert(Disabled match case _: Disabled => true - case _ => false - }) + case _ => false) } test("sbt.librarymanagement.Disabled is stable") { - assert(Disabled match { + assert(Disabled match case Disabled => true - case _ => false - }) + case _ => false) } test("sbt.librarymanagement.Disabled() is typed to be Disabled") { - assert(Disabled() match { + assert(Disabled() match case _: Disabled => true - case _ => false - }) + case _ => false) } test("CrossVersion.disabled is sbt.librarymanagement.Disabled") { @@ -69,4 +63,4 @@ object CrossVersionCompatTest extends BasicTestSuite { test("CrossVersion.Disabled() is sbt.librarymanagement.Disabled") { assert(CrossVersion.Disabled() == Disabled) } -} +end CrossVersionCompatTest diff --git a/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportCacheCodecSpec.scala b/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportCacheCodecSpec.scala index a40bb578a..5ab9deddb 100644 --- a/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportCacheCodecSpec.scala +++ b/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportCacheCodecSpec.scala @@ -98,3 +98,4 @@ object UpdateReportCacheCodecSpec extends verify.BasicTestSuite: UpdateStats(0L, 0L, 0L, false), Map.empty ) +end UpdateReportCacheCodecSpec diff --git a/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportFormatSpec.scala b/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportFormatSpec.scala index 01706228a..3927976d7 100644 --- a/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportFormatSpec.scala +++ b/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportFormatSpec.scala @@ -343,3 +343,4 @@ object UpdateReportFormatSpec extends verify.BasicTestSuite: UpdateStats(100L, 50L, 1024L, false, Some("stamp")), Map(descriptor.getAbsolutePath -> 12345L) ) +end UpdateReportFormatSpec diff --git a/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportInternerSpec.scala b/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportInternerSpec.scala index 6e6043fd3..f27d9fe3b 100644 --- a/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportInternerSpec.scala +++ b/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportInternerSpec.scala @@ -130,3 +130,4 @@ object UpdateReportInternerSpec extends verify.BasicTestSuite: Thread.sleep(20) i += 1 ref.get == null +end UpdateReportInternerSpec diff --git a/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportPersistenceBenchmark.scala b/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportPersistenceBenchmark.scala index 97e0f1ffc..595f9484a 100644 --- a/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportPersistenceBenchmark.scala +++ b/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportPersistenceBenchmark.scala @@ -37,7 +37,7 @@ object UpdateReportPersistenceBenchmark: modulesPerConfig: Int = 50, warmupIterations: Int = 10 ): Either[String, BenchmarkResult] = - for { + for _ <- Either.cond(iterations > 0, (), "iterations must be positive") _ <- Either.cond(configs.nonEmpty, (), "configs must be non-empty") _ <- Either.cond(modulesPerConfig > 0, (), "modulesPerConfig must be positive") @@ -85,7 +85,7 @@ object UpdateReportPersistenceBenchmark: ) catch case e: Exception => Left(s"Benchmark failed: ${e.getMessage}") finally IO.delete(baseDir) - } yield result + yield result def buildSampleReport( baseDir: File, @@ -138,6 +138,7 @@ object UpdateReportPersistenceBenchmark: val stamps = Map(cachedDescriptor.getAbsolutePath -> System.currentTimeMillis()) UpdateReport(cachedDescriptor, configReports, stats, stamps) + end buildSampleReport def formatResult(result: BenchmarkResult): String = f"""UpdateReport Persistence Benchmark Results diff --git a/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportPersistenceSpec.scala b/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportPersistenceSpec.scala index b6e2458c1..65d87d87a 100644 --- a/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportPersistenceSpec.scala +++ b/lm-core/src/test/scala/sbt/internal/librarymanagement/UpdateReportPersistenceSpec.scala @@ -60,6 +60,7 @@ class UpdateReportPersistenceSpec extends AnyFlatSpec with Matchers: val stamps = Map(cachedDescriptor.getAbsolutePath -> 12345L) UpdateReport(cachedDescriptor, Vector(configReport), stats, stamps) + end buildTestReport "UpdateReportPersistence.toCache and fromCache" should "preserve stats and stamps" in: IO.withTemporaryDirectory: baseDir => @@ -199,6 +200,7 @@ class UpdateReportPersistenceSpec extends AnyFlatSpec with Matchers: Vector.empty, Vector.empty ) + end moduleFor // `managedJars` derives the managed classpath's order from `ConfigurationReport.modules`, and that // order is part of the compile task's cache key. diff --git a/lm-core/src/test/scala/sbt/internal/librarymanagement/WeakInternerSpec.scala b/lm-core/src/test/scala/sbt/internal/librarymanagement/WeakInternerSpec.scala index 7c307c704..98e96f752 100644 --- a/lm-core/src/test/scala/sbt/internal/librarymanagement/WeakInternerSpec.scala +++ b/lm-core/src/test/scala/sbt/internal/librarymanagement/WeakInternerSpec.scala @@ -117,3 +117,4 @@ object WeakInternerSpec extends verify.BasicTestSuite: Thread.sleep(20) i += 1 ref.get == null +end WeakInternerSpec diff --git a/lm-core/src/test/scala/sbt/librarymanagement/ArtifactTest.scala b/lm-core/src/test/scala/sbt/librarymanagement/ArtifactTest.scala index 04bc1115f..73d866cd3 100644 --- a/lm-core/src/test/scala/sbt/librarymanagement/ArtifactTest.scala +++ b/lm-core/src/test/scala/sbt/librarymanagement/ArtifactTest.scala @@ -2,7 +2,7 @@ package sbt.librarymanagement import sbt.internal.librarymanagement.UnitSpec -class ArtifactTest extends UnitSpec { +class ArtifactTest extends UnitSpec: "Artifact.artifactName" should "include the platform suffix before the Scala suffix" in { val m = ModuleID("com.example", "root-178", "0.1.0-SNAPSHOT") @@ -38,4 +38,4 @@ class ArtifactTest extends UnitSpec { Artifact.artifactName(sv, m, a) shouldBe s"$expectedMavenArtifactId-0.0.9-SNAPSHOT.pom" } -} +end ArtifactTest diff --git a/lm-core/src/test/scala/sbt/librarymanagement/ConfigMacroTest.scala b/lm-core/src/test/scala/sbt/librarymanagement/ConfigMacroTest.scala index 27103395a..0a13c7d69 100644 --- a/lm-core/src/test/scala/sbt/librarymanagement/ConfigMacroTest.scala +++ b/lm-core/src/test/scala/sbt/librarymanagement/ConfigMacroTest.scala @@ -3,11 +3,10 @@ package sbt.librarymanagement import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers -class ConfigMacroTest extends AnyFunSpec with Matchers { +class ConfigMacroTest extends AnyFunSpec with Matchers: describe("Configurations.config") { it("should validate the ID in compile time") { """val A = Configurations.config("a")""" should compile """val b = Configurations.config("b")""" shouldNot compile } } -} diff --git a/lm-core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala b/lm-core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala index 3bb8b59c4..d4b3a65b2 100644 --- a/lm-core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala +++ b/lm-core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala @@ -5,7 +5,7 @@ import CrossVersion.* import sbt.librarymanagement.syntax.* import scala.annotation.nowarn -class CrossVersionTest extends UnitSpec { +class CrossVersionTest extends UnitSpec: "sbtApiVersion" should "for xyz return None" in { sbtApiVersion("xyz") shouldBe None } @@ -449,4 +449,4 @@ class CrossVersionTest extends UnitSpec { Some("sjs1") ) shouldBe None } -} +end CrossVersionTest diff --git a/lm-core/src/test/scala/sbt/librarymanagement/MemoizedFileFormatsSpec.scala b/lm-core/src/test/scala/sbt/librarymanagement/MemoizedFileFormatsSpec.scala index cc356acae..7a8dbb493 100644 --- a/lm-core/src/test/scala/sbt/librarymanagement/MemoizedFileFormatsSpec.scala +++ b/lm-core/src/test/scala/sbt/librarymanagement/MemoizedFileFormatsSpec.scala @@ -131,3 +131,4 @@ object MemoizedFileFormatsSpec extends BasicTestSuite: UpdateStats(0L, 0L, 0L, false), Map.empty ) +end MemoizedFileFormatsSpec diff --git a/lm-core/src/test/scala/sbt/librarymanagement/ModuleIdTest.scala b/lm-core/src/test/scala/sbt/librarymanagement/ModuleIdTest.scala index e5a063791..620608f00 100644 --- a/lm-core/src/test/scala/sbt/librarymanagement/ModuleIdTest.scala +++ b/lm-core/src/test/scala/sbt/librarymanagement/ModuleIdTest.scala @@ -2,7 +2,7 @@ package sbt.librarymanagement import sjsonnew.support.scalajson.unsafe.{ Converter, CompactPrinter, Parser } -object ModuleIdTest extends verify.BasicTestSuite { +object ModuleIdTest extends verify.BasicTestSuite: test("Module Id should return cross-disabled module id as equal to a copy") { assert(ModuleID("com.acme", "foo", "1") == ModuleID("com.acme", "foo", "1")) } @@ -46,4 +46,4 @@ object ModuleIdTest extends verify.BasicTestSuite { def expectedJson = """{"organization":"com.acme","name":"foo","revision":"1","isChanging":false,"isTransitive":true,"isForce":false,"explicitArtifacts":[],"inclusions":[],"exclusions":[],"extraAttributes":{},"crossVersion":{"type":"Disabled"}}""" -} +end ModuleIdTest diff --git a/lm-core/src/test/scala/sbt/librarymanagement/ResolverTest.scala b/lm-core/src/test/scala/sbt/librarymanagement/ResolverTest.scala index 878d7968c..35d0d8021 100644 --- a/lm-core/src/test/scala/sbt/librarymanagement/ResolverTest.scala +++ b/lm-core/src/test/scala/sbt/librarymanagement/ResolverTest.scala @@ -4,7 +4,7 @@ import java.net.URI import sbt.internal.librarymanagement.UnitSpec -class ResolverTest extends UnitSpec { +class ResolverTest extends UnitSpec: "Resolver uri" should "propagate pattern descriptorOptional and skipConsistencyCheck." in { val pats = Vector("[orgPath]") @@ -83,4 +83,4 @@ class ResolverTest extends UnitSpec { repo.patterns.artifactPatterns shouldBe Vector(artifacts) repo.patterns.isMavenCompatible shouldBe false } -} +end ResolverTest diff --git a/lm-core/src/test/scala/sbt/librarymanagement/SemanticSelectorSpec.scala b/lm-core/src/test/scala/sbt/librarymanagement/SemanticSelectorSpec.scala index 64a586430..998e1b34d 100644 --- a/lm-core/src/test/scala/sbt/librarymanagement/SemanticSelectorSpec.scala +++ b/lm-core/src/test/scala/sbt/librarymanagement/SemanticSelectorSpec.scala @@ -3,7 +3,7 @@ package sbt.librarymanagement import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers -class SemanticSelectorSpec extends AnyFreeSpec with Matchers { +class SemanticSelectorSpec extends AnyFreeSpec with Matchers: semsel("<=1.2.3") { sel => assertMatches(sel, "1.2.3") assertMatches(sel, "1.2-beta") @@ -396,4 +396,4 @@ class SemanticSelectorSpec extends AnyFreeSpec with Matchers { s"""should parse as an error""" in { an[IllegalArgumentException] should be thrownBy SemanticSelector(s.value) } -} +end SemanticSelectorSpec diff --git a/lm-core/src/test/scala/sbt/librarymanagement/VersionNumberSpec.scala b/lm-core/src/test/scala/sbt/librarymanagement/VersionNumberSpec.scala index e98da9ea1..f90ac1e8b 100644 --- a/lm-core/src/test/scala/sbt/librarymanagement/VersionNumberSpec.scala +++ b/lm-core/src/test/scala/sbt/librarymanagement/VersionNumberSpec.scala @@ -5,7 +5,7 @@ import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers // This is a specification to check VersionNumber and VersionNumberCompatibility. -class VersionNumberSpec extends AnyFreeSpec with Matchers with Inside { +class VersionNumberSpec extends AnyFreeSpec with Matchers with Inside: import VersionNumber.{ EarlySemVer, SemVer, PackVer } version("1") { v => @@ -179,12 +179,11 @@ class VersionNumberSpec extends AnyFreeSpec with Matchers with Inside { (v._4 shouldBe buildNumber) } - private def assertCascadesTo(v: VersionString, ns: Seq[String]): Unit = { + private def assertCascadesTo(v: VersionString, ns: Seq[String]): Unit = s"should cascade to $ns" in { val versionNumbers = ns.toVector map VersionNumber.apply VersionNumber(v.value).cascadingVersions shouldBe versionNumbers } - } private def assertIsCompatibleWith( v1: VersionString, @@ -205,16 +204,14 @@ class VersionNumberSpec extends AnyFreeSpec with Matchers with Inside { vnc: VersionNumberCompatibility, v1: VersionString, v2: String - ) = { - val prefix = if (expectOutcome) "should" else "should NOT" - val compatibilityStrategy = vnc match { + ) = + val prefix = if expectOutcome then "should" else "should NOT" + val compatibilityStrategy = vnc match case SemVer => "SemVer" case PackVer => "PackVer" case EarlySemVer => "EarlySemVer" case _ => val s = vnc.name; if s.contains(" ") then s""""$s"""" else s - } s"$prefix be $compatibilityStrategy compatible with $v2" in { vnc.isCompatible(VersionNumber(v1.value), VersionNumber(v2)) shouldBe expectOutcome } - } -} +end VersionNumberSpec diff --git a/lm-core/src/test/scala/sbt/librarymanagement/VersionRangeSpec.scala b/lm-core/src/test/scala/sbt/librarymanagement/VersionRangeSpec.scala index faba93d46..6a863e1bc 100644 --- a/lm-core/src/test/scala/sbt/librarymanagement/VersionRangeSpec.scala +++ b/lm-core/src/test/scala/sbt/librarymanagement/VersionRangeSpec.scala @@ -2,7 +2,7 @@ package sbt package internal package librarymanagement -class VersionRangeSpec extends UnitSpec { +class VersionRangeSpec extends UnitSpec: "Version range" should "strip 1.0 to None" in stripTo("1.0", None) it should "strip (,1.0] to 1.0" in stripTo("(,1.0]", Some("1.0")) it should "strip (,1.0) to None" in stripTo("(,1.0)", None) @@ -113,4 +113,4 @@ class VersionRangeSpec extends UnitSpec { assert(VersionRange.versionSatisfiesRange("1.3.1", "1.3.1,2.3") == true) assert(VersionRange.versionSatisfiesRange("2.4", "1.3.1,2.3") == false) } -} +end VersionRangeSpec diff --git a/lm-coursier/definitions/src/main/scala/lmcoursier/credentials/DirectCredentials.scala b/lm-coursier/definitions/src/main/scala/lmcoursier/credentials/DirectCredentials.scala index c767abf70..98769e628 100644 --- a/lm-coursier/definitions/src/main/scala/lmcoursier/credentials/DirectCredentials.scala +++ b/lm-coursier/definitions/src/main/scala/lmcoursier/credentials/DirectCredentials.scala @@ -14,7 +14,6 @@ import dataclass.* matchHost: Boolean = false, @since("1.3") httpsOnly: Boolean = true -) extends Credentials { +) extends Credentials: override def toString(): String = s"DirectCredentials(host=$host, username=$username)" -} diff --git a/lm-coursier/definitions/src/main/scala/lmcoursier/definitions/Authentication.scala b/lm-coursier/definitions/src/main/scala/lmcoursier/definitions/Authentication.scala index 054cb4c08..35b2af374 100644 --- a/lm-coursier/definitions/src/main/scala/lmcoursier/definitions/Authentication.scala +++ b/lm-coursier/definitions/src/main/scala/lmcoursier/definitions/Authentication.scala @@ -13,7 +13,6 @@ import dataclass.* httpsOnly: Boolean = true, @since("1.2") passOnRedirect: Boolean = false -) { +): override def toString(): String = s"Authentication(user=$user)" -} diff --git a/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala b/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala index 01e4f781d..170bc8366 100644 --- a/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala +++ b/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala @@ -35,7 +35,7 @@ class CoursierDependencyResolution( conf: CoursierConfiguration, protocolHandlerConfiguration: Option[CoursierConfiguration], bootstrappingProtocolHandler: Boolean -) extends DependencyResolutionInterface { +) extends DependencyResolutionInterface: def this(conf: CoursierConfiguration) = this( @@ -51,16 +51,14 @@ class CoursierDependencyResolution( configuration: UpdateConfiguration, uwconfig: UnresolvedWarningConfiguration, log: Logger - ): ClassLoader = { + ): ClassLoader = val conf0 = protocolHandlerConfiguration.getOrElse(conf) - def isUnknownProtocol(rawURL: String): Boolean = { - Try(new URI(rawURL).toURL) match { + def isUnknownProtocol(rawURL: String): Boolean = + Try(new URI(rawURL).toURL) match case Failure(ex) if ex.getMessage.startsWith("unknown protocol: ") => true case _ => false - } - } val confWithoutUnknownProtocol = conf0.withResolvers( @@ -88,7 +86,7 @@ class CoursierDependencyResolution( val reportOrUnresolved = resolution.update(moduleDescriptor(fakeModule), configuration, uwconfig, log) - val report = reportOrUnresolved match { + val report = reportOrUnresolved match case Right(report0) => report0 @@ -96,17 +94,16 @@ class CoursierDependencyResolution( import sbt.util.ShowLines.* unresolvedWarning.lines.foreach(log.warn(_)) throw unresolvedWarning.resolveException - } val jars = - for { + for reportConfiguration <- report.configurations.filter(_.configuration.name == "runtime") module <- reportConfiguration.modules (_, jar) <- module.artifacts - } yield jar + yield jar new URLClassLoader(jars.map(_.toURI().toURL()).toArray) - } + end fetchProtocolHandlerClassLoader /* * Based on earlier implementations by @leonardehrenfried (https://github.com/sbt/librarymanagement/pull/190) @@ -122,29 +119,26 @@ class CoursierDependencyResolution( configuration: UpdateConfiguration, uwconfig: UnresolvedWarningConfiguration, log: Logger - ): Either[UnresolvedWarning, UpdateReport] = { + ): Either[UnresolvedWarning, UpdateReport] = - if (bootstrappingProtocolHandler && protocolHandlerClassLoader.isEmpty) + if bootstrappingProtocolHandler && protocolHandlerClassLoader.isEmpty then protocolHandlerClassLoaderLock.synchronized { - if (bootstrappingProtocolHandler && protocolHandlerClassLoader.isEmpty) { + if bootstrappingProtocolHandler && protocolHandlerClassLoader.isEmpty then val classLoader = fetchProtocolHandlerClassLoader(configuration, uwconfig, log) protocolHandlerClassLoader = Some(classLoader) - } } val conf = this.conf.withUpdateConfiguration(configuration) // TODO Take stuff in configuration into account? uwconfig too? - val module0 = module match { + val module0 = module match case c: CoursierModuleDescriptor => c.descriptor case other => - other.moduleSettings match { + other.moduleSettings match case d: ModuleDescriptorConfiguration => d case s => sys.error(s"unrecognized module settings: $s") - } - } val soOpt = module0.scalaModuleInfo .map(_.scalaOrganization) @@ -169,16 +163,13 @@ class CoursierDependencyResolution( optionalCrossVer = true, projectPlatform = projectPlatform ) - val interProjectDependencies = { + val interProjectDependencies = val needed = conf.interProjectDependencies.exists { p => p.module == mod && p.version == ver } - if (needed) - conf.interProjectDependencies.map(ToCoursier.project) - else - Vector.empty[coursier.core.Project] - } + if needed then conf.interProjectDependencies.map(ToCoursier.project) + else Vector.empty[coursier.core.Project] val extraProjects = conf.extraProjects.map(ToCoursier.project) @@ -194,10 +185,8 @@ class CoursierDependencyResolution( val ivyProperties = ResolutionParams.defaultIvyProperties(conf.ivyHome) val classifiers = - if (conf.hasClassifiers) - Some(conf.classifiers.map(Classifier(_))) - else - None + if conf.hasClassifiers then Some(conf.classifiers.map(Classifier(_))) + else None val authenticationByRepositoryId = conf.authenticationByRepositoryId.toMap @@ -238,7 +227,7 @@ class CoursierDependencyResolution( } .map { (config, dep) => val depForResolve = - if (boms.nonEmpty && (dep.version == "*" || dep.version.isEmpty)) + if boms.nonEmpty && (dep.version == "*" || dep.version.isEmpty) then lmcoursier.definitions.Dependency( dep.module, "", @@ -248,8 +237,7 @@ class CoursierDependencyResolution( dep.optional, dep.transitive ) - else - dep + else dep (ToCoursier.configuration(config), ToCoursier.dependency(depForResolve)) } @@ -279,7 +267,7 @@ class CoursierDependencyResolution( dependencies = dependencies, fallbackDependencies = conf.fallbackDependencies, orderedConfigs = orderedConfigs, - autoScalaLibOpt = if (autoScalaLib) Some((so, sv)) else None, + autoScalaLibOpt = if autoScalaLib then Some((so, sv)) else None, mainRepositories = mainRepositories, parentProjectCache = Map.empty, interProjectDependencies = interProjectDependencies, @@ -357,7 +345,7 @@ class CoursierDependencyResolution( classLoaders = protocolHandlerClassLoader.toSeq, ) - val e = for { + val e = for (resolutions, lockDataOpt) <- ResolutionRun.resolutionsWithLockFileData( resolutionParams, verbosityLevel, @@ -365,22 +353,19 @@ class CoursierDependencyResolution( conf.lockFile, conf.scalaVersion ) - artifactResult0 <- lockDataOpt match { + artifactResult0 <- lockDataOpt match case Some(lockData) => - LockedArtifactsRun.fetchFromLockFile(lockData, cache0, verbosityLevel, log) match { + LockedArtifactsRun.fetchFromLockFile(lockData, cache0, verbosityLevel, log) match case Right(arts) => Right(arts) case Left(err) => - if (verbosityLevel >= 1) { + if verbosityLevel >= 1 then log.warn(s"Failed to fetch from lock file: $err, falling back to normal fetch") - } ArtifactsRun(artifactsParams(resolutions), verbosityLevel, log) .map(_.fullDetailedArtifacts0) - } case None => ArtifactsRun(artifactsParams(resolutions), verbosityLevel, log) .map(_.fullDetailedArtifacts0) - } - } yield { + yield val artifactResult = artifactResult0.map { case (d, p: Publication, a, o) => (d, (Right(p): Either[VariantPublication, Publication]), a, o) @@ -388,7 +373,7 @@ class CoursierDependencyResolution( } val updateParams0 = updateParams(resolutions, artifactResult) val report = UpdateRun.update(updateParams0, verbosityLevel, log) - if (lockDataOpt.isEmpty) { + if lockDataOpt.isEmpty then conf.lockFile.foreach: lockFile => val artifactMap = artifactResult .groupBy(_._1) @@ -411,11 +396,10 @@ class CoursierDependencyResolution( ) match case Right(lockData) => LockFile.write(lockFile, lockData) case Left(err) => throw err - } + end if report - } e.left.map(unresolvedWarningOrThrow(module0.module, uwconfig, _)) - } + end update private def toModuleId(module: coursier.core.Module, version: String): ModuleID = ModuleID(module.organization.value, module.name.value, version) @@ -439,9 +423,7 @@ class CoursierDependencyResolution( resolution.dependenciesOf0(dependency, false, false) match case Right(deps) => sortDependencies(deps) case Left(_) => Vector.empty - catch { - case NonFatal(_) => Vector.empty - } + catch case NonFatal(_) => Vector.empty private def pathScore(path: Vector[Dependency]): (Int, String) = path.size -> path @@ -454,26 +436,24 @@ class CoursierDependencyResolution( candidate: Vector[Dependency], currentBest: Option[Vector[Dependency]] ): Option[Vector[Dependency]] = - currentBest match { + currentBest match case Some(best) => val (bestLength, bestPathStr) = pathScore(best) val (candidateLength, candidatePathStr) = pathScore(candidate) - if ( - bestLength > candidateLength || (bestLength == candidateLength && bestPathStr >= candidatePathStr) - ) currentBest + if bestLength > candidateLength || (bestLength == candidateLength && bestPathStr >= candidatePathStr) + then currentBest else Some(candidate) case _ => Some(candidate) - } private def longestPathToTarget( resolution: Resolution, current: Dependency, target: DependencyKey, seen: Set[DependencyKey] - ): Option[Vector[Dependency]] = { + ): Option[Vector[Dependency]] = val currentKey = dependencyKey(current) - if (currentKey == target) Some(Vector(current)) - else { + if currentKey == target then Some(Vector(current)) + else safeDependenciesOf(resolution, current).iterator .filterNot(dep => seen(dependencyKey(dep))) .foldLeft(Option.empty[Vector[Dependency]]) { (best, dep) => @@ -481,36 +461,31 @@ class CoursierDependencyResolution( val candidate = longestPathToTarget(resolution, dep, target, seen + key).map { tail => current +: tail } - candidate match { + candidate match case Some(path) => betterPath(path, best) case None => best - } } - } - } private def resolvePath( resolution: Resolution, failedDependency: Dependency, rootModule: ModuleID - ): Seq[ModuleID] = { + ): Seq[ModuleID] = val normalizedRootModule = rootModule.withConfigurations(None) val roots = sortDependencies(resolution.rootDependencies) val target = dependencyKey(failedDependency) val resolvedPath = roots .foldLeft(Option.empty[Vector[Dependency]]) { (best, root) => val candidate = longestPathToTarget(resolution, root, target, Set(dependencyKey(root))) - candidate match { + candidate match case Some(path) => betterPath(path, best) case None => best - } } .getOrElse(Vector(failedDependency)) normalizedRootModule +: resolvedPath.map(dep => toModuleId(dep.module, dep.versionConstraint.asString) ) - } private def failedPaths( rootModule: ModuleID, @@ -527,19 +502,18 @@ class CoursierDependencyResolution( rootModule: ModuleID, uwconfig: UnresolvedWarningConfiguration, ex: coursier.error.CoursierError - ): UnresolvedWarning = { + ): UnresolvedWarning = // TODO Take coursier.error.FetchError.DownloadingArtifacts into account - val downloadErrors = ex match { + val downloadErrors = ex match case ex0: coursier.error.ResolutionError => ex0.errors.collect { case err: coursier.error.ResolutionError.CantDownloadModule => err } case _ => Nil - } - val otherErrors = ex match { + val otherErrors = ex match case ex0: coursier.error.ResolutionError => ex0.errors.flatMap { case _: coursier.error.ResolutionError.CantDownloadModule => None @@ -547,13 +521,11 @@ class CoursierDependencyResolution( } case _ => Seq(ex) - } - if (otherErrors.isEmpty) { - val resolution = ex match { + if otherErrors.isEmpty then + val resolution = ex match case ex0: coursier.error.ResolutionError => ex0.resolution case _ => Resolution() - } val resolvedPaths = failedPaths(rootModule, resolution, downloadErrors) val r = new ResolveException( downloadErrors.map(_.getMessage), @@ -568,12 +540,11 @@ class CoursierDependencyResolution( resolvedPaths ) UnresolvedWarning(r, uwconfig) - } else - throw ex - } -} + else throw ex + end unresolvedWarningOrThrow +end CoursierDependencyResolution -object CoursierDependencyResolution { +object CoursierDependencyResolution: def apply(configuration: CoursierConfiguration): DependencyResolution = DependencyResolution(new CoursierDependencyResolution(configuration)) @@ -594,4 +565,4 @@ object CoursierDependencyResolution { private[lmcoursier] def cacheFileToOriginalUrl(fileUrl: String, cacheDir: File): String = lmcoursier.internal.CacheUrlConversion.cacheFileToOriginalUrl(fileUrl, cacheDir) -} +end CoursierDependencyResolution diff --git a/lm-coursier/src/main/scala/lmcoursier/FromSbt.scala b/lm-coursier/src/main/scala/lmcoursier/FromSbt.scala index 0516dcd32..59706963e 100644 --- a/lm-coursier/src/main/scala/lmcoursier/FromSbt.scala +++ b/lm-coursier/src/main/scala/lmcoursier/FromSbt.scala @@ -17,7 +17,7 @@ import lmcoursier.definitions.{ import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties import sbt.librarymanagement.{ Configuration as _, * } -object FromSbt { +object FromSbt: private def sbtCrossName( name: String, @@ -27,7 +27,7 @@ object FromSbt { scalaBinaryVersion: => String, optionalCrossVer: Boolean = false, projectPlatform: Option[String], - ): String = { + ): String = val name0 = name val name1 = crossVersion match @@ -35,16 +35,12 @@ object FromSbt { case _ => addPlatformSuffix(name0, platformOpt, projectPlatform) val updatedName = CrossVersion(crossVersion, scalaVersion, scalaBinaryVersion) .fold(name1)(_(name1)) - if (!optionalCrossVer || updatedName.length <= name0.length) - updatedName - else { + if !optionalCrossVer || updatedName.length <= name0.length then updatedName + else val suffix = updatedName.substring(name0.length) - if (name0.endsWith(suffix)) - name0 - else - updatedName - } - } + if name0.endsWith(suffix) then name0 + else updatedName + end sbtCrossName // Duplicate of sbt.librarymanagement.CrossVersion.addPlatformSuffix. Keep the two in sync // until lm-coursier moves under sbt @@ -52,20 +48,17 @@ object FromSbt { name: String, platformOpt: Option[String], projectPlatform: Option[String] - ): String = { + ): String = def addSuffix(platformName: String): String = - platformName match { + platformName match case "" | "jvm" => name case _ => s"${name}_$platformName" - } - (platformOpt, projectPlatform) match { + (platformOpt, projectPlatform) match case (Some(p), _) => addSuffix(p) // Use explicit platform if set (don't override with project platform) case (None, Some(p)) => addSuffix(p) // Only use project platform if dependency has no explicit platform case _ => name - } - } private def attributes(attr: Map[String, String]): Map[String, String] = attr @@ -82,7 +75,7 @@ object FromSbt { scalaBinaryVersion: String, optionalCrossVer: Boolean, projectPlatform: Option[String], - ): (Module, String) = { + ): (Module, String) = val fullName = sbtCrossName( @@ -103,7 +96,7 @@ object FromSbt { val version = module.revision (module0, version) - } + end moduleVersion def moduleVersion( module: ModuleID, @@ -124,7 +117,7 @@ object FromSbt { scalaBinaryVersion: String, optionalCrossVer: Boolean = false, projectPlatform: Option[String] = None, - ): Seq[(Configuration, Dependency)] = { + ): Seq[(Configuration, Dependency)] = // TODO Warn about unsupported properties in `module` @@ -159,7 +152,7 @@ object FromSbt { } val publications = - if (module.explicitArtifacts.isEmpty) + if module.explicitArtifacts.isEmpty then Seq(Publication("", Type(""), Extension(""), Classifier(""))) else module.explicitArtifacts @@ -172,30 +165,28 @@ object FromSbt { ) } - for { + for (from, to) <- allMappings.distinct pub <- publications.distinct - } yield { + yield val dep0 = dep .withConfiguration(to) .withPublication(pub) from -> dep0 - } - } + end dependencies def fallbackDependencies( allDependencies: Seq[ModuleID], scalaVersion: String, scalaBinaryVersion: String ): Seq[FallbackDependency] = - for { + for module <- allDependencies artifact <- module.explicitArtifacts uri <- artifact.url.toSeq - } yield { + yield val (module0, version) = moduleVersion(module, scalaVersion, scalaBinaryVersion) FallbackDependency(module0, version, uri, module.isChanging) - } def project( projectID: ModuleID, @@ -204,7 +195,7 @@ object FromSbt { scalaVersion: String, scalaBinaryVersion: String, projectPlatform: Option[String], - ): Project = { + ): Project = val deps = allDependencies.flatMap( dependencies(_, scalaVersion, scalaBinaryVersion, projectPlatform = projectPlatform) @@ -240,5 +231,5 @@ object FromSbt { Nil, Info("", "", Nil, Nil, None) ) - } -} + end project +end FromSbt diff --git a/lm-coursier/src/main/scala/lmcoursier/Inputs.scala b/lm-coursier/src/main/scala/lmcoursier/Inputs.scala index f4ae809f9..d7c76e1bc 100644 --- a/lm-coursier/src/main/scala/lmcoursier/Inputs.scala +++ b/lm-coursier/src/main/scala/lmcoursier/Inputs.scala @@ -8,7 +8,7 @@ import sbt.util.Logger import scala.annotation.tailrec import scala.collection.mutable -object Inputs { +object Inputs: def ivyXmlMappings(mapping: String): Seq[(Configuration, Configuration)] = initialIvyXmlMappings(mapping).map { (from, to) => @@ -28,32 +28,27 @@ object Inputs { def coursierConfigurationsMap( configurations: Seq[sbt.librarymanagement.Configuration] - ): Map[Configuration, Set[Configuration]] = { + ): Map[Configuration, Set[Configuration]] = val configs0 = configExtendsSeq(configurations).toMap - def allExtends(c: Configuration) = { + def allExtends(c: Configuration) = // possibly bad complexity @tailrec - def helper(current: Set[Configuration]): Set[Configuration] = { + def helper(current: Set[Configuration]): Set[Configuration] = val newSet = current ++ current.flatMap(configs0.getOrElse(_, Nil)) - if ((newSet -- current).nonEmpty) - helper(newSet) - else - newSet - } + if (newSet -- current).nonEmpty then helper(newSet) + else newSet helper(Set(c)) - } configs0.map { case (config, _) => config -> allExtends(config) } - } def orderedConfigurations( configurations: Seq[(Configuration, Seq[Configuration])] - ): Seq[(Configuration, Seq[Configuration])] = { + ): Seq[(Configuration, Seq[Configuration])] = val map = configurations.toMap @@ -61,31 +56,25 @@ object Inputs { done: Set[Configuration], toAdd: List[Configuration] ): LazyList[(Configuration, Seq[Configuration])] = - toAdd match { + toAdd match case Nil => LazyList.empty case config :: rest => val extends0 = map.getOrElse(config, Nil) val missingExtends = extends0.filterNot(done) - if (missingExtends.isEmpty) - (config, extends0) #:: helper(done + config, rest) - else - helper(done, missingExtends.toList ::: toAdd) - } + if missingExtends.isEmpty then (config, extends0) #:: helper(done + config, rest) + else helper(done, missingExtends.toList ::: toAdd) helper(Set.empty, configurations.map(_._1).toList).toVector - } @deprecated("Now unused internally, to be removed in the future", "2.0.0-RC6-5") - def ivyGraphs(configurations: Map[Configuration, Seq[Configuration]]): Seq[Set[Configuration]] = { + def ivyGraphs(configurations: Map[Configuration, Seq[Configuration]]): Seq[Set[Configuration]] = // probably bad complexity, but that shouldn't matter given the size of the graphs involved... - final class Wrapper(val set: mutable.HashSet[Configuration]) { - def ++=(other: Wrapper): this.type = { + final class Wrapper(val set: mutable.HashSet[Configuration]): + def ++=(other: Wrapper): this.type = set ++= other.set this - } - } val sets = new mutable.HashMap[Configuration, Wrapper] ++= configurations.map { (k, l) => @@ -95,53 +84,48 @@ object Inputs { k -> new Wrapper(s) } - for (k <- configurations.keys) { + for k <- configurations.keys do val s = sets(k) var foundNew = true - while (foundNew) { + while foundNew do foundNew = false - for (other <- s.set.toVector) { + for other <- s.set.toVector do val otherS = sets(other) - if (!otherS.eq(s)) { + if !otherS.eq(s) then s ++= otherS sets += other -> s foundNew = true - } - } - } - } sets.values.toVector.distinct.map(_.set.toSet) - } + end ivyGraphs def exclusionsSeq( excludeDeps: Seq[InclExclRule], sv: String, sbv: String, log: Logger - ): Seq[(Organization, ModuleName)] = { + ): Seq[(Organization, ModuleName)] = var anyNonSupportedExclusionRule = false val res = excludeDeps .flatMap { rule => - if (rule.artifact != "*" || rule.configurations.nonEmpty) { + if rule.artifact != "*" || rule.configurations.nonEmpty then log.warn(s"Unsupported exclusion rule $rule") anyNonSupportedExclusionRule = true Nil - } else { + else val name = CrossVersion(rule.crossVersion, sv, sbv) .fold(rule.name)(_(rule.name)) Seq((Organization(rule.organization), ModuleName(name))) - } } - if (anyNonSupportedExclusionRule) + if anyNonSupportedExclusionRule then log.warn("Only supported exclusion rule fields: organization, name") res - } + end exclusionsSeq def exclusions( excludeDeps: Seq[InclExclRule], @@ -153,5 +137,4 @@ object Inputs { def forceVersions(depOverrides: Seq[ModuleID], sv: String, sbv: String): Seq[(Module, String)] = depOverrides.map(FromSbt.moduleVersion(_, sv, sbv)) - -} +end Inputs diff --git a/lm-coursier/src/main/scala/lmcoursier/IvyXml.scala b/lm-coursier/src/main/scala/lmcoursier/IvyXml.scala index 02ba01912..ad7a7754b 100644 --- a/lm-coursier/src/main/scala/lmcoursier/IvyXml.scala +++ b/lm-coursier/src/main/scala/lmcoursier/IvyXml.scala @@ -4,7 +4,7 @@ import lmcoursier.definitions.Project import scala.xml.{ Node, PrefixedAttribute } -object IvyXml { +object IvyXml: @deprecated("Use the override accepting 3 arguments", "2.0.0-RC6-6") def apply( @@ -17,7 +17,7 @@ object IvyXml { currentProject: Project, exclusions: Seq[(String, String)], overrides: Seq[(String, String, String)] - ): String = { + ): String = // Important: width = Int.MaxValue, so that no tag gets truncated. // In particular, that prevents things like to be split to @@ -29,14 +29,13 @@ object IvyXml { """""" + '\n' + printer.format(content(currentProject, exclusions, overrides)) - } // These are required for publish to be fine, later on. private def content( project: Project, exclusions: Seq[(String, String)], overrides: Seq[(String, String, String)] - ): Node = { + ): Node = val props = project.module.attributes.toSeq ++ project.properties val infoAttrs = props.foldLeft[xml.MetaData](xml.Null) { case (acc, (k, v)) => @@ -63,12 +62,10 @@ object IvyXml { } % infoAttrs val confElems = project.configurations.toVector.collect { (name, extends0) => - val visibility = if (project.privateConfigs.contains(name)) "private" else "public" + val visibility = if project.privateConfigs.contains(name) then "private" else "public" val n = - if (extends0.nonEmpty) - n % .attributes - else - n + if extends0.nonEmpty then n % .attributes + else n } val publications = project.publications @@ -79,10 +76,8 @@ object IvyXml { configs.map(_.value).mkString(",") } /> - if (pub.classifier.value.nonEmpty) - n % .attributes - else - n + if pub.classifier.value.nonEmpty then n % .attributes + else n } val dependencyElems = project.dependencies.toVector.map { (conf, dep) => @@ -120,6 +115,5 @@ object IvyXml { {publicationElems} {dependencyElems}{excludeElems}{overrideElems} - } - -} + end content +end IvyXml diff --git a/lm-coursier/src/main/scala/lmcoursier/definitions/CacheLogger.scala b/lm-coursier/src/main/scala/lmcoursier/definitions/CacheLogger.scala index e64c9ae07..1b954b6ab 100644 --- a/lm-coursier/src/main/scala/lmcoursier/definitions/CacheLogger.scala +++ b/lm-coursier/src/main/scala/lmcoursier/definitions/CacheLogger.scala @@ -1,6 +1,6 @@ package lmcoursier.definitions -abstract class CacheLogger { +abstract class CacheLogger: def foundLocally(url: String): Unit = {} def downloadingArtifact(url: String): Unit = {} @@ -30,9 +30,8 @@ abstract class CacheLogger { // sizeHint: estimated # of artifacts to be downloaded (doesn't include side stuff like checksums) def init(sizeHint: Option[Int] = None): Unit = {} def stop(): Unit = {} -} +end CacheLogger -object CacheLogger { +object CacheLogger: def nop: CacheLogger = new CacheLogger {} -} diff --git a/lm-coursier/src/main/scala/lmcoursier/definitions/CachePolicy.scala b/lm-coursier/src/main/scala/lmcoursier/definitions/CachePolicy.scala index 482c323a8..fdf00c2cf 100644 --- a/lm-coursier/src/main/scala/lmcoursier/definitions/CachePolicy.scala +++ b/lm-coursier/src/main/scala/lmcoursier/definitions/CachePolicy.scala @@ -2,7 +2,7 @@ package lmcoursier.definitions sealed abstract class CachePolicy extends Serializable -object CachePolicy { +object CachePolicy: /* NOTE: the following comments are copied from coursier.cache.CachePolicy for the benefit of users within an IDE that reads the javadocs. Please keep in sync from the original ADT. */ @@ -69,4 +69,4 @@ object CachePolicy { * Erases files already in cache. */ case object ForceDownload extends CachePolicy -} +end CachePolicy diff --git a/lm-coursier/src/main/scala/lmcoursier/definitions/FromCoursier.scala b/lm-coursier/src/main/scala/lmcoursier/definitions/FromCoursier.scala index fb92ac7d2..be999c41e 100644 --- a/lm-coursier/src/main/scala/lmcoursier/definitions/FromCoursier.scala +++ b/lm-coursier/src/main/scala/lmcoursier/definitions/FromCoursier.scala @@ -2,10 +2,10 @@ package lmcoursier.definitions // TODO Make private[lmcoursier] // private[coursier] -object FromCoursier { +object FromCoursier: def cachePolicy(r: coursier.cache.CachePolicy): CachePolicy = - (r: @unchecked) match { + (r: @unchecked) match case coursier.cache.CachePolicy.LocalOnly => CachePolicy.LocalOnly case coursier.cache.CachePolicy.LocalOnlyIfValid => CachePolicy.LocalOnlyIfValid case coursier.cache.CachePolicy.LocalUpdateChanging => CachePolicy.LocalUpdateChanging @@ -14,5 +14,3 @@ object FromCoursier { case coursier.cache.CachePolicy.Update => CachePolicy.Update case coursier.cache.CachePolicy.FetchMissing => CachePolicy.FetchMissing case coursier.cache.CachePolicy.ForceDownload => CachePolicy.ForceDownload - } -} diff --git a/lm-coursier/src/main/scala/lmcoursier/definitions/Reconciliation.scala b/lm-coursier/src/main/scala/lmcoursier/definitions/Reconciliation.scala index 31ff20a43..089428296 100644 --- a/lm-coursier/src/main/scala/lmcoursier/definitions/Reconciliation.scala +++ b/lm-coursier/src/main/scala/lmcoursier/definitions/Reconciliation.scala @@ -2,18 +2,16 @@ package lmcoursier.definitions sealed abstract class Reconciliation extends Serializable -object Reconciliation { +object Reconciliation: case object Default extends Reconciliation case object Relaxed extends Reconciliation case object Strict extends Reconciliation case object SemVer extends Reconciliation def apply(input: String): Option[Reconciliation] = - input match { + input match case "default" => Some(Default) case "relaxed" => Some(Relaxed) case "strict" => Some(Strict) case "semver" => Some(SemVer) case _ => None - } -} diff --git a/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala b/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala index 92b02eaa6..80e0f6de1 100644 --- a/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala +++ b/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala @@ -8,7 +8,7 @@ import scala.annotation.nowarn // TODO Make private[lmcoursier] // private[coursier] -object ToCoursier { +object ToCoursier: def configuration(configuration: Configuration): coursier.core.Configuration = coursier.core.Configuration(configuration.value) @@ -63,12 +63,11 @@ object ToCoursier { @nowarn def reconciliation(r: Reconciliation): coursier.core.Reconciliation = - r match { + r match case Reconciliation.Default => coursier.core.Reconciliation.Default case Reconciliation.Relaxed => coursier.core.Reconciliation.Relaxed case Reconciliation.Strict => coursier.core.Reconciliation.Strict case Reconciliation.SemVer => coursier.core.Reconciliation.SemVer - } @nowarn def reconciliation( @@ -162,7 +161,7 @@ object ToCoursier { ) def credentials(credentials: Credentials): coursier.credentials.Credentials = - credentials match { + credentials match case d: DirectCredentials => coursier.credentials .DirectCredentials() @@ -177,10 +176,9 @@ object ToCoursier { coursier.credentials .FileCredentials(f.path) .withOptional(f.optional) - } def cacheLogger(logger: CacheLogger): coursier.cache.CacheLogger = - new coursier.cache.CacheLogger { + new coursier.cache.CacheLogger: override def foundLocally(url: String): Unit = logger.foundLocally(url) override def downloadingArtifact(url: String): Unit = @@ -214,7 +212,6 @@ object ToCoursier { logger.init(sizeHint) override def stop(): Unit = logger.stop() - } def strict(strict: Strict): coursier.params.rule.Strict = coursier.params.rule @@ -234,7 +231,7 @@ object ToCoursier { .withSemVer(strict.semVer) def cachePolicy(r: CachePolicy): coursier.cache.CachePolicy = - r match { + r match case CachePolicy.LocalOnly => coursier.cache.CachePolicy.LocalOnly case CachePolicy.LocalOnlyIfValid => coursier.cache.CachePolicy.LocalOnlyIfValid case CachePolicy.LocalUpdateChanging => coursier.cache.CachePolicy.LocalUpdateChanging @@ -243,5 +240,4 @@ object ToCoursier { case CachePolicy.Update => coursier.cache.CachePolicy.Update case CachePolicy.FetchMissing => coursier.cache.CachePolicy.FetchMissing case CachePolicy.ForceDownload => coursier.cache.CachePolicy.ForceDownload - } -} +end ToCoursier diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/ArtifactsRun.scala b/lm-coursier/src/main/scala/lmcoursier/internal/ArtifactsRun.scala index 9dd4e0db4..302b2c8a6 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/ArtifactsRun.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/ArtifactsRun.scala @@ -7,34 +7,32 @@ import coursier.core.Type import sbt.util.Logger // private[lmcoursier] -object ArtifactsRun { +object ArtifactsRun: def apply( params: ArtifactsParams, verbosityLevel: Int, log: Logger - ): Either[coursier.error.FetchError, Artifacts.Result] = { + ): Either[coursier.error.FetchError, Artifacts.Result] = val printOptionalMessage = verbosityLevel >= 0 && verbosityLevel <= 1 val artifactInitialMessage = - if (verbosityLevel >= 0) + if verbosityLevel >= 0 then s"Fetching artifacts of ${params.projectName}" + - (if (params.sbtClassifiers) " (sbt classifiers)" else "") - else - "" + (if params.sbtClassifiers then " (sbt classifiers)" else "") + else "" val coursierLogger = params.loggerOpt.getOrElse { RefreshLogger.create( - if (RefreshLogger.defaultFallbackMode) - new FallbackRefreshDisplay() + if RefreshLogger.defaultFallbackMode then new FallbackRefreshDisplay() else ProgressBarRefreshDisplay.create( - if (printOptionalMessage) log.info(artifactInitialMessage), - if (printOptionalMessage || verbosityLevel >= 2) + if printOptionalMessage then log.info(artifactInitialMessage), + if printOptionalMessage || verbosityLevel >= 2 then log.info( s"Fetched artifacts of ${params.projectName}" + - (if (params.sbtClassifiers) " (sbt classifiers)" else "") + (if params.sbtClassifiers then " (sbt classifiers)" else "") ) ) ) @@ -48,7 +46,7 @@ object ArtifactsRun { ) { result(params, coursierLogger) } - } + end apply private def result( params: ArtifactsParams, @@ -61,20 +59,16 @@ object ArtifactsRun { .withClassifiers(params.classifiers.getOrElse(Nil).toSet) .withClasspathOrder(params.classpathOrder) .addExtraArtifacts { l => - if (params.includeSignatures) - l.flatMap(_._3.extra.get("sig").toSeq) - else - Nil + if params.includeSignatures then l.flatMap(_._3.extra.get("sig").toSeq) + else Nil } .addTransformArtifacts { artifacts => - if (params.missingOk) + if params.missingOk then artifacts.map { (dependency, publication, artifact) => (dependency, publication, artifact.withOptional(true)) } - else - artifacts + else artifacts } .withCache(params.cache.withLogger(coursierLogger)) .eitherResult() - -} +end ArtifactsRun diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/BuildClock.scala b/lm-coursier/src/main/scala/lmcoursier/internal/BuildClock.scala index 119c7a8ab..eab2cadac 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/BuildClock.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/BuildClock.scala @@ -4,14 +4,14 @@ import coursier.core.{ Configuration, Dependency, Repository } import java.security.MessageDigest import scala.collection.immutable.Seq -object BuildClock { +object BuildClock: def compute( dependencies: Seq[(Configuration, Dependency)], repositories: Seq[Repository], scalaVersion: Option[String], params: ResolutionParams - ): String = { + ): String = val digest = MessageDigest.getInstance("SHA-1") dependencies @@ -50,7 +50,7 @@ object BuildClock { val hashBytes = digest.digest() hashBytes.map("%02x".format(_)).mkString - } + end compute def matches( lockFileData: LockFileData, @@ -58,8 +58,7 @@ object BuildClock { repositories: Seq[Repository], scalaVersion: Option[String], params: ResolutionParams - ): Boolean = { + ): Boolean = val currentClock = compute(dependencies, repositories, scalaVersion, params) lockFileData.buildClock == currentClock - } -} +end BuildClock diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/CacheUrlConversion.scala b/lm-coursier/src/main/scala/lmcoursier/internal/CacheUrlConversion.scala index 24f490f3f..84defd057 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/CacheUrlConversion.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/CacheUrlConversion.scala @@ -10,7 +10,7 @@ package lmcoursier.internal import java.io.File -object CacheUrlConversion { +object CacheUrlConversion: final val FileUrlPrefix = "file:" final val UnconvertiblePrefix = "${CSR_CACHE}" @@ -18,38 +18,32 @@ object CacheUrlConversion { private def normalizePathForComparison(path: String): String = path.replace('\\', '/') - private def normalizedFilePath(fileUrl: String): String = { + private def normalizedFilePath(fileUrl: String): String = val afterPrefix = fileUrl.stripPrefix(FileUrlPrefix).replaceFirst("^/+", "/") val withForwardSlash = normalizePathForComparison(afterPrefix) - if ( - withForwardSlash.length >= 3 && withForwardSlash + if withForwardSlash.length >= 3 && withForwardSlash .charAt(0) == '/' && withForwardSlash.charAt(2) == ':' - ) - withForwardSlash.substring(1) - else - withForwardSlash - } + then withForwardSlash.substring(1) + else withForwardSlash - def cacheFileToOriginalUrl(fileUrl: String, cacheDir: File): String = { - if (!fileUrl.startsWith(FileUrlPrefix)) { - fileUrl - } else { + def cacheFileToOriginalUrl(fileUrl: String, cacheDir: File): String = + if !fileUrl.startsWith(FileUrlPrefix) then fileUrl + else val filePath = normalizedFilePath(fileUrl) val cachePaths = Seq( cacheDir.getAbsolutePath, cacheDir.getCanonicalPath ).distinct.map(p => - normalizePathForComparison(if (p.endsWith("/") || p.endsWith("\\")) p else p + "/") + normalizePathForComparison(if p.endsWith("/") || p.endsWith("\\") then p else p + "/") ) - def extractHttpUrl(relativePath: String): Option[String] = { + def extractHttpUrl(relativePath: String): Option[String] = val protocolSepIndex = relativePath.indexOf('/') - if (protocolSepIndex > 0) { + if protocolSepIndex > 0 then val protocol = relativePath.substring(0, protocolSepIndex) val rest = relativePath.substring(protocolSepIndex + 1) Some(s"$protocol://$rest") - } else None - } + else None cachePaths .collectFirst { @@ -59,9 +53,7 @@ object CacheUrlConversion { } .flatten .getOrElse(s"$UnconvertiblePrefix$filePath") - } - } def isPortableUrl(url: String): Boolean = !url.startsWith(FileUrlPrefix) && !url.contains(UnconvertiblePrefix) -} +end CacheUrlConversion diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/CoursierModuleDescriptor.scala b/lm-coursier/src/main/scala/lmcoursier/internal/CoursierModuleDescriptor.scala index bd74d63d7..b5dad8c01 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/CoursierModuleDescriptor.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/CoursierModuleDescriptor.scala @@ -6,7 +6,7 @@ import sbt.librarymanagement.* private[lmcoursier] final case class CoursierModuleDescriptor( descriptor: ModuleDescriptorConfiguration, conf: CoursierConfiguration -) extends ModuleDescriptor { +) extends ModuleDescriptor: def directDependencies: Vector[ModuleID] = descriptor.dependencies @@ -21,4 +21,3 @@ private[lmcoursier] final case class CoursierModuleDescriptor( // Exclude log/logger fields — they contain Logger instances with // non-deterministic hashCodes that would break update caching. conf.withLog(None).withLogger(None).## -} diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/InterProjectRepository.scala b/lm-coursier/src/main/scala/lmcoursier/internal/InterProjectRepository.scala index 17348540c..064a9864b 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/InterProjectRepository.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/InterProjectRepository.scala @@ -4,7 +4,7 @@ import coursier.core.* import coursier.util.{ EitherT, Monad } // private[coursier] -final case class InterProjectRepository(projects: Seq[Project]) extends Repository { +final case class InterProjectRepository(projects: Seq[Project]) extends Repository: private val map = projects .map(proj => @@ -20,7 +20,7 @@ final case class InterProjectRepository(projects: Seq[Project]) extends Reposito fetch: Repository.Fetch[F] )(using F: Monad[F] - ): EitherT[F, String, (ArtifactSource, Project)] = { + ): EitherT[F, String, (ArtifactSource, Project)] = val res = map .get((module, version)) @@ -28,7 +28,6 @@ final case class InterProjectRepository(projects: Seq[Project]) extends Reposito .toRight("Not found") EitherT(F.point(res)) - } override def artifacts( dependency: Dependency, @@ -36,4 +35,4 @@ final case class InterProjectRepository(projects: Seq[Project]) extends Reposito overrideClassifiers: Option[Seq[Classifier]] ) = Nil -} +end InterProjectRepository diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/Lock.scala b/lm-coursier/src/main/scala/lmcoursier/internal/Lock.scala index 6e3f789a2..56486a2ff 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/Lock.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/Lock.scala @@ -1,6 +1,6 @@ package lmcoursier.internal -private[lmcoursier] object Lock { +private[lmcoursier] object Lock: private val lock = new Object /* The lock guards coursier's interactive progress bar (ProgressBarRefreshDisplay), the only thing @@ -13,6 +13,5 @@ private[lmcoursier] object Lock { /* Progress bars require us to only work on one module at the time. Without those we can go faster */ def maybeSynchronized[T](needsLock: Boolean)(f: => T): T = - if (needsLock) lock.synchronized(f) + if needsLock then lock.synchronized(f) else f -} diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/LockFile.scala b/lm-coursier/src/main/scala/lmcoursier/internal/LockFile.scala index 920c824af..969312149 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/LockFile.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/LockFile.scala @@ -5,41 +5,35 @@ import java.nio.file.Files import sjsonnew.support.scalajson.unsafe.{ Converter, Parser, PrettyPrinter } import scala.util.{ Try, Success, Failure } -object LockFile { +object LockFile: import LockFileFormats.given val defaultLockFileName = "deps.lock" - def read(lockFile: File): Either[String, LockFileData] = { - if (!lockFile.exists()) { - Left(s"Lock file does not exist: ${lockFile.getAbsolutePath}") - } else { + def read(lockFile: File): Either[String, LockFileData] = + if !lockFile.exists() then Left(s"Lock file does not exist: ${lockFile.getAbsolutePath}") + else Try { val content = Files.readString(lockFile.toPath) val json = Parser.parseFromString(content).get Converter.fromJson[LockFileData](json).get - } match { + } match case Success(data) => Right(data) case Failure(ex) => Left(s"Failed to parse lock file: ${ex.getMessage}") - } - } - } - def write(lockFile: File, data: LockFileData): Either[String, Unit] = { + def write(lockFile: File, data: LockFileData): Either[String, Unit] = Try { val json = Converter.toJson(data).get val content = PrettyPrinter(json) lockFile.getParentFile.mkdirs() Files.writeString(lockFile.toPath, content) - } match { + } match case Success(_) => Right(()) case Failure(ex) => Left(s"Failed to write lock file: ${ex.getMessage}") - } - } def getLockFile(baseDirectory: File): File = new File(baseDirectory, defaultLockFileName) def exists(baseDirectory: File): Boolean = getLockFile(baseDirectory).exists() -} +end LockFile diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/LockFileConstants.scala b/lm-coursier/src/main/scala/lmcoursier/internal/LockFileConstants.scala index e29012ff4..71dc2069b 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/LockFileConstants.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/LockFileConstants.scala @@ -1,5 +1,4 @@ package lmcoursier.internal -object LockFileConstants { +object LockFileConstants: val currentVersion = "1.0" -} diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/LockFileFormats.scala b/lm-coursier/src/main/scala/lmcoursier/internal/LockFileFormats.scala index 424726e75..ac0e54cb8 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/LockFileFormats.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/LockFileFormats.scala @@ -2,10 +2,11 @@ package lmcoursier.internal import sjsonnew.* -trait ArtifactLockFormats { self: sjsonnew.BasicJsonProtocol => - given ArtifactLockFormat: JsonFormat[ArtifactLock] = new JsonFormat[ArtifactLock] { +trait ArtifactLockFormats: + self: sjsonnew.BasicJsonProtocol => + given ArtifactLockFormat: JsonFormat[ArtifactLock] = new JsonFormat[ArtifactLock]: override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): ArtifactLock = - jsOpt match { + jsOpt match case Some(js) => unbuilder.beginObject(js) val url = unbuilder.readField[String]("url") @@ -16,23 +17,21 @@ trait ArtifactLockFormats { self: sjsonnew.BasicJsonProtocol => ArtifactLock(url, classifier, extension, tpe) case None => deserializationError("Expected JsObject but found None") - } - override def write[J](obj: ArtifactLock, builder: Builder[J]): Unit = { + override def write[J](obj: ArtifactLock, builder: Builder[J]): Unit = builder.beginObject() builder.addField("url", obj.url) builder.addField("classifier", obj.classifier) builder.addField("extension", obj.extension) builder.addField("tpe", obj.tpe) builder.endObject() - } - } -} +end ArtifactLockFormats -trait DependencyLockFormats { self: sjsonnew.BasicJsonProtocol & ArtifactLockFormats => - given DependencyLockFormat: JsonFormat[DependencyLock] = new JsonFormat[DependencyLock] { +trait DependencyLockFormats: + self: sjsonnew.BasicJsonProtocol & ArtifactLockFormats => + given DependencyLockFormat: JsonFormat[DependencyLock] = new JsonFormat[DependencyLock]: override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): DependencyLock = - jsOpt match { + jsOpt match case Some(js) => unbuilder.beginObject(js) val organization = unbuilder.readField[String]("organization") @@ -56,9 +55,8 @@ trait DependencyLockFormats { self: sjsonnew.BasicJsonProtocol & ArtifactLockFor ) case None => deserializationError("Expected JsObject but found None") - } - override def write[J](obj: DependencyLock, builder: Builder[J]): Unit = { + override def write[J](obj: DependencyLock, builder: Builder[J]): Unit = builder.beginObject() builder.addField("organization", obj.organization) builder.addField("name", obj.name) @@ -69,15 +67,13 @@ trait DependencyLockFormats { self: sjsonnew.BasicJsonProtocol & ArtifactLockFor builder.addField("transitives", obj.transitives) builder.addField("artifacts", obj.artifacts) builder.endObject() - } - } -} +end DependencyLockFormats -trait ConfigurationLockFormats { +trait ConfigurationLockFormats: self: sjsonnew.BasicJsonProtocol & ArtifactLockFormats & DependencyLockFormats => - given ConfigurationLockFormat: JsonFormat[ConfigurationLock] = new JsonFormat[ConfigurationLock] { + given ConfigurationLockFormat: JsonFormat[ConfigurationLock] = new JsonFormat[ConfigurationLock]: override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): ConfigurationLock = - jsOpt match { + jsOpt match case Some(js) => unbuilder.beginObject(js) val name = unbuilder.readField[String]("name") @@ -86,21 +82,18 @@ trait ConfigurationLockFormats { ConfigurationLock(name, dependencies) case None => deserializationError("Expected JsObject but found None") - } - override def write[J](obj: ConfigurationLock, builder: Builder[J]): Unit = { + override def write[J](obj: ConfigurationLock, builder: Builder[J]): Unit = builder.beginObject() builder.addField("name", obj.name) builder.addField("dependencies", obj.dependencies) builder.endObject() - } - } -} -trait LockFileMetadataFormats { self: sjsonnew.BasicJsonProtocol => - given LockFileMetadataFormat: JsonFormat[LockFileMetadata] = new JsonFormat[LockFileMetadata] { +trait LockFileMetadataFormats: + self: sjsonnew.BasicJsonProtocol => + given LockFileMetadataFormat: JsonFormat[LockFileMetadata] = new JsonFormat[LockFileMetadata]: override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): LockFileMetadata = - jsOpt match { + jsOpt match case Some(js) => unbuilder.beginObject(js) val sbtVersion = unbuilder.readField[String]("sbtVersion") @@ -109,23 +102,19 @@ trait LockFileMetadataFormats { self: sjsonnew.BasicJsonProtocol => LockFileMetadata(sbtVersion, scalaVersion) case None => deserializationError("Expected JsObject but found None") - } - override def write[J](obj: LockFileMetadata, builder: Builder[J]): Unit = { + override def write[J](obj: LockFileMetadata, builder: Builder[J]): Unit = builder.beginObject() builder.addField("sbtVersion", obj.sbtVersion) builder.addField("scalaVersion", obj.scalaVersion) builder.endObject() - } - } -} -trait LockFileDataFormats { +trait LockFileDataFormats: self: sjsonnew.BasicJsonProtocol & ArtifactLockFormats & DependencyLockFormats & ConfigurationLockFormats & LockFileMetadataFormats => - given LockFileDataFormat: JsonFormat[LockFileData] = new JsonFormat[LockFileData] { + given LockFileDataFormat: JsonFormat[LockFileData] = new JsonFormat[LockFileData]: override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): LockFileData = - jsOpt match { + jsOpt match case Some(js) => unbuilder.beginObject(js) val version = unbuilder.readField[String]("version") @@ -136,18 +125,15 @@ trait LockFileDataFormats { LockFileData(version, buildClock, configurations, metadata) case None => deserializationError("Expected JsObject but found None") - } - override def write[J](obj: LockFileData, builder: Builder[J]): Unit = { + override def write[J](obj: LockFileData, builder: Builder[J]): Unit = builder.beginObject() builder.addField("version", obj.version) builder.addField("buildClock", obj.buildClock) builder.addField("configurations", obj.configurations) builder.addField("metadata", obj.metadata) builder.endObject() - } - } -} +end LockFileDataFormats object LockFileFormats extends sjsonnew.BasicJsonProtocol diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/LockedArtifactsRun.scala b/lm-coursier/src/main/scala/lmcoursier/internal/LockedArtifactsRun.scala index 0ee16818a..9674e4e8d 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/LockedArtifactsRun.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/LockedArtifactsRun.scala @@ -10,25 +10,23 @@ import java.io.File import scala.concurrent.{ Await, ExecutionContext } import scala.concurrent.duration.Duration -object LockedArtifactsRun { +object LockedArtifactsRun: def fetchFromLockFile( lockFileData: LockFileData, cache: FileCache[coursier.util.Task], verbosityLevel: Int, log: Logger - ): Either[String, Seq[(Dependency, Publication, Artifact, Option[File])]] = { + ): Either[String, Seq[(Dependency, Publication, Artifact, Option[File])]] = given ExecutionContext = cache.ec - if (verbosityLevel >= 1) { - log.info("Fetching artifacts from lock file") - } + if verbosityLevel >= 1 then log.info("Fetching artifacts from lock file") - val artifactsToFetch = for { + val artifactsToFetch = for configLock <- lockFileData.configurations depLock <- configLock.dependencies artLock <- depLock.artifacts - } yield { + yield val module = coursier.Module( coursier.Organization(depLock.organization), coursier.ModuleName(depLock.name), @@ -61,36 +59,30 @@ object LockedArtifactsRun { ) (dependency, publication, artifact) - } val fetchTasks = artifactsToFetch.map { case (dep, pub, art) => cache.file(art).run.map { result => - result match { + result match case Left(err) => - if (verbosityLevel >= 2) { - log.debug(s"Failed to fetch ${art.url}: ${err.describe}") - } + if verbosityLevel >= 2 then log.debug(s"Failed to fetch ${art.url}: ${err.describe}") (dep, pub, art, None: Option[File]) case Right(file) => (dep, pub, art, Some(file)) - } } } - try { + try val results = fetchTasks.map { task => Await.result(task.future(), Duration.Inf) } val failures = results.filter(_._4.isEmpty) - if (failures.nonEmpty && verbosityLevel >= 1) { + if failures.nonEmpty && verbosityLevel >= 1 then log.warn(s"Failed to fetch ${failures.size} artifacts from lock file") - } Right(results) - } catch { + catch case ex: Exception => Left(s"Failed to fetch artifacts: ${ex.getMessage}") - } - } -} + end fetchFromLockFile +end LockedArtifactsRun diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionParams.scala b/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionParams.scala index 784d58e31..fc1f490a1 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionParams.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionParams.scala @@ -33,23 +33,20 @@ final case class ResolutionParams( missingOk: Boolean, retry: (FiniteDuration, Int), boms: Seq[BomDependency] = Nil -) { +): - lazy val allConfigExtends: Map[Configuration, Set[Configuration]] = { + lazy val allConfigExtends: Map[Configuration, Set[Configuration]] = val map = new mutable.HashMap[Configuration, Set[Configuration]] - for ((config, extends0) <- orderedConfigs) { + for (config, extends0) <- orderedConfigs do val allExtends = extends0.iterator // the else of the getOrElse shouldn't be hit (because of the ordering of the configurations) .foldLeft(Set(config))((acc, ext) => acc ++ map.getOrElse(ext, Set(ext))) map += config -> allExtends - } map.toMap - } val fallbackDependenciesRepositories = - if (fallbackDependencies.isEmpty) - Nil - else { + if fallbackDependencies.isEmpty then Nil + else val map = fallbackDependencies.map { dep => (ToCoursier.module(dep.module), dep.version) -> ((dep.uri, dep.changing)) }.toMap @@ -57,9 +54,8 @@ final case class ResolutionParams( Seq( TemporaryInMemoryRepository(map, cache) ) - } - lazy val resolutionKey = { + lazy val resolutionKey = val cleanCache = cache .withPool(null) .withLogger(null) @@ -78,9 +74,8 @@ final case class ResolutionParams( cleanCache, missingOk ) - } - override lazy val hashCode = this match { + override lazy val hashCode = this match case ResolutionParams( a1, a2, @@ -102,16 +97,14 @@ final case class ResolutionParams( a18 ) => (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18).## - } // ResolutionParams.unapply(this).get.## - -} +end ResolutionParams // private[coursier] -object ResolutionParams { +object ResolutionParams: - def defaultIvyProperties(ivyHomeOpt: Option[File]): Map[String, String] = { + def defaultIvyProperties(ivyHomeOpt: Option[File]): Map[String, String] = val ivyHome = sys.props .get("ivy.home") @@ -127,7 +120,5 @@ object ResolutionParams { "ivy.home" -> ivyHome, "sbt.ivy.home" -> sbtIvyHome ) ++ sys.props - } val defaultRetry: (FiniteDuration, Int) = (1.seconds, 3) -} diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala b/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala index 49a5c81d7..203e1a9d4 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala @@ -17,7 +17,7 @@ import scala.concurrent.duration.FiniteDuration import scala.collection.mutable // private[coursier] -object ResolutionRun { +object ResolutionRun: private def resolution( params: ResolutionParams, @@ -25,7 +25,7 @@ object ResolutionRun { log: Logger, configs: Set[Configuration], startingResolutionOpt: Option[Resolution] - ): Either[coursier.error.ResolutionError, Resolution] = { + ): Either[coursier.error.ResolutionError, Resolution] = val isScalaToolConfig = configs(Configuration("scala-tool")) // Ref coursier/coursier#1340 coursier/coursier#1442 @@ -36,7 +36,7 @@ object ResolutionRun { def isSandboxConfig: Boolean = isScalaToolConfig val repositories = - params.internalRepositories.drop(if (isSandboxConfig) 1 else 0) ++ + params.internalRepositories.drop(if isSandboxConfig then 1 else 0) ++ params.mainRepositories ++ params.fallbackDependenciesRepositories @@ -56,20 +56,17 @@ object ResolutionRun { val initialMessage = Seq( - if (verbosityLevel >= 0) + if verbosityLevel >= 0 then Seq( - s"Updating ${params.projectName}" + (if (params.sbtClassifiers) " (sbt classifiers)" + s"Updating ${params.projectName}" + (if params.sbtClassifiers then " (sbt classifiers)" else "") ) - else - Nil, - if (verbosityLevel >= 2) - depsRepr(params.dependencies).map(depRepr => s" $depRepr") - else - Nil + else Nil, + if verbosityLevel >= 2 then depsRepr(params.dependencies).map(depRepr => s" $depRepr") + else Nil ).flatten.mkString("\n") - if (verbosityLevel >= 2) { + if verbosityLevel >= 2 then val repoReprs = repositories.map { case r: IvyRepository => s"ivy:${r.pattern}" @@ -86,13 +83,11 @@ object ResolutionRun { "Repositories:\n" + repoReprs.map(" " + _).mkString("\n") ) - } - if (verbosityLevel >= 2) - log.info(initialMessage) + if verbosityLevel >= 2 then log.info(initialMessage) @nowarn - val resolveTask: Resolve[Task] = { + val resolveTask: Resolve[Task] = Resolve() // re-using various caches from a resolution of a configuration we extend .withInitialResolution(startingResolutionOpt) @@ -107,7 +102,7 @@ object ResolutionRun { .withResolutionParams( params.params .addForceVersion( - (if (isSandboxConfig) Nil + (if isSandboxConfig then Nil else params.interProjectDependencies.map(_.moduleVersion))* ) .withForceScalaVersion(params.autoScalaLibOpt.nonEmpty) @@ -120,22 +115,20 @@ object ResolutionRun { .withLogger( params.loggerOpt.getOrElse { RefreshLogger.create( - if (RefreshLogger.defaultFallbackMode) - new FallbackRefreshDisplay() + if RefreshLogger.defaultFallbackMode then new FallbackRefreshDisplay() else ProgressBarRefreshDisplay.create( - if (printOptionalMessage) log.info(initialMessage), - if (printOptionalMessage || verbosityLevel >= 2) + if printOptionalMessage then log.info(initialMessage), + if printOptionalMessage || verbosityLevel >= 2 then log.info(s"Resolved ${params.projectName} dependencies") ) ) } ) ) - } val (period, maxAttempts) = params.retry - val finalResult: Either[ResolutionError, Resolution] = { + val finalResult: Either[ResolutionError, Resolution] = def retry( attempt: Int, @@ -144,18 +137,16 @@ object ResolutionRun { resolveTask.io.attempt .flatMap { case Left(e: ResolutionError) => - if (isTransientResolutionError(e)) - if (attempt + 1 >= maxAttempts) { + if isTransientResolutionError(e) then + if attempt + 1 >= maxAttempts then log.error(s"Failed, maximum iterations ($maxAttempts) reached") Task.point(Left(e)) - } else { + else log.warn(s"Attempt ${attempt + 1} failed: $e") Task.completeAfter(retryScheduler, waitOnError).flatMap { _ => retry(attempt + 1, waitOnError * 2) } - } - else - Task.point(Left(e)) + else Task.point(Left(e)) case Left(ex) => Task.fail(ex) case Right(value) => @@ -163,28 +154,25 @@ object ResolutionRun { } retry(0, period).unsafeRun()(using resolveTask.cache.ec) - } + end finalResult - finalResult match { + finalResult match case Left(err) if params.missingOk => Right(err.resolution) case others => others - } - } + end resolution @nowarn def resolutions( params: ResolutionParams, verbosityLevel: Int, log: Logger - ): Either[coursier.error.ResolutionError, Map[Configuration, Resolution]] = { + ): Either[coursier.error.ResolutionError, Map[Configuration, Resolution]] = // TODO Warn about possible duplicated modules from source repositories? - if (verbosityLevel >= 2) { + if verbosityLevel >= 2 then log.info("InterProjectRepository") - for (p <- params.interProjectDependencies) - log.info(s" ${p.module}:${p.version}") - } + for p <- params.interProjectDependencies do log.info(s" ${p.module}:${p.version}") SbtCoursierCache.default.resolutionOpt(params.resolutionKey).map(Right(_)).getOrElse { val resOrError = @@ -198,27 +186,24 @@ object ResolutionRun { val either = params.orderedConfigs.foldLeft[Either[coursier.error.ResolutionError, Unit]]( Right(()) ) { case (acc, (config, extends0)) => - for { + for _ <- acc - initRes = { + initRes = val it = extends0.iterator.flatMap(map.get(_).iterator) - if (it.hasNext) Some(it.next()) + if it.hasNext then Some(it.next()) else None - } allExtends = params.allConfigExtends.getOrElse(config, Set.empty) res <- resolution(params, verbosityLevel, log, allExtends, initRes) - } yield { + yield map += config -> res () - } } either.map(_ => map.toMap) } - for (res <- resOrError) - SbtCoursierCache.default.putResolution(params.resolutionKey, res) + for res <- resOrError do SbtCoursierCache.default.putResolution(params.resolutionKey, res) resOrError } - } + end resolutions def resolutionsWithLockFile( params: ResolutionParams, @@ -226,10 +211,9 @@ object ResolutionRun { log: Logger, lockFileOpt: Option[java.io.File], scalaVersion: Option[String] - ): Either[coursier.error.ResolutionError, (Map[Configuration, Resolution], Boolean)] = { + ): Either[coursier.error.ResolutionError, (Map[Configuration, Resolution], Boolean)] = resolutionsWithLockFileData(params, verbosityLevel, log, lockFileOpt, scalaVersion) .map { case (res, lockDataOpt) => (res, lockDataOpt.isDefined) } - } def resolutionsWithLockFileData( params: ResolutionParams, @@ -240,42 +224,32 @@ object ResolutionRun { ): Either[ coursier.error.ResolutionError, (Map[Configuration, Resolution], Option[LockFileData]) - ] = { + ] = lockFileOpt .flatMap { lockFile => - LockFile.read(lockFile) match { + LockFile.read(lockFile) match case Right(lockData) => - if ( - BuildClock.matches( + if BuildClock.matches( lockData, params.dependencies, params.mainRepositories, scalaVersion, params ) - ) { - if (verbosityLevel >= 1) { - log.info(s"Using lock file: ${lockFile.getAbsolutePath}") - } + then + if verbosityLevel >= 1 then log.info(s"Using lock file: ${lockFile.getAbsolutePath}") val reconstructed = ResolutionSerializer.reconstructResolutions(lockData, params) Some(Right((reconstructed, Some(lockData)))) - } else { - if (verbosityLevel >= 1) { - log.info(s"Lock file outdated, performing resolution") - } + else + if verbosityLevel >= 1 then log.info(s"Lock file outdated, performing resolution") None - } case Left(err) => - if (verbosityLevel >= 2) { - log.debug(s"Lock file error: $err") - } + if verbosityLevel >= 2 then log.debug(s"Lock file error: $err") None - } } .getOrElse { resolutions(params, verbosityLevel, log).map(res => (res, None)) } - } private lazy val retryScheduler = ThreadUtil.fixedScheduledThreadPool(1) @@ -290,4 +264,4 @@ object ResolutionRun { private def isServerError(err: CantDownloadModule): Boolean = err.perRepositoryErrors.exists(_.contains("Server returned HTTP response code: 5")) -} +end ResolutionRun diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionSerializer.scala b/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionSerializer.scala index c48efdc93..48702528a 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionSerializer.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionSerializer.scala @@ -7,7 +7,7 @@ import coursier.version.VersionConstraint import scala.annotation.nowarn import scala.collection.immutable.Seq -object ResolutionSerializer { +object ResolutionSerializer: def extractLockFileData( resolutions: Map[Configuration, Resolution], @@ -42,6 +42,7 @@ object ResolutionSerializer { configurations = configurations, metadata = metadata ) + end extractLockFileData private def extractDependencies( resolution: Resolution, @@ -69,7 +70,7 @@ object ResolutionSerializer { artifactMap.getOrElse(dep, Seq.empty).map { case (url, classifier, ext) => ArtifactLock( url = url, - classifier = if (classifier.isEmpty) None else Some(classifier), + classifier = if classifier.isEmpty then None else Some(classifier), extension = ext, tpe = dep.attributes.`type`.value ) @@ -80,15 +81,16 @@ object ResolutionSerializer { name = dep.module.name.value, version = resolvedVersion, configuration = dep.variantSelector.repr, - classifier = dep.attributes.classifier.value match { + classifier = dep.attributes.classifier.value match case "" => None case c => Some(c) - }, + , tpe = dep.attributes.`type`.value, transitives = transitives.toVector, artifacts = artifacts.toVector ) traverseEither(xs) + end extractDependencies private def traverseEither[A1, A2](xs: Vector[Either[A1, A2]]): Either[A1, Vector[A2]] = xs.foldLeft(Right(Vector.empty): Either[A1, Vector[A2]]) { @@ -100,18 +102,17 @@ object ResolutionSerializer { def reconstructResolutions( lockFileData: LockFileData, params: ResolutionParams - ): Map[Configuration, Resolution] = { + ): Map[Configuration, Resolution] = lockFileData.configurations.map { configLock => val config = Configuration(configLock.name) val resolution = reconstructResolution(configLock, params) config -> resolution }.toMap - } private def reconstructResolution( configLock: ConfigurationLock, params: ResolutionParams - ): Resolution = { + ): Resolution = val forceVersions: Map[Module, String] = configLock.dependencies.map { depLock => val module = Module( coursier.Organization(depLock.organization), @@ -168,24 +169,22 @@ object ResolutionSerializer { .withDependencies(dependencies) .withForceVersions(forceVersions ++ params.params.forceVersion) .withProjectCache(projectCache): @nowarn) - } + end reconstructResolution - private object EmptyArtifactSource extends ArtifactSource { + private object EmptyArtifactSource extends ArtifactSource: def artifacts( dependency: Dependency, project: Project, overrideClassifiers: Option[scala.collection.immutable.Seq[coursier.core.Classifier]] ): scala.collection.immutable.Seq[(coursier.core.Publication, coursier.util.Artifact)] = scala.collection.immutable.Seq.empty - } def getLockedArtifacts( lockFileData: LockFileData - ): Map[(String, String, String), Seq[ArtifactLock]] = { + ): Map[(String, String, String), Seq[ArtifactLock]] = lockFileData.configurations.flatMap { configLock => configLock.dependencies.map { depLock => (depLock.organization, depLock.name, depLock.version) -> depLock.artifacts } }.toMap - } -} +end ResolutionSerializer diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/Resolvers.scala b/lm-coursier/src/main/scala/lmcoursier/internal/Resolvers.scala index 40a3a734e..479965fad 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/Resolvers.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/Resolvers.scala @@ -12,10 +12,10 @@ import sbt.util.Logger import scala.jdk.CollectionConverters.* -object Resolvers { +object Resolvers: private def mavenCompatibleBaseOpt(patterns: Patterns): Option[String] = - if (patterns.isMavenCompatible) { + if patterns.isMavenCompatible then // input : /Users/user/custom/repo/[organisation]/[module](_[scalaVersion])(_[sbtVersion])/[revision]/[artifact]-[revision](-[classifier]).[ext] // output : /Users/user/custom/repo/ def basePattern(pattern: String): String = pattern.takeWhile(c => c != '[' && c != '(') @@ -23,12 +23,9 @@ object Resolvers { val baseIvyPattern = basePattern(patterns.ivyPatterns.head) val baseArtifactPattern = basePattern(patterns.artifactPatterns.head) - if (baseIvyPattern == baseArtifactPattern) - Some(baseIvyPattern) - else - None - } else - None + if baseIvyPattern == baseArtifactPattern then Some(baseIvyPattern) + else None + else None private def mavenRepositoryOpt( root: String, @@ -36,16 +33,16 @@ object Resolvers { authentication: Option[Authentication], classLoaders: Seq[ClassLoader] ): Option[SbtMavenRepository] = - try { + try CacheUrl.url(root, classLoaders) // ensure root is a URL whose protocol can be handled here - val root0 = if (root.endsWith("/")) root else root + "/" + val root0 = if root.endsWith("/") then root else root + "/" Some( SbtMavenRepository( root0, authentication = authentication ) ) - } catch { + catch case e: MalformedURLException => log.warn( "Error parsing Maven repository base " + @@ -55,19 +52,15 @@ object Resolvers { ) None - } // this handles whitespace in path - private def pathToUriString(path: String): String = { + private def pathToUriString(path: String): String = val stopAtIdx = path.indexWhere(c => c == '[' || c == '$' || c == '(') - if (stopAtIdx > 0) { + if stopAtIdx > 0 then val (pathPart, patternPart) = path.splitAt(stopAtIdx) Paths.get(pathPart).toUri.toASCIIString + patternPart - } else if (stopAtIdx == 0) - "file://" + path - else - Paths.get(path).toUri.toASCIIString - } + else if stopAtIdx == 0 then "file://" + path + else Paths.get(path).toUri.toASCIIString def repository( resolver: Resolver, @@ -76,7 +69,7 @@ object Resolvers { authentication: Option[Authentication], classLoaders: Seq[ClassLoader] ): Option[Repository] = - resolver match { + resolver match case r: sbt.librarymanagement.MavenRepository => mavenRepositoryOpt(r.root, log, authentication, classLoaders) @@ -85,7 +78,7 @@ object Resolvers { r.patterns.artifactPatterns.lengthCompare(1) == 0 => val mavenCompatibleBaseOpt0 = mavenCompatibleBaseOpt(r.patterns) - mavenCompatibleBaseOpt0 match { + mavenCompatibleBaseOpt0 match case None => val repo = IvyRepository.parse( pathToUriString(r.patterns.artifactPatterns.head), @@ -94,14 +87,13 @@ object Resolvers { properties = ivyProperties, dropInfoAttributes = true, authentication = authentication - ) match { + ) match case Left(err) => sys.error( s"Cannot parse Ivy patterns ${r.patterns.artifactPatterns.head} and ${r.patterns.ivyPatterns.head}: $err" ) case Right(repo) => repo - } Some(repo) @@ -112,7 +104,7 @@ object Resolvers { authentication, classLoaders ) - } + end match case r: URLRepository if patternMatchGuard(r.patterns) => parseMavenCompatResolver(log, ivyProperties, authentication, r.patterns, classLoaders) @@ -127,15 +119,14 @@ object Resolvers { case other => log.warn(s"Unrecognized repository ${other.name}, ignoring it") None - } - private object IBiblioRepository { + private object IBiblioRepository: // Use reflection to avoid a compile-time dependency on lm-ivy / Apache Ivy. // At runtime the class will be present on the classpath via the main module. private val ibiblioClass: Option[Class[?]] = try Some(Class.forName("org.apache.ivy.plugins.resolver.IBiblioResolver")) - catch { case _: ClassNotFoundException => None } + catch case _: ClassNotFoundException => None private def stringVector(v: java.util.List[?]): Vector[String] = Option(v).map(_.asScala.toVector).getOrElse(Vector.empty).collect { case s: String => @@ -159,7 +150,7 @@ object Resolvers { ) def unapply(r: Resolver): Option[Patterns] = - r match { + r match case raw: RawRepository => ibiblioClass match case Some(cls) if cls.isInstance(raw.resolver) => @@ -169,8 +160,7 @@ object Resolvers { None case _ => None - } - } + end IBiblioRepository private def patternMatchGuard(patterns: Patterns): Boolean = patterns.ivyPatterns.lengthCompare(1) == 0 && @@ -182,10 +172,10 @@ object Resolvers { authentication: Option[Authentication], patterns: Patterns, classLoaders: Seq[ClassLoader], - ): Option[Repository] = { + ): Option[Repository] = val mavenCompatibleBaseOpt0 = mavenCompatibleBaseOpt(patterns) - mavenCompatibleBaseOpt0 match { + mavenCompatibleBaseOpt0 match case None => val repo = IvyRepository.parse( patterns.artifactPatterns.head, @@ -194,19 +184,18 @@ object Resolvers { properties = ivyProperties, dropInfoAttributes = true, authentication = authentication - ) match { + ) match case Left(err) => sys.error( s"Cannot parse Ivy patterns ${patterns.artifactPatterns.head} and ${patterns.ivyPatterns.head}: $err" ) case Right(repo) => repo - } Some(repo) case Some(mavenCompatibleBase) => mavenRepositoryOpt(mavenCompatibleBase, log, authentication, classLoaders) - } - } -} + end match + end parseMavenCompatResolver +end Resolvers diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/SbtBootJars.scala b/lm-coursier/src/main/scala/lmcoursier/internal/SbtBootJars.scala index d38c5f53e..9ca1c2f85 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/SbtBootJars.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/SbtBootJars.scala @@ -5,7 +5,7 @@ import java.io.File import coursier.core.{ Module, ModuleName, Organization } // private[coursier] -object SbtBootJars { +object SbtBootJars: def apply( scalaOrg: Organization, scalaVersion: String, @@ -18,4 +18,3 @@ object SbtBootJars { (mod, scalaVersion) -> jar }.toMap -} diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/SbtCoursierCache.scala b/lm-coursier/src/main/scala/lmcoursier/internal/SbtCoursierCache.scala index eb9877ae1..735c9380c 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/SbtCoursierCache.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/SbtCoursierCache.scala @@ -8,7 +8,7 @@ import coursier.cache.FileCache import coursier.util.Task // private[coursier] -class SbtCoursierCache { +class SbtCoursierCache: import SbtCoursierCache.* @@ -28,18 +28,16 @@ class SbtCoursierCache { def putReport(key: ReportKey, report: UpdateReport): Unit = reportsCache.put(key, report) - def clear(): Unit = { + def clear(): Unit = resolutionsCache.clear() reportsCache.clear() - } def isEmpty: Boolean = resolutionsCache.isEmpty && reportsCache.isEmpty - -} +end SbtCoursierCache // private[coursier] -object SbtCoursierCache { +object SbtCoursierCache: final case class ResolutionKey( dependencies: Seq[(Configuration, Dependency)], @@ -61,5 +59,4 @@ object SbtCoursierCache { // private[coursier] val default = new SbtCoursierCache - -} +end SbtCoursierCache diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala b/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala index d1c4ee260..319c13b68 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala @@ -23,9 +23,9 @@ import scala.annotation.nowarn import scala.annotation.tailrec -private[internal] object SbtUpdateReport { +private[internal] object SbtUpdateReport: - private def caching[K, V](f: K => V): K => V = { + private def caching[K, V](f: K => V): K => V = val cache = Collections.synchronizedMap(new WeakHashMap[K, V]) @@ -37,7 +37,6 @@ private[internal] object SbtUpdateReport { val concurrentValueOpt = Option(cache.putIfAbsent(key, value)) concurrentValueOpt.getOrElse(value) } - } private def infoProperties(project: Project): Seq[(String, String)] = project.properties.filter(_._1.startsWith("info.")) @@ -185,20 +184,19 @@ private[internal] object SbtUpdateReport { classpathOrder: Boolean, missingOk: Boolean, classLoaders: Seq[ClassLoader] - ): Vector[ModuleReport] = { + ): Vector[ModuleReport] = - val deps = classifiersOpt match { + val deps = classifiersOpt match case Some(classifiers) => res.dependencyArtifacts(Some(classifiers), classpathOrder) case None => res.dependencyArtifacts(None, classpathOrder) - } - val depArtifacts1 = fullArtifactsOpt match { + val depArtifacts1 = fullArtifactsOpt match case Some(map) => deps.map { (d, p, a) => val d0 = d.withAttributes(d.attributes.withClassifier(p.classifier)) - val a0 = if (missingOk) a.withOptional(true) else a + val a0 = if missingOk then a.withOptional(true) else a val f = map.get((d0, Right(p), a0)).flatten (d, p, a0, f) // not d0 } @@ -206,18 +204,17 @@ private[internal] object SbtUpdateReport { deps.map { (d, p, a) => (d, p, a, None) } - } val depArtifacts0 = depArtifacts1.filter { case (_, pub, _, _) => pub.attributes != Attributes(Type.pom, Classifier.empty) } val depArtifacts = - if (includeSignatures) { + if includeSignatures then val notFound = depArtifacts0.filter(!_._3.extra.contains("sig")) - if (notFound.isEmpty) + if notFound.isEmpty then depArtifacts0.flatMap { (dep, pub, a, f) => val sigPub = pub // not too sure about those @@ -226,15 +223,12 @@ private[internal] object SbtUpdateReport { Seq((dep, pub, a, f)) ++ a.extra.get("sig").toSeq.map((dep, sigPub, _, None)) } - else { - for ((_, _, a, _) <- notFound) - log.error(s"No signature found for ${a.url}") + else + for (_, _, a, _) <- notFound do log.error(s"No signature found for ${a.url}") sys.error(s"${notFound.length} signature(s) not found") - } - } else - depArtifacts0 + else depArtifacts0 - val groupedDepArtifacts = { + val groupedDepArtifacts = val m = depArtifacts.groupBy(_._1) val fromLib = depArtifacts.map(_._1).distinct.map { dep => dep -> m.getOrElse(dep, Nil).map { case (_, pub, a, f) => (pub, a, f) } @@ -243,7 +237,6 @@ private[internal] object SbtUpdateReport { .withFilter(p => p.module != thisModule._1) .map(p => Dependency(p.module, p.version) -> Nil) fromLib ++ fromInterProj - } val versions = (Vector( Dependency(thisModule._1, thisModule._2) @@ -266,11 +259,10 @@ private[internal] object SbtUpdateReport { val projectCache = res.projectCache def lookupProject(mv: coursier.core.Resolution.ModuleVersion): Option[Project] = - projectCache.get(mv) match { + projectCache.get(mv) match case Some((_, p)) => Some(p) case _ => interProjectDependencies.find(p => mv == (p.module, p.version)) - } /** * Assemble the project info, resolving inherited fields. Only implements resolving @@ -279,18 +271,14 @@ private[internal] object SbtUpdateReport { * @see https://maven.apache.org/pom.html#Inheritance * @see https://maven.apache.org/ref/3-LATEST/maven-model-builder/index.html#Inheritance_Assembly */ - def assemble(project: Project): Project = { + def assemble(project: Project): Project = @tailrec - def licenseInfo(project: Project): Seq[Info.License] = { - if (project.info.licenseInfo.nonEmpty || project.parent.isEmpty) - project.info.licenseInfo - else - licenseInfo(lookupProject(project.parent.get).get) - } + def licenseInfo(project: Project): Seq[Info.License] = + if project.info.licenseInfo.nonEmpty || project.parent.isEmpty then project.info.licenseInfo + else licenseInfo(lookupProject(project.parent.get).get) project.withInfo( project.info.withLicenseInfo(licenseInfo(project)) ) - } val m = Dependency(thisModule._1, "") val directReverseDependencies = res.rootDependencies.toSet @@ -299,14 +287,13 @@ private[internal] object SbtUpdateReport { .map(dep => dep -> Vector(m)) .toMap - val reverseDependencies = { + val reverseDependencies = val transitiveReverseDependencies = res.reverseDependencies.toVector .map { (k, v) => clean(k) -> v.map(clean) } .groupMapReduce(_._1)((_, deps) => deps)(_ ++ _) (transitiveReverseDependencies.toVector ++ directReverseDependencies.toVector) .groupMapReduce(_._1)((_, deps) => deps)(_ ++ _) - } groupedDepArtifacts.toVector.map { (dep, artifacts) => val proj = lookupProject(dep.moduleVersion).get @@ -318,7 +305,7 @@ private[internal] object SbtUpdateReport { .flatMap { dependee0 => val version = versions(dependee0.module) val dependee = dependee0.withVersion(version) - lookupProject(dependee.moduleVersion) match { + lookupProject(dependee.moduleVersion) match case Some(dependeeProj) => Vector( ( @@ -332,11 +319,10 @@ private[internal] object SbtUpdateReport { ) case _ => Vector.empty - } } val filesOpt = artifacts.map { (pub, a, fileOpt) => val fileOpt0 = fileOpt.orElse { - if (fullArtifactsOpt.isEmpty) + if fullArtifactsOpt.isEmpty then artifactFileOpt(proj.module, proj.version, pub.attributes, a) else None } @@ -352,7 +338,7 @@ private[internal] object SbtUpdateReport { ) ) } - } + end moduleReports @nowarn def apply( @@ -371,7 +357,7 @@ private[internal] object SbtUpdateReport { missingOk: Boolean, forceVersions: Map[Module, String], classLoaders: Seq[ClassLoader], - ): UpdateReport = { + ): UpdateReport = val configReports = resolutions.map { (config, subRes) => val reports = moduleReports( @@ -392,7 +378,7 @@ private[internal] object SbtUpdateReport { // three times per conflict. val subProjectCache = subRes.projectCache - val reports0 = subRes.rootDependencies match { + val reports0 = subRes.rootDependencies match case Seq(dep) if subProjectCache.contains(dep.moduleVersion) => // quick hack ensuring the module for the only root dependency // appears first in the update report, see https://github.com/coursier/coursier/issues/650 @@ -405,7 +391,6 @@ private[internal] object SbtUpdateReport { } main ++ other case _ => reports - } val mainReportDetails = reports0.map { rep => OrganizationArtifactReport(rep.module.organization, rep.module.name, Vector(rep)) @@ -414,7 +399,7 @@ private[internal] object SbtUpdateReport { def conflicts: Seq[coursier.graph.Conflict] = try coursier.graph.Conflict(subRes) catch case e: Throwable if missingOk => Nil - val evicted = for { + val evicted = for c <- conflicts // ideally, forceVersions should be taken into account by coursier.core.Resolution itself, when // it computes transitive dependencies. It only handles forced versions at a global level for now, @@ -425,10 +410,10 @@ private[internal] object SbtUpdateReport { .get((c.module, c.wantedVersion)) .orElse(subProjectCache.get((c.module, c.version))) (_, proj) <- projOpt.toSeq - } yield { + yield val dep = Dependency(c.module, c.wantedVersion) val dependee = Dependency(c.dependeeModule, c.dependeeVersion) - val dependeeProj = subProjectCache.get((c.dependeeModule, c.dependeeVersion)) match { + val dependeeProj = subProjectCache.get((c.dependeeModule, c.dependeeVersion)) match case Some((_, p)) => ProjectInfo( p.version, @@ -438,14 +423,12 @@ private[internal] object SbtUpdateReport { case None => // should not happen ProjectInfo(c.dependeeVersion, Vector.empty, Vector.empty) - } val rep = moduleReport( (dep, Seq((dependee, dependeeProj)), proj.withVersion(c.wantedVersion), Nil, classLoaders) ) .withEvicted(true) .withEvictedData(Some("version selection")) // ??? put latest-revision like sbt/ivy here? OrganizationArtifactReport(c.module.organization.value, c.module.name.value, Vector(rep)) - } val details = (mainReportDetails ++ evicted) .groupBy(r => (r.organization, r.name)) @@ -468,11 +451,11 @@ private[internal] object SbtUpdateReport { UpdateStats(-1L, -1L, -1L, cached = false, stamp = Some(System.currentTimeMillis().toString)), Map.empty ) - } + end apply private case class ProjectInfo( version: String, configs: Vector[ConfigRef], properties: Seq[(String, String)] ) -} +end SbtUpdateReport diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/TemporaryInMemoryRepository.scala b/lm-coursier/src/main/scala/lmcoursier/internal/TemporaryInMemoryRepository.scala index 5d1e71f58..4fe13420e 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/TemporaryInMemoryRepository.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/TemporaryInMemoryRepository.scala @@ -10,17 +10,15 @@ import coursier.util.{ Artifact, EitherT, Monad } import scala.annotation.nowarn import scala.util.Try -object TemporaryInMemoryRepository { +object TemporaryInMemoryRepository: - def closeConn(conn: URLConnection): Unit = { + def closeConn(conn: URLConnection): Unit = Try(conn.getInputStream).toOption.filter(_ != null).foreach(_.close()) - conn match { + conn match case conn0: HttpURLConnection => Try(conn0.getErrorStream).toOption.filter(_ != null).foreach(_.close()) conn0.disconnect() case _ => - } - } def exists( uri: URI, @@ -32,29 +30,26 @@ object TemporaryInMemoryRepository { uri: URI, localArtifactsShouldBeCached: Boolean, cacheOpt: Option[FileCache[Nothing]] - ): Boolean = { + ): Boolean = // Sometimes HEAD attempts fail even though standard GETs are fine. // E.g. https://github.com/NetLogo/NetLogo/releases/download/5.3.1/NetLogo.jar // returning 403s. Hence the second attempt below. - val protocolSpecificAttemptOpt = { + val protocolSpecificAttemptOpt = - def ifFile: Option[Boolean] = { - if (localArtifactsShouldBeCached && !new File(uri).exists()) { + def ifFile: Option[Boolean] = + if localArtifactsShouldBeCached && !new File(uri).exists() then val cachePath = coursier.cache.CacheDefaults.location // 'file' here stands for the protocol (e.g. it's https instead for https:// URLs) Some(new File(cachePath, s"file/${uri.getPath}").exists()) - } else { - Some(new File(uri).exists()) // FIXME Escaping / de-escaping needed here? - } - } + else Some(new File(uri).exists()) // FIXME Escaping / de-escaping needed here? - def ifHttp: Option[Boolean] = { + def ifHttp: Option[Boolean] = // HEAD request attempt, adapted from http://stackoverflow.com/questions/22541629/android-how-can-i-make-an-http-head-request/22545275#22545275 var conn: URLConnection = null - try { + try conn = ConnectionBuilder(uri.toASCIIString) .withFollowHttpToHttpsRedirections( cacheOpt.fold(false)(_.followHttpToHttpsRedirections) @@ -71,40 +66,32 @@ object TemporaryInMemoryRepository { // iff this doesn't throw. conn.getInputStream.close() Some(true) - } catch { + catch case _: FileNotFoundException => Some(false) case _: IOException => None // error other than not found - } finally { - if (conn != null) - closeConn(conn) - } - } + finally if conn != null then closeConn(conn) + end try + end ifHttp - uri.getScheme match { + uri.getScheme match case "file" => ifFile case "http" | "https" => ifHttp case _ => None - } - } + end protocolSpecificAttemptOpt - def genericAttempt: Boolean = { + def genericAttempt: Boolean = var conn: URLConnection = null - try { + try conn = uri.toURL.openConnection() // NOT setting request type to HEAD here. conn.getInputStream.close() true - } catch { - case _: IOException => false - } finally { - if (conn != null) - closeConn(conn) - } - } + catch case _: IOException => false + finally if conn != null then closeConn(conn) protocolSpecificAttemptOpt .getOrElse(genericAttempt) - } + end exists def apply( fallbacks: Map[(Module, String), (URI, Boolean)] @@ -126,14 +113,13 @@ object TemporaryInMemoryRepository { localArtifactsShouldBeCached = cache.localArtifactsShouldBeCached, Some(cache.asInstanceOf[FileCache[Nothing]]) ) - -} +end TemporaryInMemoryRepository final class TemporaryInMemoryRepository private ( val fallbacks: Map[(Module, String), (URI, Boolean)], val localArtifactsShouldBeCached: Boolean, val cacheOpt: Option[FileCache[Nothing]] -) extends Repository { +) extends Repository: @nowarn def find[F[_]]( @@ -142,7 +128,7 @@ final class TemporaryInMemoryRepository private ( fetch: Repository.Fetch[F] )(using F: Monad[F] - ): EitherT[F, String, (ArtifactSource, Project)] = { + ): EitherT[F, String, (ArtifactSource, Project)] = def res = fallbacks .get((module, version)) @@ -151,12 +137,11 @@ final class TemporaryInMemoryRepository private ( val urlStr = uri.toURL.toExternalForm val idx = urlStr.lastIndexOf('/') - if (idx < 0 || urlStr.endsWith("/")) - Left(s"$uri doesn't point to a file") - else { + if idx < 0 || urlStr.endsWith("/") then Left(s"$uri doesn't point to a file") + else val (dirUrlStr, fileName) = urlStr.splitAt(idx + 1) - if (TemporaryInMemoryRepository.exists(uri, localArtifactsShouldBeCached, cacheOpt)) { + if TemporaryInMemoryRepository.exists(uri, localArtifactsShouldBeCached, cacheOpt) then val proj = Project( module, version, @@ -176,21 +161,21 @@ final class TemporaryInMemoryRepository private ( ) Right((this, proj)) - } else - Left(s"$fileName not found under $dirUrlStr") - } + else Left(s"$fileName not found under $dirUrlStr") + end if + end if } // EitherT(F.bind(F.point(()))(_ => F.point(res))) EitherT(F.map(F.point(()))(_ => res)) - } + end find @nowarn def artifacts( dependency: Dependency, project: Project, overrideClassifiers: Option[Seq[Classifier]] - ): Seq[(Publication, Artifact)] = { + ): Seq[(Publication, Artifact)] = fallbacks .get(dependency.moduleVersion) .toSeq @@ -205,6 +190,4 @@ final class TemporaryInMemoryRepository private ( ) (pub, Artifact(url0, Map.empty, Map.empty, changing, optional = false, None)) } - } - -} +end TemporaryInMemoryRepository diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala b/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala index cb3c1eb84..dd265e06a 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala @@ -23,30 +23,28 @@ final case class UpdateParams( classpathOrder: Boolean, missingOk: Boolean, classLoaders: Seq[ClassLoader] -) { +): def artifactFileOpt( module: Module, version: String, attributes: Attributes, artifact: Artifact - ): Option[File] = { + ): Option[File] = // Under some conditions, SBT puts the scala JARs of its own classpath // in the application classpath. Ensuring we return SBT's jars rather than // JARs from the coursier cache, so that a same JAR doesn't land twice in the // application classpath (once via SBT jars, once via coursier cache). val fromBootJars = - if (attributes.classifier.isEmpty && attributes.`type` == Type.jar) + if attributes.classifier.isEmpty && attributes.`type` == Type.jar then sbtBootJarOverrides.get((module, version)) - else - None + else None val artifact0 = - if (missingOk) artifact.withOptional(true) + if missingOk then artifact.withOptional(true) else artifact fromBootJars.orElse(artifacts.get(artifact0)) - } - -} + end artifactFileOpt +end UpdateParams diff --git a/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala b/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala index 643d08c2c..8ec259e76 100644 --- a/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala +++ b/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala @@ -9,7 +9,7 @@ import sbt.util.Logger import scala.annotation.nowarn // private[coursier] -object UpdateRun { +object UpdateRun: // Move back to coursier.util (in core module) after 1.0? @nowarn @@ -17,7 +17,7 @@ object UpdateRun { res: Map[Configuration, Resolution], depsByConfig: Map[Configuration, Seq[Dependency]], configs: Map[Configuration, Set[Configuration]] - ): Map[Configuration, Set[Dependency]] = { + ): Map[Configuration, Set[Dependency]] = val allDepsByConfig = depsByConfig.map { (config, deps) => config -> res(config).subset(deps).minDependencies @@ -32,7 +32,6 @@ object UpdateRun { } filteredAllDepsByConfig - } // Move back to coursier.util (in core module) after 1.0? @nowarn @@ -59,7 +58,7 @@ object UpdateRun { ): UpdateReport = Lock.maybeSynchronized(needsLock = !RefreshLogger.defaultFallbackMode) { val depsByConfig = grouped(params.dependencies) - if (verbosityLevel >= 2) { + if verbosityLevel >= 2 then val finalDeps = dependenciesWithConfig( params.res, depsByConfig, @@ -71,7 +70,6 @@ object UpdateRun { ) val repr = Print.dependenciesUnknownConfigs(finalDeps.toVector, projCache) log.info(repr.split('\n').map(" " + _).mkString("\n")) - } SbtUpdateReport( params.thisModule, @@ -92,5 +90,4 @@ object UpdateRun { private def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] = map.groupMap(_._1)((_, values) => values) - -} +end UpdateRun diff --git a/lm-coursier/src/main/scala/lmcoursier/syntax/package.scala b/lm-coursier/src/main/scala/lmcoursier/syntax/package.scala index d2b39dffb..97e4e7a25 100644 --- a/lm-coursier/src/main/scala/lmcoursier/syntax/package.scala +++ b/lm-coursier/src/main/scala/lmcoursier/syntax/package.scala @@ -9,8 +9,8 @@ import xsbti.Logger import scala.concurrent.duration.{ Duration, FiniteDuration } import java.io.File -package object syntax { - extension (value: CoursierConfiguration.type) { +package object syntax: + extension (value: CoursierConfiguration.type) @deprecated( "Legacy cache location support was dropped, this method does nothing.", "2.0.0-RC6-10" @@ -80,9 +80,9 @@ package object syntax { localArtifactsShouldBeCached = false, lockFile = None, ) - } + end extension - extension (value: CoursierConfiguration) { + extension (value: CoursierConfiguration) def withLog(log: Logger): CoursierConfiguration = value.withLog(Option(log)) def withSbtScalaOrganization(sbtScalaOrganization: String): CoursierConfiguration = @@ -120,9 +120,9 @@ package object syntax { def withRetry(retry: (FiniteDuration, Int)): CoursierConfiguration = value.withRetry(Some((retry._1, retry._2))) - } + end extension - extension (value: Publication) { + extension (value: Publication) def attributes: Attributes = Attributes(value.`type`, value.classifier) @@ -130,9 +130,8 @@ package object syntax { value .withType(attributes.`type`) .withClassifier(attributes.classifier) - } - extension (value: Dependency.type) { + extension (value: Dependency.type) def apply( module: Module, version: String, @@ -151,9 +150,8 @@ package object syntax { optional, transitive ) - } - extension (value: Dependency) { + extension (value: Dependency) def attributes: Attributes = value.publication.attributes def withAttributes(attributes: Attributes): Dependency = @@ -162,9 +160,8 @@ package object syntax { .withType(attributes.`type`) .withClassifier(attributes.classifier) ) - } - extension (value: ModuleMatchers.type) { + extension (value: ModuleMatchers.type) def all: ModuleMatchers = ModuleMatchers(Set.empty, Set.empty) def only(organization: String, moduleName: String): ModuleMatchers = @@ -175,21 +172,18 @@ package object syntax { ) def only(mod: Module): ModuleMatchers = ModuleMatchers(Set.empty, Set(mod), includeByDefault = false) - } - extension (value: Strict) { + extension (value: Strict) def addInclude(include: (String, String)*): Strict = value.withInclude(value.include ++ include) def addExclude(exclude: (String, String)*): Strict = value.withExclude(value.exclude ++ exclude) - } - extension (value: Authentication.type) { + extension (value: Authentication.type) def apply(headers: Seq[(String, String)]): Authentication = Authentication("", "").withHeaders(headers) - } - extension (value: DirectCredentials.type) { + extension (value: DirectCredentials.type) def apply(host: String, username: String, password: String, realm: String): DirectCredentials = DirectCredentials(host, username, password, Option(realm)) def apply( @@ -200,14 +194,12 @@ package object syntax { optional: Boolean ): DirectCredentials = DirectCredentials(host, username, password, Option(realm)) - } - extension (value: DirectCredentials) { + extension (value: DirectCredentials) def withRealm(realm: String): DirectCredentials = value.withRealm(Option(realm)) - } - extension (value: Credentials.type) { + extension (value: Credentials.type) def apply(): DirectCredentials = DirectCredentials() def apply(host: String, username: String, password: String): DirectCredentials = DirectCredentials(host, username, password) @@ -257,5 +249,5 @@ package object syntax { FileCredentials(f.getAbsolutePath) def apply(f: File, optional: Boolean): FileCredentials = FileCredentials(f.getAbsolutePath, optional) - } -} + end extension +end syntax diff --git a/lm-coursier/src/test/scala/lmcoursier/CoursierDependencyResolutionTests.scala b/lm-coursier/src/test/scala/lmcoursier/CoursierDependencyResolutionTests.scala index da164ffc3..582190c6a 100644 --- a/lm-coursier/src/test/scala/lmcoursier/CoursierDependencyResolutionTests.scala +++ b/lm-coursier/src/test/scala/lmcoursier/CoursierDependencyResolutionTests.scala @@ -5,16 +5,15 @@ import org.scalatest.propspec.AnyPropSpec import sbt.librarymanagement.* import sbt.util.Logger -class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers { +class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers: - private val logger: Logger = new Logger { + private val logger: Logger = new Logger: def log(level: sbt.util.Level.Value, message: => String): Unit = System.err.println(s"${level.id} $message") def success(message: => String): Unit = System.err.println(message) def trace(t: => Throwable): Unit = System.err.println(s"trace $t") - } private val conf211 = CoursierConfiguration().withAutoScalaLibrary(true).withScalaVersion(Some("2.11.12")) @@ -66,14 +65,13 @@ class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers { .withConfigurations(Vector(Configuration.of("Compile", "compile"))) val module = depRes.moduleDescriptor(desc) - depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger) match { + depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger) match case Left(x) => throw x.resolveException case Right(x) => x.allModules.collect { case m: ModuleID if m.organization == scalaModule212.organization && m.name == m.name => m.revision } should (contain(conf211.scalaVersion.get) and have length 1) // from config - } } property("get scalalib at local version, scalaModuleInfo:overrideScalaVersion") { @@ -86,7 +84,7 @@ class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers { .withScalaModuleInfo(scalaModuleInfo213.withOverrideScalaVersion(true)) val module = depRes.moduleDescriptor(desc) - depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger) match { + depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger) match case Left(x) => throw x.resolveException case Right(x) => x.allModules.collect { @@ -95,7 +93,6 @@ class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers { } should ( contain(scalaModuleInfo213.scalaFullVersion) and have length 1 ) // from autoScalaLib - } } property("get scalalib at local version, scalaModuleInfo:!overrideScalaVersion") { @@ -107,14 +104,13 @@ class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers { .withScalaModuleInfo(scalaModuleInfo213.withOverrideScalaVersion(false)) val module = depRes.moduleDescriptor(desc) - depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger) match { + depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger) match case Left(x) => throw x.resolveException case Right(x) => x.allModules.collect { case m: ModuleID if m.organization == scalaModule212.organization && m.name == m.name => m.revision } should (contain(scalaModule212.revision) and have length 1) // from dependency - } } property( @@ -129,7 +125,7 @@ class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers { .withScalaModuleInfo(scalaModuleInfo213.withOverrideScalaVersion(true)) val module = depRes.moduleDescriptor(desc) - depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger) match { + depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger) match case Left(x) => throw x.resolveException case Right(x) => x.allModules.collect { @@ -138,7 +134,6 @@ class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers { } should ( contain(scalaModuleInfo213.scalaFullVersion) and have length 1 ) // from autoScalaLib - } } property( @@ -153,14 +148,12 @@ class CoursierDependencyResolutionTests extends AnyPropSpec with Matchers { .withScalaModuleInfo(scalaModuleInfo213.withOverrideScalaVersion(false)) val module = depRes.moduleDescriptor(desc) - depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger) match { + depRes.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), logger) match case Left(x) => throw x.resolveException case Right(x) => x.allModules.collect { case m: ModuleID if m.organization == scalaModule212.organization && m.name == m.name => CrossVersion.binaryScalaVersion(m.revision) } should (contain("2.12") and have length 1) // from transitive dependency - } } - -} +end CoursierDependencyResolutionTests diff --git a/lm-coursier/src/test/scala/lmcoursier/EvictionErrorSpec.scala b/lm-coursier/src/test/scala/lmcoursier/EvictionErrorSpec.scala index 99e97d6a9..faab51a50 100644 --- a/lm-coursier/src/test/scala/lmcoursier/EvictionErrorSpec.scala +++ b/lm-coursier/src/test/scala/lmcoursier/EvictionErrorSpec.scala @@ -6,7 +6,7 @@ import sbt.librarymanagement.syntax.* import sbt.util.Level import TestKit.* -object EvictionErrorSpec extends verify.BasicTestSuite { +object EvictionErrorSpec extends verify.BasicTestSuite: // This is a specification to check the eviction errors import TestShowLines.* @@ -134,12 +134,11 @@ object EvictionErrorSpec extends verify.BasicTestSuite { val userDefinedSchemes: Map[(String, String), String] = Map(schemes flatMap { s => val organization = s.organization val versionScheme = s.revision - (s.crossVersion, sbvOpt) match { + (s.crossVersion, sbvOpt) match case (b: Binary, Some(sbv)) => List((s.organization, s"${s.name}${b.suffix}_$sbv") -> versionScheme) case _ => List((s.organization, s.name) -> versionScheme) - } }*) // The key should include the platform suffix "_sjs1" before the Scala version @@ -230,4 +229,4 @@ object EvictionErrorSpec extends verify.BasicTestSuite { filterImplicit = false, overrideScalaVersion = true ) -} +end EvictionErrorSpec diff --git a/lm-coursier/src/test/scala/lmcoursier/EvictionWarningSpec.scala b/lm-coursier/src/test/scala/lmcoursier/EvictionWarningSpec.scala index 8d39ad5cb..5413b2494 100644 --- a/lm-coursier/src/test/scala/lmcoursier/EvictionWarningSpec.scala +++ b/lm-coursier/src/test/scala/lmcoursier/EvictionWarningSpec.scala @@ -5,7 +5,7 @@ import sbt.internal.librarymanagement.cross.CrossVersionUtil import sbt.librarymanagement.syntax.* import TestKit.* -object EvictionWarningSpec extends verify.BasicTestSuite { +object EvictionWarningSpec extends verify.BasicTestSuite: // This is a specification to check the eviction warnings import TestShowLines.* @@ -392,4 +392,4 @@ object EvictionWarningSpec extends verify.BasicTestSuite { filterImplicit = false, overrideScalaVersion = true ) -} +end EvictionWarningSpec diff --git a/lm-coursier/src/test/scala/lmcoursier/FromSbtPlatformSpec.scala b/lm-coursier/src/test/scala/lmcoursier/FromSbtPlatformSpec.scala index f957a8770..dd6bdf79a 100644 --- a/lm-coursier/src/test/scala/lmcoursier/FromSbtPlatformSpec.scala +++ b/lm-coursier/src/test/scala/lmcoursier/FromSbtPlatformSpec.scala @@ -4,7 +4,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import sbt.librarymanagement.* -final class FromSbtPlatformSpec extends AnyPropSpec with Matchers { +final class FromSbtPlatformSpec extends AnyPropSpec with Matchers: property("explicit platform should not be overridden by project platform") { // Test case for issue #8665: Auto-injected Scala library with explicit .platform(Platform.jvm) @@ -124,4 +124,4 @@ final class FromSbtPlatformSpec extends AnyPropSpec with Matchers { module.name.value shouldBe "sniper_native0.5_3" version shouldBe "0.0.9-SNAPSHOT" } -} +end FromSbtPlatformSpec diff --git a/lm-coursier/src/test/scala/lmcoursier/InclExclSpec.scala b/lm-coursier/src/test/scala/lmcoursier/InclExclSpec.scala index d2ccadc35..8a98a5cd2 100644 --- a/lm-coursier/src/test/scala/lmcoursier/InclExclSpec.scala +++ b/lm-coursier/src/test/scala/lmcoursier/InclExclSpec.scala @@ -5,7 +5,7 @@ import sbt.librarymanagement.syntax.* import scala.annotation.nowarn import TestKit.* -object InclExclSpec extends verify.BasicTestSuite { +object InclExclSpec extends verify.BasicTestSuite: val scala210 = Some("2.10.4") test("it should exclude any version of lift-json via a new exclusion rule") { val toExclude = ExclusionRule("net.liftweb", "lift-json_2.10") @@ -81,12 +81,11 @@ object InclExclSpec extends verify.BasicTestSuite { .cross(CrossVersion.full) .excludeAll(toExclude) - def getUpdateReport(dep: ModuleID, scalaVersion: Option[String]): UpdateReport = { + def getUpdateReport(dep: ModuleID, scalaVersion: Option[String]): UpdateReport = val m = module(defaultModuleId, Vector(dep), scalaVersion) coursierUpdate(m) - } - def testLiftJsonIsMissing(report: UpdateReport): Unit = { + def testLiftJsonIsMissing(report: UpdateReport): Unit = assert( !report.allModules.exists(_.name.contains("lift-json")), "lift-json has not been excluded." @@ -95,9 +94,8 @@ object InclExclSpec extends verify.BasicTestSuite { !report.allModuleReports.exists(_.module.name.contains("lift-json")), "lift-json has not been excluded." ) - } - def testScalaLibraryIsMissing(report: UpdateReport): Unit = { + def testScalaLibraryIsMissing(report: UpdateReport): Unit = assert( !report.allModules.exists(_.name.contains("scala-library")), "scala-library has not been excluded." @@ -106,9 +104,8 @@ object InclExclSpec extends verify.BasicTestSuite { !report.allModuleReports.exists(_.module.name.contains("scala-library")), "scala-library has not been excluded." ) - } - def testScalahostIsMissing(report: UpdateReport): Unit = { + def testScalahostIsMissing(report: UpdateReport): Unit = assert( !report.allModules.exists(_.name.contains("scalahost")), "scalahost has not been excluded." @@ -117,5 +114,4 @@ object InclExclSpec extends verify.BasicTestSuite { !report.allModuleReports.exists(_.module.name.contains("scalahost")), "scalahost has not been excluded." ) - } -} +end InclExclSpec diff --git a/lm-coursier/src/test/scala/lmcoursier/IvyXmlTests.scala b/lm-coursier/src/test/scala/lmcoursier/IvyXmlTests.scala index 1261a93e3..d10b09d71 100644 --- a/lm-coursier/src/test/scala/lmcoursier/IvyXmlTests.scala +++ b/lm-coursier/src/test/scala/lmcoursier/IvyXmlTests.scala @@ -4,7 +4,7 @@ import lmcoursier.definitions.{ Configuration, Info, Module, ModuleName, Organiz import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec -class IvyXmlTests extends AnyPropSpec with Matchers { +class IvyXmlTests extends AnyPropSpec with Matchers: property("no truncation") { val project = Project( @@ -26,5 +26,4 @@ class IvyXmlTests extends AnyPropSpec with Matchers { assert(!content.contains("")) } - -} +end IvyXmlTests diff --git a/lm-coursier/src/test/scala/lmcoursier/LockFileSpec.scala b/lm-coursier/src/test/scala/lmcoursier/LockFileSpec.scala index ffd5bad9f..f38cfa487 100644 --- a/lm-coursier/src/test/scala/lmcoursier/LockFileSpec.scala +++ b/lm-coursier/src/test/scala/lmcoursier/LockFileSpec.scala @@ -5,7 +5,7 @@ import org.scalatest.funsuite.AnyFunSuite import java.io.File import sbt.io.IO -class LockFileSpec extends AnyFunSuite { +class LockFileSpec extends AnyFunSuite: test("LockFileData serialization round-trip") { val lockData = LockFileData( @@ -152,4 +152,4 @@ class LockFileSpec extends AnyFunSuite { assert(result == httpUrl) } } -} +end LockFileSpec diff --git a/lm-coursier/src/test/scala/lmcoursier/ResolutionSpec.scala b/lm-coursier/src/test/scala/lmcoursier/ResolutionSpec.scala index 3071e898d..570999570 100644 --- a/lm-coursier/src/test/scala/lmcoursier/ResolutionSpec.scala +++ b/lm-coursier/src/test/scala/lmcoursier/ResolutionSpec.scala @@ -11,7 +11,7 @@ import sbt.util.ShowLines.* // import sbt.librarymanagement.{ Resolver, UnresolvedWarningConfiguration, UpdateConfiguration } import sbt.librarymanagement.syntax.* -final class ResolutionSpec extends AnyPropSpec with Matchers { +final class ResolutionSpec extends AnyPropSpec with Matchers: lazy val log = ConsoleLogger() @@ -22,7 +22,7 @@ final class ResolutionSpec extends AnyPropSpec with Matchers { deps: Vector[ModuleID], scalaFullVersion: Option[String], overrideScalaVersion: Boolean = true - ): ModuleDescriptor = { + ): ModuleDescriptor = val scalaModuleInfo = scalaFullVersion map { fv => ScalaModuleInfo( scalaFullVersion = fv, @@ -39,7 +39,7 @@ final class ResolutionSpec extends AnyPropSpec with Matchers { .withConfigurations(configurations) .withScalaModuleInfo(scalaModuleInfo) lmEngine.moduleDescriptor(moduleSetting) - } + end module def resolvers = Vector( DefaultMavenRepository, @@ -52,11 +52,10 @@ final class ResolutionSpec extends AnyPropSpec with Matchers { private final val stubModule = "com.example" % "foo" % "0.1.0" % "compile" private def unresolvedWarningLines(module: ModuleDescriptor): Seq[String] = - lmEngine.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), log) match { + lmEngine.update(module, UpdateConfiguration(), UnresolvedWarningConfiguration(), log) match case Left(uw) => uw.lines case Right(report) => fail(s"Expected resolution to fail, but it succeeded with report: $report") - } private def assertContainsAll(lines: Seq[String], expected: Seq[String]): Unit = expected.foreach { line => @@ -337,23 +336,21 @@ final class ResolutionSpec extends AnyPropSpec with Matchers { resolution.toOption.get.configurations.find(_.configuration == Compile.toConfigRef).get val tikaCaller = "org.apache.tika:tika-core:3.3.1" - def callerOf(name: String): ModuleReport = { + def callerOf(name: String): ModuleReport = val reports = compileConfig.modules.filter(_.module.name == name) withClue(s"$name not found in: ${compileConfig.modules.map(_.module.name).mkString(", ")}") { reports should have size 1 } reports.head - } def coord(m: sbt.librarymanagement.ModuleID): String = s"${m.organization}:${m.name}:${m.revision}" - for (transitive <- Seq("commons-io", "slf4j-api")) { + for transitive <- Seq("commons-io", "slf4j-api") do val report = callerOf(transitive) val callerCoords = report.callers.map(c => coord(c.caller)).toSet withClue(s"$transitive callers: $callerCoords") { callerCoords should contain(tikaCaller) } - } } -} +end ResolutionSpec diff --git a/lm-coursier/src/test/scala/lmcoursier/TestKit.scala b/lm-coursier/src/test/scala/lmcoursier/TestKit.scala index 172b0f54c..26eb9c300 100644 --- a/lm-coursier/src/test/scala/lmcoursier/TestKit.scala +++ b/lm-coursier/src/test/scala/lmcoursier/TestKit.scala @@ -75,6 +75,7 @@ object TestKit: .withConfigurations(configurations) .withScalaModuleInfo(scalaModuleInfo) lmEngine.moduleDescriptor(moduleSetting) + end module def defaultModuleId: ModuleID = ModuleID("com.example", "foo", "0.1.0").withConfigurations(Some("compile")) diff --git a/lm-coursier/src/test/scala/lmcoursier/internal/ResolutionRunSpec.scala b/lm-coursier/src/test/scala/lmcoursier/internal/ResolutionRunSpec.scala index a3ad3f4e0..acf81b377 100644 --- a/lm-coursier/src/test/scala/lmcoursier/internal/ResolutionRunSpec.scala +++ b/lm-coursier/src/test/scala/lmcoursier/internal/ResolutionRunSpec.scala @@ -37,3 +37,4 @@ class ResolutionRunSpec extends AnyFunSuite with Matchers: "Server returned HTTP response code: 404 for URL: https://repo.example.com/org/mod/1.0/mod-1.0.pom" ) ResolutionRun.isTransientResolutionError(err) shouldBe false +end ResolutionRunSpec diff --git a/main-actions/src/main/scala/sbt/Console.scala b/main-actions/src/main/scala/sbt/Console.scala index 35378dad0..df1d74434 100644 --- a/main-actions/src/main/scala/sbt/Console.scala +++ b/main-actions/src/main/scala/sbt/Console.scala @@ -18,7 +18,7 @@ import xsbti.compile.{ Compilers, Inputs } import scala.util.Try -final class Console(compiler: AnalyzingCompiler) { +final class Console(compiler: AnalyzingCompiler): /** Starts an interactive scala interpreter session with the given classpath. */ def apply(classpath: Seq[File], log: Logger): Try[Unit] = @@ -47,18 +47,17 @@ final class Console(compiler: AnalyzingCompiler) { options: Seq[String], initialCommands: String, cleanupCommands: String - )(loader: Option[ClassLoader], bindings: Seq[(String, Any)])(using log: Logger): Try[Unit] = { + )(loader: Option[ClassLoader], bindings: Seq[(String, Any)])(using log: Logger): Try[Unit] = apply(classpath, options, initialCommands, cleanupCommands, Terminal.get)(loader, bindings) - } def apply( classpath: Seq[File], options: Seq[String], initialCommands: String, cleanupCommands: String, terminal: Terminal - )(loader: Option[ClassLoader], bindings: Seq[(String, Any)])(using log: Logger): Try[Unit] = { + )(loader: Option[ClassLoader], bindings: Seq[(String, Any)])(using log: Logger): Try[Unit] = def console0(): Unit = - try { + try compiler.console( classpath map { x => PlainVirtualFile(x.toPath) @@ -72,26 +71,26 @@ final class Console(compiler: AnalyzingCompiler) { loader, bindings ) - } catch { case _: InterruptedException | _: ClosedChannelException => } + catch + case _: InterruptedException | _: ClosedChannelException => val previous = sys.props.get("scala.color").getOrElse("auto") val jline3term = sbt.internal.util.JLine3(terminal) - try { - sys.props("scala.color") = if (terminal.isColorEnabled) "true" else "false" + try + sys.props("scala.color") = if terminal.isColorEnabled then "true" else "false" terminal.withRawOutput { jline.TerminalFactory.set(terminal.toJLine) DeprecatedJLine.setTerminalOverride(jline3term) terminal.withRawInput(Run.executeSuccess(console0())) } - } finally { + finally sys.props("scala.color") = previous jline3term.close() - } - } -} + end apply +end Console -object Console { +object Console: def apply(conf: Inputs): Console = - conf.compilers match { - case cs: Compilers => new Console(cs.scalac match { case x: AnalyzingCompiler => x }) - } -} + conf.compilers match + case cs: Compilers => + new Console(cs.scalac match + case x: AnalyzingCompiler => x) diff --git a/main-actions/src/main/scala/sbt/DotGraph.scala b/main-actions/src/main/scala/sbt/DotGraph.scala index d121893cb..db31b3335 100644 --- a/main-actions/src/main/scala/sbt/DotGraph.scala +++ b/main-actions/src/main/scala/sbt/DotGraph.scala @@ -13,25 +13,25 @@ import sbt.internal.util.Relation import sbt.io.IO -object DotGraph { +object DotGraph: def generateGraph[K, V]( file: File, graphName: String, relation: Relation[K, V], keyToString: K => String, valueToString: V => String - ): Unit = { + ): Unit = import scala.collection.mutable.{ HashMap, HashSet } val mappedGraph = new HashMap[String, HashSet[String]] - for ((key, values) <- relation.forwardMap; keyString = keyToString(key); value <- values) + for (key, values) <- relation.forwardMap; keyString = keyToString(key); value <- values do mappedGraph.getOrElseUpdate(keyString, new HashSet[String]) += valueToString(value) val mappings = - for { + for (dependsOn, dependents) <- mappedGraph.toSeq dependent <- dependents if dependent != dependsOn && !dependsOn.isEmpty && !dependent.isEmpty - } yield "\"" + dependent + "\" -> \"" + dependsOn + "\"" + yield "\"" + dependent + "\" -> \"" + dependsOn + "\"" val lines = ("digraph " + graphName + " {") +: @@ -39,13 +39,12 @@ object DotGraph { "}" IO.writeLines(file, lines) - } + end generateGraph def sourceToString(roots: Iterable[File], source: File) = relativized(roots, source).trim.stripSuffix(".scala").stripSuffix(".java") - private def relativized(roots: Iterable[File], path: File): String = { + private def relativized(roots: Iterable[File], path: File): String = val relativized = roots.flatMap(root => IO.relativize(root, path)) val shortest = relativized.foldLeft(Int.MaxValue)(_ min _.length) relativized.find(_.length == shortest).getOrElse(path.getName) - } -} +end DotGraph diff --git a/main-actions/src/main/scala/sbt/ForkTests.scala b/main-actions/src/main/scala/sbt/ForkTests.scala index 0a798d4de..ce3029ff8 100755 --- a/main-actions/src/main/scala/sbt/ForkTests.scala +++ b/main-actions/src/main/scala/sbt/ForkTests.scala @@ -51,7 +51,7 @@ private[sbt] object ForkTests: parallelism: Option[Int], virtualClasspath: Boolean, tags: (Tag, Int)* - ): Task[TestOutput] = { + ): Task[TestOutput] = import std.TaskExtra.* val dummyLoader = this.getClass.getClassLoader // can't provide the loader for test classes, which is in another jvm @@ -80,7 +80,7 @@ private[sbt] object ForkTests: .dependsOn(all(opts.setup)*) .flatMap: results => all(opts.cleanup).join.map(_ => results) - } + end apply private def mainTestTask( runners: Map[TestFramework, Runner], @@ -262,6 +262,8 @@ private class React( promise.failure(info.error) else () case _ => () + end match + end processNotification def blockForResponse(): Int = Await.result(promise.future, Duration.Inf) diff --git a/main-actions/src/main/scala/sbt/Pkg.scala b/main-actions/src/main/scala/sbt/Pkg.scala index 29a88ade1..daa19d6c3 100644 --- a/main-actions/src/main/scala/sbt/Pkg.scala +++ b/main-actions/src/main/scala/sbt/Pkg.scala @@ -41,29 +41,27 @@ import xsbti.{ FileConverter, HashedVirtualFileRef, VirtualFile, VirtualFileRef object Pkg: def JarManifest(m: Manifest) = PackageOption.JarManifest(m) def MainClass(mainClassName: String) = PackageOption.MainClass(mainClassName) - def ManifestAttributes(attributes: (String, String)*) = { - val converted = for ((name, value) <- attributes) yield (new Attributes.Name(name), value) + def ManifestAttributes(attributes: (String, String)*) = + val converted = for (name, value) <- attributes yield (new Attributes.Name(name), value) PackageOption.ManifestAttributes(converted*) - } // 2010-01-01 private val default2010Timestamp: Long = 1262304000000L def FixedTimestamp(value: Option[Long]) = PackageOption.FixedTimestamp(value) val keepTimestamps: Option[Long] = None val fixed2010Timestamp: Option[Long] = Some(default2010Timestamp) def gitCommitDateTimestamp: Option[Long] = - try { + try Some( OffsetDateTime .parse(Process("git show -s --format=%cI").!!.trim) .toInstant() .toEpochMilli() ) - } catch { + catch case e: Exception if e.getMessage.startsWith("Nonzero") => sys.error( s"git repository was expected for package timestamp; use Package.fixed2010Timestamp or Package.keepTimestamps instead" ) - } def setFixedTimestamp(value: Option[Long]): PackageOption = FixedTimestamp(value) @@ -81,16 +79,13 @@ object Pkg: def mergeAttributes(a1: Attributes, a2: Attributes) = a1.asScala ++= a2.asScala // merges `mergeManifest` into `manifest` (mutating `manifest` in the process) - def mergeManifests(manifest: Manifest, mergeManifest: Manifest): Unit = { + def mergeManifests(manifest: Manifest, mergeManifest: Manifest): Unit = mergeAttributes(manifest.getMainAttributes, mergeManifest.getMainAttributes) val entryMap = manifest.getEntries.asScala - for ((key, value) <- mergeManifest.getEntries.asScala) { - entryMap.get(key) match { + for (key, value) <- mergeManifest.getEntries.asScala do + entryMap.get(key) match case Some(attributes) => mergeAttributes(attributes, value); () case None => entryMap.put(key, value); () - } - } - } /** * The jar package configuration. Contains all relevant information to create a jar file. @@ -103,7 +98,7 @@ object Pkg: val sources: Seq[(HashedVirtualFileRef, String)], val jar: VirtualFileRef, val options: Seq[PackageOption] - ) { + ): import sbt.util.CacheImplicits.hashedVirtualFileRefToStr private def sourcesStr: String = sources @@ -117,7 +112,6 @@ object Pkg: options = ..., ) """ - } object Configuration: given IsoLList.Aux[ @@ -178,26 +172,23 @@ object Pkg: * * @param main the current jar attributes */ - def setVersion(main: Attributes): Unit = { + def setVersion(main: Attributes): Unit = val version = Attributes.Name.MANIFEST_VERSION - if (main.getValue(version) eq null) { + if main.getValue(version) eq null then main.put(version, "1.0") () - } - } - def addSpecManifestAttributes(name: String, version: String, orgName: String): PackageOption = { + def addSpecManifestAttributes(name: String, version: String, orgName: String): PackageOption = import Attributes.Name.* val attribKeys = Seq(SPECIFICATION_TITLE, SPECIFICATION_VERSION, SPECIFICATION_VENDOR) val attribVals = Seq(name, version, orgName) PackageOption.ManifestAttributes(attribKeys.zip(attribVals)*) - } def addImplManifestAttributes( name: String, version: String, homepage: Option[URI], org: String, orgName: String - ): PackageOption = { + ): PackageOption = import Attributes.Name.* // The ones in Attributes.Name are deprecated saying: @@ -215,7 +206,7 @@ object Pkg: PackageOption.ManifestAttributes(attribKeys.zip(attribVals) ++ { homepage map (h => (IMPLEMENTATION_URL, h.toString)) }*) - } + end addImplManifestAttributes def makeJar( sources: Seq[(File, String)], @@ -223,25 +214,22 @@ object Pkg: manifest: Manifest, log: Logger, time: Option[Long] - ): Unit = { + ): Unit = val path = jar.getAbsolutePath log.debug("Packaging " + path + " ...") - if (jar.exists) - if (!jar.isFile) - sys.error(path + " exists, but is not a regular file") + if jar.exists then if !jar.isFile then sys.error(path + " exists, but is not a regular file") log.debug(sourcesDebugString(sources)) IO.jarParallel(sources, jar, manifest, time) log.debug("Done packaging.") - } def sourcesDebugString(sources: Seq[(File, String)]): String = "Input file mappings:\n\t" + (sources map { (f, s) => s + "\n\t " + f } mkString ("\n\t")) given manifestFormat: JsonFormat[Manifest] = projectFormat[Manifest, Array[Byte]]( - m => { + m => val bos = new java.io.ByteArrayOutputStream() m.write(bos) bos.toByteArray - }, + , bs => new Manifest(new java.io.ByteArrayInputStream(bs)) ) end Pkg diff --git a/main-actions/src/main/scala/sbt/RawCompileLike.scala b/main-actions/src/main/scala/sbt/RawCompileLike.scala index 08893dc2b..9d80ea2a8 100644 --- a/main-actions/src/main/scala/sbt/RawCompileLike.scala +++ b/main-actions/src/main/scala/sbt/RawCompileLike.scala @@ -20,23 +20,19 @@ import sbt.util.FileInfo.{ exists, hash, lastModified } import sbt.internal.util.ManagedLogger import xsbti.compile.ClasspathOptions -object RawCompileLike { +object RawCompileLike: type Gen = (Seq[File], Seq[File], File, Seq[String], Int, ManagedLogger) => Unit - private def optionFiles(options: Seq[String], fileInputOpts: Seq[String]): List[File] = { + private def optionFiles(options: Seq[String], fileInputOpts: Seq[String]): List[File] = @tailrec - def loop(opt: List[String], result: List[File]): List[File] = { - opt.dropWhile(!fileInputOpts.contains(_)) match { - case List(_, fileOpt, tail*) => { + def loop(opt: List[String], result: List[File]): List[File] = + opt.dropWhile(!fileInputOpts.contains(_)) match + case List(_, fileOpt, tail*) => val file = new File(fileOpt) - if (file.isFile) loop(tail.toList, file :: result) + if file.isFile then loop(tail.toList, file :: result) else loop(tail.toList, result) - } case Nil | List(_) => result - } - } loop(options.toList, Nil) - } def cached(cacheStoreFactory: CacheStoreFactory, doCompile: Gen): Gen = cached(cacheStoreFactory, Seq(), doCompile) @@ -46,7 +42,7 @@ object RawCompileLike { fileInputOpts: Seq[String], doCompile: Gen ): Gen = - (sources, classpath, outputDirectory, options, maxErrors, log) => { + (sources, classpath, outputDirectory, options, maxErrors, log) => type Inputs = ( FilesInfo[HashFileInfo], FilesInfo[ModifiedFileInfo], @@ -66,36 +62,31 @@ object RawCompileLike { val cachedComp = inputChanged(cacheStoreFactory.make("inputs")) { (inChanged, in: Inputs) => inputChanged(cacheStoreFactory.make("output")) { (outChanged, outputs: FilesInfo[PlainFileInfo]) => - if (inChanged || outChanged) + if inChanged || outChanged then doCompile(sources, classpath, outputDirectory, options, maxErrors, log) - else - log.debug("Uptodate: " + outputDirectory.getAbsolutePath) + else log.debug("Uptodate: " + outputDirectory.getAbsolutePath) } } cachedComp(inputs)(exists(outputDirectory.allPaths.get().toSet)) - } def prepare(description: String, doCompile: Gen): Gen = - (sources, classpath, outputDirectory, options, maxErrors, log) => { - if (sources.isEmpty) log.info("No sources available, skipping " + description + "...") - else { + (sources, classpath, outputDirectory, options, maxErrors, log) => + if sources.isEmpty then log.info("No sources available, skipping " + description + "...") + else log.info(description.capitalize + " to " + outputDirectory.absolutePath + "...") IO.delete(outputDirectory) IO.createDirectory(outputDirectory) doCompile(sources, classpath, outputDirectory, options, maxErrors, log) log.info(description.capitalize + " successful.") - } - } def filterSources(f: File => Boolean, doCompile: Gen): Gen = (sources, classpath, outputDirectory, options, maxErrors, log) => doCompile(sources filter f, classpath, outputDirectory, options, maxErrors, log) def rawCompile(instance: ScalaInstance, cpOptions: ClasspathOptions): Gen = - (sources, classpath, outputDirectory, options, _, log) => { + (sources, classpath, outputDirectory, options, _, log) => val compiler = new RawCompiler(instance, cpOptions, log) compiler(sources.map(_.toPath), classpath.map(_.toPath), outputDirectory.toPath, options) - } def compile( label: String, @@ -106,4 +97,4 @@ object RawCompileLike { cached(cacheStoreFactory, prepare(label + " sources", rawCompile(instance, cpOptions))) val nop: Gen = (_, _, _, _, _, _) => () -} +end RawCompileLike diff --git a/main-actions/src/main/scala/sbt/Sync.scala b/main-actions/src/main/scala/sbt/Sync.scala index 541f8d36f..c67d3000f 100644 --- a/main-actions/src/main/scala/sbt/Sync.scala +++ b/main-actions/src/main/scala/sbt/Sync.scala @@ -40,7 +40,7 @@ import xsbti.{ FileConverter, VirtualFileRef } * Warning: Specifically, don't mix this with a directory containing manually created files, like sources. * It is safe to use for its intended purpose: copying resources to a class output directory. */ -object Sync { +object Sync: @deprecated("Use sync, which doesn't take the unused outStyle param", "1.1.1") def apply( store: CacheStore, @@ -67,7 +67,7 @@ object Sync { inStyle: FileInfo.Style, fileConverter: FileConverter ): Iterable[(File, File)] => Relation[File, File] = - mappings => { + mappings => val relation = Relation.empty ++ mappings noDuplicateTargets(relation) val currentInfo = relation._1s.map(s => (s, inStyle(s))).toMap @@ -92,31 +92,28 @@ object Sync { writeInfoVirtual(store, relation, currentInfo, fileConverter)(using inStyle.format) relation - } def copy(source: File, target: File): Unit = - if (source.isFile) IO.copyFile(source, target, true) - else if (!target.exists) { // we don't want to update the last modified time of an existing directory + if source.isFile then IO.copyFile(source, target, true) + else if !target.exists + then // we don't want to update the last modified time of an existing directory IO.createDirectory(target) IO.copyLastModified(source, target) () - } - def noDuplicateTargets(relation: Relation[File, File]): Unit = { + def noDuplicateTargets(relation: Relation[File, File]): Unit = val dups = relation.reverseMap .withFilter { case (_, srcs) => srcs.size >= 2 && srcs.exists(!_.isDirectory) } .map { (target, srcs) => "\n\t" + target + "\nfrom\n\t" + srcs.mkString("\n\t\t") } - if (dups.nonEmpty) - sys.error("Duplicate mappings:" + dups.mkString) - } + if dups.nonEmpty then sys.error("Duplicate mappings:" + dups.mkString) given relationFormat[A, B](using af: JsonFormat[Map[A, Set[B]]], bf: JsonFormat[Map[B, Set[A]]] ): JsonFormat[Relation[A, B]] = - new JsonFormat[Relation[A, B]] { + new JsonFormat[Relation[A, B]]: def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): Relation[A, B] = - jsOpt match { + jsOpt match case Some(js) => unbuilder.beginArray(js) val jForward = unbuilder.nextElement @@ -125,15 +122,12 @@ object Sync { Relation.make(af.read(Some(jForward), unbuilder), bf.read(Some(jReverse), unbuilder)) case None => deserializationError("Expected JsArray but found None") - } - def write[J](obj: Relation[A, B], builder: Builder[J]): Unit = { + def write[J](obj: Relation[A, B], builder: Builder[J]): Unit = builder.beginArray() af.write(obj.forwardMap, builder) bf.write(obj.reverseMap, builder) builder.endArray() - } - } private lazy val fileIsoString: IsoString[File] = val iso = summon[IsoStringLong[File]] @@ -156,7 +150,7 @@ object Sync { relation: Relation[File, File], info: Map[File, F], fileConverter: FileConverter - )(using infoFormat: JsonFormat[F]): Unit = { + )(using infoFormat: JsonFormat[F]): Unit = val virtualRelation: Relation[VirtualFileRef, VirtualFileRef] = Relation.switch(relation, (f: File) => fileConverter.toVirtualFile(f.toPath)) val virtualInfo: Map[VirtualFileRef, F] = info.map { (file, fileInfo) => @@ -172,60 +166,52 @@ object Sync { virtualInfo ) ) - } + end writeInfoVirtual type RelationInfo[F] = (Relation[File, File], Map[File, F]) type RelationInfoVirtual[F] = (Relation[VirtualFileRef, VirtualFileRef], Map[VirtualFileRef, F]) def readInfoWrapped[F <: FileInfo](store: CacheStore, fileConverter: FileConverter)(using infoFormat: JsonFormat[F] - ): RelationInfo[F] = { + ): RelationInfo[F] = convertFromVirtual(readInfoVirtual(store)(using infoFormat), fileConverter) - } def convertFromVirtual[F <: FileInfo]( info: RelationInfoVirtual[F], fileConverter: FileConverter - ): RelationInfo[F] = { + ): RelationInfo[F] = val firstPart = Relation.switch(info._1, (r: VirtualFileRef) => fileConverter.toPath(r).toFile) val secondPart = info._2.map { (file, fileInfo) => fileConverter.toPath(file).toFile -> fileInfo } firstPart -> secondPart - } def readInfo[F <: FileInfo]( store: CacheStore )(using infoFormat: JsonFormat[F]): RelationInfo[F] = - try { - readUncaught[F](store)(using infoFormat) - } catch { + try readUncaught[F](store)(using infoFormat) + catch case _: IOException => (Relation.empty[File, File], Map.empty[File, F]) case _: ZipException => (Relation.empty[File, File], Map.empty[File, F]) case e: TranslatedException => - e.getCause match { + e.getCause match case _: ZipException => (Relation.empty[File, File], Map.empty[File, F]) case _ => throw e - } - } def readInfoVirtual[F <: FileInfo]( store: CacheStore )(using infoFormat: JsonFormat[F]): RelationInfoVirtual[F] = - try { - readUncaughtVirtual[F](store)(using infoFormat) - } catch { + try readUncaughtVirtual[F](store)(using infoFormat) + catch case _: IOException => (Relation.empty[VirtualFileRef, VirtualFileRef], Map.empty[VirtualFileRef, F]) case _: ZipException => (Relation.empty[VirtualFileRef, VirtualFileRef], Map.empty[VirtualFileRef, F]) case e: TranslatedException => - e.getCause match { + e.getCause match case _: ZipException => (Relation.empty[VirtualFileRef, VirtualFileRef], Map.empty[VirtualFileRef, F]) case _ => throw e - } - } private def readUncaught[F <: FileInfo]( store: CacheStore @@ -236,12 +222,11 @@ object Sync { private def readUncaughtVirtual[F <: FileInfo]( store: CacheStore - )(using JsonFormat[F]): RelationInfoVirtual[F] = { + )(using JsonFormat[F]): RelationInfoVirtual[F] = import sjsonnew.IsoString given IsoString[VirtualFileRef] = IsoString.iso[VirtualFileRef](_.toString, VirtualFileRef.of(_)) store.read(default = (Relation.empty[VirtualFileRef, VirtualFileRef], Map.empty[VirtualFileRef, F]) ) - } -} +end Sync diff --git a/main-actions/src/main/scala/sbt/TestResultLogger.scala b/main-actions/src/main/scala/sbt/TestResultLogger.scala index fbcdbb010..591bc88e8 100644 --- a/main-actions/src/main/scala/sbt/TestResultLogger.scala +++ b/main-actions/src/main/scala/sbt/TestResultLogger.scala @@ -150,7 +150,7 @@ object TestResultLogger: printStandard: TestResultLogger = Defaults.printStandard, printFailures: TestResultLogger = Defaults.printFailures, printNoTests: TestResultLogger = Defaults.printNoTests - ) extends TestResultLogger { + ) extends TestResultLogger: override def run(log: Logger, results: Output, taskName: String): Unit = run(log, results, taskName, Vector.empty) @@ -160,18 +160,15 @@ object TestResultLogger: results: Output, taskName: String, cached: Vector[String] - ): Unit = { + ): Unit = def run(r: TestResultLogger): Unit = r.run(log, results, taskName, cached) run(printSummary) - if (printStandard_?(results)) - run(printStandard) + if printStandard_?(results) then run(printStandard) - if (results.events.isEmpty) - run(printNoTests) - else - run(printFailures) + if results.events.isEmpty then run(printNoTests) + else run(printFailures) // Logging only. Failure propagation lives in the task wrapper // (`Defaults.testFull` / `inputTests0`) so the cross-project recap @@ -179,32 +176,31 @@ object TestResultLogger: // the `TestsFailedException` thrown there. The trait contract is // "perform logging"; it does not document throwing on failure. () - } - } + end run + end Main - val printSummary = TestResultLogger((log, results, _) => { + val printSummary = TestResultLogger((log, results, _) => val multipleFrameworks = results.summaries.size > 1 for Tests.Summary(name, message) <- results.summaries do - if (message.isEmpty) log.debug("Summary for " + name + " not available.") - else { - if (multipleFrameworks) log.info(name) + if message.isEmpty then log.debug("Summary for " + name + " not available.") + else + if multipleFrameworks then log.info(name) log.info(message) - } - }) + ) val printStandard_? : Output => Boolean = results => // Print the standard one-liner statistic if no framework summary is defined, or when > 1 framework is in used. results.summaries.size > 1 || results.summaries.headOption.forall(_.summaryText.isEmpty) - val printStandard = TestResultLogger((log, results, _, cached) => { + val printStandard = TestResultLogger((log, results, _, cached) => val counts = countsString(results.events.values, cached.size, true) results.overall match case TestResult.Empty => () case TestResult.Error => log.error(s"${SummaryStatus.Errored.label}: $counts") case TestResult.Passed => log.info(s"${SummaryStatus.Passed.label}: $counts") case TestResult.Failed => log.error(s"${SummaryStatus.Failed.label}: $counts") - }) + ) private[sbt] def countsString(events: Iterable[SuiteResult]): String = countsString(events, 0, false) @@ -217,7 +213,7 @@ object TestResultLogger: events: Iterable[SuiteResult], cachedCount: Int, alwaysShowCached: Boolean, - ): String = { + ): String = val (failuresCount, errorsCount, passedCount) = events.foldLeft((0, 0, 0)) { case ((failureAcc, errorAcc, passedAcc), suite) => suite.result match @@ -231,24 +227,22 @@ object TestResultLogger: val cachedField = if cachedCount > 0 || alwaysShowCached then s", cached $cachedCount" else "" base + cachedField - } - val printFailures = TestResultLogger((log, results, _) => { + val printFailures = TestResultLogger((log, results, _) => def select(resultTpe: TestResult) = results.events collect { case (name, tpe) if tpe.result == resultTpe => scala.reflect.NameTransformer.decode(name) } def show(label: String, level: Level.Value, tests: Iterable[String]): Unit = - if (tests.nonEmpty) { + if tests.nonEmpty then log.log(level, label) log.log(level, tests.mkString(suitePadding, s"\n$suitePadding", "")) - } show("passed tests:", Level.Debug, select(TestResult.Passed)) show("failed tests:", Level.Error, select(TestResult.Failed)) show("error during tests:", Level.Error, select(TestResult.Error)) - }) + ) val printNoTests = TestResultLogger((log, results, taskName, cached) => val suffix = if cached.nonEmpty then s" (${cached.size} cached)" else "" @@ -345,6 +339,7 @@ object TestResultLogger: suites.foreach: (name, st) => lines += s"${suitePadding}${name.padTo(width, ' ')}${st.render(isColorEnabled)}" lines.result() + end detail end Summary end Defaults end TestResultLogger diff --git a/main-actions/src/main/scala/sbt/Tests.scala b/main-actions/src/main/scala/sbt/Tests.scala index 0f2ade0ea..d8d7216d6 100644 --- a/main-actions/src/main/scala/sbt/Tests.scala +++ b/main-actions/src/main/scala/sbt/Tests.scala @@ -43,7 +43,7 @@ import sbt.internal.util.complete.{ DefaultParsers, Parser } sealed trait TestOption -object Tests { +object Tests: private[sbt] sealed trait AdhocOption private[sbt] object AdhocOption: @@ -86,7 +86,7 @@ object Tests { overall: TestResult, events: Map[String, SuiteResult], summaries: Iterable[Summary] - ) { + ): /** * Returns a copy with the throwables removed from every suite result. @@ -96,7 +96,6 @@ object Tests { */ def withoutThrowables: Output = copy(events = events.view.mapValues(_.withoutThrowables).toMap) - } /** * Summarizes a test run. @@ -220,64 +219,56 @@ object Tests { val tests: Seq[TestDefinition], val runPolicy: TestRunPolicy, val tags: Seq[(Tag, Int)] - ) extends Serializable { + ) extends Serializable: - def this(name: String, tests: Seq[TestDefinition], runPolicy: TestRunPolicy) = { + def this(name: String, tests: Seq[TestDefinition], runPolicy: TestRunPolicy) = this(name, tests, runPolicy, Seq.empty) - } - def withName(name: String): Group = { + def withName(name: String): Group = new Group(name, tests, runPolicy, tags) - } - def withTests(tests: Seq[TestDefinition]): Group = { + def withTests(tests: Seq[TestDefinition]): Group = new Group(name, tests, runPolicy, tags) - } - def withRunPolicy(runPolicy: TestRunPolicy): Group = { + def withRunPolicy(runPolicy: TestRunPolicy): Group = new Group(name, tests, runPolicy, tags) - } - def withTags(tags: Seq[(Tag, Int)]): Group = { + def withTags(tags: Seq[(Tag, Int)]): Group = new Group(name, tests, runPolicy, tags) - } override def hashCode(): Int = (name, tests, runPolicy, tags).## - override def equals(x1: Any): Boolean = { + override def equals(x1: Any): Boolean = this.eq(x1.asInstanceOf[Object]) || (x1.isInstanceOf[Group] && ({ val Group1: Group = x1.asInstanceOf[Group] name == Group1.name && tests == Group1.tests && runPolicy == Group1.runPolicy && tags == Group1.tags })) - } - } + end Group object Group extends AbstractFunction3[String, Seq[TestDefinition], TestRunPolicy, Group] - with Serializable { + with Serializable: // - EXPANDED CASE CLASS METHOD BEGIN -// final override def toString(): String = "Group" def apply( name: String, tests: Seq[TestDefinition], runPolicy: TestRunPolicy - ): Group = { + ): Group = new Group(name, tests, runPolicy, Seq.empty) - } def apply( name: String, tests: Seq[TestDefinition], runPolicy: TestRunPolicy, tags: Seq[(Tag, Int)] - ): Group = { + ): Group = new Group(name, tests, runPolicy, tags) - } private def readResolve(): Object = Group // - EXPANDED CASE CLASS METHOD END -// - } + end Group private[sbt] final class ProcessedOptions( val tests: Vector[TestDefinition], @@ -289,7 +280,7 @@ object Tests { config: Execution, discovered: Vector[TestDefinition], log: Logger - ): ProcessedOptions = { + ): ProcessedOptions = import collection.mutable.{ HashSet, ListBuffer } val testFilters = new ListBuffer[String => Boolean] var orderedFilters = Seq[String => Boolean]() @@ -302,11 +293,11 @@ object Tests { def isExplicitFqn(s: String): Boolean = !s.contains('*') && !s.contains('?') && !s.contains("...") - for (option <- config.options) { - option match { + for option <- config.options do + option match case Filter(include) => testFilters += include; () case Filters(includes) => - if (orderedFilters.nonEmpty) sys.error("Cannot define multiple ordered test filters.") + if orderedFilters.nonEmpty then sys.error("Cannot define multiple ordered test filters.") else orderedFilters = includes () case ExplicitlyRequestedNames(names) => @@ -317,12 +308,10 @@ object Tests { case Setup(setupFunction, _) => setup += setupFunction; () case Cleanup(cleanupFunction, _) => cleanup += cleanupFunction; () case _: Argument => // now handled by whatever constructs `runners` - } - } - if (excludeTestsSet.nonEmpty) + if excludeTestsSet.nonEmpty then log.debug(excludeTestsSet.mkString("Excluding tests: \n\t", "\n\t", "")) - if (undefinedFrameworks.nonEmpty) + if undefinedFrameworks.nonEmpty then log.warn( "Arguments defined for test frameworks that are not present:\n\t" + undefinedFrameworks .mkString("\n\t") @@ -332,7 +321,7 @@ object Tests { !excludeTestsSet.contains(test.name) && testFilters.forall(filter => filter(test.name)) val filtered0 = discovered.filter(includeTest).toList.distinct val tests = - if (orderedFilters.isEmpty) filtered0 + if orderedFilters.isEmpty then filtered0 else orderedFilters.flatMap(f => filtered0.filter(d => f(d.name))).toList.distinct val uniqueTests = distinctBy(tests)(_.name) // Per TaskDef: explicitlySpecified=true only when user supplied a complete FQN (e.g. testOnly com.example.MySuite), @@ -351,12 +340,11 @@ object Tests { cleanup.toVector, testListeners.toVector ) - } + end processOptions - private def distinctBy[T, K](in: Seq[T])(f: T => K): Seq[T] = { + private def distinctBy[T, K](in: Seq[T])(f: T => K): Seq[T] = val seen = new collection.mutable.HashSet[K] in.filter(t => seen.add(f(t))) - } // Called by Defaults def apply( @@ -366,7 +354,7 @@ object Tests { o: ProcessedOptions, config: Execution, log: ManagedLogger - ): Task[Output] = { + ): Task[Output] = testTask( testLoader, frameworks, @@ -378,7 +366,6 @@ object Tests { o.testListeners, config ) - } def apply( frameworks: Map[TestFramework, Framework], @@ -387,10 +374,9 @@ object Tests { discovered: Vector[TestDefinition], config: Execution, log: ManagedLogger - ): Task[Output] = { + ): Task[Output] = val o = processOptions(config, discovered, log) apply(frameworks, testLoader, runners, o, config, log) - } private[sbt] def testTask( loader: ClassLoader, @@ -402,7 +388,7 @@ object Tests { log: ManagedLogger, testListeners: Vector[TestReportListener], config: Execution - ): Task[Output] = { + ): Task[Output] = def fj(actions: Iterable[() => Unit]): Task[Unit] = nop.dependsOn(actions.toSeq.fork(_()).map(_.tagw(config.tags*))*) def partApp(actions: Iterable[ClassLoader => Unit]) = actions.toSeq map { a => () => @@ -414,10 +400,8 @@ object Tests { val setupTasks = fj(partApp(userSetup) :+ frameworkSetup).tagw(config.tags*) val mainTasks = - if (config.parallel) - makeParallel(loader, runnables, setupTasks, config.tags).map(_.toList) - else - makeSerial(loader, runnables, setupTasks) + if config.parallel then makeParallel(loader, runnables, setupTasks, config.tags).map(_.toList) + else makeSerial(loader, runnables, setupTasks) val taggedMainTasks = mainTasks.tagw(config.tags*) taggedMainTasks .map(processResults) @@ -428,7 +412,7 @@ object Tests { results } } - } + end testTask type TestRunnable = (String, TestFunction) private def createNestedRunnables( @@ -466,7 +450,7 @@ object Tests { loader: ClassLoader, runnables: Seq[TestRunnable], tags: Seq[(Tag, Int)] - ): Task[Map[String, SuiteResult]] = { + ): Task[Map[String, SuiteResult]] = val tasks = runnables.map { (name, test) => toTask(loader, name, test, tags) } tasks.join.map(_.foldLeft(Map.empty[String, SuiteResult]) { (sum, e) => val merged = sum.toSeq ++ e.toSeq @@ -475,50 +459,45 @@ object Tests { .mapValues(_.map(_._2).foldLeft(SuiteResult.Empty)(_ + _)) .toMap }) - } def toTask( loader: ClassLoader, name: String, fun: TestFunction, tags: Seq[(Tag, Int)] - ): Task[Map[String, SuiteResult]] = { + ): Task[Map[String, SuiteResult]] = val base = Task(Action.Pure(() => (name, fun.apply()), `inline` = false)).setName(name) val taggedBase = base.tagw(tags*).tag(fun.tags.map(ConcurrentRestrictions.Tag(_))*) taggedBase flatMap { case (name, (result, nested)) => val nestedRunnables = createNestedRunnables(loader, fun, nested) toTasks(loader, nestedRunnables, tags).map { currentResultMap => val newResult = - currentResultMap.get(name) match { + currentResultMap.get(name) match case Some(currentResult) => currentResult + result case None => result - } currentResultMap.updated(name, newResult) } } - } def makeSerial( loader: ClassLoader, runnables: Seq[TestRunnable], setupTasks: Task[Unit], - ): Task[List[(String, SuiteResult)]] = { + ): Task[List[(String, SuiteResult)]] = @tailrec def processRunnable( runnableList: List[TestRunnable], acc: List[(String, SuiteResult)] ): List[(String, SuiteResult)] = - runnableList match { + runnableList match case hd :: rst => val testFun = hd._2 val (result, nestedTasks) = testFun.apply() val nestedRunnables = createNestedRunnables(loader, testFun, nestedTasks) processRunnable(nestedRunnables.toList ::: rst, (hd._1, result) :: acc) case Nil => acc - } task { processRunnable(runnables.toList, List.empty) }.dependsOn(setupTasks) - } def processResults(results: Iterable[(String, SuiteResult)]): Output = Output(overall(results.map(_._2.result)), results.toMap, Iterable.empty) @@ -530,28 +509,26 @@ object Tests { case TestResult.Error => 2 def foldTasks(results: Seq[Task[Output]], parallel: Boolean): Task[Output] = - if (results.isEmpty) { - task { Output(TestResult.Passed, Map.empty, Nil) } - } else if (parallel) { + if results.isEmpty then task { Output(TestResult.Passed, Map.empty, Nil) } + else if parallel then reduced[Output]( results.toIndexedSeq, { case (Output(v1, m1, _), Output(v2, m2, _)) => Output( - (if (severity(v1) < severity(v2)) v2 else v1): TestResult, + (if severity(v1) < severity(v2) then v2 else v1): TestResult, Map((m1.toSeq ++ m2.toSeq)*), Iterable.empty[Summary] ) } ) - } else { + else def sequence(tasks: List[Task[Output]], acc: List[Output]): Task[List[Output]] = - tasks match { + tasks match case Nil => task(acc.reverse) case hd :: tl => hd flatMap { out => sequence(tl, out :: acc) } - } sequence(results.toList, List()) map { res => val (rs, ms) = res.unzip(using (e => (e.overall, e.events))) val m = ms reduce { (m1: Map[String, SuiteResult], m2: Map[String, SuiteResult]) => @@ -559,10 +536,9 @@ object Tests { } Output(overall(rs), m, Iterable.empty) } - } def overall(results: Iterable[TestResult]): TestResult = results.foldLeft(TestResult.Passed: TestResult) { (acc, result) => - if (severity(acc) < severity(result)) result else acc + if severity(acc) < severity(result) then result else acc } def discover( frameworks: Seq[Framework], @@ -571,7 +547,7 @@ object Tests { ): (Seq[TestDefinition], Set[String]) = discover(frameworks flatMap TestFramework.getFingerprints, allDefs(analysis), log) - def allDefs(analysis: CompileAnalysis) = analysis match { + def allDefs(analysis: CompileAnalysis) = analysis match case analysis: Analysis => val acs: Seq[xsbti.api.AnalyzedClass] = analysis.apis.internal.values.toVector acs.flatMap { ac => @@ -589,12 +565,11 @@ object Tests { if e.getMessage.startsWith("No companions") then Nil else throw e }.toSeq - } def discover( fingerprints: Seq[Fingerprint], definitions: Seq[Definition], log: Logger - ): (Seq[TestDefinition], Set[String]) = { + ): (Seq[TestDefinition], Set[String]) = val subclasses = fingerprints collect { case sub: SubclassFingerprint => (sub.superclassName, sub.isModule, sub) }; @@ -622,22 +597,21 @@ object Tests { case _ => false }) val tests = - for { + for (df, di) <- discovered fingerprint <- toFingerprints(di) - } yield new TestDefinition(df.name, fingerprint, false, Array(new SuiteSelector: Selector)) + yield new TestDefinition(df.name, fingerprint, false, Array(new SuiteSelector: Selector)) val mains = discovered collect { case (df, di) if di.hasMain => df.name } (tests, mains.toSet) - } -} + end discover +end Tests final class TestsFailedException private[sbt] ( val taskName: String, val testOutput: Option[Tests.Output] ) extends RuntimeException("Tests unsuccessful") - with FeedbackProvidedException { + with FeedbackProvidedException: // Public no-arg constructor preserved for backward compatibility with // callers outside sbt. Internal call sites always use the primary // constructor with a real task name. def this() = this(taskName = "", testOutput = None) -} diff --git a/main-actions/src/main/scala/sbt/internal/WorkerExchange.scala b/main-actions/src/main/scala/sbt/internal/WorkerExchange.scala index 602e73d21..a528d222a 100644 --- a/main-actions/src/main/scala/sbt/internal/WorkerExchange.scala +++ b/main-actions/src/main/scala/sbt/internal/WorkerExchange.scala @@ -67,6 +67,7 @@ object WorkerExchange: major >= 16 catch case NonFatal(_) => false jdkIpcSupportCache.getOrElseUpdate(javaHome, doDetect) + end supportsUnixDomainSockets /** * Start a worker process. @@ -90,10 +91,10 @@ object WorkerExchange: val (connArgs, closer) = connectionType match case WorkerConnection.Tcp => val serverSocket = Retry(ServerSocket(0, 1, loopback)) - val accepter = Thread(() => { + val accepter = Thread(() => val socket = serverSocket.accept() runAccepter(socket.getOutputStream(), socket.getInputStream()) - }) + ) accepter.setName("sbt-fork-test-response-reader") accepter.setPriority(Thread.NORM_PRIORITY + 1) accepter.start() @@ -106,22 +107,21 @@ object WorkerExchange: ch } @volatile var acceptedChannel: SocketChannel = null - val accepter = Thread(() => { + val accepter = Thread(() => val channel = serverChannel.accept() acceptedChannel = channel runAccepter( DuplexChannels.newOutputStream(channel), DuplexChannels.newInputStream(channel) ) - }) + ) accepter.setName("sbt-fork-test-response-reader") accepter.setPriority(Thread.NORM_PRIORITY + 1) accepter.start() - val closer: AutoCloseable = () => { + val closer: AutoCloseable = () => if acceptedChannel != null then acceptedChannel.close() serverChannel.close() Files.deleteIfExists(path) - } (Seq("--ipc", path.toString()), Some(closer)) case WorkerConnection.Stdio => (Nil, None) val options = Seq( @@ -146,6 +146,7 @@ object WorkerExchange: val forkTimeout = fo.connectionTimeout.getOrElse(30.seconds) val input = Await.result(inputRef.future, forkTimeout) WorkerProxy(input, p, options, closer) + end startWorker /** Generates a fresh path suitable for binding a `WorkerConnection.Ipc` socket. */ def newIpcSocketPath(): NioPath = @@ -194,10 +195,10 @@ class WorkerProxy( inputStream.println(str) inputStream.flush() - val watch = Thread(() => { + val watch = Thread(() => while process.isAlive() do Thread.sleep(100) WorkerExchange.listeners.foreach(_.notifyExit(process)) - }) + ) watch.start() end WorkerProxy diff --git a/main-actions/src/main/scala/sbt/internal/sona/PackageDeploymentValidationError.scala b/main-actions/src/main/scala/sbt/internal/sona/PackageDeploymentValidationError.scala index f7a5a02d1..a34013856 100644 --- a/main-actions/src/main/scala/sbt/internal/sona/PackageDeploymentValidationError.scala +++ b/main-actions/src/main/scala/sbt/internal/sona/PackageDeploymentValidationError.scala @@ -24,7 +24,7 @@ private case class PackageDeploymentValidationError( packageErrors: Seq[String] ) -private object PackageDeploymentValidationError { +private object PackageDeploymentValidationError: /** * Example: (it's not an array but an object which makes it hard to parse with the standard contraband means) @@ -47,7 +47,7 @@ private object PackageDeploymentValidationError { * None - otherwise (Sonatype Central could change the format of the output) */ def parse(errorsNode: JValue): Option[Seq[PackageDeploymentValidationError]] = - errorsNode match { + errorsNode match case JObject(fields) => val errors = fields.toSeq.flatMap { case JField(packageInfo, JArray(packageErrors)) => @@ -56,19 +56,15 @@ private object PackageDeploymentValidationError { case other => None } val noParsingIssues = packageErrors.length == packageErrorsTexts.length - if (noParsingIssues) + if noParsingIssues then Some(PackageDeploymentValidationError(packageInfo, packageErrorsTexts.toSeq)) - else - None + else None case _ => None } val noParsingIssues = errors.size == fields.length - if (noParsingIssues) - Some(errors) - else - None + if noParsingIssues then Some(errors) + else None case _ => None - } -} +end PackageDeploymentValidationError diff --git a/main-actions/src/main/scala/sbt/internal/sona/Sona.scala b/main-actions/src/main/scala/sbt/internal/sona/Sona.scala index 074f8fdff..802af8330 100644 --- a/main-actions/src/main/scala/sbt/internal/sona/Sona.scala +++ b/main-actions/src/main/scala/sbt/internal/sona/Sona.scala @@ -25,24 +25,22 @@ import java.util.Base64 import scala.concurrent.* import scala.concurrent.duration.* -class Sona(client: SonaClient) extends AutoCloseable { +class Sona(client: SonaClient) extends AutoCloseable: def uploadBundle( bundleZipPath: Path, deploymentName: String, pt: PublishingType, log: Logger, - ): Unit = { + ): Unit = val deploymentId = client.uploadBundle(bundleZipPath, deploymentName, pt, log) client.waitForDeploy(deploymentId, deploymentName, pt, 1, log) - } def close(): Unit = client.close() -} class SonaClient(reqTransform: Request => Request, uploadRequestTimeout: FiniteDuration) - extends AutoCloseable { + extends AutoCloseable: import SonaClient.baseUrl - private val http = { + private val http = val defaultHttpRequestTimeout = 2.minutes val gigahorseConfig = Gigahorse.config @@ -50,14 +48,13 @@ class SonaClient(reqTransform: Request => Request, uploadRequestTimeout: FiniteD .withReadTimeout(defaultHttpRequestTimeout) Gigahorse.http(gigahorseConfig) - } def uploadBundle( bundleZipPath: Path, deploymentName: String, publishingType: PublishingType, log: Logger, - ): String = { + ): String = val maxAttempt = 2 val waitDurationBetweenAtttempt = 5.seconds // Adding an extra 5.seconds as security margins @@ -69,10 +66,9 @@ class SonaClient(reqTransform: Request => Request, uploadRequestTimeout: FiniteD // addQuery string doesn't work for post val q = queryString( "name" -> deploymentName, - "publishingType" -> (publishingType match { + "publishingType" -> (publishingType match case PublishingType.Automatic => "AUTOMATIC" - case PublishingType.UserManaged => "USER_MANAGED" - }) + case PublishingType.UserManaged => "USER_MANAGED") ) val req = Gigahorse .url(s"${baseUrl}/publisher/upload?$q") @@ -85,7 +81,7 @@ class SonaClient(reqTransform: Request => Request, uploadRequestTimeout: FiniteD http.run(reqTransform(req), SonaClient.asStringWithErrorBody) } awaitWithMessage(res, "uploading...", log, totalAwaitDuration) - } + end uploadBundle private def queryString(kv: (String, String)*): String = kv.map { case (k, v) => @@ -99,13 +95,13 @@ class SonaClient(reqTransform: Request => Request, uploadRequestTimeout: FiniteD publishingType: PublishingType, attempt: Int, log: Logger, - ): Unit = { + ): Unit = val status = deploymentStatus(deploymentId) log.info(s"deployment $deploymentName ${status.deploymentState} ${attempt}/n") val sleepSec = - if (attempt <= 3) List(5, 5, 10, 15)(attempt) + if attempt <= 3 then List(5, 5, 10, 15)(attempt) else 30 - status.deploymentState match { + status.deploymentState match case DeploymentState.FAILED => val errorText = failedDeploymentErrorText(deploymentId, status.errors, log) sys.error(errorText) @@ -120,61 +116,53 @@ class SonaClient(reqTransform: Request => Request, uploadRequestTimeout: FiniteD case _ => Thread.sleep(sleepSec * 1000L) waitForDeploy(deploymentId, deploymentName, publishingType, attempt + 1, log) - } - } + end waitForDeploy - private def deploymentStatus(deploymentId: String): PublisherStatus = { + private def deploymentStatus(deploymentId: String): PublisherStatus = val res = retryF(maxAttempt = 5, waitDurationBetweenAttempt = 5.seconds) { (attempt: Int) => deploymentStatusF(deploymentId) } Await.result(res, 10.minutes) - } /** * https://central.sonatype.org/publish/publish-portal-api/#verify-status-of-the-deployment */ - private def deploymentStatusF(deploymentId: String): Future[PublisherStatus] = { + private def deploymentStatusF(deploymentId: String): Future[PublisherStatus] = val req = Gigahorse .url(s"${baseUrl}/publisher/status") .addQueryString("id" -> deploymentId) .post("", StandardCharsets.UTF_8) http.run(reqTransform(req), SonaClient.asPublisherStatus) - } /** * Retry future function on any error. */ private def retryF[A1](maxAttempt: Int, waitDurationBetweenAttempt: FiniteDuration)( f: Int => Future[A1] - ): Future[A1] = { + ): Future[A1] = import scala.concurrent.ExecutionContext.Implicits.* - def impl(retry: Int): Future[A1] = { + def impl(retry: Int): Future[A1] = val res = f(retry + 1) res.recoverWith { case _ if retry < maxAttempt => sleep(waitDurationBetweenAttempt).flatMap(_ => impl(retry + 1)) } - } impl(0) - } private def awaitWithMessage[A1]( f: Future[A1], msg: String, log: Logger, awaitDuration: FiniteDuration, - ): A1 = { + ): A1 = import scala.concurrent.ExecutionContext.Implicits.* def logLoop(attempt: Int): Unit = - if (!f.isCompleted) { - if (attempt > 0) { - log.info(msg) - } + if !f.isCompleted then + if attempt > 0 then log.info(msg) sleep(30.second).foreach(_ => logLoop(attempt + 1)) - } else () + else () logLoop(0) Await.result(f, awaitDuration) - } def close(): Unit = http.close() @@ -184,15 +172,14 @@ class SonaClient(reqTransform: Request => Request, uploadRequestTimeout: FiniteD Thread.sleep(duration.toMillis) } } -} +end SonaClient -object Sona { +object Sona: def host: String = SonaClient.host def oauthClient(userName: String, userToken: String, uploadRequestTimeout: FiniteDuration): Sona = new Sona(SonaClient.oauthClient(userName, userToken, uploadRequestTimeout)) -} -object SonaClient { +object SonaClient: import sbt.internal.sona.codec.JsonProtocol.given val host: String = "central.sonatype.com" val baseUrl: String = s"https://$host/api/v1" @@ -206,11 +193,10 @@ object SonaClient { * or throws a [[SonaStatusError]] with both the status code and response body on failure. * This provides more detailed error information than [[gigahorse.StatusError]]. */ - val asStringWithErrorBody: FullResponse => String = { response => + val asStringWithErrorBody: FullResponse => String = response => val body = response.bodyAsString - if (response.status >= 200 && response.status < 300) body + if response.status >= 200 && response.status < 300 then body else throw new SonaStatusError(response.status, body) - } def oauthClient( userName: String, userToken: String, @@ -225,20 +211,18 @@ object SonaClient { deploymentId: String, errors: Option[JValue], log: Logger - ): String = { + ): String = val errorsText = errors.map(presentDeploymentValidationErrors(_, log)) - val errorsMessagePart = errorsText match { + val errorsMessagePart = errorsText match case Some(value) => s" with validation errors:\n$value" case None => "" - } s"deployment $deploymentId failed$errorsMessagePart" - } import sbt.internal.sona.SonaClient.PrettyPrint.* - private def presentDeploymentValidationErrors(errorsNode: JValue, log: Logger): String = { - PackageDeploymentValidationError.parse(errorsNode) match { + private def presentDeploymentValidationErrors(errorsNode: JValue, log: Logger): String = + PackageDeploymentValidationError.parse(errorsNode) match case Some(errors) => val errorsPresented: Seq[String] = errors.map { case PackageDeploymentValidationError(packageDescriptor, packageErrors) => @@ -253,34 +237,28 @@ object SonaClient { "Sonatype deployment validation errors JSON format has changed. Please update to the latest sbt version or report the issue to the sbt project" ) PrettyPrinter(errorsNode) - } - } - private object PrettyPrint { + private object PrettyPrint: def asList(lines: Seq[String]): String = lines.map("- " + _).mkString("\n") - def indent(text: String, indentSize: Int): String = { + def indent(text: String, indentSize: Int): String = val indent = " " * indentSize text.linesIterator.map(indent + _).mkString("\n") - } - } -} +end SonaClient private case class OAuthClient(userName: String, userToken: String) - extends Function1[Request, Request] { + extends Function1[Request, Request]: val base64Credentials = Base64.getEncoder.encodeToString(s"${userName}:${userToken}".getBytes(StandardCharsets.UTF_8)) def apply(request: Request): Request = request.addHeaders("Authorization" -> s"Bearer $base64Credentials") override def toString: String = "OAuthClient(****)" -} sealed trait PublishingType -object PublishingType { +object PublishingType: case object Automatic extends PublishingType case object UserManaged extends PublishingType -} /** * Exception thrown when an HTTP request to the Sonatype API fails with a non-2xx status. @@ -292,6 +270,6 @@ object PublishingType { */ class SonaStatusError(val status: Int, val body: String) extends RuntimeException( - if (body.nonEmpty) s"Unexpected status: $status\n$body" + if body.nonEmpty then s"Unexpected status: $status\n$body" else s"Unexpected status: $status" ) diff --git a/main-actions/src/test/scala/sbt/CacheIvyTest.scala b/main-actions/src/test/scala/sbt/CacheIvyTest.scala index 3294d975d..d66e7a008 100644 --- a/main-actions/src/test/scala/sbt/CacheIvyTest.scala +++ b/main-actions/src/test/scala/sbt/CacheIvyTest.scala @@ -14,36 +14,37 @@ import Prop.* import sbt.librarymanagement.* import sjsonnew.shaded.scalajson.ast.unsafe.JValue -class CacheIvyTest extends Properties("CacheIvy") { +class CacheIvyTest extends Properties("CacheIvy"): import sbt.util.{ CacheStore, SingletonCache } import SingletonCache.given import sjsonnew.* import sjsonnew.support.scalajson.unsafe.Converter - private class InMemoryStore(converter: SupportConverter[JValue]) extends CacheStore { + private class InMemoryStore(converter: SupportConverter[JValue]) extends CacheStore: private var content: JValue = scala.compiletime.uninitialized override def delete(): Unit = () override def close(): Unit = () override def read[T: JsonReader](): T = try converter.fromJsonUnsafe[T](content) - catch { case t: Throwable => t.printStackTrace(); throw t } + catch + case t: Throwable => + t.printStackTrace() + throw t override def read[T: JsonReader](default: => T): T = try read[T]() - catch { case _: Throwable => default } + catch case _: Throwable => default override def write[T: JsonWriter](value: T): Unit = content = converter.toJsonUnsafe(value) - } private def testCache[T, U]( f: (SingletonCache[T], CacheStore) => U - )(using cache: SingletonCache[T]): U = { + )(using cache: SingletonCache[T]): U = val store = new InMemoryStore(Converter) f(cache, store) - } private def cachePreservesEquality[T: JsonFormat]( m: T, @@ -56,19 +57,18 @@ class CacheIvyTest extends Properties("CacheIvy") { } given arbConfigRef: Arbitrary[ConfigRef] = Arbitrary( - for { - n <- Gen.alphaStr - } yield ConfigRef(n) + for n <- Gen.alphaStr + yield ConfigRef(n) ) given arbExclusionRule: Arbitrary[InclExclRule] = Arbitrary( - for { + for o <- Gen.alphaStr n <- Gen.alphaStr a <- Gen.alphaStr v <- arbCrossVersion.arbitrary cs <- arbitrary[List[ConfigRef]] - } yield InclExclRule(o, n, a, cs.toVector, v) + yield InclExclRule(o, n, a, cs.toVector, v) ) given arbCrossVersion: Arbitrary[CrossVersion] = Arbitrary { @@ -77,16 +77,15 @@ class CacheIvyTest extends Properties("CacheIvy") { } given arbArtifact: Arbitrary[Artifact] = Arbitrary { - for { - (n, t, e, cls) <- arbitrary[(String, String, String, String)] - } yield Artifact(n, t, e, cls) // keep it simple + for (n, t, e, cls) <- arbitrary[(String, String, String, String)] + yield Artifact(n, t, e, cls) // keep it simple } given arbModuleID: Arbitrary[ModuleID] = Arbitrary { - for { + for o <- Gen.identifier n <- Gen.identifier - r <- for { n <- Gen.numChar; ns <- Gen.numStr } yield s"$n$ns" + r <- for n <- Gen.numChar; ns <- Gen.numStr yield s"$n$ns" cs <- arbitrary[Option[String]] branch <- arbitrary[Option[String]] isChanging <- arbitrary[Boolean] @@ -97,7 +96,7 @@ class CacheIvyTest extends Properties("CacheIvy") { inclusions <- Gen.listOf(arbitrary[InclExclRule]) extraAttributes <- Gen.mapOf(arbitrary[(String, String)]) crossVersion <- arbitrary[CrossVersion] - } yield ModuleID( + yield ModuleID( organization = o, name = n, revision = r, @@ -115,22 +114,19 @@ class CacheIvyTest extends Properties("CacheIvy") { } property("moduleIDFormat") = forAll { (m: ModuleID) => - def str(m: ModuleID) = { + def str(m: ModuleID) = import m.* s"ModuleID($organization, ${m.name}, $revision, $configurations, $isChanging, $isTransitive, $isForce, $explicitArtifacts, $exclusions, " + s"$inclusions, $extraAttributes, $crossVersion, $branchName)" - } - def eq(a: ModuleID, b: ModuleID): Prop = { + def eq(a: ModuleID, b: ModuleID): Prop = def rest = a.withCrossVersion(b.crossVersion) == b - (a.crossVersion, b.crossVersion) match { + (a.crossVersion, b.crossVersion) match case (_: Disabled, _: Disabled) => rest case (_: Binary, _: Binary) => rest case (_: Full, _: Full) => rest case (a, b) => Prop(false) :| s"CrossVersions don't match: $a vs $b" - } - } import sbt.librarymanagement.LibraryManagementCodec.given cachePreservesEquality(m, eq, str) } -} +end CacheIvyTest diff --git a/main-actions/src/test/scala/sbt/internal/sona/SonaClientTest.scala b/main-actions/src/test/scala/sbt/internal/sona/SonaClientTest.scala index a376519b3..b30ffeff8 100644 --- a/main-actions/src/test/scala/sbt/internal/sona/SonaClientTest.scala +++ b/main-actions/src/test/scala/sbt/internal/sona/SonaClientTest.scala @@ -123,10 +123,10 @@ final class RecordingLogger extends BasicLogger: private var events: List[LogEvent] = Nil def getEvents: List[LogEvent] = events.reverse - def trace(t: => Throwable): Unit = { events ::= new Trace(t) } - def log(level: Level.Value, message: => String): Unit = { events ::= new Log(level, message) } - def success(message: => String): Unit = { events ::= new Success(message) } - def logAll(es: Seq[LogEvent]): Unit = { events :::= es.toList } + def trace(t: => Throwable): Unit = events ::= new Trace(t) + def log(level: Level.Value, message: => String): Unit = events ::= new Log(level, message) + def success(message: => String): Unit = events ::= new Success(message) + def logAll(es: Seq[LogEvent]): Unit = events :::= es.toList def control(event: ControlEvent.Value, message: => String): Unit = events ::= new ControlEvent(event, message) diff --git a/main-command/src/main/scala/sbt/BasicCommandStrings.scala b/main-command/src/main/scala/sbt/BasicCommandStrings.scala index 0034d48de..fc583397f 100644 --- a/main-command/src/main/scala/sbt/BasicCommandStrings.scala +++ b/main-command/src/main/scala/sbt/BasicCommandStrings.scala @@ -11,7 +11,7 @@ package sbt import sbt.util.Level import sbt.internal.util.complete.HistoryCommands -object BasicCommandStrings { +object BasicCommandStrings: val HelpCommand: String = "help" val CompletionsCommand: String = "completions" val Exit: String = "exit" @@ -64,14 +64,13 @@ $HelpCommand def exitBrief: String = "Terminates the remote client or the build when called from the console." def shutdownBrief: String = "Terminates the build." - def logLevelHelp: Help = { + def logLevelHelp: Help = val levels = Level.values.toSeq val levelList = levels.mkString(", ") val brief = ("", "Sets the logging level to 'log-level'. Valid levels: " + levelList) val detailed = levels.map(l => (l.toString, logLevelDetail(l))).toMap Help(brief, detailed) - } private def logLevelDetail(level: Level.Value): String = s"""$level @@ -89,14 +88,13 @@ $HelpCommand """ def runEarly(command: String): String = s"$EarlyCommand($command)" - private[sbt] def isEarlyCommand(s: String): Boolean = { + private[sbt] def isEarlyCommand(s: String): Boolean = val levelOptions = Level.values.toSeq flatMap { elem => List("-" + elem, "--" + elem) } (s.startsWith(EarlyCommand + "(") && s.endsWith(")")) || (levelOptions contains s) || (s.startsWith("-" + AddPluginSbtFileCommand) || s.startsWith("--" + AddPluginSbtFileCommand)) - } val EarlyCommand: String = "early" val EarlyCommandBrief: (String, String) = @@ -108,11 +106,10 @@ $HelpCommand The order is preserved between all early commands, so `sbt "early(a)" "early(b)"` executes `a` and `b` in order. """ - def addPluginSbtFileHelp(): Help = { + def addPluginSbtFileHelp(): Help = val brief = (s"--$AddPluginSbtFileCommand=", "Adds the given *.sbt file to the plugin build.") Help(brief) - } val AddPluginSbtFileCommand: String = "addPluginSbtFile" @@ -261,4 +258,4 @@ $AliasCommand name= private[sbt] val networkExecPrefix = "__" private[sbt] val DisconnectNetworkChannel = s"${networkExecPrefix}disconnectNetworkChannel" -} +end BasicCommandStrings diff --git a/main-command/src/main/scala/sbt/BasicCommands.scala b/main-command/src/main/scala/sbt/BasicCommands.scala index dd97d3deb..28fbc726f 100644 --- a/main-command/src/main/scala/sbt/BasicCommands.scala +++ b/main-command/src/main/scala/sbt/BasicCommands.scala @@ -41,7 +41,7 @@ import scala.collection.mutable.ListBuffer import scala.util.control.NonFatal import xsbti.VirtualFile -object BasicCommands { +object BasicCommands: lazy val allBasicCommands: Seq[Command] = Seq( nop, ignore, @@ -79,29 +79,26 @@ object BasicCommands { token(l.toString) ) reduce (_ | _) - private def addPluginSbtFileParser: Parser[File] = { + private def addPluginSbtFileParser: Parser[File] = token(AddPluginSbtFileCommand) ~> (":" | "=" | Space.map(_.toString)) ~> (StringBasic).examples( "/some/extra.sbt" ) map { new File(_) } - } - private def addPluginSbtFileStringParser: Parser[String] = { + private def addPluginSbtFileStringParser: Parser[String] = token( token(AddPluginSbtFileCommand) ~ (":" | "=" | Space.map(_.toString)) ~ (StringBasic) .examples("/some/extra.sbt") map { case s1 ~ s2 ~ s3 => s1 + s2 + s3 } ) - } - private def earlyParser: State => Parser[String] = (s: State) => { + private def earlyParser: State => Parser[String] = (s: State) => val p1 = token(EarlyCommand + "(").flatMap(_ => otherCommandParser(s) <~ token(")")) val p2 = (token("-") | token("--")).flatMap(_ => levelParser) val p3 = (token("-") | token("--")).flatMap(_ => addPluginSbtFileStringParser) p1 | p2 | p3 - } private def earlyHelp = Help(EarlyCommand, EarlyCommandBrief, EarlyCommandDetailed) @@ -118,23 +115,21 @@ object BasicCommands { def help: Command = Command.make(HelpCommand, helpBrief, helpDetailed)(helpParser) - def helpParser(s: State): Parser[() => State] = { + def helpParser(s: State): Parser[() => State] = val h = s.definedCommands.foldLeft(Help.empty)((a, b) => a ++ (try b.help(s) - catch { case NonFatal(_) => Help.empty }) + catch case NonFatal(_) => Help.empty) ) val helpCommands = h.detail.keySet val spacedArg = singleArgument(helpCommands).? applyEffect(spacedArg)(runHelp(s, h)) - } - def runHelp(s: State, h: Help)(arg: Option[String]): State = { + def runHelp(s: State, h: Help)(arg: Option[String]): State = val message = try Help.message(h, arg) - catch { case NonFatal(ex) => ex.toString } + catch case NonFatal(ex) => ex.toString System.out.println(message) s - } def completionsCommand: Command = Command(CompletionsCommand, CompletionsBrief, CompletionsDetailed)(_ => completionsParser)( @@ -144,27 +139,25 @@ object BasicCommands { @deprecated("No longer public", "1.1.1") def completionsParser(state: State): Parser[String] = completionsParser - private def completionsParser: Parser[String] = { + private def completionsParser: Parser[String] = val notQuoted = (NotQuoted ~ any.*) map { (nq, s) => nq + s } val quotedOrUnquotedSingleArgument = Space ~> (StringVerbatim | StringEscapable | notQuoted) token((quotedOrUnquotedSingleArgument ?? "").examples("", " ")) - } - def runCompletions(state: State)(input: String): State = { + def runCompletions(state: State)(input: String): State = Parser.completions(state.combinedParser, input, 9).get map { c => - if (c.isEmpty) input else input + c.append + if c.isEmpty then input else input + c.append } foreach { c => System.out.println("[completions] " + c.replaceAll("\n", " ")) } state - } private[sbt] def multiParserImpl(state: Option[State]): Parser[List[String]] = multiParserImpl(state, "alias" :: Nil) private[sbt] def multiParserImpl( state: Option[State], exclude: Seq[String] - ): Parser[List[String]] = { + ): Parser[List[String]] = val nonSemi = charClass(_ != ';', "not ';'") val nonDelim = charClass(c => c != '"' && c != '{' && c != '}', label = "not '\"', '{', '}'") // Accept empty commands to simplify the parser. @@ -174,10 +167,9 @@ object BasicCommands { val completionParser: Option[Parser[String]] = state.map(s => (matched(s.nonMultiParser) & cmdPart) | cmdPart) - val cmdParser = { + val cmdParser = val parser = completionParser.getOrElse(cmdPart).map(_.trim) exclude.foldLeft(parser) { (p, e) => p & not(OptSpace ~ s"$e ", s"!$e").examples() } - } val multiCmdParser: Parser[String] = token(';') ~> OptSpace ~> cmdParser /* @@ -188,30 +180,27 @@ object BasicCommands { * we wanted to relax that restriction, then we could just replace the flatMap below with * `rest.filterNot(_.isEmpty)`. */ - def validateCommands(s: Seq[String]): Parser[List[String]] = { + def validateCommands(s: Seq[String]): Parser[List[String]] = val result = new ListBuffer[String] val it = s.iterator var fail = false - while (it.hasNext && !fail) { - it.next() match { + while it.hasNext && !fail do + it.next() match case "" => fail = it.hasNext; () case next => result += next; () - } - } - if (fail) Parser.failure(s"Couldn't parse empty commands in ${s.mkString(";")}") + if fail then Parser.failure(s"Couldn't parse empty commands in ${s.mkString(";")}") else Parser.success(result.toList) - } (cmdParser ~ multiCmdParser.+) .flatMap { case ("", rest) => validateCommands(rest) case (p, rest) => validateCommands(rest).map(p :: _) } - } + end multiParserImpl def multiParser(s: State): Parser[List[String]] = multiParserImpl(Some(s)) - def multiApplied(state: State): Parser[() => State] = { + def multiApplied(state: State): Parser[() => State] = Command.applyEffect(multiParserImpl(Some(state))) { // the (@ _ :: _) ensures tail length >= 1. case commands @ first :: (tail @ _ :: _) => @@ -236,7 +225,7 @@ object BasicCommands { // fall back to prefixing the multi commands to the state. // state.nonMultiCommands.view.flatMap { command => - command.nameOption match { + command.nameOption match case Some(commandName) if first.startsWith(commandName) => // A lot of commands expect leading semicolons in their parsers. In order to // ensure that they are multi-command capable, we strip off any leading spaces. @@ -251,14 +240,11 @@ object BasicCommands { (first.drop(commandName.length).trim :: Nil ::: tail).mkString(";") parse(commandArgs, command.parser(state)).toOption case _ => none[() => State] - } - }.headOption match { + }.headOption match case Some(s) => s() case _ => commands ::: state - } case commands => commands ::: state } - } val multi: Command = Command.custom(multiApplied, Help(Multi, MultiBrief, MultiDetailed), Multi) @@ -271,7 +257,7 @@ object BasicCommands { def ifLast: Command = Command(IfLast, Help.more(IfLast, IfLastDetailed))(otherCommandParser)((s, arg) => - if (s.remainingCommands.isEmpty) arg :: s else s + if s.remainingCommands.isEmpty then arg :: s else s ) def append: Command = @@ -294,14 +280,14 @@ object BasicCommands { def popOnFailure: Command = Command.command(PopOnFailure) { s => val stack = s.get(OnFailureStack).getOrElse(nil) val updated = - if (stack.isEmpty) s.remove(OnFailureStack) else s.put(OnFailureStack, stack.tail) + if stack.isEmpty then s.remove(OnFailureStack) else s.put(OnFailureStack, stack.tail) updated.copy(onFailure = stack.headOption.flatten) } def reboot: Command = Command(RebootCommand, Help.more(RebootCommand, RebootDetailed))(_ => rebootOptionParser) { case (s, (full, currentOnly)) => - val option = if (full) " full" else if (currentOnly) " dev" else "" + val option = if full then " full" else if currentOnly then " dev" else "" RebootNetwork :: s"$RebootImpl$option" :: s } def rebootImpl: Command = @@ -313,11 +299,10 @@ object BasicCommands { @deprecated("Use rebootOptionParser", "1.1.0") def rebootParser(s: State): Parser[Boolean] = rebootOptionParser map { case (full, _) => full } - private[sbt] def rebootOptionParser: Parser[(Boolean, Boolean)] = { + private[sbt] def rebootOptionParser: Parser[(Boolean, Boolean)] = val fullOption = "full" ^^^ ((true, false)) val devOption = "dev" ^^^ ((false, true)) token(Space ~> (fullOption | devOption)) ?? ((false, false)) - } def call: Command = Command(ApplyCommand, Help.more(ApplyCommand, ApplyDetailed))(_ => callParser) { @@ -325,10 +310,10 @@ object BasicCommands { val parentLoader = getClass.getClassLoader def argsStr = args mkString ", " def cpStr = cp mkString File.pathSeparator - def fromCpStr = if (cp.isEmpty) "" else s" from $cpStr" + def fromCpStr = if cp.isEmpty then "" else s" from $cpStr" state.log.info(s"Applying State transformations $argsStr$fromCpStr") val loader = - if (cp.isEmpty) parentLoader else toLoader(cp.map(f => Paths.get(f)), parentLoader) + if cp.isEmpty then parentLoader else toLoader(cp.map(f => Paths.get(f)), parentLoader) val loaded = args.map(arg => ModuleUtilities.getObject(arg, loader).asInstanceOf[State => State]) loaded.foldLeft(state)((s, obj) => obj(s)) @@ -337,14 +322,13 @@ object BasicCommands { def callParser: Parser[(Seq[String], Seq[String])] = token(Space) ~> ((classpathOptionParser ?? nilSeq) ~ rep1sep(className, token(Space))) - private def className: Parser[String] = { + private def className: Parser[String] = val base = StringBasic & not('-' ~> any.*, "Class name cannot start with '-'.") def single(s: String) = Completions.single(Completion.displayOnly(s)) val compl = TokenCompletions.fixed((seen, _) => - if (seen.startsWith("-")) Completions.nil else single("") + if seen.startsWith("-") then Completions.nil else single("") ) token(base, compl) - } private def classpathOptionParser: Parser[Seq[String]] = token(("-cp" | "-classpath") ~> Space) ~> classpathStrings <~ token(Space) @@ -353,18 +337,16 @@ object BasicCommands { token(StringBasic.map(s => IO.pathSplit(s).toSeq), "") def exit: Command = Command.command(TerminateAction, exitBrief, exitBrief) { s => - s.source match { + s.source match case Some(c) if c.channelName.startsWith("network") => s"${DisconnectNetworkChannel} ${c.channelName}" :: s case _ => s.exit(true) - } } def shutdown: Command = Command.command(Shutdown, shutdownBrief, shutdownBrief) { s => - s.source match { + s.source match case Some(c) if c.channelName.startsWith("network") => s"${DisconnectNetworkChannel} ${c.channelName}" :: (Exec(Shutdown, None) +: s) case _ => s.exit(true) - } } def history: Command = Command.custom(historyParser, BasicCommandStrings.historyHelp) @@ -373,12 +355,11 @@ object BasicCommands { Command.applyEffect(HistoryCommands.actionParser) { histFun => val hp = s.get(historyPath).flatten val lines = hp.toList.flatMap(p => IO.readLines(p)).toIndexedSeq - histFun(CHistory(lines, hp)) match { + histFun(CHistory(lines, hp)) match case Some(commands) => commands foreach println // printing is more appropriate than logging (commands ::: s).continue case None => s.fail - } } def oldshell: Command = Command.command(OldShell, Help.more(Shell, OldShellDetailed)) { s => @@ -388,7 +369,7 @@ object BasicCommands { case None => "> " val reader = new FullReader(history, s.combinedParser, LineReader.HandleCONT, Terminal.console) val line = reader.readLine(prompt) - line match { + line match case Some(line) => val newState = s .copy( @@ -396,9 +377,8 @@ object BasicCommands { remainingCommands = Exec(line, s.source) +: Exec(OldShell, None) +: s.remainingCommands ) .setInteractive(true) - if (line.trim.isEmpty) newState else newState.clearGlobalLog + if line.trim.isEmpty then newState else newState.clearGlobalLog case None => s.setInteractive(false) - } } def client: Command = @@ -407,31 +387,28 @@ object BasicCommands { def clientParser: Parser[Seq[String]] = (token(Space) ~> repsep(StringBasic, token(Space))) | (token(EOF).map(_ => nilSeq)) - def runClient(s0: State, inputArg: Seq[String]): State = { + def runClient(s0: State, inputArg: Seq[String]): State = val arguments = inputArg.toList ++ - (s0.remainingCommands match { + (s0.remainingCommands match case e :: Nil if e.commandLine == "shell" => nil - case xs => xs map (_.commandLine) - }) + case xs => xs map (_.commandLine)) NetworkClient.run(s0.configuration, arguments) TerminateAction :: s0.copy(remainingCommands = Nil) - } def read: Command = Command(ReadCommand, Help.more(ReadCommand, ReadDetailed))(readParser)(doRead(_)(_)) - def readParser(s: State): Parser[Either[Int, Seq[File]]] = { + def readParser(s: State): Parser[Either[Int, Seq[File]]] = val files = (token(Space) ~> fileParser(s.baseDir)).+ val portAndSuccess = token(OptSpace) ~> Port portAndSuccess || files - } def doRead(s: State)(arg: Either[Int, Seq[File]]): State = - arg match { + arg match case Left(portAndSuccess) => val port = math.abs(portAndSuccess) val previousSuccess = portAndSuccess >= 0 - readMessage(port, previousSuccess) match { + readMessage(port, previousSuccess) match case Some(message) => (message :: (ReadCommand + " " + port) :: s).copy( onFailure = Some(Exec(ReadCommand + " " + (-port), s.source)) @@ -439,27 +416,23 @@ object BasicCommands { case None => System.err.println("Connection closed.") s.fail - } case Right(from) => val notFound = notReadable(from) - if (notFound.isEmpty) + if notFound.isEmpty then // this means that all commands from all files are loaded, parsed, & inserted before any are executed readLines(from).toList ::: s - else { + else s.log.error("Command file(s) not readable: \n\t" + notFound.mkString("\n\t")) s - } - } - private def readMessage(port: Int, previousSuccess: Boolean): Option[String] = { + private def readMessage(port: Int, previousSuccess: Boolean): Option[String] = // split into two connections because this first connection ends the previous communication xsbt.IPC.client(port) { _.send(previousSuccess.toString) } // and this second connection starts the next communication xsbt.IPC.client(port) { ipc => val message = ipc.receive - if (message eq null) None else Some(message) + if message eq null then None else Some(message) } - } def alias: Command = Command(AliasCommand, Help.more(AliasCommand, AliasDetailed)) { s => @@ -478,12 +451,11 @@ object BasicCommands { args: Option[(String, Option[Option[String]])], definedKeyNames: State => Set[String] ): State = - args match { + args match case Some(x ~ None) if !x.isEmpty => printAlias(s, x.trim); s case Some(name ~ Some(None)) => removeAlias(s, name.trim) case Some(name ~ Some(Some(value))) => addAlias(s, name.trim, value.trim, definedKeyNames) case _ => printAliases(s); s - } def addAlias(s: State, name: String, value: String): State = addAlias(s, name, value, _ => Set.empty) @@ -522,15 +494,16 @@ object BasicCommands { def isAliasNamed(name: String, c: Command): Boolean = isNamed(name, getAlias(c)) def isNamed(name: String, alias: Option[(String, String)]): Boolean = - alias match { case None => false; case Some((n, _)) => name == n } + alias match + case None => false; + case Some((n, _)) => name == n def getAlias(c: Command): Option[(String, String)] = c.tags.get(CommandAliasKey) def printAlias(s: State, name: String): Unit = printAliases(aliases(s, (n, _) => n == name)) def printAliases(s: State): Unit = printAliases(allAliases(s)) def printAliases(as: Seq[(String, String)]): Unit = - for ((name, value) <- as) - println("\t" + name + " = " + value) + for (name, value) <- as do println("\t" + name + " = " + value) def aliasNames(s: State): Seq[String] = allAliases(s).map(_._1) def allAliases(s: State): Seq[(String, String)] = aliases(s, (_, _) => true) @@ -542,7 +515,7 @@ object BasicCommands { .make(name, (name, s"'$value'"), s"Alias of '$value'")(aliasBody(name, value)) .tag(CommandAliasKey, (name, value)) - def aliasBody(name: String, value: String)(state: State): Parser[() => State] = { + def aliasBody(name: String, value: String)(state: State): Parser[() => State] = val aliasRemoved = removeAlias(state, name) // apply the alias value to the commands of `state` except for the alias to avoid recursion (#933) val partiallyApplied = Parser(aliasRemoved.combinedParser)(value) @@ -552,15 +525,13 @@ object BasicCommands { // by scheduling the expanded alias instead of directly executing, // we get errors on the expanded string (#598) arg.map(str => () => (value + str) :: state) - } def delegateToAlias(name: String, orElse: Parser[() => State])( state: State ): Parser[() => State] = - aliases(state, (nme, _) => nme == name).headOption match { + aliases(state, (nme, _) => nme == name).headOption match case None => orElse case Some((n, v)) => aliasBody(n, v)(state) - } val CommandAliasKey: AttributeKey[(String, String)] = AttributeKey[(String, String)]( @@ -572,37 +543,31 @@ object BasicCommands { (key: Parser[String]).examples() ~> " ".examples() ~> matched(any.*).examples() def reportResultsCommand = Command.arb(_ => reportParser(ReportResult)) { (state, id) => - val newState = state.get(execMap) match { + val newState = state.get(execMap) match case Some(m) => state.put(execMap, m - id) case _ => state - } - newState.get(execResults) match { + newState.get(execResults) match case Some(m) if m.contains(id) => state.put(execResults, m - id) case _ => state.fail - } } def mapExecCommand = Command.arb(_ => reportParser(MapExec)) { (state, mapping) => - mapping.split(" ") match { + mapping.split(" ") match case Array(key, value) => - state.get(execMap) match { + state.get(execMap) match case Some(m) => state.put(execMap, m + (key -> value)) case None => state.put(execMap, Map(key -> value)) - } case _ => state - } } def completeExecCommand = Command.arb(_ => reportParser(CompleteExec)) { (state, id) => - val newState = state.get(execResults) match { + val newState = state.get(execResults) match case Some(m) => state.put(execResults, m + (id -> true)) case _ => state.put(execResults, Map(id -> true)) - } - newState.get(execMap) match { + newState.get(execMap) match case Some(m) => newState.put(execMap, m - id) case _ => newState - } } private[sbt] val execResults = AttributeKey[Map[String, Boolean]]("execResults", Int.MaxValue) private[sbt] val execMap = AttributeKey[Map[String, String]]("execMap", Int.MaxValue) -} +end BasicCommands diff --git a/main-command/src/main/scala/sbt/BasicKeys.scala b/main-command/src/main/scala/sbt/BasicKeys.scala index bbb2a4029..962d7214a 100644 --- a/main-command/src/main/scala/sbt/BasicKeys.scala +++ b/main-command/src/main/scala/sbt/BasicKeys.scala @@ -19,7 +19,7 @@ import sbt.util.{ ActionCacheStore, Level } import scala.concurrent.duration.FiniteDuration import xsbti.{ FileConverter, VirtualFile } -object BasicKeys { +object BasicKeys: val historyPath = AttributeKey[Option[File]]( "history", "The location where command line history is persisted.", @@ -178,6 +178,6 @@ object BasicKeys { "Toggles whether or not to close system in, out and error when the server starts.", 1000 ) -} +end BasicKeys case class TemplateResolverInfo(module: ModuleID, implementationClass: String) diff --git a/main-command/src/main/scala/sbt/Command.scala b/main-command/src/main/scala/sbt/Command.scala index e0606156c..d7e5d0288 100644 --- a/main-command/src/main/scala/sbt/Command.scala +++ b/main-command/src/main/scala/sbt/Command.scala @@ -21,25 +21,23 @@ import sbt.internal.util.Util.nilSeq * This means that a command can look at or modify other sbt settings, for example. * Typically you would resort to a command when you need to do something that's impossible in a regular task. */ -sealed trait Command { +sealed trait Command: def help: State => Help def parser: State => Parser[() => State] def tags: AttributeMap def tag[T](key: AttributeKey[T], value: T): Command - def nameOption: Option[String] = this match { + def nameOption: Option[String] = this match case sc: SimpleCommand => Some(sc.name) case _ => None - } -} private[sbt] final class SimpleCommand( val name: String, private[sbt] val help0: Help, val parser: State => Parser[() => State], val tags: AttributeMap -) extends Command { +) extends Command: assert(Command.validID(name), s"'$name' is not a valid command name.") @@ -49,21 +47,19 @@ private[sbt] final class SimpleCommand( new SimpleCommand(name, help0, parser, tags.put(key, value)) override def toString = s"SimpleCommand($name)" -} private[sbt] final class ArbitraryCommand( val parser: State => Parser[() => State], val help: State => Help, val tags: AttributeMap, override val nameOption: Option[String] -) extends Command { +) extends Command: def this(parser: State => Parser[() => State], help: State => Help, tags: AttributeMap) = this(parser, help, tags, None) def tag[T](key: AttributeKey[T], value: T): ArbitraryCommand = new ArbitraryCommand(parser, help, tags.put(key, value)) -} -object Command { +object Command: import DefaultParsers.* // Lowest-level command construction @@ -148,10 +144,9 @@ object Command { )(effect: (State, T) => State): State => Parser[() => State] = s => applyEffect(parser(s))(t => effect(s, t)) - def combine(cmds: Seq[Command]): State => Parser[() => State] = { + def combine(cmds: Seq[Command]): State => Parser[() => State] = val (simple, arbs) = separateCommands(cmds) state => arbs.map(_.parser(state)).foldLeft(simpleParser(simple)(state))(_ | _) - } private def separateCommands( cmds: Seq[Command] @@ -163,43 +158,38 @@ object Command { def simpleParser(cmds: Seq[SimpleCommand]): State => Parser[() => State] = simpleParser(cmds.map(sc => (sc.name, argParser(sc))).toMap) - private def argParser(sc: SimpleCommand): State => Parser[() => State] = { + private def argParser(sc: SimpleCommand): State => Parser[() => State] = def usageError = s"${sc.name} usage:" + Help.message(sc.help0, None) s => (Parser.softFailure(usageError, definitive = true): Parser[() => State]) | sc.parser(s) - } def simpleParser( commandMap: Map[String, State => Parser[() => State]] ): State => Parser[() => State] = state => token(OpOrID.examples(commandMap.keys.toSet)).flatMap: id => - (commandMap get id) match { + (commandMap get id) match case None => failure(invalidValue("command", commandMap.keys)(id)) case Some(c) => c(state) - } // overload instead of default parameter to keep binary compatibility @deprecated("Use overload that takes the onParseError callback", since = "1.9.4") def process(command: String, state: State): State = process(command, state, _ => ()) - def process(command: String, state: State, onParseError: String => Unit): State = { - (if (command.contains(";")) parse(command, state.combinedParser) - else parse(command, state.nonMultiParser)) match { + def process(command: String, state: State, onParseError: String => Unit): State = + (if command.contains(";") then parse(command, state.combinedParser) + else parse(command, state.nonMultiParser)) match case Right(s) => s() // apply command. command side effects happen here case Left(errMsg) => state.log.error(errMsg) onParseError(errMsg) state.fail - } - } def invalidValue(label: String, allowed: Iterable[String])(value: String): String = s"Not a valid $label: $value" + similar(value, allowed) - def similar(value: String, allowed: Iterable[String]): String = { - val suggested = if (value.length > 2) suggestions(value, allowed.toSeq) else nilSeq - if (suggested.isEmpty) "" else suggested.mkString(" (similar: ", ", ", ")") - } + def similar(value: String, allowed: Iterable[String]): String = + val suggested = if value.length > 2 then suggestions(value, allowed.toSeq) else nilSeq + if suggested.isEmpty then "" else suggested.mkString(" (similar: ", ", ", ")") def suggestions( a: String, @@ -227,29 +217,27 @@ object Command { def spacedC(name: String, c: Parser[Char]): Parser[String] = ((c & opOrIDSpaced(name)) ~ c.+) map { (f, rem) => (f +: rem).mkString } -} +end Command -trait Help { +trait Help: def detail: Map[String, String] def brief: Seq[(String, String)] def more: Set[String] def ++(o: Help): Help -} private final class Help0( val brief: Seq[(String, String)], val detail: Map[String, String], val more: Set[String] -) extends Help { +) extends Help: def ++(h: Help): Help = new Help0( Help0.this.brief ++ h.brief, Map(Help0.this.detail.toSeq ++ h.detail.toSeq*), more ++ h.more ) -} -object Help { +object Help: val empty: Help = briefDetail(Nil) def apply(name: String, briefHelp: (String, String), detail: String): Help = @@ -277,22 +265,18 @@ object Help { import CommandUtil.* def message(h: Help, arg: Option[String]): String = - arg match { + arg match case Some(x) => detail(x, h.detail) case None => val brief = aligned(" ", " ", h.brief).mkString("\n", "\n", "\n") val more = h.more - if (more.isEmpty) - brief - else - brief + "\n" + moreMessage(more.toSeq.sorted) - } + if more.isEmpty then brief + else brief + "\n" + moreMessage(more.toSeq.sorted) def moreMessage(more: Seq[String]): String = more.mkString("More command help available using 'help ' for:\n ", ", ", "\n") -} +end Help -trait CommandDefinitions extends (State => State) { +trait CommandDefinitions extends (State => State): def commands: Seq[Command] = ReflectUtilities.allValsC(this, classOf[Command]).values.toSeq def apply(s: State): State = s ++ commands -} diff --git a/main-command/src/main/scala/sbt/CommandUtil.scala b/main-command/src/main/scala/sbt/CommandUtil.scala index c9a9ac7c6..f8124a672 100644 --- a/main-command/src/main/scala/sbt/CommandUtil.scala +++ b/main-command/src/main/scala/sbt/CommandUtil.scala @@ -19,12 +19,11 @@ import sbt.internal.util.Util.nilSeq import sbt.io.IO import sbt.io.syntax.* -object CommandUtil { +object CommandUtil: def readLines(files: Seq[File]): Seq[String] = files flatMap (IO.readLines(_)) flatMap processLine - def processLine(s: String): Option[String] = { - val s2 = s.trim; if (ignoreLine(s2)) None else Some(s2) - } + def processLine(s: String): Option[String] = + val s2 = s.trim; if ignoreLine(s2) then None else Some(s2) def ignoreLine(s: String): Boolean = s.isEmpty || s.startsWith("#") private def canRead = (_: File).canRead @@ -34,58 +33,48 @@ object CommandUtil { // slightly better fallback in case of older launcher def bootDirectory(state: State): File = try state.configuration.provider.scalaProvider.launcher.bootDirectory - catch { case _: NoSuchMethodError => new File(".").getAbsoluteFile } + catch case _: NoSuchMethodError => new File(".").getAbsoluteFile def aligned(pre: String, sep: String, in: Seq[(String, String)]): Seq[String] = - if (in.isEmpty) nilSeq - else { + if in.isEmpty then nilSeq + else val width = in.iterator.map(_._1.length).max - for ((a, b) <- in) yield pre + fill(a, width) + sep + b - } + for (a, b) <- in yield pre + fill(a, width) + sep + b def fill(s: String, size: Int): String = s + " " * math.max(size - s.length, 0) def withAttribute[T](s: State, key: AttributeKey[T], ifMissing: String)(f: T => State): State = - s.get(key) match { + s.get(key) match case None => s.log.error(ifMissing); s.fail case Some(nav) => f(nav) - } - def singleArgument(exampleStrings: Set[String]): Parser[String] = { + def singleArgument(exampleStrings: Set[String]): Parser[String] = val arg = (NotSpaceClass ~ any.*) map { (ns, s) => (ns +: s).mkString } token(Space) ~> token(arg.examples(exampleStrings)) - } def detail(selected: String, detailMap: Map[String, String]): String = - detailMap.get(selected) match { + detailMap.get(selected) match case Some(exactDetail) => exactDetail case None => - try { + try val details = searchHelp(selected, detailMap) - if (details.isEmpty) - "No matches for regular expression '" + selected + "'." - else - layoutDetails(details) - } catch { + if details.isEmpty then "No matches for regular expression '" + selected + "'." + else layoutDetails(details) + catch case pse: PatternSyntaxException => sys.error("Invalid regular expression (java.util.regex syntax).\n" + pse.getMessage) - } - } - def searchHelp(selected: String, detailMap: Map[String, String]): Map[String, String] = { + def searchHelp(selected: String, detailMap: Map[String, String]): Map[String, String] = val pattern = Pattern.compile(selected, HelpPatternFlags) detailMap flatMap { (k, v) => val contentMatches = Highlight.showMatches(pattern)(v) val keyMatches = Highlight.showMatches(pattern)(k) val keyString = Highlight.bold(keyMatches getOrElse k) val contentString = contentMatches getOrElse v - if (keyMatches.isDefined || contentMatches.isDefined) - Seq((keyString, contentString)) - else - nilSeq + if keyMatches.isDefined || contentMatches.isDefined then Seq((keyString, contentString)) + else nilSeq } - } def layoutDetails(details: Map[String, String]): String = details.map { (k, v) => k + "\n\n " + v }.mkString("\n", "\n\n", "\n") @@ -94,4 +83,4 @@ object CommandUtil { private[sbt] def isSbtBuild(baseDir: File) = (baseDir / "project").exists() || (baseDir * "*.sbt").get().nonEmpty -} +end CommandUtil diff --git a/main-command/src/main/scala/sbt/ExceptionCategory.scala b/main-command/src/main/scala/sbt/ExceptionCategory.scala index 9fdf40eab..bf653171f 100644 --- a/main-command/src/main/scala/sbt/ExceptionCategory.scala +++ b/main-command/src/main/scala/sbt/ExceptionCategory.scala @@ -13,23 +13,19 @@ import scala.annotation.tailrec import sbt.internal.util.{ AlreadyHandledException, MessageOnlyException, UnprintableException } -private[sbt] sealed abstract class ExceptionCategory { +private[sbt] sealed abstract class ExceptionCategory: def isFull: Boolean = false -} -private[sbt] object ExceptionCategory { +private[sbt] object ExceptionCategory: - @tailrec def apply(t: Throwable): ExceptionCategory = t match { + @tailrec def apply(t: Throwable): ExceptionCategory = t match case _: AlreadyHandledException | _: UnprintableException => AlreadyHandled case ite: InvocationTargetException => val cause = ite.getCause - if (cause == null || cause == ite) new Full(ite) else apply(cause) + if cause == null || cause == ite then new Full(ite) else apply(cause) case _: MessageOnlyException => new MessageOnly(t.toString) case _ => new Full(t) - } object AlreadyHandled extends ExceptionCategory final class MessageOnly(val message: String) extends ExceptionCategory - final class Full(val exception: Throwable) extends ExceptionCategory { + final class Full(val exception: Throwable) extends ExceptionCategory: override def isFull = true - } -} diff --git a/main-command/src/main/scala/sbt/Highlight.scala b/main-command/src/main/scala/sbt/Highlight.scala index f54d320bc..42307bae2 100644 --- a/main-command/src/main/scala/sbt/Highlight.scala +++ b/main-command/src/main/scala/sbt/Highlight.scala @@ -13,19 +13,15 @@ import scala.Console.{ BOLD, RESET } import sbt.internal.util.Terminal as UTerminal -object Highlight { +object Highlight: - def showMatches(pattern: Pattern)(line: String): Option[String] = { + def showMatches(pattern: Pattern)(line: String): Option[String] = val matcher = pattern.matcher(line) - if (UTerminal.isColorEnabled) { + if UTerminal.isColorEnabled then // ANSI codes like \033[39m (normal text color) don't work on Windows val highlighted = matcher.replaceAll(scala.Console.RED + "$0" + RESET) - if (highlighted == line) None else Some(highlighted) - } else if (matcher.find) - Some(line) - else - None - } + if highlighted == line then None else Some(highlighted) + else if matcher.find then Some(line) + else None def bold(s: String) = - if (UTerminal.isColorEnabled) BOLD + s.replace(RESET, RESET + BOLD) + RESET else s -} + if UTerminal.isColorEnabled then BOLD + s.replace(RESET, RESET + BOLD) + RESET else s diff --git a/main-command/src/main/scala/sbt/MainControl.scala b/main-command/src/main/scala/sbt/MainControl.scala index 3efb41ffa..b9ad2801a 100644 --- a/main-command/src/main/scala/sbt/MainControl.scala +++ b/main-command/src/main/scala/sbt/MainControl.scala @@ -10,17 +10,15 @@ package sbt import java.io.File -final case class Exit(code: Int) extends xsbti.Exit { +final case class Exit(code: Int) extends xsbti.Exit: require(code >= 0) -} final case class Reboot( scalaVersion: String, argsList: Seq[String], app: xsbti.ApplicationID, baseDirectory: File -) extends xsbti.Reboot { +) extends xsbti.Reboot: def arguments = argsList.toArray -} final case class ApplicationID( groupID: String, @@ -30,12 +28,11 @@ final case class ApplicationID( components: Seq[String], crossVersionedValue: xsbti.CrossValue, extra: Seq[File] -) extends xsbti.ApplicationID { +) extends xsbti.ApplicationID: def mainComponents = components.toArray def classpathExtra = extra.toArray def crossVersioned = crossVersionedValue != xsbti.CrossValue.Disabled -} -object ApplicationID { +object ApplicationID: def apply(delegate: xsbti.ApplicationID, newVersion: String): ApplicationID = apply(delegate).copy(version = newVersion) def apply(delegate: xsbti.ApplicationID): ApplicationID = @@ -48,4 +45,3 @@ object ApplicationID { delegate.crossVersionedValue, delegate.classpathExtra.toSeq ) -} diff --git a/main-command/src/main/scala/sbt/State.scala b/main-command/src/main/scala/sbt/State.scala index ec575bc9a..29ea5163b 100644 --- a/main-command/src/main/scala/sbt/State.scala +++ b/main-command/src/main/scala/sbt/State.scala @@ -50,15 +50,14 @@ final case class State( globalLogging: GlobalLogging, currentCommand: Option[Exec], next: State.Next -) extends Identity { +) extends Identity: private[sbt] lazy val (multiCommands, nonMultiCommands) = definedCommands.partition(_.nameOption.contains(BasicCommandStrings.Multi)) private[sbt] lazy val nonMultiParser = Command.combine(nonMultiCommands)(this) lazy val combinedParser: Parser[() => State] = - multiCommands.headOption match { + multiCommands.headOption match case Some(multi) => multi.parser(this) | nonMultiParser case _ => nonMultiParser - } def source: Option[CommandSource] = currentCommand match @@ -68,16 +67,15 @@ final case class State( source match case Some(s) => s.channelName.startsWith("network") case _ => false -} +end State -trait Identity { +trait Identity: override final def hashCode = super.hashCode override final def equals(a: Any) = super.equals(a) override final def toString = super.toString -} /** Convenience methods for State transformations and operations. */ -trait StateOps extends Any { +trait StateOps extends Any: def process(f: (Exec, State) => State): State /** Schedules `commands` to be run before any remaining commands. */ @@ -193,13 +191,12 @@ trait StateOps extends Any { /** Create and register a class loader cache. This should be called once at the application entry-point. */ def initializeClassLoaderCache: State -} +end StateOps -object State { +object State: private class UncloseableURLLoader(cp: Seq[File], parent: ClassLoader) - extends URLClassLoader(Array.empty, parent) { + extends URLClassLoader(Array.empty, parent): override def getURLs: Array[URL] = cp.map(_.toURI.toURL).toArray - } /** Indicates where command execution should resume after a failure. */ val FailureWall = BasicCommandStrings.FailureWall @@ -225,26 +222,24 @@ object State { * @param executed the list of the most recently executed commands, with the most recent command first. * @param maxSize the maximum number of commands to keep, or 0 to keep an unlimited number. */ - final class History private[State] (val executed: Seq[Exec], val maxSize: Int) { + final class History private[State] (val executed: Seq[Exec], val maxSize: Int): /** Adds `command` as the most recently executed command. */ - def ::(command: Exec): History = { + def ::(command: Exec): History = val prependTo = - if (maxSize > 0 && executed.size >= maxSize) executed.take(maxSize - 1) else executed + if maxSize > 0 && executed.size >= maxSize then executed.take(maxSize - 1) else executed new History(command +: prependTo, maxSize) - } /** Changes the maximum number of commands kept, adjusting the current history if necessary. */ def setMaxSize(size: Int): History = - new History(if (size <= 0) executed else executed.take(size), size) + new History(if size <= 0 then executed else executed.take(size), size) def currentOption: Option[Exec] = executed.headOption def previous: Option[Exec] = executed.drop(1).headOption - } /** Constructs an empty command History with a default, finite command limit. */ def newHistory = new History(Vector.empty, HistoryCommands.MaxLines) - def defaultReload(state: State): Reboot = { + def defaultReload(state: State): Reboot = val app = state.configuration.provider new Reboot( app.scalaProvider.version, @@ -252,7 +247,6 @@ object State { app.id, state.configuration.baseDirectory ) - } /** * Builds the `early(...)` commands that add the extra plugin sbt files back after a reboot. @@ -265,10 +259,9 @@ object State { */ private[sbt] def addPluginSbtFileArguments(state: State): List[String] = state.get(BasicKeys.extraMetaSbtFiles).toList.flatten.distinct.map { vf => - val path = vf match { + val path = vf match case f: xsbti.PathBasedFile => f.toPath.toString case f => f.id - } val command = s"${BasicCommandStrings.AddPluginSbtFileCommand}=${quote(path)}" s"${BasicCommandStrings.EarlyCommand}($command)" } @@ -286,9 +279,9 @@ object State { def stateOps(s: State): StateOps = new StateOpsImpl(s) /** Provides operations and transformations on State. */ - implicit class StateOpsImpl(val s: State) extends AnyVal with StateOps { - def process(f: (Exec, State) => State): State = { - def runCmd(cmd: Exec, remainingCommands: List[Exec]) = { + implicit class StateOpsImpl(val s: State) extends AnyVal with StateOps: + def process(f: (Exec, State) => State): State = + def runCmd(cmd: Exec, remainingCommands: List[Exec]) = log.debug(s"> $cmd") val s1 = s.copy( remainingCommands = remainingCommands, @@ -296,11 +289,10 @@ object State { history = cmd :: s.history, ) f(cmd, s1) - } - s.remainingCommands match { + s.remainingCommands match case Nil => exit(true) case x :: xs => - (x.execId, x.source) match { + (x.execId, x.source) match /* * If the command is coming from a network source, it might be a multi-command. To handle * that, we need to give the command a new exec id and wrap some commands around the @@ -332,9 +324,8 @@ object State { val remaining = map :: cmd :: complete :: failureWall :: pop :: report :: xs runCmd(stash, remaining) case _ => runCmd(x, xs) - } - } - } + end match + end process def :::(newCommands: List[String]): State = ++:(newCommands map { Exec(_, s.source) }) def ++:(newCommands: List[Exec]): State = s.copy(remainingCommands = newCommands ::: s.remainingCommands) @@ -351,23 +342,21 @@ object State { def reboot(full: Boolean): State = reboot(full, false) /** Implementation of reboot. */ - private[sbt] def reboot(full: Boolean, currentOnly: Boolean): State = { + private[sbt] def reboot(full: Boolean, currentOnly: Boolean): State = runExitHooks() val remaining: List[String] = s.remainingCommands.map(_.commandLine) - val fullRemaining = s.source match { + val fullRemaining = s.source match case Some(s) if s.channelName.startsWith("network") => StartServer :: remaining.dropWhile(!_.startsWith(ReportResult)).tail ::: "shell" :: Nil case _ => remaining - } val arguments = State.addPluginSbtFileArguments(s) ::: fullRemaining - if (currentOnly) throw new RebootCurrent(arguments) + if currentOnly then throw new RebootCurrent(arguments) else throw new xsbti.FullReload(arguments.toArray, full) - } def reload = runExitHooks().setNext(new Return(defaultReload(s))) def clearGlobalLog = setNext(ClearGlobalLog) def keepLastLog = setNext(KeepLastLog) - def exit(ok: Boolean) = runExitHooks().setNext(new Return(Exit(if (ok) 0 else 1))) + def exit(ok: Boolean) = runExitHooks().setNext(new Return(Exit(if ok then 0 else 1))) def get[T](key: AttributeKey[T]) = s.attributes.get(key) def put[T](key: AttributeKey[T], value: T) = s.copy(attributes = s.attributes.put(key, value)) def update[T](key: AttributeKey[T])(f: Option[T] => T): State = put(key, f(get(key))) @@ -375,31 +364,25 @@ object State { def remove(key: AttributeKey[?]) = s.copy(attributes = s.attributes.remove(key)) def log = s.globalLogging.full def handleError(t: Throwable): State = handleException(t, s, log) - def fail = { + def fail = val remaining = s.remainingCommands.dropWhile(c => c.commandLine != FailureWall) - if (remaining.isEmpty) - applyOnFailure(s, Nil, exit(ok = false)) - else - applyOnFailure(s, remaining, s.copy(remainingCommands = remaining)) - } + if remaining.isEmpty then applyOnFailure(s, Nil, exit(ok = false)) + else applyOnFailure(s, remaining, s.copy(remainingCommands = remaining)) private def applyOnFailure(s: State, remaining: List[Exec], noHandler: => State): State = - s.onFailure match { + s.onFailure match case Some(c) => s.copy(remainingCommands = c +: remaining, onFailure = None) case None => noHandler - } def addExitHook(act: => Unit): State = s.copy(exitHooks = s.exitHooks + ExitHook(act)) - def runExitHooks(): State = { + def runExitHooks(): State = ExitHooks.runExitHooks(s.exitHooks.toSeq) s.copy(exitHooks = Set.empty) - } def locked[T](file: File)(t: => T): T = s.configuration.provider.scalaProvider.launcher.globalLock.apply( file, - new Callable[T] { + new Callable[T]: def call = t - } ) def interactive = getBoolean(s, BasicKeys.interactive, false) @@ -419,21 +402,21 @@ object State { "Tried to get extended classloader cache for uninitialized state." ) ) - def initializeClassLoaderCache: State = { + def initializeClassLoaderCache: State = s.get(BasicKeys.extendedClassLoaderCache).foreach(_.close()) val cache = newClassLoaderCache - s.configuration.provider.scalaProvider.loader match { + s.configuration.provider.scalaProvider.loader match case null => // This can happen in scripted case fullScalaLoader => val jars = s.configuration.provider.scalaProvider.jars val (library, rest) = jars.partition(_.getName == "scala-library.jar") - library.toList match { + library.toList match case l @ lj :: Nil => - fullScalaLoader.getParent match { + fullScalaLoader.getParent match case null => // This can happen for old launchers. case libraryLoader => cache.cachedCustomClassloader(l, () => new UncloseableURLLoader(l, libraryLoader)) - fullScalaLoader match { + fullScalaLoader match case u: URLClassLoader if u.getURLs .filterNot(_ == lj.toURI.toURL) @@ -444,36 +427,30 @@ object State { ) () case _ => - } - } case _ => - } - } + end match s.put(BasicKeys.extendedClassLoaderCache, cache) .put(BasicKeys.classLoaderCache, new IncClassLoaderCache(cache)) - } + end initializeClassLoaderCache private def newClassLoaderCache = new ClassLoaderCache(s.configuration.provider.scalaProvider) - } + end StateOpsImpl import ExceptionCategory.* - private def handleException(t: Throwable, s: State, log: Logger): State = { - ExceptionCategory(t) match { + private def handleException(t: Throwable, s: State, log: Logger): State = + ExceptionCategory(t) match case AlreadyHandled => () case m: MessageOnly => log.error(m.message) case f: Full => logFullException(f.exception, log) - } s.fail - } - private[sbt] def logFullException(e: Throwable, log: Logger): Unit = { + private[sbt] def logFullException(e: Throwable, log: Logger): Unit = e.printStackTrace(System.err) log.trace(e) log.error(ErrorHandling.reducedToString(e)) log.error("Use 'last' for the full log.") - } private[sbt] def getBoolean(s: State, key: AttributeKey[Boolean], default: Boolean): Boolean = s.get(key) getOrElse default -} +end State private[sbt] final class RebootCurrent(val arguments: List[String]) extends RuntimeException diff --git a/main-command/src/main/scala/sbt/Watched.scala b/main-command/src/main/scala/sbt/Watched.scala index ceb521d2a..6c5f1651a 100644 --- a/main-command/src/main/scala/sbt/Watched.scala +++ b/main-command/src/main/scala/sbt/Watched.scala @@ -24,7 +24,7 @@ object Watched: def clearScreen: String = "\u001b[2J\u001b[0;0H" - object WatchSource { + object WatchSource: /** * Creates a new `WatchSource` for watching files, with the given filters. @@ -45,16 +45,14 @@ object Watched: */ def apply(base: File): Source = apply(base, AllPassFilter, NothingFilter) - } - private val defaultPollInterval: FiniteDuration = 500.milliseconds // @nowarn private[sbt] val newWatchService: () => WatchService = (() => createWatchService(defaultPollInterval)).label("Watched.newWatchService") - def createWatchService(pollDelay: FiniteDuration): WatchService = { + def createWatchService(pollDelay: FiniteDuration): WatchService = def closeWatch = new MacOSXWatchService() - sys.props.get("sbt.watch.mode") match { + sys.props.get("sbt.watch.mode") match case Some("polling") => new PollingWatchService(pollDelay) case Some("nio") => @@ -63,7 +61,5 @@ object Watched: case _ if Properties.isMac => closeWatch case _ => FileSystems.getDefault.newWatchService() - } - } end Watched diff --git a/main-command/src/main/scala/sbt/internal/BootServerSocketProbe.scala b/main-command/src/main/scala/sbt/internal/BootServerSocketProbe.scala index 56bc767d0..76af9fbac 100644 --- a/main-command/src/main/scala/sbt/internal/BootServerSocketProbe.scala +++ b/main-command/src/main/scala/sbt/internal/BootServerSocketProbe.scala @@ -53,4 +53,5 @@ private[sbt] object BootServerSocketProbe: t.start() done.await(timeoutMillis, TimeUnit.MILLISECONDS) answered.get() + end liveServerDetected end BootServerSocketProbe diff --git a/main-command/src/main/scala/sbt/internal/CommandChannel.scala b/main-command/src/main/scala/sbt/internal/CommandChannel.scala index c5dcbaad0..348e035f8 100644 --- a/main-command/src/main/scala/sbt/internal/CommandChannel.scala +++ b/main-command/src/main/scala/sbt/internal/CommandChannel.scala @@ -24,7 +24,7 @@ import scala.jdk.CollectionConverters.* * that can issue command or listen for some outputs. * We can think of a command channel to be an abstraction of the terminal window. */ -abstract class CommandChannel { +abstract class CommandChannel: private val commandQueue: ConcurrentLinkedQueue[Exec] = new ConcurrentLinkedQueue() private val registered: java.util.Set[java.util.Queue[Exec]] = new java.util.HashSet private val fastTrack: java.util.Set[java.util.Queue[FastTrackTask]] = new java.util.HashSet @@ -34,10 +34,9 @@ abstract class CommandChannel { ): Unit = registered.synchronized { registered.add(queue) - if (!commandQueue.isEmpty) { + if !commandQueue.isEmpty then queue.addAll(commandQueue) commandQueue.clear() - } fastTrack.add(fastTrackQueue) () } @@ -50,19 +49,17 @@ abstract class CommandChannel { fastTrack.remove(fastTrackQueue) () } - private[sbt] final def addFastTrackTask(task: String): Unit = { + private[sbt] final def addFastTrackTask(task: String): Unit = fastTrack.forEach(q => q.synchronized { q.add(new FastTrackTask(this, task)); () }) - } private[sbt] def mkUIThread: (State, CommandChannel) => UITask private[sbt] def makeUIThread(state: State): UITask = mkUIThread(state, this) - final def append(exec: Exec): Boolean = { + final def append(exec: Exec): Boolean = registered.synchronized { exec.commandLine.nonEmpty && { - if (registered.isEmpty) commandQueue.add(exec) + if registered.isEmpty then commandQueue.add(exec) else registered.asScala.forall(_.add(exec)) } } - } protected def appendExec(commandLine: String, execId: Option[String]): Boolean = append(Exec(commandLine, execId.orElse(Some(Exec.newExecId)), Some(CommandSource(name)))) def poll: Option[Exec] = Option(commandQueue.poll) @@ -71,44 +68,40 @@ abstract class CommandChannel { def unprompt(e: ConsoleUnpromptEvent): Unit = userThread.onConsoleUnpromptEvent(e) def publishBytes(bytes: Array[Byte]): Unit private[sbt] def userThread: UserThread - def shutdown(logShutdown: Boolean): Unit = { + def shutdown(logShutdown: Boolean): Unit = userThread.stopThread() userThread.close() - } @deprecated("Use the variant that takes the logShutdown parameter", "1.4.0") def shutdown(): Unit = shutdown(true) def name: String private val level = new AtomicReference[Level.Value](Level.Info) private[sbt] final def setLevel(l: Level.Value): Unit = level.set(l) private[sbt] final def logLevel: Level.Value = level.get - private def setLevel(value: Level.Value, cmd: String): Boolean = { + private def setLevel(value: Level.Value, cmd: String): Boolean = level.set(value) appendExec(cmd, None) - } private[sbt] def onCommandLine(cmd: String): Boolean = - cmd match { + cmd match case "error" => setLevel(Level.Error, "error") case "debug" => setLevel(Level.Debug, "debug") case "info" => setLevel(Level.Info, "info") case "warn" => setLevel(Level.Warn, "warn") case cmd => - if (cmd.nonEmpty) appendExec(cmd, None) + if cmd.nonEmpty then appendExec(cmd, None) else false - } - private[sbt] def onFastTrackTask(cmd: String): Boolean = { + private[sbt] def onFastTrackTask(cmd: String): Boolean = fastTrack.synchronized(fastTrack.forEach { q => q.add(new FastTrackTask(this, cmd)) () }) true - } private[sbt] def terminal: Terminal private[sbt] var _active: Boolean = true private[sbt] def pause(): Unit = _active = false private[sbt] def isPaused: Boolean = !_active private[sbt] def resume(): Unit = _active = true -} +end CommandChannel // case class Exec(commandLine: String, source: Option[CommandSource]) diff --git a/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala b/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala index d6ec18cde..ee9b204dc 100644 --- a/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala +++ b/main-command/src/main/scala/sbt/internal/ConsoleChannel.scala @@ -16,7 +16,7 @@ import sjsonnew.JsonFormat private[sbt] final class ConsoleChannel( val name: String, override private[sbt] val mkUIThread: (State, CommandChannel) => UITask -) extends CommandChannel { +) extends CommandChannel: def run(s: State): State = s @@ -26,7 +26,5 @@ private[sbt] final class ConsoleChannel( override val userThread: UserThread = new UserThread(this) private[sbt] def terminal = Terminal.console -} -private[sbt] object ConsoleChannel { +private[sbt] object ConsoleChannel: private[sbt] def defaultName = "console0" -} diff --git a/main-command/src/main/scala/sbt/internal/LabeledFunctions.scala b/main-command/src/main/scala/sbt/internal/LabeledFunctions.scala index 0c7f585ec..08a1c86ca 100644 --- a/main-command/src/main/scala/sbt/internal/LabeledFunctions.scala +++ b/main-command/src/main/scala/sbt/internal/LabeledFunctions.scala @@ -19,14 +19,14 @@ package sbt.internal * is not ready to be exposed to users. Until the api matures or we decide that it's worth * exposing to users, these should remain sbt package private. */ -private[sbt] object LabeledFunctions { +private[sbt] object LabeledFunctions: /** * Adds extension methods to a zero argument function. * @param f the function to extend * @tparam R the function result type */ - extension [R](f: () => R) { + extension [R](f: () => R) /** * Add a label to the function. @@ -34,7 +34,6 @@ private[sbt] object LabeledFunctions { * @return a wrapped function with an overridden toString method. */ def label(string: String): () => R = new LabeledFunction0(f, string) - } /** * Adds extension methods to a single argument function. @@ -42,7 +41,7 @@ private[sbt] object LabeledFunctions { * @tparam T the input parameter * @tparam R the function result type */ - extension [T, R](f: T => R) { + extension [T, R](f: T => R) /** * Add a label to the function. @@ -50,7 +49,6 @@ private[sbt] object LabeledFunctions { * @return a wrapped function with an overridden toString method. */ def label(string: String): T => R = new LabeledFunction1(f, string) - } /** * Adds extension methods to a two argument function. @@ -59,7 +57,7 @@ private[sbt] object LabeledFunctions { * @tparam T2 the second function input parameter * @tparam R the function result type */ - extension [T1, T2, R](f: (T1, T2) => R) { + extension [T1, T2, R](f: (T1, T2) => R) /** * Add a label to the function. @@ -67,7 +65,6 @@ private[sbt] object LabeledFunctions { * @return a wrapped function with an overridden toString method. */ def label(string: String): (T1, T2) => R = new LabeledFunction2(f, string) - } /** * Adds extension methods to a three argument function. @@ -77,7 +74,7 @@ private[sbt] object LabeledFunctions { * @tparam T3 the third function input parameter * @tparam R the function result type */ - extension [T1, T2, T3, R](f: (T1, T2, T3) => R) { + extension [T1, T2, T3, R](f: (T1, T2, T3) => R) /** * Add a label to the function. @@ -85,7 +82,6 @@ private[sbt] object LabeledFunctions { * @return a wrapped function with an overridden toString method. */ def label(string: String): (T1, T2, T3) => R = new LabeledFunction3(f, string) - } /** * Adds extension methods to a three argument function. @@ -96,7 +92,7 @@ private[sbt] object LabeledFunctions { * @tparam T4 the fourth function input parameter * @tparam R the function result type */ - extension [T1, T2, T3, T4, R](f: (T1, T2, T3, T4) => R) { + extension [T1, T2, T3, T4, R](f: (T1, T2, T3, T4) => R) /** * Add a label to the function. @@ -104,30 +100,24 @@ private[sbt] object LabeledFunctions { * @return a wrapped function with an overridden toString method. */ def label(string: String): (T1, T2, T3, T4) => R = new LabeledFunction4(f, string) - } - private class LabeledFunction0[+R](private val f: () => R, label: String) extends (() => R) { + private class LabeledFunction0[+R](private val f: () => R, label: String) extends (() => R): override def apply(): R = f() override def toString: String = label - } - private class LabeledFunction1[-T, +R](private val f: T => R, label: String) extends (T => R) { + private class LabeledFunction1[-T, +R](private val f: T => R, label: String) extends (T => R): override def apply(t: T): R = f(t) override def toString: String = label - } private class LabeledFunction2[-T1, -T2, +R](private val f: (T1, T2) => R, label: String) - extends ((T1, T2) => R) { + extends ((T1, T2) => R): override def apply(t1: T1, t2: T2): R = f(t1, t2) override def toString: String = label - } private class LabeledFunction3[-T1, -T2, -T3, +R](private val f: (T1, T2, T3) => R, label: String) - extends ((T1, T2, T3) => R) { + extends ((T1, T2, T3) => R): override def apply(t1: T1, t2: T2, t3: T3): R = f(t1, t2, t3) override def toString: String = label - } private class LabeledFunction4[-T1, -T2, -T3, T4, +R]( private val f: (T1, T2, T3, T4) => R, label: String - ) extends ((T1, T2, T3, T4) => R) { + ) extends ((T1, T2, T3, T4) => R): override def apply(t1: T1, t2: T2, t3: T3, t4: T4): R = f(t1, t2, t3, t4) override def toString: String = label - } -} +end LabeledFunctions diff --git a/main-command/src/main/scala/sbt/internal/classpath/ClassLoaderCache.scala b/main-command/src/main/scala/sbt/internal/classpath/ClassLoaderCache.scala index 1bc7cb979..20969bfce 100644 --- a/main-command/src/main/scala/sbt/internal/classpath/ClassLoaderCache.scala +++ b/main-command/src/main/scala/sbt/internal/classpath/ClassLoaderCache.scala @@ -27,13 +27,12 @@ import scala.annotation.tailrec import scala.jdk.CollectionConverters.* import scala.util.control.NonFatal -private object ClassLoaderCache { +private object ClassLoaderCache: private def threadID = new AtomicInteger(0) -} private[sbt] class ClassLoaderCache( val parent: ClassLoader, private val miniProvider: Option[(File, ClassLoader)] -) extends AbstractClassLoaderCache { +) extends AbstractClassLoaderCache: private val parentHolder = new AtomicReference(parent) def commonParent = parentHolder.get() def setParent(parent: ClassLoader): Unit = parentHolder.set(parent) @@ -43,36 +42,32 @@ private[sbt] class ClassLoaderCache( scalaProvider.launcher.topLoader, { scalaProvider.jars.find(_.getName == "scala-library.jar").flatMap { lib => val clazz = scalaProvider.getClass - try { + try val loader = clazz.getDeclaredMethod("libraryLoaderOnly").invoke(scalaProvider) Some(lib -> loader.asInstanceOf[ClassLoader]) - } catch { case NonFatal(_) => None } + catch case NonFatal(_) => None } } ) private val scalaProviderKey = miniProvider.map { (f, cl) => - new Key((f -> IO.getModifiedTimeOrZero(f)) :: Nil, commonParent) { + new Key((f -> IO.getModifiedTimeOrZero(f)) :: Nil, commonParent): override def toClassLoader: ClassLoader = cl - } } - private class Key(val fileStamps: Seq[(File, Long)], val parent: ClassLoader) { + private class Key(val fileStamps: Seq[(File, Long)], val parent: ClassLoader): def this(files: List[File], parent: ClassLoader) = this(files.map(f => f -> IO.getModifiedTimeOrZero(f)), parent) def this(files: List[File]) = this(files, commonParent) lazy val files: Seq[File] = fileStamps.map(_._1) lazy val maxStamp: Long = fileStamps.maxBy(_._2)._2 class CachedClassLoader - extends URLClassLoader(fileStamps.map(_._1.toURI.toURL).toArray, parent) { + extends URLClassLoader(fileStamps.map(_._1.toURI.toURL).toArray, parent): override def toString: String = s"CachedClassloader {\n parent: $parent\n urls:\n" + getURLs.mkString(" ", "\n", "\n}") - } def toClassLoader: ClassLoader = new CachedClassLoader - override def equals(o: Any): Boolean = o match { + override def equals(o: Any): Boolean = o match case that: Key => this.fileStamps == that.fileStamps && this.parent == that.parent - } override def hashCode(): Int = (fileStamps.hashCode * 31) ^ parent.hashCode override def toString: String = s"Key(${fileStamps mkString ","}, $parent)" - } private val delegate = new java.util.concurrent.ConcurrentHashMap[Key, Reference[ClassLoader]]() private val referenceQueue = new ReferenceQueue[ClassLoader] @@ -91,59 +86,48 @@ private[sbt] class ClassLoaderCache( java.util.Collections.synchronizedMap(new java.util.WeakHashMap[ClassLoader, java.lang.Boolean]) private def clearExpiredLoaders(): Unit = lock.synchronized { - val clear = (k: Key, ref: Reference[ClassLoader]) => { - ref.get() match { + val clear = (k: Key, ref: Reference[ClassLoader]) => + ref.get() match case w: WrappedLoader => w.invalidate() case _ => - } - ref match { + ref match case ClassLoaderReference(_, underlying) => retired.put(underlying, java.lang.Boolean.TRUE) case r => - r.get() match { + r.get() match case null => case loader => retired.put(loader, java.lang.Boolean.TRUE) - } - } delegate.remove(k) () - } - def isInvalidated(classLoader: ClassLoader): Boolean = classLoader match { + def isInvalidated(classLoader: ClassLoader): Boolean = classLoader match case w: WrappedLoader => w.invalidated() case _ => false - } delegate.asScala.groupBy { case (k, _) => k.parent -> k.files.toSet }.foreach { case (_, pairs) if pairs.size > 1 => val max = pairs.map(_._1.maxStamp).max - pairs.foreach { (k, v) => if (k.maxStamp != max) clear(k, v) } + pairs.foreach { (k, v) => if k.maxStamp != max then clear(k, v) } case _ => } - delegate.forEach((k, v) => if (isInvalidated(k.parent)) clear(k, v)) + delegate.forEach((k, v) => if isInvalidated(k.parent) then clear(k, v)) } - private class CleanupThread(private val id: Int) - extends Thread(s"classloader-cache-cleanup-$id") { + private class CleanupThread(private val id: Int) extends Thread(s"classloader-cache-cleanup-$id"): setDaemon(true) start() @tailrec - override final def run(): Unit = { + override final def run(): Unit = val stop = - try { - referenceQueue.remove(1000) match { + try + referenceQueue.remove(1000) match case ClassLoaderReference(key, classLoader) => close(classLoader) retired.remove(classLoader) delegate.remove(key) () case _ => - } clearExpiredLoaders() false - } catch { - case _: InterruptedException => true - } - if (!stop) run() - } - } + catch case _: InterruptedException => true + if !stop then run() /* * We need to manage the cache differently depending on whether or not sbt is started up with @@ -180,16 +164,15 @@ private[sbt] class ClassLoaderCache( ManagementFactory.getMemoryPoolMXBeans.asScala .exists(b => (b.getName == "Metaspace") && (b.getUsage.getMax > 0)) private val mkReference: (Key, ClassLoader) => Reference[ClassLoader] = - if (metaspaceIsLimited) { (_, cl) => - (new SoftReference[ClassLoader](cl, referenceQueue): Reference[ClassLoader]) - } else ClassLoaderReference.apply + if metaspaceIsLimited then + (_, cl) => (new SoftReference[ClassLoader](cl, referenceQueue): Reference[ClassLoader]) + else ClassLoaderReference.apply private val cleanupThread = new CleanupThread(ClassLoaderCache.threadID.getAndIncrement()) private val lock = new Object - private def close(classLoader: ClassLoader): Unit = classLoader match { + private def close(classLoader: ClassLoader): Unit = classLoader match case a: AutoCloseable => a.close() case _ => - } private case class ClassLoaderReference(key: Key, classLoader: ClassLoader) extends SoftReference[ClassLoader]( new WrappedLoader(classLoader), @@ -199,62 +182,51 @@ private[sbt] class ClassLoaderCache( files: List[(File, Long)], parent: ClassLoader, mkLoader: () => ClassLoader - ): ClassLoader = { + ): ClassLoader = val key = new Key(files, parent) get(key, mkLoader) - } - def apply(files: List[File], parent: ClassLoader): ClassLoader = { + def apply(files: List[File], parent: ClassLoader): ClassLoader = val key = new Key(files, parent) get(key, () => key.toClassLoader) - } - override def apply(files: List[File]): ClassLoader = { - files match { + override def apply(files: List[File]): ClassLoader = + files match case d :: s :: Nil if d.getName.startsWith("dotty-library") || d.getName.startsWith("scala3-library") => apply(files, classOf[org.jline.terminal.Terminal].getClassLoader) case _ => val key = new Key(files) get(key, () => key.toClassLoader) - } - } override def cachedCustomClassloader( files: List[File], mkLoader: () => ClassLoader - ): ClassLoader = { + ): ClassLoader = val key = new Key(files) get(key, mkLoader) - } - private def get(key: Key, f: () => ClassLoader): ClassLoader = { - scalaProviderKey match { + private def get(key: Key, f: () => ClassLoader): ClassLoader = + scalaProviderKey match case Some(k) if k == key => k.toClassLoader case _ => - def addLoader(): ClassLoader = { + def addLoader(): ClassLoader = val ref = mkReference(key, f()) val loader = ref.get delegate.put(key, ref) clearExpiredLoaders() loader - } lock.synchronized { - delegate.get(key) match { + delegate.get(key) match case null => addLoader() case ref => - ref.get match { + ref.get match case null => addLoader() case l => l - } - } } - } - } - private def clear(lock: Object): Unit = { + private def clear(lock: Object): Unit = delegate.asScala.foreach { case (_, ClassLoaderReference(_, classLoader)) => close(classLoader) case (_, r: Reference[ClassLoader]) => - r.get match { + r.get match case null => case classLoader => close(classLoader) - } } delegate.clear() /* Also close loaders that were evicted from the delegate map but never closed (see @@ -263,7 +235,6 @@ private[sbt] class ClassLoaderCache( val evicted = retired.synchronized(new java.util.ArrayList(retired.keySet()).asScala.toList) evicted.foreach(close) retired.clear() - } /** * Clears any ClassLoader instances from the internal cache and closes them. Calling this @@ -282,15 +253,15 @@ private[sbt] class ClassLoaderCache( cleanupThread.join() clear(lock) } -} +end ClassLoaderCache -private[sbt] object AlternativeZincUtil { +private[sbt] object AlternativeZincUtil: def scalaCompiler( scalaInstance: ScalaInstance, compilerBridgeJar: File, classpathOptions: ClasspathOptions, classLoaderCache: Option[IncClassLoaderCache] - ): AnalyzingCompiler = { + ): AnalyzingCompiler = val bridgeProvider = ZincUtil.constantBridgeProvider(scalaInstance, compilerBridgeJar) new AnalyzingCompiler( scalaInstance, @@ -299,5 +270,3 @@ private[sbt] object AlternativeZincUtil { _ => (), classLoaderCache ) - } -} diff --git a/main-command/src/main/scala/sbt/internal/client/BspClient.scala b/main-command/src/main/scala/sbt/internal/client/BspClient.scala index 7aadc4cb4..22b905e8e 100644 --- a/main-command/src/main/scala/sbt/internal/client/BspClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/BspClient.scala @@ -15,57 +15,49 @@ import java.util.concurrent.atomic.AtomicBoolean import sbt.Exit import scala.util.control.NonFatal -object BspClient { - private[sbt] def bspRun(sbtServer: Socket): Int = { +object BspClient: + private[sbt] def bspRun(sbtServer: Socket): Int = val lock = new AnyRef val terminated = new AtomicBoolean(false) transferTo(terminated, lock, sbtServer.getInputStream, System.out).start() transferTo(terminated, lock, System.in, sbtServer.getOutputStream).start() - try { + try lock.synchronized { - while (!terminated.get) lock.wait() + while !terminated.get do lock.wait() } 0 - } catch { case _: Throwable => 1 } + catch case _: Throwable => 1 finally sbtServer.close() - } private[sbt] def transferTo( terminated: AtomicBoolean, lock: AnyRef, input: InputStream, output: OutputStream - ): Thread = { - val thread = new Thread { - override def run(): Unit = { + ): Thread = + val thread = new Thread: + override def run(): Unit = val buffer = Array.ofDim[Byte](1024) - try { - while (!terminated.get) { + try + while !terminated.get do val size = input.read(buffer) - if (size == -1) { - terminated.set(true) - } else { + if size == -1 then terminated.set(true) + else output.write(buffer, 0, size) output.flush() - } - } input.close() output.close() - } catch { + catch case _: InterruptedException => terminated.set(true) case NonFatal(_) => () - } finally { + finally lock.synchronized { terminated.set(true) lock.notify() } - } - } - } thread.setDaemon(true) thread - } - def run(configuration: xsbti.AppConfiguration): Exit = { + end transferTo + def run(configuration: xsbti.AppConfiguration): Exit = Exit(NetworkClient.run(configuration, configuration.arguments.toList, redirectOutput = true)) - } -} +end BspClient diff --git a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala index 76e49bd2f..9f5412166 100644 --- a/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala +++ b/main-command/src/main/scala/sbt/internal/client/NetworkClient.scala @@ -74,10 +74,9 @@ import Serialization.{ import NetworkClient.Arguments import java.util.concurrent.TimeoutException -trait ConsoleInterface { +trait ConsoleInterface: def appendLog(level: Level.Value, message: => String): Unit def success(msg: String): Unit -} /** * A NetworkClient connects to a running sbt instance or starts a @@ -117,7 +116,8 @@ class NetworkClient( errorStream: PrintStream, printStream: PrintStream, useJNI: Boolean, -) extends AutoCloseable { self => +) extends AutoCloseable: + self => def this(configuration: xsbti.AppConfiguration, arguments: Arguments) = this( arguments = arguments.withoutSysProps.withBaseDirectory(configuration.baseDirectory), @@ -164,10 +164,9 @@ class NetworkClient( private def mkSocket(file: File): (Socket, Option[String]) = ClientSocket.socket(file, useJNI) - private[sbt] def logFailure(e: Exception): Unit = { + private[sbt] def logFailure(e: Exception): Unit = errorStream.println(s"sbt client failed: $e") e.printStackTrace(errorStream) - } private def portfile = arguments.baseDirectory / "project" / "target" / "active.json" @@ -186,11 +185,10 @@ class NetworkClient( private class ConnectionRefusedException(t: Throwable) extends Throwable(t) private class ServerFailedException extends Exception private[client] def startInputThread(): Unit = inputThread.request() - private lazy val log: Logger = new Logger { + private lazy val log: Logger = new Logger: def trace(t: => Throwable): Unit = () def success(message: => String): Unit = () def log(level: Level.Value, message: => String): Unit = console.appendLog(level, message) - } private val interactive = arguments.commandArguments.isEmpty private val startupMessages: List[String] = "entering thin client - BEEP WHIRR" :: @@ -274,7 +272,11 @@ class NetworkClient( Level.Warn, "run 'sbt shutdown' for the ones passed here to take effect" ) + end if + end if } + end if + end restartServerIfSysPropsChanged /** * Asks the running server to shut down and waits for it to let go of its socket, so that @@ -326,75 +328,70 @@ class NetworkClient( val deadline = waitFor.fromNow // the server drops the portfile when it starts tearing down, and only then is it // worth asking its socket whether it is still there - while (portfile.exists && !deadline.isOverdue()) Thread.sleep(20) + while portfile.exists && !deadline.isOverdue() do Thread.sleep(20) // each of these asks costs a connection on a server that is still up, so they // get further apart the longer it takes var delay = 20L - while (!gone && !deadline.isOverdue()) { + while !gone && !deadline.isOverdue() do Thread.sleep(delay) - if (delay < 500) delay = delay * 2 - } + if delay < 500 then delay = delay * 2 Some(gone) finally session.close() + end try + end match + end shutdownRunningServer private[sbt] def connectOrStartServerAndConnect( promptCompleteUsers: Boolean, retry: Boolean ): (Socket, Option[String]) = try - if (portfile.exists) restartServerIfSysPropsChanged(promptCompleteUsers) - if (!portfile.exists) { - if (shutdownOnly) { + if portfile.exists then restartServerIfSysPropsChanged(promptCompleteUsers) + if !portfile.exists then + if shutdownOnly then console.appendLog(Level.Info, "no sbt server is running. ciao") System.exit(0) - } else if (!serverAutoStart) { + else if !serverAutoStart then console.appendLog(Level.Error, "no sbt server is running (sbt.server.autostart=false)") System.exit(1) - } else if (promptCompleteUsers) { - val msg = if (noTab) "" else "No sbt server is running. Press to start one..." + else if promptCompleteUsers then + val msg = if noTab then "" else "No sbt server is running. Press to start one..." errorStream.print(s"\n$msg") - if (noStdErr) System.exit(0) - else if (noTab) waitForServer(portfile, log = true, startServer = true) - else { + if noStdErr then System.exit(0) + else if noTab then waitForServer(portfile, log = true, startServer = true) + else startInputThread() - stdinBytes.poll(5, TimeUnit.SECONDS) match { + stdinBytes.poll(5, TimeUnit.SECONDS) match case null => System.exit(0) case i if i == 9 => errorStream.println("\nStarting server...") waitForServer(portfile, !promptCompleteUsers, startServer = true) case _ => System.exit(0) - } - } - } else { - waitForServer(portfile, log = true, startServer = true) - } - } - @tailrec def connect(attempt: Int): (Socket, Option[String]) = { + else waitForServer(portfile, log = true, startServer = true) + end if + @tailrec def connect(attempt: Int): (Socket, Option[String]) = val res = try Some(mkSocket(portfile)) - catch { + catch case _: ClientSocket.ConnectionFileReadException if attempt < 10 => None // server may be in the middle of writing the portfile case e: IOException => - if (attempt >= 10) throw new ConnectionRefusedException(e) + if attempt >= 10 then throw new ConnectionRefusedException(e) val msg = Option(e.getMessage).getOrElse("") // This catches a pipe busy exception which can happen if two windows clients // attempt to connect in rapid succession - if (msg.contains("Couldn't open")) - if (msg.contains("Access is denied") || msg.contains("(5)")) { + if msg.contains("Couldn't open") then + if msg.contains("Access is denied") || msg.contains("(5)") then errorStream.println(s"Access denied for portfile $portfile") throw new NetworkClient.AccessDeniedException - } None // server could be busy, not down, so try again - } - res match { + res match case Some(r) => r case None => // Use a random sleep to spread out the competing processes Thread.sleep(new java.util.Random().nextInt(20).toLong) connect(attempt + 1) - } - } + end connect connect(0) catch case e @ (_: ConnectionRefusedException | _: ClientSocket.ConnectionFileReadException) @@ -406,20 +403,19 @@ class NetworkClient( end connectOrStartServerAndConnect // Open server connection based on the portfile - private def initImpl(promptCompleteUsers: Boolean, retry: Boolean): ServerSession = { + private def initImpl(promptCompleteUsers: Boolean, retry: Boolean): ServerSession = val (sk, tkn) = connectOrStartServerAndConnect(promptCompleteUsers, retry) - val conn = new ServerSessionImpl(sk, s"sbt-serverconnection-${sk.getPort}") { - override protected def onNotification(msg: JsonRpcNotificationMessage): Unit = { - msg.method match { + val conn = new ServerSessionImpl(sk, s"sbt-serverconnection-${sk.getPort}"): + override protected def onNotification(msg: JsonRpcNotificationMessage): Unit = + msg.method match case `Shutdown` => - val (log, rebootCommands) = msg.params match { + val (log, rebootCommands) = msg.params match case Some(jvalue) => Converter .fromJson[(Boolean, Option[(String, String)])](jvalue) .getOrElse((true, None)) case _ => (false, None) - } - if (rebootCommands.nonEmpty) { + if rebootCommands.nonEmpty then rebooting.set(true) attached.set(false) connectionHolder.close() @@ -427,54 +423,46 @@ class NetworkClient( init(promptCompleteUsers = false, retry = false) attachUUID.set(sendJson(attach, s"""{"interactive": ${!batchMode.get}}""")) rebooting.set(false) - rebootCommands match { + rebootCommands match case Some((execId, cmd)) if execId.nonEmpty => - if (batchMode.get && !pendingResults.containsKey(execId) && cmd.nonEmpty) { + if batchMode.get && !pendingResults.containsKey(execId) && cmd.nonEmpty then console.appendLog( Level.Error, s"received request to re-run unknown command '$cmd' after reboot" ) - } else if (cmd.nonEmpty) { - if (batchMode.get) self.sendCommand(ExecCommand(cmd, execId)) + else if cmd.nonEmpty then + if batchMode.get then self.sendCommand(ExecCommand(cmd, execId)) else inLock.synchronized { val toSend = cmd.getBytes :+ '\r'.toByte toSend.foreach(b => sendNotification(systemIn, b.toString)) } - } else completeExec(execId, 0) + else completeExec(execId, 0) case _ => - } - } else { - if (!rebooting.get() && running.compareAndSet(true, false) && log) { - if (!arguments.commandArguments.contains(Shutdown)) { + else + if !rebooting.get() && running.compareAndSet(true, false) && log then + if !arguments.commandArguments.contains(Shutdown) then console.appendLog(Level.Error, "sbt server disconnected") exitClean.set(false) - } - } else { - console.appendLog(Level.Info, s"${if (log) "sbt server " else ""}disconnected") - } + else + console.appendLog(Level.Info, s"${if log then "sbt server " else ""}disconnected") stdinBytes.offer(-1) inputThread.close() Option(interactiveThread.get).foreach(_.interrupt) - } + end if case `readSystemIn` => startInputThread() case `cancelReadSystemIn` => inputThread.cancel() case _ => self.onNotification(msg) - } - } override protected def onRequest(msg: JsonRpcRequestMessage): Unit = self.onRequest(msg) override protected def onResponse(msg: JsonRpcResponseMessage): Unit = self.onResponse(msg) - override protected def onClose(): Unit = if (!rebooting.get) { - if (exitClean.get) { + override protected def onClose(): Unit = if !rebooting.get then + if exitClean.get then val serverDropped = running.get exitClean.set(!serverDropped) - if (serverDropped && !shutdownOnly) + if serverDropped && !shutdownOnly then console.appendLog(Level.Error, "sbt server disconnected") - } running.set(false) Option(interactiveThread.get).foreach(_.interrupt()) - } - } // initiate handshake val execId = UUID.randomUUID.toString val skipAnalysis = true @@ -492,13 +480,12 @@ class NetworkClient( ) conn.sendCommand(initCommand) conn - } + end initImpl - def init(promptCompleteUsers: Boolean, retry: Boolean): ServerSession = { + def init(promptCompleteUsers: Boolean, retry: Boolean): ServerSession = val conn = initImpl(promptCompleteUsers = promptCompleteUsers, retry = retry) connectionHolder.set(conn) conn - } private def bootSocketOpt(bootSocketName: String, namedPipeName: String): Option[Socket] = Try(ClientSocket.bootSocket(bootSocketName)).toOption match @@ -519,7 +506,7 @@ class NetworkClient( * Forks another instance of sbt in the background. * This instance must be shutdown explicitly via `sbt -client shutdown` */ - def waitForServer(portfile: File, log: Boolean, startServer: Boolean): Unit = { + def waitForServer(portfile: File, log: Boolean, startServer: Boolean): Unit = val base = arguments.baseDirectory.toPath.toRealPath() val target = base.resolve("project").resolve("target") val hash = HashUtil.farmHash(target.toString().getBytes("UTF-8")) @@ -534,7 +521,7 @@ class NetworkClient( val term = Terminal.console term.exitRawMode() var serverStderrFile: Option[File] = None - val process = socket match { + val process = socket match case None if startServer => if log then startupMessages.foreach: msg => @@ -548,8 +535,8 @@ class NetworkClient( term.isSupershellEnabled ).mkString(",") - if (log && arguments.sbtLaunchJar.isDefined) { - val sbtScript = if (Properties.isWin) "sbt.bat" else "sbt" + if log && arguments.sbtLaunchJar.isDefined then + val sbtScript = if Properties.isWin then "sbt.bat" else "sbt" console.appendLog(Level.Warn, s"server is started using sbt-launch jar directly") console.appendLog( Level.Warn, @@ -559,12 +546,11 @@ class NetworkClient( Level.Warn, s"either upgrade $sbtScript to its latest version or make sure it is accessible from $$PATH, and run 'sbt bspConfig'" ) - } val cmd = NetworkClient.serverCommand(arguments) // https://github.com/sbt/sbt/issues/6271 val nohup = - if (Util.isEmacs && !Util.isWindows) List("nohup") + if Util.isEmacs && !Util.isWindows then List("nohup") else Nil // https://github.com/sbt/sbt/issues/8442 @@ -572,7 +558,7 @@ class NetworkClient( // the server process will block on writes. Redirect to files instead of // inheriting or piping to avoid buffer deadlocks while still capturing // errors for diagnostics (https://github.com/sbt/sbt/issues/8812). - val nullFile = new File(if (Util.isWindows) "NUL" else "/dev/null") + val nullFile = new File(if Util.isWindows then "NUL" else "/dev/null") val stderrFile = Files.createTempFile("sbt-server-err", ".log").toFile stderrFile.deleteOnExit() serverStderrFile = Some(stderrFile) @@ -583,38 +569,31 @@ class NetworkClient( .redirectOutput(nullFile) .redirectError(stderrFile) processBuilder.environment.put(Terminal.TERMINAL_PROPS, props) - if (arguments.forwardsSysProps) { + if arguments.forwardsSysProps then processBuilder.environment.put( NetworkClient.sysPropsEnv, NetworkClient.recordedSysProps(arguments.sbtArguments) ) processBuilder.environment .put(NetworkClient.sysPropsPortfileEnv, portfile.getCanonicalPath) - } else { + else Util.ignoreResult(processBuilder.environment.remove(NetworkClient.sysPropsEnv)) Util.ignoreResult(processBuilder.environment.remove(NetworkClient.sysPropsPortfileEnv)) - } - Try(processBuilder.start()) match { + Try(processBuilder.start()) match case Success(process) => sbtProcess.set(process) Some(process) case Failure(e) => - if (log) console.appendLog(Level.Error, s"Failed to start server : $e") + if log then console.appendLog(Level.Error, s"Failed to start server : $e") throw new ServerFailedException - } case _ => - if (log) { - console.appendLog(Level.Info, "sbt server is booting up") - } + if log then console.appendLog(Level.Info, "sbt server is booting up") None - } - if (!startServer) { + if !startServer then val deadline = 5.seconds.fromNow - while (socket.isEmpty && !deadline.isOverdue()) { + while socket.isEmpty && !deadline.isOverdue() do socket = bootSocketOpt(bootSocketName, namedPipeName) - if (socket.isEmpty) Thread.sleep(20) - } - } + if socket.isEmpty then Thread.sleep(20) val shutdown = new Thread(() => Option(sbtProcess.get).foreach(_.destroyForcibly())) Runtime.getRuntime.addShutdownHook(shutdown) var gotInputBack = false @@ -624,19 +603,17 @@ class NetworkClient( * so it is necessary to read the process output from the socket on a background * thread. */ - val readThread = new Thread("client-read-thread") { + val readThread = new Thread("client-read-thread"): setDaemon(true) start() - override def run(): Unit = { - try { + override def run(): Unit = + try val buffer = mutable.ArrayBuffer.empty[Byte] - while (readThreadAlive.get) { - if (socket.isEmpty) { - socket = bootSocketOpt(bootSocketName, namedPipeName) - } + while readThreadAlive.get do + if socket.isEmpty then socket = bootSocketOpt(bootSocketName, namedPipeName) socket.foreach { s => - try { - s.getInputStream.read match { + try + s.getInputStream.read match case -1 | 0 => readThreadAlive.set(false) case 2 => // STX: start of text gotInputBack = true @@ -651,54 +628,43 @@ class NetworkClient( buffer.clear() case i => buffer.append(i.toByte) - } - } catch { + catch case e @ (_: IOException | _: InterruptedException) => readThreadAlive.set(false) - } } - if (socket.isEmpty && readThreadAlive.get) { + if socket.isEmpty && readThreadAlive.get then try Thread.sleep(10) - catch { case _: InterruptedException => } - } - } - } catch { case e: IOException => e.printStackTrace(System.err) } - } - } + catch + case _: InterruptedException => + end while + catch case e: IOException => e.printStackTrace(System.err) val connectDeadline = connectTimeout.fromNow @tailrec - def blockUntilStart(): Unit = { + def blockUntilStart(): Unit = val stop = - try { - socket match { + try + socket match case None => process.foreach { p => val output = p.getInputStream - while (output.available > 0) { - printStream.write(output.read()) - } + while output.available > 0 do printStream.write(output.read()) } case Some(s) => - while (!gotInputBack && !stdinBytes.isEmpty && socket.isDefined) { + while !gotInputBack && !stdinBytes.isEmpty && socket.isDefined do val out = s.getOutputStream val b = stdinBytes.poll - if (b == -1) { + if b == -1 then // server waits for user input but stinBytes has ended shutdown.run() - } else { + else out.write(b) out.flush() - } - } - } process.foreach { p => val error = p.getErrorStream - while (error.available > 0) { - errorStream.write(error.read()) - } + while error.available > 0 do errorStream.write(error.read()) } false - } catch { case e: IOException => true } + catch case e: IOException => true Thread.sleep(10) printStream.flush() errorStream.flush() @@ -709,9 +675,9 @@ class NetworkClient( */ val existsValidProcess = process.fold(readThreadAlive.get)(p => p.isAlive || (Properties.isWin || p.exitValue == 2)) - if (!portfile.exists && !stop && existsValidProcess && !connectDeadline.isOverdue()) { + if !portfile.exists && !stop && existsValidProcess && !connectDeadline.isOverdue() then blockUntilStart() - } else { + else connectDeadlineExpired = connectDeadline.isOverdue() && !portfile.exists socket.foreach { s => s.getInputStream.close() @@ -724,60 +690,52 @@ class NetworkClient( p.getErrorStream.close() p.getInputStream.close() } - } - } + end blockUntilStart try blockUntilStart() - catch { case t: Throwable => t.printStackTrace() } - finally { + catch case t: Throwable => t.printStackTrace() + finally sbtProcess.set(null) Util.ignoreResult(Runtime.getRuntime.removeShutdownHook(shutdown)) - } - if (!portfile.exists()) { - if (connectDeadlineExpired) { + if !portfile.exists() then + if connectDeadlineExpired then errorStream.write( s"sbt server did not start within ${connectTimeout.toSeconds} seconds\n".getBytes("UTF-8") ) errorStream.flush() - } // Print captured server stderr so users can see why the server failed to start - for (errFile <- serverStderrFile) { - try { - val bytes = Files.readAllBytes(errFile.toPath) - if (bytes.nonEmpty) { - errorStream.write(bytes) - errorStream.flush() - } - } catch { case _: Exception => } + for errFile <- serverStderrFile do + try + try + val bytes = Files.readAllBytes(errFile.toPath) + if bytes.nonEmpty then + errorStream.write(bytes) + errorStream.flush() + catch + case _: Exception => finally errFile.delete() - } throw new ServerFailedException - } // Clean up stderr temp file on successful startup serverStderrFile.foreach(_.delete()) - if (attached.get && !stdinBytes.isEmpty) inputThread.drain() - } + if attached.get && !stdinBytes.isEmpty then inputThread.drain() + end waitForServer /** Called on the response for a returning message. */ - def onReturningResponse(msg: JsonRpcResponseMessage): Unit = { - def printResponse(): Unit = { - msg.result match { + def onReturningResponse(msg: JsonRpcResponseMessage): Unit = + def printResponse(): Unit = + msg.result match case Some(result) => // ignore result JSON console.success("completed") case _ => - msg.error match { + msg.error match case Some(err) => // ignore err details console.appendLog(Level.Error, "completed") case _ => // ignore - } - } - } printResponse() - } - private def getExitCode(jvalue: Option[JValue]): Integer = jvalue match { + private def getExitCode(jvalue: Option[JValue]): Integer = jvalue match case Some(o: JObject) => o.value .collectFirst { @@ -786,7 +744,6 @@ class NetworkClient( } .getOrElse(1) case _ => 1 - } private val onAttachResponse: PartialFunction[JsonRpcResponseMessage, Unit] = { case msg if attachUUID.get == msg.id => @@ -795,58 +752,52 @@ class NetworkClient( inputThread.drain() () } - def completeExec(execId: String, exitCode: Int) = { - pendingResults.remove(execId) match { + def completeExec(execId: String, exitCode: Int) = + pendingResults.remove(execId) match case null => () case (q, startTime, name) => val now = System.currentTimeMillis val message = NetworkClient.elapsedString(startTime, now) - if (batchMode.get || !attached.get) { - if (exitCode == 0) console.success(message) + if batchMode.get || !attached.get then + if exitCode == 0 then console.success(message) else console.appendLog(Level.Error, message) - } Util.ignoreResult(q.offer(exitCode)) - } - } private val onExecResponse: PartialFunction[JsonRpcResponseMessage, Unit] = { case msg if pendingResults.containsKey(msg.id) => completeExec(msg.id, getExitCode(msg.result)) } private val onCancellationResponse: PartialFunction[JsonRpcResponseMessage, Unit] = { case msg if pendingCancellations.containsKey(msg.id) => - pendingCancellations.remove(msg.id) match { + pendingCancellations.remove(msg.id) match case null => () case q => Util.ignoreResult(q.offer(msg.toString.contains("Task cancelled"))) - } } private val onCompletionResponse: PartialFunction[JsonRpcResponseMessage, Unit] = { case msg if pendingCompletions.containsKey(msg.id) => - pendingCompletions.remove(msg.id) match { + pendingCompletions.remove(msg.id) match case null => () case completions => - completions(msg.result match { + completions(msg.result match case Some(o: JObject) => o.value .foldLeft(CompletionResponse(Vector.empty[String])) { (resp, i) => - if (i.field == "items") + if i.field == "items" then resp.withItems( Converter .fromJson[Vector[String]](i.value) .getOrElse(Vector.empty[String]) ) - else if (i.field == "cachedTestNames") + else if i.field == "cachedTestNames" then resp.withCachedTestNames( Converter.fromJson[Boolean](i.value).getOrElse(true) ) - else if (i.field == "cachedMainClassNames") + else if i.field == "cachedMainClassNames" then resp.withCachedMainClassNames( Converter.fromJson[Boolean](i.value).getOrElse(true) ) else resp } - case _ => CompletionResponse(Vector.empty[String]) - }) - } + case _ => CompletionResponse(Vector.empty[String])) } // cache the composed plan private val responsePlan = Util.reduceIntents[JsonRpcResponseMessage, Unit]( @@ -858,30 +809,27 @@ class NetworkClient( ) def onResponse(msg: JsonRpcResponseMessage): Unit = responsePlan(msg) - def onNotification(msg: JsonRpcNotificationMessage): Unit = { + def onNotification(msg: JsonRpcNotificationMessage): Unit = def splitToMessage: Vector[(Level.Value, String)] = - (msg.method, msg.params) match { + (msg.method, msg.params) match case ("build/logMessage", Some(json)) => - if (!attached.get) { + if !attached.get then import sbt.internal.langserver.codec.JsonProtocol.given - Converter.fromJson[LogMessageParams](json) match { + Converter.fromJson[LogMessageParams](json) match case Success(params) => splitLogMessage(params) case Failure(_) => Vector() - } - } else Vector() + else Vector() case (`systemOut`, Some(json)) => - Converter.fromJson[Array[Byte]](json) match { + Converter.fromJson[Array[Byte]](json) match case Success(bytes) if bytes.nonEmpty && attached.get => synchronized(printStream.write(bytes)) case _ => - } Vector.empty case (`systemErr`, Some(json)) => - Converter.fromJson[Array[Byte]](json) match { + Converter.fromJson[Array[Byte]](json) match case Success(bytes) if bytes.nonEmpty && attached.get => synchronized(errorStream.write(bytes)) case _ => - } Vector.empty case (`systemOutFlush`, _) => synchronized(printStream.flush()) @@ -894,13 +842,12 @@ class NetworkClient( Vector.empty case ("textDocument/publishDiagnostics", Some(json)) => import sbt.internal.langserver.codec.JsonProtocol.given - Converter.fromJson[PublishDiagnosticsParams](json) match { + Converter.fromJson[PublishDiagnosticsParams](json) match case Success(params) => splitDiagnostics(params); Vector() case Failure(_) => Vector() - } case (`clientJob`, Some(json)) => import sbt.internal.worker.codec.JsonProtocol.given - Converter.fromJson[ClientJobParams](json) match { + Converter.fromJson[ClientJobParams](json) match case Success(params) => clientSideRun(params) match case Success(_) => @@ -914,17 +861,15 @@ class NetworkClient( ) else throw e case Failure(_) => Vector.empty - } case (`Shutdown`, Some(_)) => Vector.empty case (msg, _) if msg.startsWith("build/") => Vector.empty case ("sbt/exec", Some(json)) => import sbt.protocol.codec.JsonProtocol.given - Converter.fromJson[ExecStatusEvent](json) match { + Converter.fromJson[ExecStatusEvent](json) match case Success(event) if event.status == "Queued" => event.message.foreach(m => errorStream.println(s"[info] $m")) Vector.empty case _ => Vector.empty - } case _ => Vector( ( @@ -932,57 +877,50 @@ class NetworkClient( s"unknown event: ${msg.method} " + Serialization.compactPrintJsonOpt(msg.params) ) ) - } splitToMessage foreach { (level, msg) => console.appendLog(level, msg) } - } + end onNotification - def splitLogMessage(params: LogMessageParams): Vector[(Level.Value, String)] = { + def splitLogMessage(params: LogMessageParams): Vector[(Level.Value, String)] = val level = messageTypeToLevel(params.`type`) - if (level == Level.Debug) Vector() + if level == Level.Debug then Vector() else Vector((level, params.message)) - } - def messageTypeToLevel(severity: Long): Level.Value = { - severity match { + def messageTypeToLevel(severity: Long): Level.Value = + severity match case MessageType.Error => Level.Error case MessageType.Warning => Level.Warn case MessageType.Info => Level.Info case MessageType.Log => Level.Debug - } - } - def splitDiagnostics(params: PublishDiagnosticsParams): Vector[(Level.Value, String)] = { + def splitDiagnostics(params: PublishDiagnosticsParams): Vector[(Level.Value, String)] = val uri = new URI(params.uri) val f = IO.toFile(uri) params.diagnostics map { d => - val level = d.severity match { + val level = d.severity match case Some(severity) => messageTypeToLevel(severity) case _ => Level.Error - } val line = d.range.start.line + 1 val offset = d.range.start.character + 1 val msg = s"$f:$line:$offset: ${d.message}" (level, msg) } - } private def clientSideRun(params: ClientJobParams): Try[Unit] = - params.runInfo match { + params.runInfo match case Some(info) => clientSideRun(info) case _ => Failure(new MessageOnlyException(s"runInfo is not specified in $params")) - } private def setWindowTitle(title: String): Unit = if System.console() != null && System.getenv("TERM") != null then Console.print(s"\u001b]0;$title\u0007") Console.flush() - private def clientSideRun(runInfo: RunInfo): Try[Unit] = { + private def clientSideRun(runInfo: RunInfo): Try[Unit] = runInfo.windowTitle.foreach(setWindowTitle) - def nativeRun(info: NativeRunInfo): Try[Unit] = { + def nativeRun(info: NativeRunInfo): Try[Unit] = import java.lang.ProcessBuilder as JProcessBuilder val option = ForkOptions( javaHome = None, @@ -997,26 +935,24 @@ class NetworkClient( val jpb = new JProcessBuilder(command*) val exitCode = try Fork.blockForExitCode(Fork.forkInternal(option, Nil, jpb)) - catch { + catch case _: InterruptedException => log.warn("run canceled") 1 - } Run.processExitCode(exitCode, "runner") - } inClientSideRun.set(true) try - if (runInfo.jvm) + if runInfo.jvm then RunHandler.jvmRun(runInfo.jvmRunInfo.getOrElse(sys.error("missing jvmRunInfo")), log) else nativeRun(runInfo.nativeRunInfo.getOrElse(sys.error("missing nativeRunInfo"))) finally inClientSideRun.set(false) - } + end clientSideRun - def onRequest(msg: JsonRpcRequestMessage): Unit = { + def onRequest(msg: JsonRpcRequestMessage): Unit = import sbt.protocol.codec.JsonProtocol.given - (msg.method, msg.params) match { + (msg.method, msg.params) match case (`terminalCapabilities`, Some(json)) => - Converter.fromJson[TerminalCapabilitiesQuery](json) match { + Converter.fromJson[TerminalCapabilitiesQuery](json) match case Success(terminalCapabilitiesQuery) => val response = TerminalCapabilitiesResponse( terminalCapabilitiesQuery.boolean @@ -1032,7 +968,6 @@ class NetworkClient( msg.id, ) case Failure(_) => - } case (`terminalPropertiesQuery`, _) => val response = TerminalPropertiesResponse.apply( width = Terminal.console.getWidth, @@ -1044,7 +979,7 @@ class NetworkClient( ) sendCommandResponse(terminalPropertiesResponse, response, msg.id) case (`setTerminalAttributes`, Some(json)) => - Converter.fromJson[TerminalSetAttributesCommand](json) match { + Converter.fromJson[TerminalSetAttributesCommand](json) match case Success(attributes) => val attrs = Map( "iflag" -> attributes.iflag, @@ -1056,7 +991,6 @@ class NetworkClient( Terminal.console.setAttributes(attrs) sendCommandResponse("", TerminalSetAttributesResponse(), msg.id) case Failure(_) => - } case (`getTerminalAttributes`, _) => val attrs = Terminal.console.getAttributes val response = TerminalAttributesResponse( @@ -1074,50 +1008,44 @@ class NetworkClient( ) sendCommandResponse("", response, msg.id) case (`terminalSetSize`, Some(json)) => - Converter.fromJson[TerminalSetSizeCommand](json) match { + Converter.fromJson[TerminalSetSizeCommand](json) match case Success(size) => Terminal.console.setSize(size.width, size.height) sendCommandResponse("", TerminalSetSizeResponse(), msg.id) case Failure(_) => - } case (`terminalSetEcho`, Some(json)) => - Converter.fromJson[TerminalSetEchoCommand](json) match { + Converter.fromJson[TerminalSetEchoCommand](json) match case Success(echo) => Terminal.console.setEchoEnabled(echo.toggle) sendCommandResponse("", TerminalSetEchoResponse(), msg.id) case Failure(_) => - } case (`terminalSetRawMode`, Some(json)) => - Converter.fromJson[TerminalSetRawModeCommand](json) match { + Converter.fromJson[TerminalSetRawModeCommand](json) match case Success(raw) => - if (raw.toggle) Terminal.console.enterRawMode() + if raw.toggle then Terminal.console.enterRawMode() else Terminal.console.exitRawMode() sendCommandResponse("", TerminalSetRawModeResponse(), msg.id) case Failure(_) => - } case _ => - } - } + end match + end onRequest def connect(promptCompleteUsers: Boolean): Boolean = - try { + try init(promptCompleteUsers, retry = true) true - } catch { + catch case _: ServerFailedException => console.appendLog(Level.Error, "failed to connect to server") false - } - private val contHandler: () => Unit = () => { - if (Terminal.console.getLastLine.nonEmpty) + private val contHandler: () => Unit = () => + if Terminal.console.getLastLine.nonEmpty then printStream.print(ConsoleAppender.DeleteLine + Terminal.console.getLastLine.get) - } - private def withSignalHandler[R](handler: () => Unit, sig: String)(f: => R): R = { + private def withSignalHandler[R](handler: () => Unit, sig: String)(f: => R): R = val registration = Signals.register(handler, sig) try f finally registration.remove() - } private val cancelled = new AtomicBoolean(false) def run(): Int = @@ -1127,188 +1055,164 @@ class NetworkClient( val userCommands = arguments.commandArguments.takeWhile(_ != TerminateAction) val exit = arguments.commandArguments.nonEmpty && userCommands.isEmpty attachUUID.set(sendJson(attach, s"""{"interactive": $interactive}""")) - val handler: () => Unit = () => { - def exitAbruptly() = { + val handler: () => Unit = () => + def exitAbruptly() = exitClean.set(false) close() - } if inClientSideRun.get() then () else if cancelled.compareAndSet(false, true) then - val cancelledTasks = { + val cancelledTasks = val queue = sendCancelAllCommand() Option(queue.poll(1, TimeUnit.SECONDS)).getOrElse(true) - } if (batchMode.get && pendingResults.isEmpty) || !cancelledTasks then exitAbruptly() else cancelled.set(false) else exitAbruptly() // handles double ctrl+c to force a shutdown - } withSignalHandler(handler, Signals.INT) { - def block(): Int = { + def block(): Int = try this.synchronized(this.wait()) - catch { case _: InterruptedException => } - if (exitClean.get) 0 else 1 - } + catch + case _: InterruptedException => + if exitClean.get then 0 else 1 if interactive then block() else if exit then 0 - else { + else batchMode.set(true) val res = batchExecute(userCommands.toList) - if (!batchMode.get) block() else res - } + if !batchMode.get then block() else res } } - def batchExecute(userCommands: List[String]): Int = { + def batchExecute(userCommands: List[String]): Int = val cmd = userCommands.mkString(" ") sendAndWait(cmd, None) - } - def getCompletions(query: String): Seq[String] = { + def getCompletions(query: String): Seq[String] = val quoteCount = query.foldLeft(0) { case (count, '"') => count + 1 case (count, _) => count } val inQuote = quoteCount % 2 != 0 - val (rawPrefix, prefix, rawSuffix, suffix) = if (quoteCount > 0) { - query.lastIndexOf('"') match { + val (rawPrefix, prefix, rawSuffix, suffix) = if quoteCount > 0 then + query.lastIndexOf('"') match case -1 => (query, query, None, None) // shouldn't happen case i => val rawPrefix = query.substring(0, i) val prefix = rawPrefix.replace("\"", "").replace("\\;", ";") val rawSuffix = query.substring(i).replace("\\;", ";") - val suffix = if (rawSuffix.length > 1) rawSuffix.substring(1) else "" + val suffix = if rawSuffix.length > 1 then rawSuffix.substring(1) else "" (rawPrefix, prefix, Some(rawSuffix), Some(suffix)) - } - } else (query, query.replace("\\;", ";"), None, None) + else (query, query.replace("\\;", ";"), None, None) val tailSpace = query.endsWith(" ") || query.endsWith("\"") val sanitizedQuery = suffix.foldLeft(prefix) { _ + _ } - def getCompletions(query: String, sendCommand: Boolean): Seq[String] = { + def getCompletions(query: String, sendCommand: Boolean): Seq[String] = val result = new LinkedBlockingQueue[CompletionResponse]() val json = s"""{"query":"$query","level":1}""" val execId = sendJson("sbt/completion", json) pendingCompletions.put(execId, result.put) - val response = result.poll(30, TimeUnit.SECONDS) match { + val response = result.poll(30, TimeUnit.SECONDS) match case null => throw new TimeoutException("no response from server within 30 seconds") case r => r - } - def fillCompletions(label: String, regex: String, command: String): Seq[String] = { - def updateCompletions(): Seq[String] = { + def fillCompletions(label: String, regex: String, command: String): Seq[String] = + def updateCompletions(): Seq[String] = errorStream.println() sendJson(attach, s"""{"interactive": false}""") sendAndWait(query.replaceAll(regex + ".*", command).trim, None) getCompletions(query, false) - } - if (noStdErr) Nil - else if (noTab) updateCompletions() - else { + if noStdErr then Nil + else if noTab then updateCompletions() + else errorStream.print(s"\nNo cached $label names found. Press '' to compile: ") startInputThread() - stdinBytes.poll(5, TimeUnit.SECONDS) match { + stdinBytes.poll(5, TimeUnit.SECONDS) match case null => Nil case i if i == 9 => updateCompletions() case _ => Nil - } - } - } val testNameCompletions = - if (!response.cachedTestNames.getOrElse(true) && sendCommand) + if !response.cachedTestNames.getOrElse(true) && sendCommand then fillCompletions("test", "test(Only|Quick)", "definedTestNames") else Nil val classNameCompletions = - if (!response.cachedMainClassNames.getOrElse(true) && sendCommand) + if !response.cachedMainClassNames.getOrElse(true) && sendCommand then fillCompletions("main class", "runMain", "discoveredMainClasses") else Nil val completions = response.items testNameCompletions ++ classNameCompletions ++ completions - } + end getCompletions getCompletions(sanitizedQuery, true) collect { case c if inQuote => c case c if tailSpace && c.contains(" ") => c.replace(prefix, "") case c if !tailSpace => c.split(" ").last } - } + end getCompletions - private def sendAndWait(cmd: String, limit: Option[Deadline]): Int = { + private def sendAndWait(cmd: String, limit: Option[Deadline]): Int = val queue = sendExecCommand(cmd) var result: Integer = null - while (running.get && result == null && limit.fold(true)(!_.isOverdue())) { - try { - result = limit match { + while running.get && result == null && limit.fold(true)(!_.isOverdue()) do + try + result = limit match case Some(l) => queue.poll((l - Deadline.now).toMillis, TimeUnit.MILLISECONDS) case _ => queue.take - } - } catch { + catch case _: InterruptedException if cmd == Shutdown => result = 0 - case _: InterruptedException => result = if (exitClean.get) 0 else 1 - } - } - if (result == null) 1 else result - } + case _: InterruptedException => result = if exitClean.get then 0 else 1 + if result == null then 1 else result - def sendExecCommand(commandLine: String): LinkedBlockingQueue[Integer] = { + def sendExecCommand(commandLine: String): LinkedBlockingQueue[Integer] = val execId = UUID.randomUUID.toString val queue = new LinkedBlockingQueue[Integer] sendCommand(ExecCommand(commandLine, execId)) pendingResults.put(execId, (queue, System.currentTimeMillis, commandLine)) queue - } - def sendCancelAllCommand(): LinkedBlockingQueue[Boolean] = { + def sendCancelAllCommand(): LinkedBlockingQueue[Boolean] = val queue = new LinkedBlockingQueue[Boolean] val execId = sendJson(cancelRequest, s"""{"id":"$CancelAll"}""") pendingCancellations.put(execId, queue) queue - } - def sendCommand(command: CommandMessage): Unit = { - try { + def sendCommand(command: CommandMessage): Unit = + try connection.sendCommand(command) lock.synchronized { status.set("Processing") } - } catch { + catch case e: SocketException if command.toString.contains("exit") => running.set(false) case e: IOException => errorStream.println(s"Caught exception writing command to server: $e") running.set(false) - } - } - def sendCommandResponse(method: String, command: EventMessage, id: String): Unit = { + def sendCommandResponse(method: String, command: EventMessage, id: String): Unit = import sbt.protocol.codec.JsonProtocol.given - try { - connection.sendJsonRpcResponse(id, command) - } catch { + try connection.sendJsonRpcResponse(id, command) + catch case e: IOException => errorStream.println(s"Caught exception writing command to server: $e") running.set(false) - } - } - def sendJson(method: String, params: String): String = { + def sendJson(method: String, params: String): String = val uuid = UUID.randomUUID.toString sendJson(method, params, uuid) uuid - } - def sendJson(method: String, params: String, uuid: String): Unit = { + def sendJson(method: String, params: String, uuid: String): Unit = connection.sendJsonRpcRaw(uuid, method, params) - } - def sendNotification(method: String, params: String): Unit = { + def sendNotification(method: String, params: String): Unit = connection.sendJsonRpcNotificationRaw(method, params) - } override def close(): Unit = - try { + try running.set(false) stdinBytes.offer(-1) val mainThread = interactiveThread.getAndSet(null) - if (mainThread != null && mainThread != Thread.currentThread) mainThread.interrupt - if (connectionHolder.get ne null) + if mainThread != null && mainThread != Thread.currentThread then mainThread.interrupt + if connectionHolder.get ne null then try sendExecCommand("exit") finally connectionHolder.close() inputThread.close() - } catch { - case t: Throwable => t.printStackTrace(); throw t - } + catch + case t: Throwable => + t.printStackTrace() + throw t /** * Reads stdin on behalf of the server, which asks for it one byte at a time via @@ -1342,10 +1246,9 @@ class NetworkClient( def request(): Unit = readGate.release() def cancel(): Unit = interrupt() def drain(): Unit = inLock.synchronized { - while (!stdinBytes.isEmpty) { + while !stdinBytes.isEmpty do val byte = stdinBytes.poll() sendNotification(systemIn, byte.toString) - } } override def close(): Unit = @@ -1353,38 +1256,35 @@ class NetworkClient( readGate.release() RawInputThread.this.interrupt() end RawInputThread -} +end NetworkClient -object NetworkClient { +object NetworkClient: private[sbt] val CancelAll = "__CancelAll" - private def consoleAppenderInterface(printStream: PrintStream): ConsoleInterface = { + private def consoleAppenderInterface(printStream: PrintStream): ConsoleInterface = val appender = ConsoleAppender("thin", ConsoleOut.printStreamOut(printStream)) - new ConsoleInterface { + new ConsoleInterface: override def appendLog(level: Level.Value, message: => String): Unit = appender.appendLog(level, message) override def success(msg: String): Unit = appender.success(msg) - } - } private def simpleConsoleInterface( doPrintln: String => Unit, useColor: Boolean ): ConsoleInterface = - new ConsoleInterface { + new ConsoleInterface: import scala.Console.{ GREEN, RED, RESET, YELLOW } override def appendLog(level: Level.Value, message: => String): Unit = synchronized { val prefix = - if (useColor) level match { - case Level.Error => s"[$RED$level$RESET]" - case Level.Warn => s"[$YELLOW$level$RESET]" - case _ => s"[$RESET$level$RESET]" - } + if useColor then + level match + case Level.Error => s"[$RED$level$RESET]" + case Level.Warn => s"[$YELLOW$level$RESET]" + case _ => s"[$RESET$level$RESET]" else s"[$level]" message.linesIterator.foreach(line => doPrintln(s"$prefix $line")) } override def success(msg: String): Unit = - if (useColor) doPrintln(s"[${GREEN}success$RESET] $msg") + if useColor then doPrintln(s"[${GREEN}success$RESET] $msg") else doPrintln(s"[success] $msg") - } private[client] class Arguments( val baseDirectory: File, val sbtArguments: Seq[String], @@ -1397,7 +1297,7 @@ object NetworkClient { // false when -D options went to the JVM running this client instead of its arguments, // in which case they say nothing about the server val forwardsSysProps: Boolean = true, - ) { + ): def withBaseDirectory(file: File): Arguments = copy(baseDirectory = file) def withoutSysProps: Arguments = @@ -1417,9 +1317,9 @@ object NetworkClient { launcherValueArgs, forwardsSysProps, ) - } + end Arguments private[client] def serverCommand(arguments: Arguments): List[String] = - arguments.sbtLaunchJar match { + arguments.sbtLaunchJar match case Some(lj) => val java = Option(Properties.javaHome).map(javaHome => s"$javaHome/bin/java").getOrElse("java") @@ -1428,7 +1328,6 @@ object NetworkClient { case _ => List(arguments.sbtScript) ++ arguments.launcherValueArgs ++ arguments.sbtArguments ++ List(DashDashDetachStdio, DashDashServer) - } /** Carries the `-D` options a client forwards to the server it starts. */ private[sbt] val sysPropsEnv = "SBT_SERVER_SYS_PROPS" @@ -1617,8 +1516,8 @@ object NetworkClient { ) private[client] val launcherValueEqPrefixes: Seq[String] = launcherValueFlags.toSeq.map(_ + "=") - private[client] def parseArgs(args: Array[String]): Arguments = { - val defaultSbtScript = if (Properties.isWin) "sbt.bat" else "sbt" + private[client] def parseArgs(args: Array[String]): Arguments = + val defaultSbtScript = if Properties.isWin then "sbt.bat" else "sbt" var sbtScript = Properties.propOrNone("sbt.script") var launchJar: Option[String] = None var bsp = false @@ -1636,25 +1535,22 @@ object NetworkClient { case a => var first = true a.split(" ").foreach { part => - if (part.nonEmpty) { + if part.nonEmpty then sanitized += part splitFromPrev += !first first = false - } } } - def valueFrom(start: Int): (String, Int) = { + def valueFrom(start: Int): (String, Int) = var last = start val sb = new StringBuilder(sanitized(start)) - while (last + 1 < sanitized.length && splitFromPrev(last + 1)) { + while last + 1 < sanitized.length && splitFromPrev(last + 1) do last += 1 sb.append(" ").append(sanitized(last)) - } (sb.toString, last) - } var i = 0 - while (i < sanitized.length) { - sanitized(i) match { + while i < sanitized.length do + sanitized(i) match case a if completionArguments.nonEmpty => completionArguments += a case a if commandArgs.nonEmpty && emptyBuildFlags.contains(a) => sbtArguments += a @@ -1685,20 +1581,18 @@ object NetworkClient { case a if a.startsWith("-autostart=") => System.setProperty("sbt.server.autostart", a.stripPrefix("-autostart=")) case a if launcherValueFlags.contains(a) => - if (i + 1 < sanitized.length) { + if i + 1 < sanitized.length then launcherValueArgs += a val (value, last) = valueFrom(i + 1) launcherValueArgs += value i = last - } case a if launcherValueEqPrefixes.exists(p => a.startsWith(p)) => val (full, last) = valueFrom(i) i = last val eq = full.indexOf('=') - if (eq < full.length - 1) { + if eq < full.length - 1 then launcherValueArgs += full.substring(0, eq) launcherValueArgs += full.substring(eq + 1) - } case a if launcherNoValueFlags.contains(a) => () case a if launcherEqPrefixes.exists(p => a.startsWith(p)) => () case a if a.startsWith("-J") => () @@ -1707,16 +1601,15 @@ object NetworkClient { System.setProperty(key, value) sbtArguments += a case a => sbtArguments += a - } + end match i += 1 - } + end while val base = new File("").getCanonicalFile - if (!sbtArguments.contains("-Dsbt.io.virtual=true")) sbtArguments += "-Dsbt.io.virtual=true" - if (!sbtArguments.exists(_.startsWith("-Dsbt.script"))) { + if !sbtArguments.contains("-Dsbt.io.virtual=true") then sbtArguments += "-Dsbt.io.virtual=true" + if !sbtArguments.exists(_.startsWith("-Dsbt.script")) then sbtScript.foreach { sbtScript => sbtArguments += s"-Dsbt.script=$sbtScript" } - } new Arguments( base, sbtArguments.toSeq, @@ -1727,24 +1620,22 @@ object NetworkClient { launchJar, launcherValueArgs.toSeq, ) - } + end parseArgs def elapsedString(startTime: Long, endTime: Long): String = s"elapsed time: ${elapsedStr(startTime, endTime)}" - private def elapsedStr(startTime: Long, endTime: Long): String = { + private def elapsedStr(startTime: Long, endTime: Long): String = val total = (endTime - startTime + 500) / 1000 s"$total s" + - (if (total <= 60) "" - else { + (if total <= 60 then "" + else val hours = total / 3600 match case 0 => "0" case h => f"$h%02d" val mins = f"${total % 3600 / 60}%02d" val secs = f"${total % 60}%02d" - s" ($hours:$mins:$secs.0)" - }) - } + s" ($hours:$mins:$secs.0)") def client( baseDirectory: File, @@ -1753,7 +1644,7 @@ object NetworkClient { printStream: PrintStream, errorStream: PrintStream, useJNI: Boolean - ): Int = { + ): Int = val client = simpleClient( NetworkClient.parseArgs(args).withBaseDirectory(baseDirectory), @@ -1762,15 +1653,15 @@ object NetworkClient { errorStream, useJNI, ) - try { - if (client.connect(promptCompleteUsers = false)) client.run() + try + if client.connect(promptCompleteUsers = false) then client.run() else 1 - } catch { + catch case e: Exception => client.logFailure(e) 1 - } finally client.close() - } + finally client.close() + end client def client( baseDirectory: File, args: Arguments, @@ -1778,8 +1669,8 @@ object NetworkClient { errorStream: PrintStream, terminal: Terminal, useJNI: Boolean - ): Int = { - val printStream = if (args.bsp) errorStream else terminal.printStream + ): Int = + val printStream = if args.bsp then errorStream else terminal.printStream val client = simpleClient( args.withBaseDirectory(baseDirectory), @@ -1789,23 +1680,19 @@ object NetworkClient { useJNI, ) clientImpl(client, args.bsp) - } - private def clientImpl(client: NetworkClient, isBsp: Boolean): Int = { - try { - if (isBsp) { + private def clientImpl(client: NetworkClient, isBsp: Boolean): Int = + try + if isBsp then val (socket, _) = client.connectOrStartServerAndConnect(promptCompleteUsers = false, retry = true) BspClient.bspRun(socket) - } else { - if (client.connect(promptCompleteUsers = false)) client.run() - else 1 - } - } catch { + else if client.connect(promptCompleteUsers = false) then client.run() + else 1 + catch case e: Exception => client.logFailure(e) 1 - } finally client.close() - } + finally client.close() def client( baseDirectory: File, args: Array[String], @@ -1821,60 +1708,54 @@ object NetworkClient { printStream: PrintStream, errorStream: PrintStream, useJNI: Boolean, - ): NetworkClient = { + ): NetworkClient = val interface = NetworkClient.simpleConsoleInterface(printStream.println, Terminal.isColorEnabled) new NetworkClient(arguments, interface, inputStream, errorStream, printStream, useJNI) - } - def main(args: Array[String]): Unit = { + def main(args: Array[String]): Unit = val (jnaArg, restOfArgs) = args.partition(_ == "--jna") val useJNI = jnaArg.isEmpty val base = new File("").getCanonicalFile - if (restOfArgs.exists(_.startsWith(NetworkClient.completions))) + if restOfArgs.exists(_.startsWith(NetworkClient.completions)) then System.exit(complete(base, restOfArgs, useJNI, System.in, System.out)) - else { - val hook = new Thread(() => { + else + val hook = new Thread(() => System.out.print(ConsoleAppender.ClearScreenAfterCursor) System.out.flush() - }) + ) Runtime.getRuntime.addShutdownHook(hook) val parsed = parseArgs(restOfArgs) System.exit(Terminal.withStreams(isServer = false, isSubProcess = false) { val term = Terminal.console try client(base, parsed, term.inputStream, System.err, term, useJNI) - catch { case _: AccessDeniedException => 1 } - finally { + catch case _: AccessDeniedException => 1 + finally Runtime.getRuntime.removeShutdownHook(hook) hook.run() - } }) - } - } + end main def complete( baseDirectory: File, args: Array[String], useJNI: Boolean, in: InputStream, out: PrintStream - ): Int = { - val cmd: String = args.find(_.startsWith(NetworkClient.completions)) match { + ): Int = + val cmd: String = args.find(_.startsWith(NetworkClient.completions)) match case Some(c) => - c.split('=').lastOption match { + c.split('=').lastOption match case Some(query) => - query.indexOf(" ") match { + query.indexOf(" ") match case -1 => throw new IllegalArgumentException(query) case i => query.substring(i + 1) - } case _ => throw new IllegalArgumentException(c) - } case _ => throw new IllegalStateException("should be unreachable") - } val quiet = args.exists(_ == "--quiet") - val errorStream = if (quiet) new PrintStream(_ => {}, false) else System.err + val errorStream = if quiet then new PrintStream(_ => {}, false) else System.err val sbtArgs = args.takeWhile(!_.startsWith(NetworkClient.completions)) val arguments = NetworkClient.parseArgs(sbtArgs) val noTab = args.contains("--no-tab") - try { + try val client = simpleClient( arguments.withBaseDirectory(baseDirectory), @@ -1883,16 +1764,16 @@ object NetworkClient { printStream = errorStream, useJNI = useJNI, ) - try { + try val results = - if (client.connect(promptCompleteUsers = true)) client.getCompletions(cmd) + if client.connect(promptCompleteUsers = true) then client.getCompletions(cmd) else Nil out.println(results.sorted.distinct mkString "\n") 0 - } catch { case _: Exception => 1 } + catch case _: Exception => 1 finally client.close() - } catch { case _: AccessDeniedException => 1 } - } + catch case _: AccessDeniedException => 1 + end complete def run(configuration: xsbti.AppConfiguration, arguments: List[String]): Int = run(configuration, arguments, false) @@ -1900,10 +1781,10 @@ object NetworkClient { configuration: xsbti.AppConfiguration, arguments: List[String], redirectOutput: Boolean - ): Int = { + ): Int = val term = Terminal.console val err = new PrintStream(term.errorStream) - val out = if (redirectOutput) err else new PrintStream(term.outputStream) + val out = if redirectOutput then err else new PrintStream(term.outputStream) val args = parseArgs(arguments.toArray).withoutSysProps.withBaseDirectory(configuration.baseDirectory) val useJNI = @@ -1911,6 +1792,5 @@ object NetworkClient { System.getProperty("sbt.ipcsocket.jni", "false") == "true" val client = simpleClient(args, term.inputStream, out, err, useJNI = useJNI) clientImpl(client, args.bsp) - } private class AccessDeniedException extends Throwable -} +end NetworkClient diff --git a/main-command/src/main/scala/sbt/internal/server/Server.scala b/main-command/src/main/scala/sbt/internal/server/Server.scala index 4b83766e5..e47454f4e 100644 --- a/main-command/src/main/scala/sbt/internal/server/Server.scala +++ b/main-command/src/main/scala/sbt/internal/server/Server.scala @@ -31,14 +31,13 @@ import sbt.internal.bsp.BuildServerConnection import sbt.protocol.ClientSocket import xsbti.AppConfiguration -private[sbt] sealed trait ServerInstance { +private[sbt] sealed trait ServerInstance: def shutdown(): Unit def serverId: String def ready: Future[Unit] def authenticate(challenge: String): Boolean -} -private[sbt] object Server { +private[sbt] object Server: sealed trait JsonProtocol extends sjsonnew.BasicJsonProtocol with PortFileFormats @@ -54,7 +53,8 @@ private[sbt] object Server { onIncomingSocket: (AtomicReference[Socket], ServerInstance) => Unit, log: Logger ): ServerInstance = - new ServerInstance { self => + new ServerInstance: + self => import connection.* val running = new AtomicBoolean(false) val p: Promise[Unit] = Promise[Unit]() @@ -64,10 +64,10 @@ private[sbt] object Server { private val serverSocketHolder = AtomicCloseable[ServerSocket]() override val serverId: String = java.util.UUID.randomUUID().toString - val serverThread = new Thread("sbt-socket-server") { - override def run(): Unit = { + val serverThread = new Thread("sbt-socket-server"): + override def run(): Unit = Try { - connection.connectionType match { + connection.connectionType match case ConnectionType.Local if isWindows => // Named pipe already has an exclusive lock. addServerError( @@ -81,7 +81,7 @@ private[sbt] object Server { val maxSocketLength = UnixDomainSocketLibraryProvider.maxSocketLength(connection.useJni) - 1 val path = socketfile.getAbsolutePath - if (path.length > maxSocketLength) + if path.length > maxSocketLength then sys.error( "socket file absolute path too long; " + "either switch to another connection type " + @@ -94,56 +94,47 @@ private[sbt] object Server { case ConnectionType.Tcp => tryClient(new Socket(InetAddress.getByName(host), port)) addServerError(new ServerSocket(port, 50, InetAddress.getByName(host))) - } - } match { + } match case Failure(e) => p.failure(e) case Success(serverSocket) => serverSocket.setSoTimeout(5000) serverSocketHolder.set(serverSocket) log.debug(s"sbt server started at ${connection.shortName}") writePortfile() - if (connection.bspEnabled) { + if connection.bspEnabled then log.debug("Writing bsp connection file") BuildServerConnection.writeConnectionFile( appConfiguration.provider.id.version, appConfiguration.baseDirectory ) - } running.set(true) p.success(()) - while (running.get()) { + while running.get() do val clientSocket = AtomicCloseable[Socket]() - try { + try clientSocket.set(serverSocket.accept()) onIncomingSocket(clientSocket.ref, self) - } catch { + catch case scala.util.control.NonFatal(e) if clientSocket.get ne null => log.error(s"sbt server failed to serve a client: $e") log.trace(e) case e: IOException if Option(e.getMessage).exists(_.contains("connect")) => case _: SocketTimeoutException => // its ok case _: SocketException if !running.get => // the server is shutting down - } clientSocket.close() - } serverSocketHolder.close() - } - } - } serverThread.start() // Try the socket as a client to make sure that the server is not already up. // f tries to connect to the server, and flip the result. - def tryClient(f: => Socket): Unit = { - if (portfile.exists) { - Try { f } match { + def tryClient(f: => Socket): Unit = + if portfile.exists then + Try { f } match case Failure(_) => () case Success(socket) => socket.close() throw new AlreadyRunningException() - } - } else () - } + else () def addServerError(f: => ServerSocket): ServerSocket = ErrorHandling.translate(s"server failed to start on ${connection.shortName}. ") { @@ -151,27 +142,25 @@ private[sbt] object Server { } override def authenticate(challenge: String): Boolean = synchronized { - if (token == challenge) { + if token == challenge then token = nextToken writeTokenfile() true - } else false + else false } /** Generates 128-bit non-negative integer, and represent it as decimal string. */ - private def nextToken: String = { + private def nextToken: String = new BigInteger(128, rand).toString - } - override def shutdown(): Unit = { - if (serverIdOf(portfile).getOrElse(None).contains(serverId)) IO.delete(portfile) + override def shutdown(): Unit = + if serverIdOf(portfile).getOrElse(None).contains(serverId) then IO.delete(portfile) IO.delete(tokenfile) running.set(false) serverSocketHolder.close() log.info("shutting down sbt server") - } - private def writeTokenfile(): Unit = { + private def writeTokenfile(): Unit = import JsonProtocol.given val uri = connection.shortName @@ -181,10 +170,9 @@ private[sbt] object Server { IO.writeFileAtomically(tokenfile, ownerOnly = true)(tmp => IO.write(tmp, CompactPrinter(jsonToken), IO.utf8, false) ) - } // This file exists through the lifetime of the server. - private def writePortfile(): Unit = { + private def writePortfile(): Unit = import JsonProtocol.given val uri = connection.shortName @@ -194,34 +182,30 @@ private[sbt] object Server { .get(NetworkClient.sysPropsPortfileEnv) .map(new File(_).getCanonicalFile) .contains(portfile.getCanonicalFile) - val recorded = if (startedByThisBuild) sys.env.get(NetworkClient.sysPropsEnv) else None + val recorded = if startedByThisBuild then sys.env.get(NetworkClient.sysPropsEnv) else None val sysProps = recorded.toVector.flatMap(NetworkClient.decodeSysProps) // an empty list of options and no idea what the options are read the same, so say // which of the two this is: a client can restart a server over the first but has no // business taking down one whose options it never saw val sysPropsRecorded = Option(startedByThisBuild) val authOK = auth(ServerAuthentication.Token) - if (authOK) writeTokenfile() + if authOK then writeTokenfile() val p = PortFile( uri, - if (authOK) Some(tokenfile.toString) else None, - if (authOK) Some(IO.toURI(tokenfile).toString) else None, + if authOK then Some(tokenfile.toString) else None, + if authOK then Some(IO.toURI(tokenfile).toString) else None, sysProps, sysPropsRecorded, Some(serverId) ) val json = Converter.toJson(p).get IO.writeFileAtomically(portfile)(tmp => IO.write(tmp, CompactPrinter(json))) - } + end writePortfile - private[sbt] def prepareSocketfile(): Unit = { - if (socketfile.exists) { - IO.delete(socketfile) - } + private[sbt] def prepareSocketfile(): Unit = + if socketfile.exists then IO.delete(socketfile) IO.createDirectory(socketfile.getParentFile) - } - } -} +end Server private[sbt] case class ServerConnection( connectionType: ConnectionType, @@ -236,15 +220,12 @@ private[sbt] case class ServerConnection( windowsServerSecurityLevel: Int, useJni: Boolean, bspEnabled: Boolean, -) { - def shortName: String = { - connectionType match { +): + def shortName: String = + connectionType match case ConnectionType.Local if isWindows => s"local:$pipeName" case ConnectionType.Local => s"local://$socketfile" case ConnectionType.Tcp => s"tcp://$host:$port" // case ConnectionType.Ssh => s"ssh://$host:$port" - } - } -} private[sbt] class AlreadyRunningException extends IOException("sbt server is already running.") diff --git a/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala index be88d5237..248340734 100644 --- a/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala +++ b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala @@ -20,11 +20,10 @@ import sbt.internal.langserver.CancelRequestParams as CRP * ServerHandler allows plugins to extend sbt server. * It's a wrapper around curried function ServerCallback => JsonRpcRequestMessage => Unit. */ -final class ServerHandler(val handler: ServerCallback => ServerIntent) { +final class ServerHandler(val handler: ServerCallback => ServerIntent): override def toString: String = s"Serverhandler(...)" -} -object ServerHandler { +object ServerHandler: def apply(handler: ServerCallback => ServerIntent): ServerHandler = new ServerHandler(handler) @@ -40,17 +39,15 @@ object ServerHandler { }, ) }) -} final class ServerIntent( val onRequest: PartialFunction[JsonRpcRequestMessage, Unit], val onResponse: PartialFunction[JsonRpcResponseMessage, Unit], val onNotification: PartialFunction[JsonRpcNotificationMessage, Unit] -) { +): override def toString: String = s"ServerIntent(...)" -} -object ServerIntent { +object ServerIntent: def apply( onRequest: PartialFunction[JsonRpcRequestMessage, Unit], onResponse: PartialFunction[JsonRpcResponseMessage, Unit], @@ -65,12 +62,11 @@ object ServerIntent { new ServerIntent(PartialFunction.empty, onResponse, PartialFunction.empty) def notify(onNotification: PartialFunction[JsonRpcNotificationMessage, Unit]): ServerIntent = new ServerIntent(PartialFunction.empty, PartialFunction.empty, onNotification) -} /** * Interface to invoke JSON-RPC response. */ -trait ServerCallback { +trait ServerCallback: def jsonRpcRespond[A: JsonFormat](event: A, execId: Option[String]): Unit def jsonRpcRespondError(execId: Option[String], code: Long, message: String): Unit def jsonRpcNotify[A: JsonFormat](method: String, params: A): Unit @@ -87,4 +83,3 @@ trait ServerCallback { private[sbt] def onSettingQuery(execId: Option[String], req: Q): Unit private[sbt] def onCompletionRequest(execId: Option[String], cp: CP): Unit private[sbt] def onCancellationRequest(execId: Option[String], crp: CRP): Unit -} diff --git a/main-command/src/main/scala/sbt/internal/ui/UITask.scala b/main-command/src/main/scala/sbt/internal/ui/UITask.scala index 6789e8fa0..6971426c3 100644 --- a/main-command/src/main/scala/sbt/internal/ui/UITask.scala +++ b/main-command/src/main/scala/sbt/internal/ui/UITask.scala @@ -23,37 +23,30 @@ import sbt.internal.util.complete.Parser import scala.annotation.tailrec -private[sbt] trait UITask extends Runnable with AutoCloseable { +private[sbt] trait UITask extends Runnable with AutoCloseable: private[sbt] val channel: CommandChannel private[sbt] def reader: UITask.Reader - private final def handleInput(s: Either[String, String]): Boolean = s match { + private final def handleInput(s: Either[String, String]): Boolean = s match case Left(m) => channel.onFastTrackTask(m) case Right(cmd) => channel.onCommandLine(cmd) - } private val isStopped = new AtomicBoolean(false) - override def run(): Unit = { - @tailrec def impl(): Unit = if (!isStopped.get) { + override def run(): Unit = + @tailrec def impl(): Unit = if !isStopped.get then val res = reader.readLine() - if (!handleInput(res) && !isStopped.get) impl() - } + if !handleInput(res) && !isStopped.get then impl() try impl() - catch { case _: InterruptedException | _: ClosedChannelException => isStopped.set(true) } - } - override def close(): Unit = { + catch case _: InterruptedException | _: ClosedChannelException => isStopped.set(true) + override def close(): Unit = isStopped.set(true) reader.close() - } -} -private[sbt] object UITask { - case object NoShellPrompt extends (State => String) { +private[sbt] object UITask: + case object NoShellPrompt extends (State => String): override def apply(state: State): String = "" - } - trait Reader extends AutoCloseable { + trait Reader extends AutoCloseable: def readLine(): Either[String, String] override def close(): Unit = {} - } - object Reader { + object Reader: // Avoid filling the stack trace since it isn't helpful here object interrupted extends InterruptedException @@ -63,28 +56,27 @@ private[sbt] object UITask { def splitCommand(cmd: String): Either[String, String] = // We need to put the empty string on the fast track queue so that we can // reprompt the user if another command is running on the server. - if (cmd.isEmpty()) Left("") + if cmd.isEmpty() then Left("") else - cmd match { + cmd match case Shutdown | TerminateAction | Cancel => Left(cmd) case cmd => Right(cmd) - } def terminalReader(parser: Parser[?])( terminal: Terminal, state: State - ): Reader = new Reader { + ): Reader = new Reader: val closed = new AtomicBoolean(false) def readLine(): Either[String, String] = - try { + try val clear = terminal.ansi(ClearPromptLine, "") - val res = { + val res = val thread = Thread.currentThread - if (thread.isInterrupted || closed.get) throw interrupted + if thread.isInterrupted || closed.get then throw interrupted val reader = LineReader.createReader(history(state), parser, terminal) - if (thread.isInterrupted || closed.get) throw interrupted + if thread.isInterrupted || closed.get then throw interrupted (try reader.readLine(clear + terminal.prompt.mkPrompt()) - finally reader.close) match { + finally reader.close) match case None if terminal == Terminal.console && !hasConsole => // No stdin is attached to the process so just ignore the result and // block until the thread is interrupted. @@ -94,33 +86,26 @@ private[sbt] object UITask { // ctrl+d with no imput as an exit case None => Left(TerminateAction) case Some(s: String) => splitCommand(s.trim()) - } - } terminal.setPrompt(Prompt.Pending) res - } catch { case e: InterruptedException => Left("") } + catch case e: InterruptedException => Left("") override def close(): Unit = closed.set(true) - } - } + end Reader private def history(s: State): Option[File] = s.get(historyPath).getOrElse(Some(new File(s.baseDir, ".history"))) private[sbt] def shellPrompt(terminal: Terminal, s: State): String = - s.get(sbt.BasicKeys.shellPrompt) match { + s.get(sbt.BasicKeys.shellPrompt) match case Some(NoShellPrompt) | None => - s.get(colorShellPrompt) match { + s.get(colorShellPrompt) match case Some(pf) => pf(terminal.isColorEnabled, s) case None => - def color(s: String): String = if (terminal.isColorEnabled) s"$s" else "" + def color(s: String): String = if terminal.isColorEnabled then s"$s" else "" s"${color(DeleteLine)}> ${color(ClearScreenAfterCursor)}" - } case Some(p) => p(s) - } private[sbt] class AskUserTask( state: State, override val channel: CommandChannel, - ) extends UITask { - override private[sbt] lazy val reader: UITask.Reader = { + ) extends UITask: + override private[sbt] lazy val reader: UITask.Reader = UITask.Reader.terminalReader(state.combinedParser)(channel.terminal, state) - } - } -} +end UITask diff --git a/main-command/src/main/scala/sbt/internal/ui/UserThread.scala b/main-command/src/main/scala/sbt/internal/ui/UserThread.scala index 4d83485cb..9d7f1205c 100644 --- a/main-command/src/main/scala/sbt/internal/ui/UserThread.scala +++ b/main-command/src/main/scala/sbt/internal/ui/UserThread.scala @@ -18,59 +18,50 @@ import scala.concurrent.duration.* import sbt.internal.util.JoinThread.* import sbt.internal.util.{ ConsoleAppender, ProgressEvent, ProgressState, Prompt } -private[sbt] class UserThread(val channel: CommandChannel) extends AutoCloseable { +private[sbt] class UserThread(val channel: CommandChannel) extends AutoCloseable: private val uiThread = new AtomicReference[(UITask, Thread)] - private[sbt] final def onProgressEvent(pe: ProgressEvent): Unit = { + private[sbt] final def onProgressEvent(pe: ProgressEvent): Unit = lastProgressEvent.set(pe) ProgressState.updateProgressState(pe, channel.terminal) - } private val executor = Executors.newSingleThreadExecutor(r => new Thread(r, s"sbt-$name-ui-thread")) private val lastProgressEvent = new AtomicReference[ProgressEvent] private val isClosed = new AtomicBoolean(false) - private[sbt] def reset(state: State): Unit = if (!isClosed.get) { + private[sbt] def reset(state: State): Unit = if !isClosed.get then uiThread.synchronized { val task = channel.makeUIThread(state) - def submit(): Unit = { - val thread: Thread = new Thread(s"sbt-$name-ui-thread") { + def submit(): Unit = + val thread: Thread = new Thread(s"sbt-$name-ui-thread"): setDaemon(true) override def run(): Unit = try task.run() - finally { - uiThread.getAndSet(null) match { + finally + uiThread.getAndSet(null) match case prev @ (_, th) if th != this => uiThread.set(prev) case _ => - } - } - } - uiThread.getAndSet((task, thread)) match { + uiThread.getAndSet((task, thread)) match case null => thread.start() case (prevTask, prevThread) if prevTask.getClass != task.getClass => prevTask.close() prevThread.joinFor(1.second) thread.start() case t => uiThread.set(t) - } - } - uiThread.get match { + uiThread.get match case null => submit() case (prevTask, _) if prevTask.getClass == task.getClass => case (t, thread) => stopThreadImpl() submit() - } } Option(lastProgressEvent.get).foreach(onProgressEvent) - } private[sbt] def stopThreadImpl(): Unit = uiThread.synchronized { - uiThread.getAndSet(null) match { + uiThread.getAndSet(null) match case null => case (t, thread) => t.close() thread.joinFor(1.second) - } } private[sbt] def stopThread(): Unit = uiThread.synchronized(stopThreadImpl()) @@ -78,32 +69,29 @@ private[sbt] class UserThread(val channel: CommandChannel) extends AutoCloseable // synchronize to ensure that the state isn't modified during the call to reset // at the bottom synchronized { - if (terminal.isAnsiSupported) { + if terminal.isAnsiSupported then channel.terminal.withPrintStream { ps => ps.print(ConsoleAppender.ClearScreenAfterCursor) ps.flush() } - } val state = consolePromptEvent.state - terminal.prompt match { + terminal.prompt match case Prompt.Running | Prompt.Pending => terminal.setPrompt(Prompt.AskUser(() => UITask.shellPrompt(terminal, state))) case _ => - } onProgressEvent(ProgressEvent("Info", Vector(), None, None, None)) reset(state) } private[sbt] def onConsoleUnpromptEvent( consoleUnpromptEvent: ConsoleUnpromptEvent - ): Unit = { + ): Unit = terminal.setPrompt(Prompt.Pending) - if (consoleUnpromptEvent.lastSource.fold(true)(_.channelName != name)) { + if consoleUnpromptEvent.lastSource.fold(true)(_.channelName != name) then terminal.progressState.reset() - } else stopThread() - } + else stopThread() - override def close(): Unit = if (isClosed.compareAndSet(false, true)) executor.shutdown() + override def close(): Unit = if isClosed.compareAndSet(false, true) then executor.shutdown() private def terminal = channel.terminal private def name: String = channel.name -} +end UserThread diff --git a/main-command/src/main/scala/xsbt/IPC.scala b/main-command/src/main/scala/xsbt/IPC.scala index ef71e7ee7..ba97d79ee 100644 --- a/main-command/src/main/scala/xsbt/IPC.scala +++ b/main-command/src/main/scala/xsbt/IPC.scala @@ -15,7 +15,7 @@ import scala.annotation.tailrec import scala.util.control.NonFatal import scala.util.Using -object IPC { +object IPC: private val portMin = 1025 private val portMax = 65536 private val loopback = InetAddress.getByName(null) @@ -23,62 +23,56 @@ object IPC { def client[T](port: Int)(f: IPC => T): T = ipc(new Socket(loopback, port))(f) - def pullServer[T](f: Server => T): T = { + def pullServer[T](f: Server => T): T = Using.resource(makeServer) { server => f(new Server(server)) } - } def unmanagedServer: Server = new Server(makeServer) - def makeServer: ServerSocket = { + def makeServer: ServerSocket = val random = new java.util.Random def nextPort = random.nextInt(portMax - portMin + 1) + portMin @tailrec def createServer(attempts: Int): ServerSocket = - if (attempts > 0) { + if attempts > 0 then try new ServerSocket(nextPort, socketBacklog, loopback) - catch { case NonFatal(_) => createServer(attempts - 1) } - } else sys.error("Could not connect to socket: maximum attempts exceeded") + catch case NonFatal(_) => createServer(attempts - 1) + else sys.error("Could not connect to socket: maximum attempts exceeded") createServer(10) - } def server[T](f: IPC => Option[T]): T = serverImpl(makeServer, f) def server[T](port: Int)(f: IPC => Option[T]): T = serverImpl(new ServerSocket(port, 1, loopback), f) - private def serverImpl[T](server: ServerSocket, f: IPC => Option[T]): T = { - @tailrec def listen(): T = { - ipc(server.accept())(f) match { + private def serverImpl[T](server: ServerSocket, f: IPC => Option[T]): T = + @tailrec def listen(): T = + ipc(server.accept())(f) match case Some(done) => done case None => listen() - } - } try listen() finally server.close() - } private def ipc[T](s: Socket)(f: IPC => T): T = try f(new IPC(s)) finally s.close() - final class Server private[IPC] (s: ServerSocket) { + final class Server private[IPC] (s: ServerSocket): def port = s.getLocalPort def close() = s.close() def isClosed: Boolean = s.isClosed def connection[T](f: IPC => T): T = IPC.ipc(s.accept())(f) - } -} +end IPC -final class IPC private (s: Socket) { +final class IPC private (s: Socket): def port = s.getLocalPort private val in = new BufferedReader(new InputStreamReader(s.getInputStream)) private val out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream)) - def send(s: String) = { out.write(s); out.newLine(); out.flush() } + def send(s: String) = + out.write(s); out.newLine(); out.flush() def receive: String = in.readLine() -} diff --git a/main-command/src/test/scala/sbt/internal/BootServerSocketSpec.scala b/main-command/src/test/scala/sbt/internal/BootServerSocketSpec.scala index c8daf5dcd..088d9bbfb 100644 --- a/main-command/src/test/scala/sbt/internal/BootServerSocketSpec.scala +++ b/main-command/src/test/scala/sbt/internal/BootServerSocketSpec.scala @@ -16,11 +16,10 @@ object BootServerSocketSpec extends BasicTestSuite: // the constructor only reads baseDirectory; provider is never touched private def config(base: java.io.File): xsbti.AppConfiguration = - new xsbti.AppConfiguration { + new xsbti.AppConfiguration: override def arguments(): Array[String] = Array.empty override def baseDirectory(): java.io.File = base override def provider(): xsbti.AppProvider = null - } private def probe(location: String): Boolean = BootServerSocketProbe.liveServerDetected(location, false) @@ -56,7 +55,7 @@ object BootServerSocketSpec extends BasicTestSuite: } test("a stale socket file is not a live server and does not block a new socket") { - if (!Util.isWindows) { + if !Util.isWindows then val (base, token) = freshBase("boot-socket-stale") val location = Paths.get(BootServerSocket.socketLocation(base.toPath, token)) Files.createDirectories(location.getParent) @@ -68,7 +67,6 @@ object BootServerSocketSpec extends BasicTestSuite: try probe(location.toString) finally server.close() assert(liveAfterReclaim) - } } end BootServerSocketSpec diff --git a/main-command/src/test/scala/sbt/internal/client/ClientConnectSpec.scala b/main-command/src/test/scala/sbt/internal/client/ClientConnectSpec.scala index 14a048780..9f8733eac 100644 --- a/main-command/src/test/scala/sbt/internal/client/ClientConnectSpec.scala +++ b/main-command/src/test/scala/sbt/internal/client/ClientConnectSpec.scala @@ -63,6 +63,7 @@ object ClientConnectSpec extends BasicTestSuite: Try(client.close()) instance.shutdown() sbt.io.IO.delete(base) + end withServerAndClient test("a portfile that a server is still writing"): if !isWindows then diff --git a/main-command/src/test/scala/sbt/internal/client/NetworkClientSilentDisconnectTest.scala b/main-command/src/test/scala/sbt/internal/client/NetworkClientSilentDisconnectTest.scala index ae4f31096..9c3323a16 100644 --- a/main-command/src/test/scala/sbt/internal/client/NetworkClientSilentDisconnectTest.scala +++ b/main-command/src/test/scala/sbt/internal/client/NetworkClientSilentDisconnectTest.scala @@ -129,7 +129,9 @@ object NetworkClientSilentDisconnectTest extends BasicTestSuite: val code = NetworkClient.client( base, args, - new InputStream { override def read(): Int = -1 }, + new InputStream: + override def read(): Int = -1 + , out, err, false, diff --git a/main-command/src/test/scala/sbt/internal/client/NetworkClientSysPropsTest.scala b/main-command/src/test/scala/sbt/internal/client/NetworkClientSysPropsTest.scala index 82c8e7023..3b17363c6 100644 --- a/main-command/src/test/scala/sbt/internal/client/NetworkClientSysPropsTest.scala +++ b/main-command/src/test/scala/sbt/internal/client/NetworkClientSysPropsTest.scala @@ -123,3 +123,4 @@ object NetworkClientSysPropsTest extends BasicTestSuite: NetworkClient.sysPropsDiff(Seq("my.prop=garbage"), sysProps("-Dmy.prop=first")) == (Nil, Nil, Seq("my.prop")) ) +end NetworkClientSysPropsTest diff --git a/main-command/src/test/scala/sbt/internal/server/ServerAcceptSpec.scala b/main-command/src/test/scala/sbt/internal/server/ServerAcceptSpec.scala index 0b07c5038..5669fd518 100644 --- a/main-command/src/test/scala/sbt/internal/server/ServerAcceptSpec.scala +++ b/main-command/src/test/scala/sbt/internal/server/ServerAcceptSpec.scala @@ -49,6 +49,7 @@ object ServerAcceptSpec extends BasicTestSuite: finally instance.shutdown() sbt.io.IO.delete(dir) + end withServer private def waitUntil(p: => Boolean): Boolean = val deadline = 10.seconds.fromNow diff --git a/main-command/src/test/scala/sbt/internal/server/ServerTokenSpec.scala b/main-command/src/test/scala/sbt/internal/server/ServerTokenSpec.scala index d8d9dff31..726c10d0d 100644 --- a/main-command/src/test/scala/sbt/internal/server/ServerTokenSpec.scala +++ b/main-command/src/test/scala/sbt/internal/server/ServerTokenSpec.scala @@ -49,6 +49,7 @@ object ServerTokenSpec extends BasicTestSuite: finally instance.shutdown() sbt.io.IO.delete(dir) + end withServer private def tokenIn(tokenfile: File): String = import Server.JsonProtocol.given diff --git a/main-settings/src/main/scala/sbt/Append.scala b/main-settings/src/main/scala/sbt/Append.scala index 625dcace0..7787c3a41 100644 --- a/main-settings/src/main/scala/sbt/Append.scala +++ b/main-settings/src/main/scala/sbt/Append.scala @@ -32,10 +32,9 @@ object Append: trait Sequence[A1, -A2, A3] extends Value[A1, A3] with Values[A1, A2] given appendSeq[T, V <: T]: Sequence[Seq[T], Seq[V], V] = - new Sequence[Seq[T], Seq[V], V] { + new Sequence[Seq[T], Seq[V], V]: def appendValues(a: Seq[T], b: Seq[V]): Seq[T] = a ++ (b: Seq[T]) def appendValue(a: Seq[T], b: V): Seq[T] = a :+ (b: T) - } given appendSeqImplicit[A1, V](using ev: Conversion[V, A1]): Sequence[Seq[A1], Seq[V], V] with override def appendValues(a: Seq[A1], b: Seq[V]): Seq[A1] = a ++ b.map(x => (x: A1)) @@ -49,10 +48,9 @@ object Append: given appendTaskKeySeq[T, V <: T]: Value[Seq[Task[T]], TaskKey[V]] = (_, _) => ??? // SAM given appendList[T, V <: T]: Sequence[List[T], List[V], V] = - new Sequence[List[T], List[V], V] { + new Sequence[List[T], List[V], V]: def appendValues(a: List[T], b: List[V]): List[T] = a ::: (b: List[T]) def appendValue(a: List[T], b: V): List[T] = a :+ (b: T) - } given appendListImplicit[A1, V](using ev: Conversion[V, A1]): Sequence[List[A1], List[V], V] with override def appendValues(a: List[A1], b: List[V]): List[A1] = a ++ b.map(x => (x: A1)) @@ -76,26 +74,23 @@ object Append: a :+ Attributed.blank(b) given appendSet[T, V <: T]: Sequence[Set[T], Set[V], V] = - new Sequence[Set[T], Set[V], V] { + new Sequence[Set[T], Set[V], V]: def appendValues(a: Set[T], b: Set[V]): Set[T] = a ++ (b.toSeq: Seq[T]).toSet def appendValue(a: Set[T], b: V): Set[T] = a + (b: T) - } given appendMap[A, B, X <: A, Y <: B]: Sequence[Map[A, B], Map[X, Y], (X, Y)] = - new Sequence[Map[A, B], Map[X, Y], (X, Y)] { + new Sequence[Map[A, B], Map[X, Y], (X, Y)]: def appendValues(a: Map[A, B], b: Map[X, Y]): Map[A, B] = (a.toSeq ++ (b.toSeq: Seq[(A, B)])).toMap def appendValue(a: Map[A, B], b: (X, Y)): Map[A, B] = a + (b: (A, B)) - } given appendOption[T]: Sequence[Seq[T], Option[T], Option[T]] = - new Sequence[Seq[T], Option[T], Option[T]] { + new Sequence[Seq[T], Option[T], Option[T]]: def appendValue(a: Seq[T], b: Option[T]): Seq[T] = b.fold(a)(a :+ _) def appendValues(a: Seq[T], b: Option[T]): Seq[T] = b.fold(a)(a :+ _) - } given appendSource: Sequence[Seq[Source], Seq[File], File] = - new Sequence[Seq[Source], Seq[File], File] { + new Sequence[Seq[Source], Seq[File], File]: def appendValue(a: Seq[Source], b: File): Seq[Source] = appendValues(a, Seq(b)) def appendValues(a: Seq[Source], b: Seq[File]): Seq[Source] = a ++ b.map { f => @@ -104,19 +99,17 @@ object Append: // added via `watchSource += ...` to not trigger a build when they are modified. Since // watchSources will be deprecated in 1.3.0, I'm hoping that most people will migrate // their builds to the new system, but this will work for most builds in the interim. - if (f.isFile && f.getParentFile != null) + if f.isFile && f.getParentFile != null then new Source(f.getParentFile, f.getName, NothingFilter, recursive = false) else new Source(f, AllPassFilter, NothingFilter) } - } // Implemented with SAM conversion short-hand given appendFunction[A, B]: Value[A => A, A => A] = _.andThen(_) - given appendSideEffectToFunc[A, B]: Value[A => B, () => Unit] = (f, sideEffect) => { + given appendSideEffectToFunc[A, B]: Value[A => B, () => Unit] = (f, sideEffect) => f.andThen { b => sideEffect() b } - } end Append diff --git a/main-settings/src/main/scala/sbt/ConfigKey.scala b/main-settings/src/main/scala/sbt/ConfigKey.scala index 44e14bdb1..bfbfd33f2 100644 --- a/main-settings/src/main/scala/sbt/ConfigKey.scala +++ b/main-settings/src/main/scala/sbt/ConfigKey.scala @@ -9,7 +9,6 @@ package sbt final case class ConfigKey(name: String) -object ConfigKey { +object ConfigKey: implicit def configurationToKey(c: sbt.librarymanagement.Configuration): ConfigKey = ConfigKey(c.name) -} diff --git a/main-settings/src/main/scala/sbt/Def.scala b/main-settings/src/main/scala/sbt/Def.scala index e0222d7b2..5ec5a07d8 100644 --- a/main-settings/src/main/scala/sbt/Def.scala +++ b/main-settings/src/main/scala/sbt/Def.scala @@ -91,14 +91,14 @@ object Def extends BuildSyntax with Init with InitializeImplicits: current: ProjectRef, keyNameColor: Option[String] = None, ): Show[ScopedKey[?]] = - Show[ScopedKey[?]](key => { + Show[ScopedKey[?]](key => val color: String => String = withColor(_, keyNameColor) key.scope.extra.toOption .flatMap(_.get(Scope.customShowString).map(color)) .getOrElse { Scope.display(key.scope, color(key.key.label), ref => displayRelative2(current, ref)) } - }) + ) private[sbt] def showShortKey( keyNameColor: Option[String], @@ -108,17 +108,15 @@ object Def extends BuildSyntax with Init with InitializeImplicits: private[sbt] def showShortKey( keyNameColor: Option[String], configNameToIdent: String => String, - ): Show[ScopedKey[?]] = { + ): Show[ScopedKey[?]] = def displayShort( project: Reference - ): String = { + ): String = val trailing = " /" - project match { + project match case BuildRef(_) => "ThisBuild" + trailing case ProjectRef(_, x) => x + trailing case _ => Reference.display(project) + trailing - } - } Show[ScopedKey[?]](key => Scope.display( key.scope, @@ -127,7 +125,6 @@ object Def extends BuildSyntax with Init with InitializeImplicits: configNameToIdent ) ) - } @deprecated("Use showBuildRelativeKey2 which doesn't take the unused multi param", "1.1.1") def showBuildRelativeKey( @@ -171,33 +168,30 @@ object Def extends BuildSyntax with Init with InitializeImplicits: current: ProjectRef, project: Reference, trailingSlash: Boolean - ): String = { + ): String = import Reference.display as displayRef - @tailrec def loop(ref: Reference): String = ref match { - case ProjectRef(b, p) => if (b == current.build) loop(LocalProject(p)) else displayRef(ref) - case BuildRef(b) => if (b == current.build) loop(ThisBuild) else displayRef(ref) - case RootProject(b) => if (b == current.build) loop(LocalRootProject) else displayRef(ref) - case LocalProject(p) => if (p == current.project) "" else p - case ThisBuild => "ThisBuild" + @tailrec def loop(ref: Reference): String = ref match + case ProjectRef(b, p) => if b == current.build then loop(LocalProject(p)) else displayRef(ref) + case BuildRef(b) => if b == current.build then loop(ThisBuild) else displayRef(ref) + case RootProject(b) => if b == current.build then loop(LocalRootProject) else displayRef(ref) + case LocalProject(p) => if p == current.project then "" else p + case ThisBuild => "ThisBuild" case LocalRootProject => "" case LocalAggregate => "" case ThisProject => "" - } val str = loop(project) - if (trailingSlash && !str.isEmpty) s"$str /" + if trailingSlash && !str.isEmpty then s"$str /" else str - } @deprecated("Use variant without multi", "1.1.1") def displayBuildRelative(currentBuild: URI, multi: Boolean, project: Reference): String = displayBuildRelative(currentBuild, project) def displayBuildRelative(currentBuild: URI, project: Reference): String = - project match { + project match case BuildRef(`currentBuild`) => "ThisBuild /" case ProjectRef(`currentBuild`, x) => x + " /" case _ => Reference.display(project) + " /" - } def displayFull(scoped: ScopedKey[?]): String = displayFull(scoped, None) @@ -212,10 +206,9 @@ object Def extends BuildSyntax with Init with InitializeImplicits: def withColor(s: String, color: Option[String]): String = withColor(s, color, useColor = ITerminal.isColorEnabled) - def withColor(s: String, color: Option[String], useColor: Boolean): String = color match { + def withColor(s: String, color: Option[String], useColor: Boolean): String = color match case Some(c) if useColor => c + s + scala.Console.RESET case _ => s - } override def deriveAllowed[T](s: Setting[T], allowDynamic: Boolean): Option[String] = super.deriveAllowed(s, allowDynamic) orElse @@ -231,14 +224,16 @@ object Def extends BuildSyntax with Init with InitializeImplicits: override def intersect(s1: Scope, s2: Scope)(using delegates: Scope => Seq[Scope] ): Option[Scope] = - if (s2 == GlobalScope) Some(s1) // s1 is more specific - else if (s1 == GlobalScope) Some(s2) // s2 is more specific + if s2 == GlobalScope then Some(s1) // s1 is more specific + else if s1 == GlobalScope then Some(s2) // s2 is more specific else super.intersect(s1, s2) private def definedSettingString(s: Setting[?]): String = s"derived setting ${s.key.key.label}${positionString(s)}" private def positionString(s: Setting[?]): String = - s.positionString match { case None => ""; case Some(pos) => s" defined at $pos" } + s.positionString match + case None => ""; + case Some(pos) => s" defined at $pos" /** * A default Parser for splitting input into space-separated arguments. @@ -352,13 +347,12 @@ object Def extends BuildSyntax with Init with InitializeImplicits: private[sbt] def branchS[A, B, C]( x: Def.Initialize[Task[Either[A, B]]] )(l: Def.Initialize[Task[A => C]])(r: Def.Initialize[Task[B => C]]): Def.Initialize[Task[C]] = - val lhs: Initialize[Task[Either[B, C]]] = { + val lhs: Initialize[Task[Either[B, C]]] = val innerLhs: Def.Initialize[Task[Either[A, Either[B, C]]]] = x.map((fab: Either[A, B]) => fab.map(Left(_))) val innerRhs: Def.Initialize[Task[A => Either[B, C]]] = l.map((fn: A => C) => fn.andThen(Right(_))) selectITask[A, Either[B, C]](innerLhs, innerRhs) - } selectITask[B, C](lhs, r) // derived from select @@ -368,9 +362,9 @@ object Def extends BuildSyntax with Init with InitializeImplicits: val condition: Def.Initialize[Task[Either[Unit, Unit]]] = x.map { (p: Boolean) => if p then Left(()) else Right(()) } val left: Def.Initialize[Task[Unit => A]] = - t.map { (a: A) => { (_: Unit) => a } } + t.map { (a: A) => (_: Unit) => a } val right: Def.Initialize[Task[Unit => A]] = - e.map { (a: A) => { (_: Unit) => a } } + e.map { (a: A) => (_: Unit) => a } branchS(condition)(left)(right) /** @@ -449,13 +443,11 @@ object Def extends BuildSyntax with Init with InitializeImplicits: ) ) - class InitOps[T](private val x: Initialize[T]) extends AnyVal { + class InitOps[T](private val x: Initialize[T]) extends AnyVal: def toTaskable: Taskable[T] = x - } - class InitTaskOps[T](private val x: Initialize[Task[T]]) extends AnyVal { + class InitTaskOps[T](private val x: Initialize[Task[T]]) extends AnyVal: def toTaskable: Taskable[T] = x - } /** * This works around Scala 2.12.12's @@ -471,7 +463,7 @@ object Def extends BuildSyntax with Init with InitializeImplicits: private[sbt] def dummy[A: ClassTag](name: String, description: String): (TaskKey[A], Task[A]) = (TaskKey[A](name, description, DTask), dummyTask(name)) - private[sbt] def dummyTask[T](name: String): Task[T] = { + private[sbt] def dummyTask[T](name: String): Task[T] = import TaskExtra.named val base: Task[T] = TaskExtra .task( @@ -479,7 +471,6 @@ object Def extends BuildSyntax with Init with InitializeImplicits: ) .named(name) base.set(isDummyTask, true) - } private[sbt] def isDummy(t: Task[?]): Boolean = t.get(isDummyTask).getOrElse(false) @@ -490,7 +481,8 @@ object Def extends BuildSyntax with Init with InitializeImplicits: inline def uncached[A1](inline a: A1): A1 = Uncached(a) end Def -sealed trait InitializeImplicits { self: Def.type => +sealed trait InitializeImplicits: + self: Def.type => implicit def initOps[T](x: Def.Initialize[T]): Def.InitOps[T] = new Def.InitOps(x) implicit def initTaskOps[T](x: Def.Initialize[Task[T]]): Def.InitTaskOps[T] = @@ -506,4 +498,3 @@ sealed trait InitializeImplicits { self: Def.type => */ extension [A](in: Seq[Def.Initialize[Task[A]]]) def join: Def.Initialize[Task[Seq[A]]] = Scoped.richTaskSeq(in).join -} diff --git a/main-settings/src/main/scala/sbt/DelegateIndex.scala b/main-settings/src/main/scala/sbt/DelegateIndex.scala index a9354a167..7748a6b57 100644 --- a/main-settings/src/main/scala/sbt/DelegateIndex.scala +++ b/main-settings/src/main/scala/sbt/DelegateIndex.scala @@ -10,26 +10,22 @@ package sbt import ScopeAxis.{ Select, zero } -sealed trait DelegateIndex { +sealed trait DelegateIndex: def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] // def task(ref: ProjectRef, task: ScopedKey[_]): Seq[ScopeAxis[ScopedKey[_]]] // def extra(ref: ProjectRef, e: AttributeMap): Seq[ScopeAxis[AttributeMap]] -} -private final class DelegateIndex0(refs: Map[ProjectRef, ProjectDelegates]) extends DelegateIndex { - def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] = refs.get(ref) match { - case Some(pd) => pd.refs; case None => Nil - } +private final class DelegateIndex0(refs: Map[ProjectRef, ProjectDelegates]) extends DelegateIndex: + def project(ref: ProjectRef): Seq[ScopeAxis[ResolvedReference]] = refs.get(ref) match + case Some(pd) => pd.refs; + case None => Nil def config(ref: ProjectRef, conf: ConfigKey): Seq[ScopeAxis[ConfigKey]] = - refs.get(ref) match { + refs.get(ref) match case Some(pd) => - pd.confs.get(conf) match { + pd.confs.get(conf) match case Some(cs) => cs case None => Select(conf) :: zero[ConfigKey] :: Nil - } case None => Select(conf) :: zero[ConfigKey] :: Nil - } -} private final class ProjectDelegates( val ref: ProjectRef, val refs: Seq[ScopeAxis[ResolvedReference]], diff --git a/main-settings/src/main/scala/sbt/InputTask.scala b/main-settings/src/main/scala/sbt/InputTask.scala index b766da3da..b3ea93117 100644 --- a/main-settings/src/main/scala/sbt/InputTask.scala +++ b/main-settings/src/main/scala/sbt/InputTask.scala @@ -25,12 +25,11 @@ final class InputTask[A1] private (val parser: State => Parser[Task[A1]]): def fullInput(in: String): InputTask[A1] = InputTask[A1](s => - Parser.parse(in, parser(s)) match { + Parser.parse(in, parser(s)) match case Right(v) => Parser.success(v) case Left(msg) => val indented = msg.linesIterator.map(" " + _).mkString("\n") Parser.failure(s"Invalid programmatic input:\n$indented") - } ) end InputTask diff --git a/main-settings/src/main/scala/sbt/KeyRanks.scala b/main-settings/src/main/scala/sbt/KeyRanks.scala index f4eb4b22f..ca9d0c083 100644 --- a/main-settings/src/main/scala/sbt/KeyRanks.scala +++ b/main-settings/src/main/scala/sbt/KeyRanks.scala @@ -8,7 +8,7 @@ package sbt -object KeyRanks { +object KeyRanks: // task and setting ranks, used to prioritize displaying information // main tasks final val APlusTask = 4 @@ -49,4 +49,4 @@ object KeyRanks { // implementation details val Invisible = Int.MaxValue -} +end KeyRanks diff --git a/main-settings/src/main/scala/sbt/Plugins.scala b/main-settings/src/main/scala/sbt/Plugins.scala index d3700f6cc..5900eea18 100644 --- a/main-settings/src/main/scala/sbt/Plugins.scala +++ b/main-settings/src/main/scala/sbt/Plugins.scala @@ -68,7 +68,7 @@ import PluginTrigger.* * * then the `MyPlugin` settings (and anything that activates only when `MyPlugin` is activated) will not be added. */ -abstract class AutoPlugin extends Plugins.Basic with PluginsFunctions { +abstract class AutoPlugin extends Plugins.Basic with PluginsFunctions: /** * Determines whether this AutoPlugin will be activated for this project when the `requires` clause is satisfied. @@ -119,38 +119,34 @@ abstract class AutoPlugin extends Plugins.Basic with PluginsFunctions { /** If this plugin does not have any requirements, it means it is actually a root plugin. */ private[sbt] final def isRoot: Boolean = - requires match { + requires match case Empty => true case _ => false - } /** If this plugin does not have any requirements, it means it is actually a root plugin. */ private[sbt] final def isAlwaysEnabled: Boolean = isRoot && (trigger == AllRequirements) -} +end AutoPlugin /** * An error that occurs when auto-plugins aren't configured properly. * It translates the error from the underlying logic system to be targeted at end users. */ final class AutoPluginException private (val message: String, val origin: Option[LogicException]) - extends RuntimeException(message) { + extends RuntimeException(message): /** Prepends `p` to the error message derived from `origin`. */ def withPrefix(p: String) = new AutoPluginException(p + message, origin) -} -object AutoPluginException { +object AutoPluginException: def apply(msg: String): AutoPluginException = new AutoPluginException(msg, None) def apply(origin: LogicException): AutoPluginException = new AutoPluginException(Plugins.translateMessage(origin), Some(origin)) -} /** An expression that matches `AutoPlugin`s. */ -sealed trait Plugins { +sealed trait Plugins: def &&(o: Basic): Plugins -} -sealed trait PluginsFunctions { +sealed trait PluginsFunctions: /** [[Plugins]] instance that doesn't require any [[Plugins]]s. */ def empty: Plugins = Plugins.Empty @@ -160,9 +156,8 @@ sealed trait PluginsFunctions { /** This plugin is activated only when it is manually activated. */ def noTrigger: PluginTrigger = NoTrigger -} -object Plugins extends PluginsFunctions { +object Plugins extends PluginsFunctions: private[sbt] var defaultRequires: Plugins = scala.compiletime.uninitialized @@ -171,20 +166,20 @@ object Plugins extends PluginsFunctions { * The [[AutoPlugin]]s are topologically sorted so that a required [[AutoPlugin]] comes before its requiring [[AutoPlugin]]. */ def deducer(defined0: List[AutoPlugin]): (Plugins, Logger) => Seq[AutoPlugin] = - if (defined0.isEmpty) (_, _) => Nil - else { + if defined0.isEmpty then (_, _) => Nil + else // TODO: defined should return all the plugins val allReqs = (defined0 flatMap { asRequirements }).toSet val diff = allReqs diff defined0.toSet val defined = - if (diff.nonEmpty) diff.toList ::: defined0 + if diff.nonEmpty then diff.toList ::: defined0 else defined0 val byAtom = defined map { x => (Atom(x.label), x) } val byAtomMap = byAtom.toMap - if (byAtom.size != byAtomMap.size) duplicateProvidesError(byAtom) + if byAtom.size != byAtomMap.size then duplicateProvidesError(byAtom) // Ignore clauses for plugins that does not require anything else. // Avoids the requirement for pure Nature strings *and* possible // circular dependencies in the logic. @@ -193,7 +188,7 @@ object Plugins extends PluginsFunctions { val allEnabledByClause = defined.filterNot(_.isRoot).flatMap(d => asEnabledByClauses(d)) // Note: Here is where the function begins. We're given a list of plugins now. - (requestedPlugins, log) => { + (requestedPlugins, log) => timed("Plugins.deducer#function", log) { def explicitlyDisabled(p: AutoPlugin): Boolean = hasExclude(requestedPlugins, p) val alwaysEnabled: List[AutoPlugin] = @@ -214,7 +209,7 @@ object Plugins extends PluginsFunctions { Logic.reduce( clauses, (flattenConvert(requestedPlugins) ++ convertAll(alwaysEnabled)).toSet - ) match { + ) match case Left(problem) => throw AutoPluginException(problem) case Right(results) => log.debug(s" :: deduced result: ${results}") @@ -228,7 +223,7 @@ object Plugins extends PluginsFunctions { val forbidden: Set[AutoPlugin] = (selectedPlugins flatMap { Plugins.asExclusions }).toSet val c = selectedPlugins.toSet & forbidden - if (c.nonEmpty) { + if c.nonEmpty then exclusionConflictError( requestedPlugins, selectedPlugins, @@ -236,81 +231,73 @@ object Plugins extends PluginsFunctions { _.label } ) - } val retval = topologicalSort(selectedPlugins) // log.debug(s" :: sorted deduced result: ${retval.toString}") retval - } + end match } - } - } - private[sbt] def topologicalSort(ns: List[AutoPlugin]): List[AutoPlugin] = { + private[sbt] def topologicalSort(ns: List[AutoPlugin]): List[AutoPlugin] = @tailrec def doSort( found0: List[AutoPlugin], notFound0: List[AutoPlugin], limit0: Int - ): List[AutoPlugin] = { - if (limit0 < 0) throw AutoPluginException(s"Failed to sort ${ns} topologically") - else if (notFound0.isEmpty) found0 - else { + ): List[AutoPlugin] = + if limit0 < 0 then throw AutoPluginException(s"Failed to sort ${ns} topologically") + else if notFound0.isEmpty then found0 + else val (found1, notFound1) = notFound0 partition { n => asRequirements(n).toSet subsetOf found0.toSet } doSort(found0 ::: found1, notFound1, limit0 - 1) - } - } val (roots, nonRoots) = ns partition (_.isRoot) doSort(roots, nonRoots, ns.size * ns.size + 1) - } - private[sbt] def translateMessage(e: LogicException) = e match { + private[sbt] def translateMessage(e: LogicException) = e match case ic: InitialContradictions => s"Contradiction in selected plugins. These plugins were both included and excluded: ${literalsString(ic.literals.toSeq)}" case io: InitialOverlap => s"Cannot directly enable plugins. Plugins are enabled when their required plugins are satisfied. The directly selected plugins were: ${literalsString(io.literals.toSeq)}" case cn: CyclicNegation => s"Cycles in plugin requirements cannot involve excludes. The problematic cycle is: ${literalsString(cn.cycle)}" - } private def literalsString(lits: Seq[Literal]): String = lits map { case Atom(l) => l; case Negated(Atom(l)) => l } mkString (", ") - private def duplicateProvidesError(byAtom: Seq[(Atom, AutoPlugin)]): Unit = { + private def duplicateProvidesError(byAtom: Seq[(Atom, AutoPlugin)]): Unit = val dupsByAtom = Map(byAtom.groupBy(_._1).toSeq.map { (k, v) => k -> v.map(_._2) }*) val dupStrings = - for ((atom, dups) <- dupsByAtom if dups.size > 1) - yield s"${atom.label} by ${dups.mkString(", ")}" - val (ns, nl) = if (dupStrings.size > 1) ("s", "\n\t") else ("", " ") + for (atom, dups) <- dupsByAtom if dups.size > 1 + yield s"${atom.label} by ${dups.mkString(", ")}" + val (ns, nl) = if dupStrings.size > 1 then ("s", "\n\t") else ("", " ") val message = s"Plugin$ns provided by multiple AutoPlugins:$nl${dupStrings.mkString(nl)}" throw AutoPluginException(message) - } private def exclusionConflictError( requested: Plugins, selected: Seq[AutoPlugin], conflicting: Seq[AutoPlugin] - ): Unit = { + ): Unit = def listConflicts(ns: Seq[AutoPlugin]) = (ns map { c => - val reasons = (if (flatten(requested) contains c) List("requested") + val reasons = (if flatten(requested) contains c then List("requested") else Nil) ++ - (if (c.requires != empty && c.trigger == allRequirements) + (if c.requires != empty && c.trigger == allRequirements then List(s"enabled by ${c.requires.toString}") else Nil) ++ { val reqs = selected filter { x => asRequirements(x) contains c } - if (reqs.nonEmpty) List(s"""required by ${reqs.mkString(", ")}""") + if reqs.nonEmpty then List(s"""required by ${reqs.mkString(", ")}""") else Nil } ++ { val exs = selected filter { x => asExclusions(x) contains c } - if (exs.nonEmpty) List(s"""excluded by ${exs.mkString(", ")}""") + if exs.nonEmpty then List(s"""excluded by ${exs.mkString(", ")}""") else Nil } s""" - conflict: ${c.label} is ${reasons.mkString("; ")}""" @@ -319,52 +306,45 @@ object Plugins extends PluginsFunctions { - requested: ${requested.toString} - enabled: ${selected.mkString(", ")} ${listConflicts(conflicting)}""") - } + end exclusionConflictError - private[sbt] object Empty extends Plugins { + private[sbt] object Empty extends Plugins: def &&(o: Basic): Plugins = o override def toString = "" - } /** An included or excluded Nature/Plugin. */ // TODO: better name than Basic. Also, can we dump this class - sealed abstract class Basic extends Plugins { + sealed abstract class Basic extends Plugins: def &&(o: Basic): Plugins = And(this :: o :: Nil) - } - private[sbt] final case class Exclude(n: AutoPlugin) extends Basic { + private[sbt] final case class Exclude(n: AutoPlugin) extends Basic: override def toString = s"!$n" - } - private[sbt] final case class And(plugins: List[Basic]) extends Plugins { + private[sbt] final case class And(plugins: List[Basic]) extends Plugins: def &&(o: Basic): Plugins = And(o :: plugins) override def toString = plugins.mkString(" && ") - } - private[sbt] def and(a: Plugins, b: Plugins) = b match { + private[sbt] def and(a: Plugins, b: Plugins) = b match case Empty => a case And(ns) => ns.foldLeft(a)(_ && _) case b: Basic => a && b - } - private[sbt] def overrideWith(current: Plugins, update: Plugins): Plugins = { + private[sbt] def overrideWith(current: Plugins, update: Plugins): Plugins = val opposite: Set[Basic] = flatten(update).map { case Exclude(p) => p: Basic case p: AutoPlugin => Exclude(p): Basic }.toSet and(remove(current, opposite), update) - } - private[sbt] def remove(a: Plugins, del: Set[Basic]): Plugins = a match { - case b: Basic => if (del(b)) Empty else b + private[sbt] def remove(a: Plugins, del: Set[Basic]): Plugins = a match + case b: Basic => if del(b) then Empty else b case Empty => Empty case And(ns) => val removed = ns.filterNot(del) - if (removed.isEmpty) Empty else And(removed) - } + if removed.isEmpty then Empty else And(removed) /** Defines enabled-by clauses for `ap`. */ private[sbt] def asEnabledByClauses(ap: AutoPlugin): List[Clause] = // `ap` is the head and the required plugins for `ap` is the body. - if (ap.trigger == AllRequirements) Clause(convert(ap.requires), Set(Atom(ap.label))) :: Nil + if ap.trigger == AllRequirements then Clause(convert(ap.requires), Set(Atom(ap.label))) :: Nil else Nil /** Defines requirements clauses for `ap`. */ @@ -382,7 +362,7 @@ ${listConflicts(conflicting)}""") x } // TODO - This doesn't handle nested AND boolean logic... - private[sbt] def hasExclude(n: Plugins, p: AutoPlugin): Boolean = n match { + private[sbt] def hasExclude(n: Plugins, p: AutoPlugin): Boolean = n match case `p` => false case Exclude(`p`) => true // TODO - This is stupidly advanced. We do a nested check through possible and-ed @@ -392,34 +372,28 @@ ${listConflicts(conflicting)}""") case And(ns) => ns.forall(n => hasExclude(n, p)) case _: Basic => false case Empty => false - } - private[sbt] def hasInclude(n: Plugins, p: AutoPlugin): Boolean = n match { + private[sbt] def hasInclude(n: Plugins, p: AutoPlugin): Boolean = n match case `p` => true case Exclude(n) => hasExclude(n, p) case And(ns) => ns.forall(n => hasInclude(n, p)) case _: Basic => false case Empty => false - } - private def flattenConvert(n: Plugins): Seq[Literal] = n match { + private def flattenConvert(n: Plugins): Seq[Literal] = n match case And(ns) => convertAll(ns) case b: Basic => convertBasic(b) :: Nil case Empty => Nil - } - private[sbt] def flatten(n: Plugins): Seq[Basic] = n match { + private[sbt] def flatten(n: Plugins): Seq[Basic] = n match case And(ns) => ns case b: Basic => b :: Nil case Empty => Nil - } - private def convert(n: Plugins): Formula = n match { + private def convert(n: Plugins): Formula = n match case And(ns) => convertAll(ns).reduce[Formula](_ && _) case b: Basic => convertBasic(b) case Empty => Formula.True - } - private def convertBasic(b: Basic): Literal = b match { + private def convertBasic(b: Basic): Literal = b match case Exclude(n) => !convertBasic(n) case a: AutoPlugin => Atom(a.label) - } private def convertAll(ns: Seq[Basic]): Seq[Literal] = ns map convertBasic /** True if the trigger clause `n` is satisfied by `model`. */ @@ -443,15 +417,14 @@ ${listConflicts(conflicting)}""") * @param loader The plugin loader. * @return True if plugin has a stable member `autoImport`, otherwise false. */ - private[sbt] def hasAutoImportGetter(ap: AutoPlugin, loader: ClassLoader): Boolean = { + private[sbt] def hasAutoImportGetter(ap: AutoPlugin, loader: ClassLoader): Boolean = import java.lang.reflect.Field import scala.util.control.Exception.catching // Make sure that we don't detect user-defined methods called autoImport - def existsAutoImportVal(clazz: Class[?]): Option[Field] = { + def existsAutoImportVal(clazz: Class[?]): Option[Field] = catching(classOf[NoSuchFieldException]) .opt(clazz.getDeclaredField(autoImport)) .orElse(Option(clazz.getSuperclass).flatMap(existsAutoImportVal)) - } val pluginClazz = ap.getClass existsAutoImportVal(pluginClazz) @@ -460,14 +433,12 @@ ${listConflicts(conflicting)}""") .opt(Class.forName(s"${pluginClazz.getName}$autoImport$$", false, loader)) ) .isDefined - } /** Debugging method to time how long it takes to run various compilation tasks. */ - private def timed[T](label: String, log: Logger)(t: => T): T = { + private def timed[T](label: String, log: Logger)(t: => T): T = val start = System.nanoTime val result = t val elapsed = System.nanoTime - start log.debug(label + " took " + (elapsed / 1e6) + " ms") result - } -} +end Plugins diff --git a/main-settings/src/main/scala/sbt/Previous.scala b/main-settings/src/main/scala/sbt/Previous.scala index 427e545d3..9affeea53 100644 --- a/main-settings/src/main/scala/sbt/Previous.scala +++ b/main-settings/src/main/scala/sbt/Previous.scala @@ -23,7 +23,7 @@ import scala.util.control.NonFatal * Reads the previous value of tasks on-demand. The read values are cached so that they are only read once per task execution. * `referenced` provides the `Format` to use for each key. */ -private[sbt] final class Previous(streams: Streams, referenced: IMap[Previous.Key, Referenced]) { +private[sbt] final class Previous(streams: Streams, referenced: IMap[Previous.Key, Referenced]): private var map = IMap.empty[Previous.Key, ReferencedValue] // We can't use mapValues to transform the map because mapValues is lazy and evaluates the // transformation function every time a value is fetched from the map, defeating the entire @@ -31,15 +31,13 @@ private[sbt] final class Previous(streams: Streams, referenced: IMap[Previous.Ke for case referenced.TPair(k, v) <- referenced.toTypedSeq do map = map.put(k, new ReferencedValue(v)) - private final class ReferencedValue[T](referenced: Referenced[T]) { + private final class ReferencedValue[T](referenced: Referenced[T]): lazy val previousValue: Option[T] = referenced.read(streams) - } /** Used by the .previous runtime implementation to get the previous value for task `key`. */ private def get[T](key: Key[T]): Option[T] = map.get(key).flatMap(_.previousValue) -} -object Previous { +object Previous: import sjsonnew.BasicJsonProtocol.StringJsonFormat private[sbt] type ScopedTaskKey[T] = ScopedKey[Task[T]] private type AnyTaskKey = ScopedTaskKey[Any] @@ -50,7 +48,7 @@ object Previous { private[sbt] final val DependencyDirectory = "previous-dependencies" /** Represents a reference task.previous */ - private[sbt] final class Referenced[T](val key: Key[T], val format: JsonFormat[T]) { + private[sbt] final class Referenced[T](val key: Key[T], val format: JsonFormat[T]): def this(task: ScopedTaskKey[T], format: JsonFormat[T]) = this(Key(task, task), format) lazy val stamped: JsonFormat[T] = @@ -59,8 +57,7 @@ object Previous { def setTask(newTask: ScopedKey[Task[T]]) = new Referenced(newTask, format) private[sbt] def read(streams: Streams): Option[T] = try Option(streams(key.cacheKey).cacheStoreFactory.make(StreamName).read[T]()(using stamped)) - catch { case NonFatal(_) => None } - } + catch case NonFatal(_) => None private[sbt] val references = SettingKey[References]( "previous-references", @@ -73,30 +70,24 @@ object Previous { KeyRanks.Invisible ) - private[sbt] class Key[T](val task: ScopedKey[Task[T]], val enclosing: AnyTaskKey) { - override def equals(o: Any): Boolean = o match { + private[sbt] class Key[T](val task: ScopedKey[Task[T]], val enclosing: AnyTaskKey): + override def equals(o: Any): Boolean = o match case that: Key[?] => this.task == that.task && this.enclosing == that.enclosing case _ => false - } override def hashCode(): Int = (task.## * 31) ^ enclosing.## - def cacheKey: AnyTaskKey = { - if (task == enclosing) task.asInstanceOf[ScopedKey[Task[Any]]] - else { - val am = enclosing.scope.extra match { + def cacheKey: AnyTaskKey = + if task == enclosing then task.asInstanceOf[ScopedKey[Task[Any]]] + else + val am = enclosing.scope.extra match case Select(a) => a.put(scopedKeyAttribute, task.asInstanceOf[AnyTaskKey]) case _ => AttributeMap.empty.put(scopedKeyAttribute, task.asInstanceOf[AnyTaskKey]) - } Def.ScopedKey(enclosing.scope.copy(extra = Select(am)), enclosing.key) - } - } - } - private[sbt] object Key { + private[sbt] object Key: def apply[T, U](key: ScopedKey[Task[T]], enclosing: ScopedKey[Task[U]]): Key[T] = new Key(key, enclosing.asInstanceOf[AnyTaskKey]) - } /** Records references to previous task value. This should be completely populated after settings finish loading. */ - private[sbt] final class References { + private[sbt] final class References: private var map = IMap.empty[Key, Referenced] // TODO: this arbitrarily chooses a JsonFormat. @@ -105,14 +96,13 @@ object Previous { map = map.put(key, new Referenced(key, format)) } def getReferences: IMap[Key, Referenced] = synchronized { map } - } /** Persists values of tasks t where there is some task referencing it via t.previous. */ private[sbt] def complete( referenced: References, results: RMap[TaskId, Result], streams: Streams - ): Unit = { + ): Unit = val map = referenced.getReferences val reverse = map.keys.groupBy(_.task) @@ -129,17 +119,17 @@ object Previous { // the previous value of another task. For each reference we find, we check if the task has // been successfully evaluated. If so, we write it to the appropriate previous cache for // the completed task. - for { + for (k, v) <- successfulTaskResults keys <- reverse.get(k) key <- keys if successfulTaskResults.contains(key.enclosing) ref <- map.get(key) - } { + do val out = streams(key.cacheKey).cacheStoreFactory.make(StreamName) try out.write(v)(using ref.stamped) - catch { case NonFatal(_) => } - } - } + catch + case NonFatal(_) => + end complete private[sbt] val scopedKeyAttribute = AttributeKey[AnyTaskKey]( "previous-scoped-key-attribute", "Specifies a scoped key for a task on which .previous is called. Used to " + @@ -147,7 +137,7 @@ object Previous { ) /** Public as a macro implementation detail. Do not call directly. */ - def runtime[T](skey: TaskKey[T])(using format: JsonFormat[T]): Initialize[Task[Option[T]]] = { + def runtime[T](skey: TaskKey[T])(using format: JsonFormat[T]): Initialize[Task[Option[T]]] = type Inputs = (Task[Previous], ScopedKey[Task[T]], References) val inputs = (Global / cache, Def.validated(skey, selfRefOk = true), Global / references) Def.app[Inputs, Task[Option[T]]](inputs) { (prevTask, resolved, refs) => @@ -155,12 +145,11 @@ object Previous { refs.recordReference(key, format) // always evaluated on project load prevTask.map(_.get(key)) // evaluated if this task is evaluated } - } /** Public as a macro implementation detail. Do not call directly. */ def runtimeInEnclosingTask[T](skey: TaskKey[T])(using format: JsonFormat[T] - ): Initialize[Task[Option[T]]] = { + ): Initialize[Task[Option[T]]] = type Inputs = (Task[Previous], ScopedKey[Task[T]], References, ScopedKey[?]) val inputs = ( Global / cache, @@ -173,5 +162,4 @@ object Previous { refs.recordReference(key, format) // always evaluated on project load prevTask.map(_.get(key)) } - } -} +end Previous diff --git a/main-settings/src/main/scala/sbt/Project.scala b/main-settings/src/main/scala/sbt/Project.scala index f89beb908..70d04edb5 100644 --- a/main-settings/src/main/scala/sbt/Project.scala +++ b/main-settings/src/main/scala/sbt/Project.scala @@ -19,7 +19,7 @@ import Scope.ThisScope import sbt.Scope.ThisBuildScope import sbt.internal.util.Util -sealed trait ProjectDefinition[PR <: ProjectReference] { +sealed trait ProjectDefinition[PR <: ProjectReference]: /** * The project ID is used to uniquely identify a project within a build. @@ -72,12 +72,11 @@ sealed trait ProjectDefinition[PR <: ProjectReference] { override final def hashCode: Int = id.hashCode ^ base.hashCode ^ getClass.hashCode - override final def equals(o: Any) = o match { + override final def equals(o: Any) = o match case p: ProjectDefinition[?] => p.getClass == this.getClass && p.id == id && p.base == base case _ => false - } - override def toString = { + override def toString = val agg = ifNonEmpty("aggregate", aggregate) val dep = ifNonEmpty("dependencies", dependencies) val conf = ifNonEmpty("configurations", configurations) @@ -85,17 +84,16 @@ sealed trait ProjectDefinition[PR <: ProjectReference] { val fields = s"id $id" :: s"base: $base" :: agg ::: dep ::: conf ::: (s"plugins: List($plugins)" :: autos) s"Project(${fields.mkString(", ")})" - } private def ifNonEmpty[T](label: String, ts: Iterable[T]): List[String] = - if (ts.isEmpty) Nil else s"$label: $ts" :: Nil -} + if ts.isEmpty then Nil else s"$label: $ts" :: Nil +end ProjectDefinition trait CompositeProject: def componentProjects: Seq[Project] end CompositeProject -private[sbt] object CompositeProject { +private[sbt] object CompositeProject: /** * Expand user defined projects with the component projects of `compositeProjects`. @@ -117,14 +115,12 @@ private[sbt] object CompositeProject { */ def expand(compositeProjects: Seq[CompositeProject]): Seq[Project] = { val userProjects = compositeProjects.collect { case p: Project => p } - for (p <- compositeProjects.flatMap(_.componentProjects)) yield { - userProjects.find(_.id == p.id) match { - case Some(userProject) => userProject - case None => p - } - } + for p <- compositeProjects.flatMap(_.componentProjects) + yield userProjects.find(_.id == p.id) match + case Some(userProject) => userProject + case None => p }.distinct -} +end CompositeProject sealed trait Project extends ProjectDefinition[ProjectReference] with CompositeProject: override def componentProjects: Seq[Project] = this :: Nil @@ -278,10 +274,9 @@ object Project: val plugins: Plugins, val autoPlugins: Seq[AutoPlugin], val projectOrigin: ProjectOrigin - ) extends ProjectDefinition[PR] { + ) extends ProjectDefinition[PR]: // checks for cyclic references here instead of having to do it in Scope.delegates Dag.topologicalSort(configurations)(_.extendsConfigs) - } // Data structure representing an unresolved Project in terms of the project references. // This is created in build.sbt by the build user. @@ -310,6 +305,7 @@ object Project: autoPlugins, origin ) with Project + end unresolved // Data structure representing resolved Project in terms of references to // other projects in dependencies etc. @@ -388,14 +384,13 @@ object Project: def normalizeModuleID(id: String): String = normalizeBase(id) /** Constructs a valid Project ID based on `id` and returns it in Right or returns the error message in Left if one cannot be constructed. */ - private[sbt] def normalizeProjectID(id: String): Either[String, String] = { + private[sbt] def normalizeProjectID(id: String): Either[String, String] = val attempt = normalizeBase(id) val refined = - if (attempt.length < 1) "root" - else if (!validProjectIDStart(attempt.substring(0, 1))) "root-" + attempt + if attempt.length < 1 then "root" + else if !validProjectIDStart(attempt.substring(0, 1)) then "root-" + attempt else attempt validProjectID(refined).toLeft(refined) - } private def normalizeBase(s: String) = s.toLowerCase(Locale.ENGLISH).replaceAll("""\W+""", "-") @@ -405,19 +400,16 @@ object Project: case Current case Plugins - private[sbt] lazy val loadActionParser: Parser[LoadAction] = { + private[sbt] lazy val loadActionParser: Parser[LoadAction] = import DefaultParsers.* token( Space ~> ("plugins" ^^^ LoadAction.Plugins | "return" ^^^ LoadAction.Return) ) ?? LoadAction.Current - } end Project -sealed trait ResolvedProject extends ProjectDefinition[ProjectRef] { +sealed trait ResolvedProject extends ProjectDefinition[ProjectRef]: /** The [[AutoPlugin]]s enabled for this project as computed from [[plugins]]. */ def autoPlugins: Seq[AutoPlugin] -} - private[sbt] trait GeneratedRootProject diff --git a/main-settings/src/main/scala/sbt/PromiseWrap.scala b/main-settings/src/main/scala/sbt/PromiseWrap.scala index ba485cfc7..dd85d47f8 100644 --- a/main-settings/src/main/scala/sbt/PromiseWrap.scala +++ b/main-settings/src/main/scala/sbt/PromiseWrap.scala @@ -13,15 +13,13 @@ import scala.concurrent.Promise as XPromise final class PromiseWrap[A]: private[sbt] val underlying: XPromise[A] = XPromise() def complete(result: Result[A]): Unit = - result match { + result match case Result.Inc(cause) => underlying.failure(cause) case Result.Value(value) => underlying.success(value) - } def tryComplete(result: Result[A]): Boolean = - result match { + result match case Result.Inc(cause) => underlying.tryFailure(cause) case Result.Value(value) => underlying.trySuccess(value) - } def success(value: A): Unit = underlying.success(value) def failure(cause: Throwable): Unit = underlying.failure(cause) def isCompleted: Boolean = underlying.isCompleted diff --git a/main-settings/src/main/scala/sbt/Reference.scala b/main-settings/src/main/scala/sbt/Reference.scala index 600211572..17bd103f0 100644 --- a/main-settings/src/main/scala/sbt/Reference.scala +++ b/main-settings/src/main/scala/sbt/Reference.scala @@ -74,15 +74,13 @@ case object ThisProject extends ProjectReference /** A placeholder for auto aggregation. */ case object LocalAggregate extends ProjectReference -object ProjectRef { +object ProjectRef: def apply(base: File, id: String): ProjectRef = ProjectRef(IO.toURI(base), id) -} -object RootProject { +object RootProject: /** Reference to the root project at 'base'. */ def apply(base: File): RootProject = RootProject(IO.toURI(base)) -} -object Reference { +object Reference: given resolvedReferenceOrdering: Ordering[ResolvedReference] = { case (ba: BuildRef, bb: BuildRef) => buildRefOrdering.compare(ba, bb) case (pa: ProjectRef, pb: ProjectRef) => projectRefOrdering.compare(pa, pb) @@ -91,42 +89,36 @@ object Reference { } given buildRefOrdering: Ordering[BuildRef] = (a, b) => a.build.compareTo(b.build) - given projectRefOrdering: Ordering[ProjectRef] = (a, b) => { + given projectRefOrdering: Ordering[ProjectRef] = (a, b) => val bc = a.build.compareTo(b.build) if bc == 0 then a.project.compareTo(b.project) else bc - } def display(ref: Reference): String = - ref match { + ref match case pr: ProjectReference => display(pr) case br: BuildReference => display(br) - } def display(ref: BuildReference): String = - ref match { + ref match case ThisBuild => "{}" case BuildRef(uri) => "{" + uri + "}" - } def display(ref: ProjectReference): String = - ref match { + ref match case ThisProject => "{}" case LocalRootProject => "{}" case LocalAggregate => "{}" case LocalProject(id) => "{}" + id case RootProject(uri) => "{" + uri + " }" case ProjectRef(uri, id) => s"""ProjectRef(uri("$uri"), "$id")""" - } - def buildURI(ref: ResolvedReference): URI = ref match { + def buildURI(ref: ResolvedReference): URI = ref match case BuildRef(b) => b case ProjectRef(b, _) => b - } /** Extracts the build URI from a Reference if one has been explicitly defined. */ - def uri(ref: Reference): Option[URI] = ref match { + def uri(ref: Reference): Option[URI] = ref match case RootProject(b) => Some(b) case ProjectRef(b, _) => Some(b) case BuildRef(b) => Some(b) case _ => None - } -} +end Reference diff --git a/main-settings/src/main/scala/sbt/Remove.scala b/main-settings/src/main/scala/sbt/Remove.scala index 37ab5489a..6b08eb01b 100644 --- a/main-settings/src/main/scala/sbt/Remove.scala +++ b/main-settings/src/main/scala/sbt/Remove.scala @@ -10,39 +10,33 @@ package sbt import scala.annotation.implicitNotFound -object Remove { +object Remove: @implicitNotFound( msg = "No implicit for Remove.Value[${A}, ${B}] found,\n so ${B} cannot be removed from ${A}" ) - trait Value[A, B] extends Any { + trait Value[A, B] extends Any: def removeValue(a: A, b: B): A - } @implicitNotFound( msg = "No implicit for Remove.Values[${A}, ${B}] found,\n so ${B} cannot be removed from ${A}" ) - trait Values[A, -B] extends Any { + trait Values[A, -B] extends Any: def removeValues(a: A, b: B): A - } trait Sequence[A, -B, T] extends Value[A, T] with Values[A, B] given removeSeq[T, V <: T]: Sequence[Seq[T], Seq[V], V] = - new Sequence[Seq[T], Seq[V], V] { + new Sequence[Seq[T], Seq[V], V]: def removeValue(a: Seq[T], b: V): Seq[T] = a filterNot b.== def removeValues(a: Seq[T], b: Seq[V]): Seq[T] = a diff (b: Seq[T]) - } given removeOption[T]: Sequence[Seq[T], Option[T], Option[T]] = - new Sequence[Seq[T], Option[T], Option[T]] { + new Sequence[Seq[T], Option[T], Option[T]]: def removeValue(a: Seq[T], b: Option[T]): Seq[T] = b.fold(a)(a filterNot _.==) def removeValues(a: Seq[T], b: Option[T]): Seq[T] = b.fold(a)(a filterNot _.==) - } given removeSet[T, V <: T]: Sequence[Set[T], Set[V], V] = - new Sequence[Set[T], Set[V], V] { + new Sequence[Set[T], Set[V], V]: def removeValue(a: Set[T], b: V): Set[T] = a - b def removeValues(a: Set[T], b: Set[V]): Set[T] = a diff (b.toSeq: Seq[T]).toSet - } given removeMap[A, B, X <: A]: Sequence[Map[A, B], Seq[X], X] = - new Sequence[Map[A, B], Seq[X], X] { + new Sequence[Map[A, B], Seq[X], X]: def removeValue(a: Map[A, B], b: X): Map[A, B] = a - b def removeValues(a: Map[A, B], b: Seq[X]): Map[A, B] = a -- b - } -} +end Remove diff --git a/main-settings/src/main/scala/sbt/Scope.scala b/main-settings/src/main/scala/sbt/Scope.scala index 5596f7d87..b5964cf67 100644 --- a/main-settings/src/main/scala/sbt/Scope.scala +++ b/main-settings/src/main/scala/sbt/Scope.scala @@ -87,7 +87,7 @@ object Scope: ) def subThis[T](sub: ScopeAxis[T], into: ScopeAxis[T]): ScopeAxis[T] = - if (into == This) sub else into + if into == This then sub else into /** * `Select(ThisProject)` cannot be resolved by [[resolveProject]] (it doesn't know what to replace it with), so we @@ -99,10 +99,9 @@ object Scope: } def fillTaskAxis(scope: Scope, key: AttributeKey[?]): Scope = - scope.task match { + scope.task match case _: Select[?] => scope case _ => scope.copy(task = Select(key)) - } def mapReference(f: Reference => Reference): Scope => Scope = { case Scope(Select(ref), a, b, c) => Scope(Select(f(ref)), a, b, c) @@ -114,58 +113,51 @@ object Scope: mapReference(ref => resolveBuildOnly(uri, ref)) def resolveBuildOnly(current: URI, ref: Reference): Reference = - ref match { + ref match case br: BuildReference => resolveBuild(current, br) case pr: ProjectReference => resolveProjectBuild(current, pr) - } def resolveBuild(current: URI, ref: BuildReference): BuildReference = - ref match { + ref match case ThisBuild => BuildRef(current) case BuildRef(uri) => BuildRef(resolveBuild(current, uri)) - } def resolveProjectBuild(current: URI, ref: ProjectReference): ProjectReference = - ref match { + ref match case LocalRootProject => RootProject(current) case LocalProject(id) => ProjectRef(current, id) case RootProject(uri) => RootProject(resolveBuild(current, uri)) case ProjectRef(uri, id) => ProjectRef(resolveBuild(current, uri), id) case ThisProject => ThisProject // haven't exactly "resolved" anything.. case LocalAggregate => LocalAggregate - } def resolveBuild(current: URI, uri: URI): URI = - if (!uri.isAbsolute && current.isOpaque && uri.getSchemeSpecificPart == ".") + if !uri.isAbsolute && current.isOpaque && uri.getSchemeSpecificPart == "." then current // this handles the shortcut of referring to the current build using "." - else - IO.directoryURI(current.resolve(uri)) + else IO.directoryURI(current.resolve(uri)) def resolveReference( current: URI, rootProject: URI => String, ref: Reference ): ResolvedReference = - ref match { + ref match case br: BuildReference => resolveBuildRef(current, br) case pr: ProjectReference => resolveProjectRef(current, rootProject, pr) - } def resolveProjectRef( current: URI, rootProject: URI => String, ref: ProjectReference ): ProjectRef = - ref match { + ref match case LocalRootProject => ProjectRef(current, rootProject(current)) case LocalProject(id) => ProjectRef(current, id) case RootProject(uri) => val u = resolveBuild(current, uri); ProjectRef(u, rootProject(u)) case ProjectRef(uri, id) => ProjectRef(resolveBuild(current, uri), id) case ThisProject => sys.error("Cannot resolve ThisProject w/o the current project") case LocalAggregate => sys.error("Cannot resolve LocalAggregate") - } def resolveBuildRef(current: URI, ref: BuildReference): BuildRef = - ref match { + ref match case ThisBuild => BuildRef(current) case BuildRef(uri) => BuildRef(resolveBuild(current, uri)) - } def display(config: ConfigKey): String = guessConfigIdent(config.name) + " /" @@ -277,9 +269,10 @@ object Scope: sep, postfix ) + end displayMasked private[sbt] def appendSpace(s: String): String = - if (s == "") "" + if s == "" then "" else s + " " def equal(a: Scope, b: Scope, mask: ScopeMask): Boolean = @@ -313,27 +306,25 @@ object Scope: projectInherit: ProjectRef => Seq[ProjectRef], configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey], taskInherit: AttributeKey[?] => Seq[AttributeKey[?]], - ): Scope => Seq[Scope] = { + ): Scope => Seq[Scope] = val index = delegates(refs, configurations, projectInherit, configInherit) scope => indexedDelegates(resolve, index, rootProject, taskInherit)(scope) - } private def indexedDelegates( resolve: Reference => ResolvedReference, index: DelegateIndex, rootProject: URI => String, taskInherit: AttributeKey[?] => Seq[AttributeKey[?]], - )(rawScope: Scope): Seq[Scope] = { + )(rawScope: Scope): Seq[Scope] = val scope = Scope.replaceThis(GlobalScope)(rawScope) // This is a hot method that gets called many times def expandDelegateScopes( resolvedProj: ResolvedReference - )(pLin: Seq[ScopeAxis[ResolvedReference]]): Vector[Scope] = { - val tLin = scope.task match { + )(pLin: Seq[ScopeAxis[ResolvedReference]]): Vector[Scope] = + val tLin = scope.task match case t @ Select(_) => linearize(t)(taskInherit) case _ => withZeroAxis(scope.task) - } // val eLin = withZeroAxis(scope.extra) // The following while loops handroll the nested for-expression + flatMap // projAxes flatMap nonProjectScopes(resolvedProj) @@ -341,87 +332,76 @@ object Scope: // for (c <- cLin; t <- tLin; e <- eLin) yield Scope(px, c, t, e) val res = Vector.newBuilder[Scope] val pIt = pLin.iterator - while (pIt.hasNext) { + while pIt.hasNext do val px = pIt.next() val p = px.toOption getOrElse resolvedProj - val configProj = p match { + val configProj = p match case pr: ProjectRef => pr case br: BuildRef => ProjectRef(br.build, rootProject(br.build)) - } - val cLin = scope.config match { + val cLin = scope.config match case Select(conf) => index.config(configProj, conf) case _ => withZeroAxis(scope.config) - } val cLinIt = cLin.iterator - while (cLinIt.hasNext) { + while cLinIt.hasNext do val c = cLinIt.next() val tLinIt = tLin.iterator - while (tLinIt.hasNext) { + while tLinIt.hasNext do val t = tLinIt.next() - if (scope.extra.isSelect) { + if scope.extra.isSelect then res += Scope(px, c, t, scope.extra) () - } res += Scope(px, c, t, Zero) - } - } - } res.result() - } + end expandDelegateScopes - scope.project match { + scope.project match case Zero | This => globalProjectDelegates(scope) case Select(proj) => val resolvedProj = resolve(proj) val projAxes: Seq[ScopeAxis[ResolvedReference]] = - resolvedProj match { + resolvedProj match case pr: ProjectRef => index.project(pr) case br: BuildRef => List(Select(br): ScopeAxis[ResolvedReference], Zero: ScopeAxis[ResolvedReference]) - } expandDelegateScopes(resolvedProj)(projAxes) - } - } + end indexedDelegates private val zeroL = List(Zero) private val globalL = List(GlobalScope) def withZeroAxis[T](base: ScopeAxis[T]): Seq[ScopeAxis[T]] = - if (base.isSelect) base :: zeroL else zeroL + if base.isSelect then base :: zeroL else zeroL def withGlobalScope(base: Scope): Seq[Scope] = - if (base == GlobalScope) globalL else base :: globalL + if base == GlobalScope then globalL else base :: globalL def withRawBuilds(ps: Seq[ScopeAxis[ProjectRef]]): Seq[ScopeAxis[ResolvedReference]] = ps ++ ps.flatMap(rawBuild).distinct :+ Zero - def rawBuild(ps: ScopeAxis[ProjectRef]): Seq[ScopeAxis[BuildRef]] = ps match { + def rawBuild(ps: ScopeAxis[ProjectRef]): Seq[ScopeAxis[BuildRef]] = ps match case Select(ref) => Select(BuildRef(ref.build)) :: Nil case _ => Nil - } def delegates[Proj]( refs: Seq[(ProjectRef, Proj)], configurations: Proj => Seq[ConfigKey], projectInherit: ProjectRef => Seq[ProjectRef], configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey] - ): DelegateIndex = { + ): DelegateIndex = val pDelegates = refs .map: (ref, project) => (ref, delegateIndex(ref, configurations(project))(projectInherit, configInherit)) .toMap new DelegateIndex0(pDelegates) - } private def delegateIndex(ref: ProjectRef, confs: Seq[ConfigKey])( projectInherit: ProjectRef => Seq[ProjectRef], configInherit: (ResolvedReference, ConfigKey) => Seq[ConfigKey] - ): ProjectDelegates = { + ): ProjectDelegates = val refDelegates = withRawBuilds(linearize(Select(ref), false)(projectInherit)) val configs = confs map { c => axisDelegates(configInherit, ref, c) } new ProjectDelegates(ref, refDelegates, configs.toMap) - } def axisDelegates[T]( direct: (ResolvedReference, T) => Seq[T], ref: ResolvedReference, @@ -432,25 +412,23 @@ object Scope: def linearize[T](axis: ScopeAxis[T], appendZero: Boolean = true)( inherit: T => Seq[T] ): Seq[ScopeAxis[T]] = - axis match { + axis match case Select(x) => topologicalSort[T](x, appendZero)(inherit) - case Zero | This => if (appendZero) zeroL else Nil - } + case Zero | This => if appendZero then zeroL else Nil def topologicalSort[T](node: T, appendZero: Boolean)( dependencies: T => Seq[T] - ): Seq[ScopeAxis[T]] = { + ): Seq[ScopeAxis[T]] = val o = Dag.topologicalSortUnchecked(node)(dependencies).map(x => Select(x): ScopeAxis[T]) - if (appendZero) o ::: zeroL else o - } + if appendZero then o ::: zeroL else o def globalProjectDelegates(scope: Scope): Seq[Scope] = - if (scope == GlobalScope) globalL + if scope == GlobalScope then globalL else - for { + for c <- withZeroAxis(scope.config) t <- withZeroAxis(scope.task) e <- withZeroAxis(scope.extra) - } yield Scope(Zero, c, t, e) + yield Scope(Zero, c, t, e) /** * Temporary data structure to capture first two axis using slash syntax. diff --git a/main-settings/src/main/scala/sbt/ScopeMask.scala b/main-settings/src/main/scala/sbt/ScopeMask.scala index 5133c5f87..2531f10a9 100644 --- a/main-settings/src/main/scala/sbt/ScopeMask.scala +++ b/main-settings/src/main/scala/sbt/ScopeMask.scala @@ -14,22 +14,16 @@ final case class ScopeMask( config: Boolean = true, task: Boolean = true, extra: Boolean = true -) { - def concatShow(p: String, c: String, t: String, sep: String, x: String): String = { +): + def concatShow(p: String, c: String, t: String, sep: String, x: String): String = val sb = new StringBuilder - if (project) { + if project then sb.append(p); () - } - if (config) { + if config then sb.append(c); () - } - if (task) { + if task then sb.append(t); () - } sb.append(sep) - if (extra) { + if extra then sb.append(x); () - } sb.toString - } -} diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index ce80527ca..899834b0a 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -27,10 +27,9 @@ sealed trait Scoped extends Equals: val key: AttributeKey[?] override def equals(that: Any): Boolean = - (this eq that.asInstanceOf[AnyRef]) || (that match { + (this eq that.asInstanceOf[AnyRef]) || (that match case that: Scoped => scope == that.scope && key == that.key && canEqual(that) - case _ => false - }) + case _ => false) override def hashCode(): Int = (scope, key).## @@ -43,15 +42,16 @@ sealed trait Taskable[A]: def toTask: Initialize[Task[A]] end Taskable -sealed trait TaskableImplicits { self: Taskable.type => +sealed trait TaskableImplicits: + self: Taskable.type => implicit def fromInit[A](x: Initialize[A]): Taskable[A] = - new Taskable[A] { def toTask = Def.toITask(x) } -} + new Taskable[A]: + def toTask = Def.toITask(x) -object Taskable extends TaskableImplicits { +object Taskable extends TaskableImplicits: implicit def fromITask[A](x: Initialize[Task[A]]): Taskable[A] = - new Taskable[A] { def toTask = x } -} + new Taskable[A]: + def toTask = x /** A common type for SettingKey and TaskKey so that both can be used as inputs to tasks. */ sealed trait ScopedTaskable[A] extends Scoped with Taskable[A] @@ -276,18 +276,22 @@ object Scoped: end ScopingSetting def scopedSetting[T](s: Scope, k: AttributeKey[T]): SettingKey[T] = - new SettingKey[T] { val scope = s; val key = k } + new SettingKey[T]: + val scope = s; val key = k def scopedInput[T](s: Scope, k: AttributeKey[InputTask[T]]): InputKey[T] = - new InputKey[T] { val scope = s; val key = k } + new InputKey[T]: + val scope = s; val key = k def scopedTask[T](s: Scope, k: AttributeKey[Task[T]]): TaskKey[T] = - new TaskKey[T] { val scope = s; val key = k } + new TaskKey[T]: + val scope = s; val key = k /** * Mixin trait for adding convenience vocabulary associated with applying a setting to a configuration item. */ - sealed trait DefinableSetting[A1] { self => + sealed trait DefinableSetting[A1]: + self => def scopedKey: ScopedKey[A1] private[sbt] final inline def :==(inline app: A1): Setting[A1] = @@ -336,7 +340,7 @@ object Scoped: Def.optional(scopedKey)(_ getOrElse or) final def ??[A2 >: A1](or: => A2): Initialize[A2] = getOrElse(or) - } + end DefinableSetting private[sbt] trait Syntax: @@ -384,6 +388,7 @@ object Scoped: Initialize .joinAny[Task](coerceToAnyTaskSeq(tasks)) .zipWith(init)((ts, i) => i.set(key, ts)) + end extension extension [A1](init: Initialize[InputTask[A1]]) @targetName("onTaskInitializeInputTask") @@ -423,12 +428,14 @@ object Scoped: init.zipWith(Initialize.joinAny[Task](coerceToAnyTaskSeq(tasks)))((thisTask, deps) => thisTask.mapTask(_.dependsOn(deps*)) ) + end extension end Syntax // Duplicated with ProjectExtra. private[sbt] object syntax extends Syntax - sealed trait DefinableTask[A1] { self: TaskKey[A1] => + sealed trait DefinableTask[A1]: + self: TaskKey[A1] => /** Internal function for the task macro. */ inline def taskMacro[A2](inline a: A2): Initialize[Task[A2]] = @@ -487,7 +494,7 @@ object Scoped: oa1.getOrElse(a2) } } - } + end DefinableTask private def coerceToAnyTaskSeq(tasks: Seq[AnyInitTask]): Seq[Def.Initialize[Task[Any]]] = tasks.asInstanceOf[Seq[Def.Initialize[Task[Any]]]] @@ -495,18 +502,16 @@ object Scoped: type AnyInitTask = Initialize[Task[?]] implicit def richTaskSeq[T](in: Seq[Initialize[Task[T]]]): RichTaskSeq[T] = new RichTaskSeq(in) - final class RichTaskSeq[T](keys: Seq[Initialize[Task[T]]]) { + final class RichTaskSeq[T](keys: Seq[Initialize[Task[T]]]): def join: Initialize[Task[Seq[T]]] = tasks(_.join) def tasks: Initialize[Seq[Task[T]]] = Initialize.join(keys) - } implicit def richAnyTaskSeq(in: Seq[AnyInitTask]): RichAnyTaskSeq = new RichAnyTaskSeq(in) - final class RichAnyTaskSeq(keys: Seq[AnyInitTask]) { + final class RichAnyTaskSeq(keys: Seq[AnyInitTask]): def dependOn: Initialize[Task[Unit]] = Initialize .joinAny[Task](coerceToAnyTaskSeq(keys)) .apply(deps => nop.dependsOn(deps*)) - } sealed abstract class RichTaskables[Tup <: Tuple](final val keys: Tuple.Map[Tup, Taskable]): diff --git a/main-settings/src/main/scala/sbt/dsl/LinterLevel.scala b/main-settings/src/main/scala/sbt/dsl/LinterLevel.scala index a4a6c9066..344d7f226 100644 --- a/main-settings/src/main/scala/sbt/dsl/LinterLevel.scala +++ b/main-settings/src/main/scala/sbt/dsl/LinterLevel.scala @@ -45,16 +45,15 @@ package sbt.dsl * [[LinterLevel.Ignore]] using the low priority trait pattern. */ sealed trait LinterLevel -object LinterLevel extends LinterLevelLowPriority { +object LinterLevel extends LinterLevelLowPriority: /** * Apply the linter but print warnings instead of aborting macro expansion when linter violations * are found. */ implicit case object Warn extends LinterLevel -} -private[dsl] trait LinterLevelLowPriority { +private[dsl] trait LinterLevelLowPriority: /** * Abort the macro expansion if any linter check fails. @@ -65,4 +64,3 @@ private[dsl] trait LinterLevelLowPriority { * Do not perform any linting. */ implicit case object Ignore extends LinterLevel -} diff --git a/main-settings/src/main/scala/sbt/std/InputTaskMacro.scala b/main-settings/src/main/scala/sbt/std/InputTaskMacro.scala index b5861ec3a..b79f95e61 100644 --- a/main-settings/src/main/scala/sbt/std/InputTaskMacro.scala +++ b/main-settings/src/main/scala/sbt/std/InputTaskMacro.scala @@ -101,6 +101,7 @@ object InputTaskMacro: val inner: convert1.TermTransform[F1] = (in: Term) => f(in.asExprOf[A1]).asTerm val cond = conditionInputTaskTree(tree.asTerm).asExprOf[A1] convert1.contMapN[A1, Def.Initialize, F1](cond, convert1.appExpr, None, inner) + end iInitializeMacro private def iParserMacro[F1[_]: Type, A1: Type](tree: Expr[A1])( f: Expr[A1] => Expr[F1[A1]] @@ -157,7 +158,7 @@ object InputTaskMacro: val lambda = Lambda( owner = Symbol.spliceOwner, tpe = tpe, - rhsFn = (sym, params) => { + rhsFn = (sym, params) => val param = params.head.asInstanceOf[Term] val substitute = [a] => (name: String, tpe: Type[a], qual: Term, replace: Term) => @@ -169,12 +170,12 @@ object InputTaskMacro: convert1 .transformWrappers(body.changeOwner(sym), substitute, sym) modifiedBody - } ) val action = expandTask[Arg => Def.Initialize[Task[A1]]](false, lambda) '{ InputTask.createDyn[Arg, A1](p = $param)(action = $action) } + end genCreateDyn val body = convert1.transformWrappers(expr.asTerm, record, Symbol.spliceOwner) inputBuf.toList match case Nil => genCreateFree(body) @@ -214,7 +215,7 @@ object InputTaskMacro: val lambda = Lambda( owner = Symbol.spliceOwner, tpe = lambdaTpe, - rhsFn = (sym, params) => { + rhsFn = (sym, params) => val p0 = params.head.asInstanceOf[Ident] val body2 = convert1 @@ -231,7 +232,6 @@ object InputTaskMacro: case _ => super.transformTerm(tree)(owner) end refTransformer refTransformer.transformTerm(body2.changeOwner(sym))(sym) - } ) val action = lambda.asExprOf[A1 => Def.Initialize[Task[A2]]] '{ @@ -239,6 +239,7 @@ object InputTaskMacro: Def.valueStrict(TaskExtra.task[A1 => Def.Initialize[Task[A2]]]($action)) ) } + end mkInputTask tree.asTerm match case Lambda(params, body) => mkInputTask(params, body) @@ -254,4 +255,5 @@ object InputTaskMacro: Block(List(), Lambda(params, body)), ) => mkInputTask(params, body) + end parserGenFlatMapTaskImpl end InputTaskMacro diff --git a/main-settings/src/main/scala/sbt/std/Instances.scala b/main-settings/src/main/scala/sbt/std/Instances.scala index ddfef2af1..4eb9f4005 100644 --- a/main-settings/src/main/scala/sbt/std/Instances.scala +++ b/main-settings/src/main/scala/sbt/std/Instances.scala @@ -76,6 +76,7 @@ object FullInstance: override def flatten[A1](in: Initialize[Task[Initialize[Task[A1]]]]): Initialize[Task[A1]] = FullInstance.flatten[A1](in) + end initializeTaskMonad def flatten[A1](in: Initialize[Task[Initialize[Task[A1]]]]): Initialize[Task[A1]] = type Tup = (Task[Initialize[Task[A1]]], Task[SS], [a] => Initialize[a] => Initialize[a]) diff --git a/main-settings/src/main/scala/sbt/std/TaskMacro.scala b/main-settings/src/main/scala/sbt/std/TaskMacro.scala index da9ec06ad..c3fd87d17 100644 --- a/main-settings/src/main/scala/sbt/std/TaskMacro.scala +++ b/main-settings/src/main/scala/sbt/std/TaskMacro.scala @@ -72,8 +72,8 @@ object TaskMacro: val cl = cu0.cacheLevels(key.asTerm) val cached = ContextUtil.isTaskCacheByDefault && !isUncacheApplied && cl.nonEmpty t match - case '{ if ($cond) then $thenp else $elsep } => taskIfImpl[A1](t, cached) - case _ => + case '{ if $cond then $thenp else $elsep } => taskIfImpl[A1](t, cached) + case _ => val convert1 = new FullConvert(qctx, 0) if cached then convert1.contMapN[A1, F, Id]( @@ -86,13 +86,14 @@ object TaskMacro: }) ) else convert1.contMapN[A1, F, Id](t, convert1.appExpr, None) + end taskMacroImpl def taskMacroImpl[A1: Type](t: Expr[A1], cached: Boolean)(using qctx: Quotes ): Expr[Initialize[Task[A1]]] = t match - case '{ if ($cond) then $thenp else $elsep } => taskIfImpl[A1](t, cached) - case _ => + case '{ if $cond then $thenp else $elsep } => taskIfImpl[A1](t, cached) + case _ => val convert1 = new FullConvert(qctx, 0) if cached then convert1.contMapN[A1, F, Id]( @@ -112,11 +113,11 @@ object TaskMacro: import qctx.reflect.* val convert1 = new FullConvert(qctx, 1000) expr match - case '{ if ($cond) then $thenp else $elsep } => + case '{ if $cond then $thenp else $elsep } => '{ Def.ifS[A1](Def.task($cond))(Def.task[A1]($thenp))(Def.task[A1]($elsep)) } - case '{ ${ stats }: a; if ($cond) then $thenp else $elsep } => + case '{ ${ stats }: a; if $cond then $thenp else $elsep } => '{ Def.ifS[A1](Def.task { $stats; $cond })(Def.task[A1]($thenp))(Def.task[A1]($elsep)) } @@ -200,6 +201,7 @@ object TaskMacro: '{ $rec.append1[A2]($init)(using $ev) } + end settingAppend1Impl private[sbt] def sourcePosition(using qctx: Quotes): Expr[SourcePosition] = SourcePositionImpl.fromEnclosingImpl diff --git a/main-settings/src/test/scala/sbt/AppendSpec.scala b/main-settings/src/test/scala/sbt/AppendSpec.scala index b3ecb23ae..722d25f2e 100644 --- a/main-settings/src/test/scala/sbt/AppendSpec.scala +++ b/main-settings/src/test/scala/sbt/AppendSpec.scala @@ -7,7 +7,7 @@ */ package sbt -object AppendSpec { +object AppendSpec: val onLoad = SettingKey[State => State]("onLoad") import Scope.Global @@ -27,4 +27,3 @@ object AppendSpec { Global / onLoad += (() => doSideEffect()) Global / onLoad += (() => doSideEffect()) Global / onLoad += (() => println("foo")) -} diff --git a/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala b/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala index bc0e9df15..1c72e5f06 100644 --- a/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala +++ b/main-settings/src/test/scala/sbt/BuildSettingsInstances.scala @@ -44,6 +44,7 @@ object BuildSettingsInstances: 436 -> genLocalProject.map(x => x: Reference), 1133 -> genRootProject.map(x => x: Reference), ) + end given given Gen[ConfigKey] = Gen.frequency1( 2 -> Gen.constant[ConfigKey](Compile), diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala b/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala index cb17887d6..f55d317ce 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala @@ -19,7 +19,7 @@ import sbt.librarymanagement.ModuleID import sbt.librarymanagement.syntax.* import sbt.{ LocalProject, ProjectReference, ThisBuild } -object SlashSyntaxTest extends sbt.SlashSyntax { +object SlashSyntaxTest extends sbt.SlashSyntax: final case class Proj(id: String) implicit def projToRef(p: Proj): ProjectReference = LocalProject(p.id) @@ -65,4 +65,4 @@ object SlashSyntaxTest extends sbt.SlashSyntax { foo := (Test / bar).value + 1, libraryDependencies += uTest % Test, ) -} +end SlashSyntaxTest diff --git a/main-settings/src/test/scala/sbt/TupleSyntaxTest.scala b/main-settings/src/test/scala/sbt/TupleSyntaxTest.scala index 4a6f8d094..e90ef3d4f 100644 --- a/main-settings/src/test/scala/sbt/TupleSyntaxTest.scala +++ b/main-settings/src/test/scala/sbt/TupleSyntaxTest.scala @@ -11,12 +11,11 @@ package sbt.test import sbt.* object TupleSyntaxTest: - def t1[A](a: SettingKey[A], b: TaskKey[A], c: Def.Initialize[A], d: Def.Initialize[Task[A]]) = { + def t1[A](a: SettingKey[A], b: TaskKey[A], c: Def.Initialize[A], d: Def.Initialize[Task[A]]) = import sbt.TupleSyntax.* (a, b, c.toTaskable, d.toTaskable).mapN { (x: A, y: A, z: A, w: A) => "" + x + y + z + w } - } def t2[A](a: SettingKey[A], b: TaskKey[A], c: Def.Initialize[A], d: Def.Initialize[Task[A]]) = TupleWrap[(A, A, A, A)]((a, b, c.toTaskable, d)).mapN { case (x: A, y: A, z: A, w: A) => diff --git a/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala b/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala index c82916515..43b80e2d1 100644 --- a/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala +++ b/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala @@ -8,7 +8,7 @@ package sbt.std -class TaskPosSpec { +class TaskPosSpec: // Starting sbt 1.4.0, Def.task can have task value lookups inside // if branches since tasks with single if-expressions are automatically // converted into a conditional task. @@ -18,7 +18,7 @@ class TaskPosSpec { val bar = taskKey[String]("") val condition = true Def.task[String] { - if (condition) foo.value + if condition then foo.value else bar.value } } @@ -30,7 +30,7 @@ class TaskPosSpec { val bar = taskKey[String]("") val condition = true Def.taskDyn[String] { - if (condition) foo + if condition then foo else bar } } @@ -42,7 +42,7 @@ class TaskPosSpec { val bar = settingKey[String]("") val condition = true Def.settingDyn[String] { - if (condition) foo + if condition then foo else bar } } @@ -53,7 +53,7 @@ class TaskPosSpec { val condition = true Def.task[String] { val fooAnon = () => foo.value: @sbtUnchecked - if (condition) fooAnon() + if condition then fooAnon() else fooAnon() } } @@ -64,7 +64,7 @@ class TaskPosSpec { val condition = true Def.task[String] { val fooAnon = () => (foo.value: @sbtUnchecked) + "" - if (condition) fooAnon() + if condition then fooAnon() else fooAnon() } } @@ -75,7 +75,7 @@ class TaskPosSpec { val bar = taskKey[String]("") val condition = true Def.task[String] { - if (condition) foo.value: @sbtUnchecked + if condition then foo.value: @sbtUnchecked else bar.value: @sbtUnchecked } } @@ -88,7 +88,7 @@ class TaskPosSpec { Def.task[String] { val fooResult = foo.value val anon = () => fooResult + " " - if (condition) anon() + if condition then anon() else "" } } @@ -100,9 +100,8 @@ class TaskPosSpec { val condition = true Def.taskDyn[String] { val anon1 = (value: String) => value + " " - if (condition) { - Def.task(anon1(foo.value)) - } else Def.task("") + if condition then Def.task(anon1(foo.value)) + else Def.task("") } } @@ -156,11 +155,11 @@ class TaskPosSpec { // In theory, this should be reported, but missing .value analysis is dumb at the cost of speed import sbt.*, Def.* val foo = taskKey[String]("") - def avoidDCE = { println(""); "" } + def avoidDCE = + println(""); "" Def.task[String] { - val (_, _) = "" match { + val (_, _) = "" match case _ => (foo, 1 + 2) - } avoidDCE } } @@ -179,13 +178,11 @@ class TaskPosSpec { locally { import sbt.*, Def.* - def withKey(foo: => SettingKey[String]): Def.Initialize[Task[Unit]] = { + def withKey(foo: => SettingKey[String]): Def.Initialize[Task[Unit]] = Def.task { - if (true) { + if true then Def.unit(foo.value); () - } } - } val foo = settingKey[String]("") withKey(foo) } @@ -196,7 +193,7 @@ class TaskPosSpec { val condition = true Def.task[String] { // settings can be evaluated in a condition - if (condition) foo.value + if condition then foo.value else "..." } } @@ -211,14 +208,13 @@ class TaskPosSpec { locally { import sbt.*, Def.* - def withKey(bar: => SettingKey[Int]) = { + def withKey(bar: => SettingKey[Int]) = Def.task { List(42).map { _ => - if (true) bar.value + if true then bar.value } } - } val bar = settingKey[Int]("bar") withKey(bar) } -} +end TaskPosSpec diff --git a/main-settings/src/test/scala/sbt/std/UsageTest.scala b/main-settings/src/test/scala/sbt/std/UsageTest.scala index eee200def..2c38dc5b4 100644 --- a/main-settings/src/test/scala/sbt/std/UsageTest.scala +++ b/main-settings/src/test/scala/sbt/std/UsageTest.scala @@ -20,7 +20,7 @@ object UseTask: val set = Def.setting { 23 } val x = Def.task { set.value } val y = Def.task { true } - val z = Def.task { if (y.value) x.value else set.value } + val z = Def.task { if y.value then x.value else set.value } val a = Def.taskDyn { // if y.value then z // else x @@ -53,7 +53,7 @@ object UseHktTypeArgument: ) end UseHktTypeArgument -object Assign { +object Assign: import java.io.File import Def.{ @@ -195,4 +195,4 @@ object Assign { trait HigherKind[B[_]] // https://github.com/sbt/sbt/issues/2188 val issue2188 = settingKey[HigherKind[Option]]("asdf") -} +end Assign diff --git a/main/src/main/scala/sbt/BackgroundJobService.scala b/main/src/main/scala/sbt/BackgroundJobService.scala index efaa43222..2b8bcba8e 100644 --- a/main/src/main/scala/sbt/BackgroundJobService.scala +++ b/main/src/main/scala/sbt/BackgroundJobService.scala @@ -19,7 +19,7 @@ import scala.util.control.NonFatal import scala.util.{ Failure, Success, Try } import xsbti.FileConverter -abstract class BackgroundJobService extends Closeable { +abstract class BackgroundJobService extends Closeable: /** * Launch a background job which is a function that runs inside another thread; @@ -59,15 +59,14 @@ abstract class BackgroundJobService extends Closeable { * @param job the job to wait for * @return the result of waiting for the job to complete. */ - def waitForTry(job: JobHandle): Try[Unit] = { + def waitForTry(job: JobHandle): Try[Unit] = try Success(waitFor(job)) - catch { + catch case NonFatal(e) => try stop(job) - catch { case NonFatal(_) => } + catch + case NonFatal(_) => Failure(e) - } - } def waitFor(job: JobHandle): Unit @@ -90,12 +89,12 @@ abstract class BackgroundJobService extends Closeable { ): Classpath = copyClasspath(products, full, workingDirectory, converter) private[sbt] def pauseChannelDuringJob(state: State, handle: JobHandle): Unit -} +end BackgroundJobService -object BackgroundJobService { - private[sbt] def jobIdParser: (State, Seq[JobHandle]) => Parser[Seq[JobHandle]] = { +object BackgroundJobService: + private[sbt] def jobIdParser: (State, Seq[JobHandle]) => Parser[Seq[JobHandle]] = import DefaultParsers.* - (state, handles) => { + (state, handles) => val idParser: Parser[Seq[Long]] = Space ~> token( LongBasic.examples(handles.map(_.id.toString).toSet), description = "" @@ -103,16 +102,12 @@ object BackgroundJobService { idParser.map { ids => ids.flatMap(id => handles.find(_.id == id)) } - } - } -} -abstract class JobHandle { +abstract class JobHandle: def id: Long def humanReadableName: String def spawningTask: ScopedKey[?] def isAutoCancel: Boolean -} /** * This datatype is used signal the task engine or the commands diff --git a/main/src/main/scala/sbt/BuildPaths.scala b/main/src/main/scala/sbt/BuildPaths.scala index ed4eb116b..0ee83fcd6 100644 --- a/main/src/main/scala/sbt/BuildPaths.scala +++ b/main/src/main/scala/sbt/BuildPaths.scala @@ -50,10 +50,9 @@ object BuildPaths: import sbt.io.syntax.* - def getGlobalBase(state: State): File = { + def getGlobalBase(state: State): File = val default = defaultVersionedGlobalBase(binarySbtVersion(state)) getFileSetting(globalBaseDirectory, GlobalBaseProperty, default)(state) - } def getStagingDirectory(state: State, globalBase: File): File = fileSetting(stagingDirectory, StagingProperty, defaultStaging(globalBase))(state) @@ -86,30 +85,23 @@ object BuildPaths: def getFileProperty(name: String): Option[File] = Option(System.getProperty(name)) flatMap { path => - if (path.isEmpty) None - else { - if (path.head == '~') { - val tildePath = expandTildePrefix(path) - Some(new File(tildePath)) - } else { - Some(new File(path).getAbsoluteFile) - } - } + if path.isEmpty then None + else if path.head == '~' then + val tildePath = expandTildePrefix(path) + Some(new File(tildePath)) + else Some(new File(path).getAbsoluteFile) } - def expandTildePrefix(path: String): String = { - val tildePath = path.split("\\/").headOption match { + def expandTildePrefix(path: String): String = + val tildePath = path.split("\\/").headOption match case Some("~") => sys.env.getOrElse("HOME", "") case Some("~+") => sys.env.getOrElse("PWD", "") case Some("~-") => sys.env.getOrElse("OLDPWD", "") case _ => "" - } - path.indexOf("/") match { + path.indexOf("/") match case -1 => tildePath case _ => tildePath + path.substring(path.indexOf("/")) - } - } def defaultVersionedGlobalBase(sbtVersion: String): File = defaultGlobalBase / sbtVersion def defaultGlobalBase: File = internal.SysProp.defaultGlobalBaseDirectory diff --git a/main/src/main/scala/sbt/ClassLoaderLayeringStrategy.scala b/main/src/main/scala/sbt/ClassLoaderLayeringStrategy.scala index 71a8a9284..454fc7fec 100644 --- a/main/src/main/scala/sbt/ClassLoaderLayeringStrategy.scala +++ b/main/src/main/scala/sbt/ClassLoaderLayeringStrategy.scala @@ -69,7 +69,7 @@ sealed trait ClassLoaderLayeringStrategy * Provides instances of [[ClassLoaderLayeringStrategy]] that can be used to define the ClassLoader used by * [[Keys.run]], [[Keys.test]] or any other task that runs java code inside of the sbt jvm. */ -object ClassLoaderLayeringStrategy { +object ClassLoaderLayeringStrategy: /** * Include all of the dependencies in the loader. The base loader will be the Application @@ -103,4 +103,4 @@ object ClassLoaderLayeringStrategy { * Add a layer on top of the ScalaLibrary layer for all of the jar dependencies. */ case object AllLibraryJars extends AllLibraryJars -} +end ClassLoaderLayeringStrategy diff --git a/main/src/main/scala/sbt/CommandLineUIService.scala b/main/src/main/scala/sbt/CommandLineUIService.scala index 951150775..c2546061c 100644 --- a/main/src/main/scala/sbt/CommandLineUIService.scala +++ b/main/src/main/scala/sbt/CommandLineUIService.scala @@ -10,27 +10,21 @@ package sbt import sbt.internal.util.{ SimpleReader, Terminal as ITerminal } -trait CommandLineUIService extends InteractionService { - override def readLine(prompt: String, mask: Boolean): Option[String] = { - val maskChar = if (mask) Some('*') else None +trait CommandLineUIService extends InteractionService: + override def readLine(prompt: String, mask: Boolean): Option[String] = + val maskChar = if mask then Some('*') else None SimpleReader(ITerminal.get).readLine(prompt, maskChar) - } // TODO - Implement this better. - override def confirm(msg: String): Boolean = { - object Assent { - def unapply(in: String): Boolean = { + override def confirm(msg: String): Boolean = + object Assent: + def unapply(in: String): Boolean = (in == "y" || in == "yes") - } - } - SimpleReader(ITerminal.get).readLine(msg + " (yes/no): ", None) match { + SimpleReader(ITerminal.get).readLine(msg + " (yes/no): ", None) match case Some(Assent()) => true case _ => false - } - } override def terminalWidth: Int = ITerminal.get.getWidth override def terminalHeight: Int = ITerminal.get.getHeight -} object CommandLineUIService extends CommandLineUIService diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index 6719da63d..a76f4c4d7 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -27,36 +27,34 @@ import sbt.librarymanagement.{ SemanticSelector, VersionNumber } * Cross implements the Scala cross building commands: * + ("cross") command and ++ ("switch") command. */ -object Cross { +object Cross: private[sbt] def spacedFirst(name: String) = opOrIDSpaced(name) ~ any.+ private case class Switch(version: ScalaVersion, verbose: Boolean, command: Option[String]) - private trait ScalaVersion { + private trait ScalaVersion: def force: Boolean - } private case class NamedScalaVersion(name: String, force: Boolean) extends ScalaVersion private case class ScalaHomeVersion(home: File, resolveVersion: Option[String], force: Boolean) extends ScalaVersion - private def switchParser(state: State): Parser[Switch] = { + private def switchParser(state: State): Parser[Switch] = import DefaultParsers.* - def versionAndCommand(commandName: String)(spacePresent: Boolean) = { + def versionAndCommand(commandName: String)(spacePresent: Boolean) = val x = Project.extract(state) import x.* val knownVersions = crossVersions(x, currentRef) val version = token(StringBasic.examples(knownVersions*)).map { arg => val force = arg.endsWith("!") - val versionArg = if (force) arg.dropRight(1) else arg - versionArg.split("=", 2) match { + val versionArg = if force then arg.dropRight(1) else arg + versionArg.split("=", 2) match case Array(home) if new File(home).exists() => ScalaHomeVersion(new File(home), None, force) case Array(v) => NamedScalaVersion(v, force) case Array(v, home) => ScalaHomeVersion(new File(home), Some(v).filterNot(_.isEmpty), force) - } } - val spacedVersion = if (spacePresent) version else version & spacedFirst(commandName) + val spacedVersion = if spacePresent then version else version & spacedFirst(commandName) val verboseOpt = Parser.opt(token(Space ~> "-v")) // Accept valid commands, or project/command patterns that may reference projects // not yet available after version switch (fixes #7574) @@ -72,18 +70,18 @@ object Cross { Switch(v, verbose.isDefined, command) } switch1 | switch2 - } + end versionAndCommand def parse(commandName: String) = token(commandName ~> OptSpace) flatMap { sp => versionAndCommand(commandName)(sp.nonEmpty) } parse(SwitchCommand) | parse(SwitchAlias) - } + end switchParser private case class CrossArgs(command: String, verbose: Boolean) - private def crossParser(state: State): Parser[CrossArgs] = { + private def crossParser(state: State): Parser[CrossArgs] = def parse(commandName: String) = token(commandName <~ OptSpace) flatMap { _ => (token(Parser.opt("-v" <~ Space)) ~ token(matched(state.combinedParser))).map { @@ -91,67 +89,59 @@ object Cross { } } parse(CrossCommand) | parse(CrossAlias) - } private def crossRestoreSessionParser: Parser[String] = token(CrossRestoreSessionCommand) private[sbt] def requireSession[T](p: State => Parser[T]): State => Parser[T] = s => if s.get(sessionSettings).isEmpty then failure("No project loaded") else p(s) - private def resolveAggregates(extracted: Extracted): Seq[ProjectRef] = { + private def resolveAggregates(extracted: Extracted): Seq[ProjectRef] = - def findAggregates(project: ProjectRef): Seq[ProjectRef] = { + def findAggregates(project: ProjectRef): Seq[ProjectRef] = project :: (extracted.structure .allProjects(project.build) - .find(_.id == project.project) match { + .find(_.id == project.project) match case Some(resolved) => resolved.aggregate.toList.flatMap(findAggregates) - case None => Nil - }) - } + case None => Nil) (extracted.currentRef +: extracted.currentProject.aggregate.flatMap(findAggregates)).distinct - } - private def crossVersions(extracted: Extracted, proj: ResolvedReference): Seq[String] = { + private def crossVersions(extracted: Extracted, proj: ResolvedReference): Seq[String] = import extracted.* (proj / crossScalaVersions).get(structure.data).getOrElse { // reading scalaVersion is a one-time deal (proj / scalaVersion).get(structure.data).toSeq } - } /** * Parse the given command into a list of aggregate projects and command to issue. */ private[sbt] def parseSlashCommand( extracted: Extracted - )(command: String): (Seq[ProjectRef], String) = { + )(command: String): (Seq[ProjectRef], String) = import extracted.* import DefaultParsers.* val parser = ((('{' ~> URIClass <~ '}').? ~ OpOrID <~ charClass(_ == '/', "/")) ~ any.*.string) .map { case uri ~ seg1 ~ cmd => (uri, seg1, cmd) } - Parser.parse(command, parser) match { + Parser.parse(command, parser) match case Right((uri, seg1, cmd)) => structure.allProjectRefs.find { case p if uri.isDefined => seg1 == p.project && uri.contains(p.build.toString) case p => seg1 == p.project - } match { + } match case Some(proj) => (Seq(proj), cmd) case _ => (resolveAggregates(extracted), command) - } case _ => (resolveAggregates(extracted), command) - } - } def crossBuild: Command = Command.arb(requireSession(crossParser), crossHelp)(crossBuildCommandImpl) - private def crossBuildCommandImpl(state: State, args: CrossArgs): State = { + private def crossBuildCommandImpl(state: State, args: CrossArgs): State = val extracted = Project.extract(state) val parser = Act.aggregatedKeyParser(extracted) ~ (matched(Space ~ any.*) | Parser.success("")) - val verbose = if (args.verbose) "-v" else "" - val allCommands = Parser.parse(args.command, parser) match { + val verbose = if args.verbose then "-v" else "" + val allCommands = Parser.parse(args.command, parser) match case Left(_) => val (aggs, aggCommand) = parseSlashCommand(extracted)(args.command) val projCrossVersions = aggs map { proj => @@ -162,7 +152,7 @@ object Cross { val validCommand = Parser.parse(aggCommand, state.combinedParser).isRight val distinctCrossConfigs = projCrossVersions.map(_._2.toSet).distinct - if (validCommand && distinctCrossConfigs.size > 1) { + if validCommand && distinctCrossConfigs.size > 1 then state.log.warn( "Issuing a cross building command, but not all sub projects have the same cross build " + "configuration. This could result in subprojects cross building against Scala versions that they are " + @@ -174,7 +164,6 @@ object Cross { projCrossVersions.foreach { (project, versions) => state.log.debug(s"$project: $versions") } - } // Execute using a blanket switch projCrossVersions.toMap.apply(extracted.currentRef).flatMap { version => @@ -182,11 +171,10 @@ object Cross { Seq(s"$SwitchCommand $verbose $version!", aggCommand) } case Right((keys, taskArgs)) => - def project(key: ScopedKey[?]): Option[ProjectRef] = key.scope.project.toOption match { + def project(key: ScopedKey[?]): Option[ProjectRef] = key.scope.project.toOption match case Some(p: ProjectRef) => Some(p) case _ => None - } - val fullArgs = if (taskArgs.trim.isEmpty) "" else s" ${taskArgs.trim}" + val fullArgs = if taskArgs.trim.isEmpty then "" else s" ${taskArgs.trim}" val keysByVersion = keys .flatMap { k => project(k).toSeq.flatMap(crossVersions(extracted, _).map(v => v -> k)) @@ -199,14 +187,14 @@ object Cross { val projects = keys.flatMap(project) keys.toSeq.flatMap { k => project(k).withFilter(projects.contains).flatMap { p => - if (p == extracted.currentRef || !projects.contains(extracted.currentRef)) { + if p == extracted.currentRef || !projects.contains(extracted.currentRef) then val parts = project(k).map(p => s"{${p.build}}${p.project}") ++ k.scope.config.toOption.map(c => c.name.capitalize) ++ k.scope.task.toOption.map(_.label) ++ Some(k.key.label) Some(v -> parts.mkString("", "/", fullArgs)) - } else None + else None } } } @@ -216,100 +204,89 @@ object Cross { .toSeq .sortBy(_._1) commandsByVersion.flatMap { (v, commands) => - commands match { + commands match case Seq(c) => Seq(s"$SwitchCommand $verbose $v $c") case Seq() => Nil // should be unreachable case multi if fullArgs.isEmpty => Seq(s"$SwitchCommand $verbose $v all ${multi.mkString(" ")}") case multi => Seq(s"$SwitchCommand $verbose $v") ++ multi - } } - } allCommands.toList ::: CrossRestoreSessionCommand :: captureCurrentSession(state, extracted) - } + end crossBuildCommandImpl def crossRestoreSession: Command = Command.arb(_ => crossRestoreSessionParser, crossRestoreSessionHelp)((s, _) => crossRestoreSessionImpl(s) ) - private def crossRestoreSessionImpl(state: State): State = { + private def crossRestoreSessionImpl(state: State): State = restoreCapturedSession(state, Project.extract(state)) - } private val CapturedSession = AttributeKey[Seq[Setting[?]]]("crossCapturedSession") - private def captureCurrentSession(state: State, extracted: Extracted): State = { + private def captureCurrentSession(state: State, extracted: Extracted): State = state.put(CapturedSession, extracted.session.rawAppend) - } - private def restoreCapturedSession(state: State, extracted: Extracted): State = { - state.get(CapturedSession) match { + private def restoreCapturedSession(state: State, extracted: Extracted): State = + state.get(CapturedSession) match case Some(rawAppend) => val restoredSession = extracted.session.copy(rawAppend = rawAppend) BuiltinCommands .reapply(restoredSession, extracted.structure, state) .remove(CapturedSession) case None => state - } - } def switchVersion: Command = Command.arb(requireSession(switchParser), switchHelp)(switchCommandImpl) - private def switchCommandImpl(state: State, args: Switch): State = { + private def switchCommandImpl(state: State, args: Switch): State = val (switchedState, affectedRefs) = switchScalaVersion(args, state) val strictCmd = - if (args.version.force) { + if args.version.force then // The Scala version was forced on the whole build, run as is args.command - } else + else args.command.map { rawCmd => // for now, treat `all` command specially - if (rawCmd.startsWith("all ")) rawCmd - else { + if rawCmd.startsWith("all ") then rawCmd + else val (aggs, aggCommand) = parseSlashCommand(Project.extract(state))(rawCmd) aggs .intersect(affectedRefs) .map(p => s"{${p.build}}${p.project}/$aggCommand") .mkString("all ", " ", "") - } } strictCmd.toList ::: switchedState - } - private def switchScalaVersion(switch: Switch, state: State): (State, Seq[ResolvedReference]) = { + private def switchScalaVersion(switch: Switch, state: State): (State, Seq[ResolvedReference]) = val extracted = Project.extract(state) import extracted.* type ScalaVersion = String - val (version, instance) = switch.version match { + val (version, instance) = switch.version match case ScalaHomeVersion(homePath, resolveVersion, _) => val home = IO.resolve(extracted.currentProject.base, homePath) - if (home.exists()) { + if home.exists() then val instance = ScalaInstance(home)(state.classLoaderCache.apply) val version = resolveVersion.getOrElse(instance.actualVersion) (version, Some((home, instance))) - } else { - sys.error(s"Scala home directory did not exist: $home") - } + else sys.error(s"Scala home directory did not exist: $home") case NamedScalaVersion(v, _) => (v, None) - } def logSwitchInfo( included: Seq[(ResolvedReference, ScalaVersion, Seq[ScalaVersion])], excluded: Seq[(ResolvedReference, Seq[ScalaVersion])] - ) = { + ) = instance.foreach { (home, instance) => state.log.info(s"Using Scala home $home with actual version ${instance.actualVersion}") } - if (switch.version.force) { + if switch.version.force then state.log.info(s"Forcing Scala version to $version on all projects.") - } else { + else included .groupBy(_._2) .foreachEntry { (selectedVersion, projects) => @@ -317,51 +294,44 @@ object Cross { s"Setting Scala version to $selectedVersion on ${projects.size} projects." ) } - } - if (excluded.nonEmpty && !switch.verbose) { + if excluded.nonEmpty && !switch.verbose then state.log.info(s"Excluded ${excluded.size} projects, run ++ $version -v for more details.") - } def detailedLog(msg: => String) = - if (switch.verbose) state.log.info(msg) else state.log.debug(msg) + if switch.verbose then state.log.info(msg) else state.log.debug(msg) - def logProject: (ResolvedReference, Seq[ScalaVersion]) => Unit = (ref, scalaVersions) => { - val current = if (ref == currentRef) "*" else " " - ref match { + def logProject: (ResolvedReference, Seq[ScalaVersion]) => Unit = (ref, scalaVersions) => + val current = if ref == currentRef then "*" else " " + ref match case proj: ProjectRef => detailedLog(s" $current ${proj.project} ${scalaVersions.mkString("(", ", ", ")")}") case _ => // don't log BuildRefs - } - } detailedLog("Switching Scala version on:") included.foreach { case (project, _, versions) => logProject(project, versions) } detailedLog("Excluding projects:") excluded.foreach(logProject.tupled) - } + end logSwitchInfo - val projects: Seq[(ResolvedReference, Option[ScalaVersion], Seq[ScalaVersion])] = { + val projects: Seq[(ResolvedReference, Option[ScalaVersion], Seq[ScalaVersion])] = val projectScalaVersions = structure.allProjectRefs.map(proj => proj -> crossVersions(extracted, proj)) - if (switch.version.force) { + if switch.version.force then projectScalaVersions.map { (ref, options) => (ref, Some(version), options) } ++ structure.units.keys .map(BuildRef.apply) .map(proj => (proj, Some(version), crossVersions(extracted, proj))) - } else { + else projectScalaVersions.map { (project, scalaVersions) => val selector = SemanticSelector(version) - scalaVersions.filter(v => selector.matches(VersionNumber(v))) match { + scalaVersions.filter(v => selector.matches(VersionNumber(v))) match case Nil => (project, None, scalaVersions) case Seq(version) => (project, Some(version), scalaVersions) case multiple => sys.error( s"Multiple crossScalaVersions matched query '$version': ${multiple.mkString(", ")}" ) - } } - } - } val included = projects.collect { case (project, Some(version), scalaVersions) => (project, version, scalaVersions) @@ -370,8 +340,8 @@ object Cross { (project, scalaVersions) } - if (included.isEmpty) { - if (isSelector(version)) + if included.isEmpty then + if isSelector(version) then throw new MessageOnlyException( s"""Switch failed: no subprojects have a version matching "$version" in the crossScalaVersions setting.""" ) @@ -380,12 +350,11 @@ object Cross { s"""Switch failed: no subprojects list "$version" (or compatible version) in crossScalaVersions setting. |If you want to force it regardless, call ++ $version!""".stripMargin ) - } logSwitchInfo(included, excluded) (setScalaVersionsForProjects(instance, included, state, extracted), included.map(_._1)) - } + end switchScalaVersion // determine whether this is a 'specific' version or a selector // to be passed to SemanticSelector @@ -402,13 +371,13 @@ object Cross { projects: Seq[(ResolvedReference, String, Seq[String])], state: State, extracted: Extracted - ): State = { + ): State = import extracted.* val newSettings = projects.flatMap { (project, version, scalaVersions) => val scope = Scope(Select(project), Zero, Zero, Zero) - instance match { + instance match case Some((home, inst1)) => Seq( scope / scalaVersion := version, @@ -422,7 +391,6 @@ object Cross { scope / crossScalaVersions := scalaVersions, scope / scalaHome := None ) - } } val filterKeys: Set[AttributeKey[?]] = Set(scalaVersion, scalaHome, scalaInstance).map(_.key) @@ -430,16 +398,14 @@ object Cross { val projectsContains: Reference => Boolean = projects.map(_._1).toSet.contains // Filter out any old scala version settings that were added, this is just for hygiene. - val filteredRawAppend = session.rawAppend.filter(_.key match { + val filteredRawAppend = session.rawAppend.filter(_.key match case ScopedKey(Scope(Select(ref), Zero, Zero, Zero), key) if filterKeys.contains(key) && projectsContains(ref) => false - case _ => true - }) + case _ => true) val newSession = session.copy(rawAppend = filteredRawAppend ++ newSettings) BuiltinCommands.reapply(newSession, structure, state) - } - -} + end setScalaVersionsForProjects +end Cross diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 9e5be1b9d..b8b493a0b 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -114,15 +114,15 @@ import xsbti.compile.{ TransactionalManagerType } -object Defaults extends BuildCommon with DefExtra { +object Defaults extends BuildCommon with DefExtra: final val CacheDirectoryName = "cache" def configSrcSub(key: SettingKey[File]): Initialize[File] = Def.setting { (ThisScope.copy(config = Zero) / key).value / nameForSrc(configuration.value.name) } - def nameForSrc(config: String) = if (config == Configurations.Compile.name) "main" else config - def prefix(config: String) = if (config == Configurations.Compile.name) "" else config + "-" + def nameForSrc(config: String) = if config == Configurations.Compile.name then "main" else config + def prefix(config: String) = if config == Configurations.Compile.name then "" else config + "-" def lock(app: xsbti.AppConfiguration): xsbti.GlobalLock = LibraryManagement.lock(app) @@ -250,7 +250,7 @@ object Defaults extends BuildCommon with DefExtra { scalaArtifacts :== ScalaArtifacts.Artifacts, sbtResolver := { val v = sbtVersion.value - if (v.endsWith("-SNAPSHOT") || v.contains("-bin-")) Classpaths.sbtMavenSnapshots + if v.endsWith("-SNAPSHOT") || v.contains("-bin-") then Classpaths.sbtMavenSnapshots else Resolver.DefaultMavenRepository }, sbtResolvers := Vector(sbtResolver.value), @@ -313,7 +313,7 @@ object Defaults extends BuildCommon with DefExtra { connectInput :== false, cancelable :== true, taskCancelStrategy := { (state: State) => - if (cancelable.value) TaskCancellationStrategy.Signal + if cancelable.value then TaskCancellationStrategy.Signal else TaskCancellationStrategy.Null }, envVars :== Map.empty, @@ -347,7 +347,7 @@ object Defaults extends BuildCommon with DefExtra { turbo :== SysProp.turbo, usePipelining :== SysProp.pipelining, exportPipelining := usePipelining.value, - useSuperShell := { if (insideCI.value) false else ITerminal.console.isSupershellEnabled }, + useSuperShell := { if insideCI.value then false else ITerminal.console.isSupershellEnabled }, superShellThreshold :== SysProp.supershellThreshold, superShellMaxTasks :== SysProp.supershellMaxTasks, superShellSleep :== SysProp.supershellSleep.millis, @@ -395,7 +395,7 @@ object Defaults extends BuildCommon with DefExtra { .## % 1000), serverConnectionType := ConnectionType.Local, serverAuthentication := { - if (serverConnectionType.value == ConnectionType.Tcp) Set(ServerAuthentication.Token) + if serverConnectionType.value == ConnectionType.Tcp then Set(ServerAuthentication.Token) else Set() }, serverHandlers :== Nil, @@ -428,13 +428,13 @@ object Defaults extends BuildCommon with DefExtra { // BSP has no authentication, so over TCP its handlers would be reachable without the // token handshake the other language-server calls require. Disable it there. val bspSupported = serverConnectionType.value != ConnectionType.Tcp - if (!bspSupported) + if !bspSupported then sLog.value.warn( "BSP is not supported when serverConnectionType is Tcp; disabling the Build Server " + "Protocol handler for this session." ) val bspHandler = - if (bspSupported) + if bspSupported then Seq( BuildServerProtocol.handler( loadedBuild.value, @@ -459,7 +459,7 @@ object Defaults extends BuildCommon with DefExtra { val unmanagedCache = unmanagedFileStampCache.value val managedCache = managedFileStampCache.value val backing = timeWrappedStamper.value - new xsbti.compile.analysis.ReadStamps { + new xsbti.compile.analysis.ReadStamps: def getAllLibraryStamps() : java.util.Map[xsbti.VirtualFileRef, xsbti.compile.analysis.Stamp] = backing.getAllLibraryStamps() @@ -471,34 +471,30 @@ object Defaults extends BuildCommon with DefExtra { new java.util.HashMap[xsbti.VirtualFileRef, xsbti.compile.analysis.Stamp] def library(fr: xsbti.VirtualFileRef): xsbti.compile.analysis.Stamp = backing.library(fr) def product(fr: xsbti.VirtualFileRef): xsbti.compile.analysis.Stamp = backing.product(fr) - def source(fr: xsbti.VirtualFile): xsbti.compile.analysis.Stamp = { + def source(fr: xsbti.VirtualFile): xsbti.compile.analysis.Stamp = val path = converter.toPath(fr) unmanagedCache .get(path) .orElse(managedCache.getOrElseUpdate(path, sbt.nio.FileStamper.Hash)) .map(_.stamp) .getOrElse(backing.source(fr)) - } - } }, ) - private[sbt] def toAbsoluteSource(fc: FileConverter)(pos: Position): Position = { + private[sbt] def toAbsoluteSource(fc: FileConverter)(pos: Position): Position = val newPath: Option[NioPath] = pos .sourcePath() .asScala .flatMap { path => - try { - Some(fc.toPath(VirtualFileRef.of(path))) - } catch { + try Some(fc.toPath(VirtualFileRef.of(path))) + catch // catch all to trap wierd path injected by compiler, users, or plugins case NonFatal(_) => None - } } newPath .map { path => - new Position { + new Position: override def line(): Optional[Integer] = pos.line() override def lineContent(): String = pos.lineContent() @@ -512,11 +508,8 @@ object Defaults extends BuildCommon with DefExtra { override def sourcePath(): Optional[String] = Optional.of(path.toAbsolutePath.toString) override def sourceFile(): Optional[File] = - (try { - Some(path.toFile.getAbsoluteFile) - } catch { - case NonFatal(_) => None - }).toJava + (try Some(path.toFile.getAbsoluteFile) + catch case NonFatal(_) => None).toJava override def startOffset(): Optional[Integer] = pos.startOffset() @@ -529,10 +522,9 @@ object Defaults extends BuildCommon with DefExtra { override def endLine(): Optional[Integer] = pos.endLine() override def endColumn(): Optional[Integer] = pos.endColumn() - } } .getOrElse(pos) - } + end toAbsoluteSource def defaultTestTasks(key: Scoped): Seq[Setting[?]] = inTask(key)( @@ -562,10 +554,9 @@ object Defaults extends BuildCommon with DefExtra { resourceManaged := target.value / "resource_managed", // Adds subproject build.sbt files to the global list of build files to monitor Scope.Global / checkBuildSources / fileInputs ++= { - if ((Scope.Global / onChangedBuildSource).value != IgnoreSourceChanges) + if (Scope.Global / onChangedBuildSource).value != IgnoreSourceChanges then Seq(baseDirectory.value.toGlob / "*.sbt") - else - Nil + else Nil }, ) @@ -596,13 +587,13 @@ object Defaults extends BuildCommon with DefExtra { }, unmanagedSources / fileInputs := { val include = (unmanagedSources / includeFilter).value - val filter = (unmanagedSources / excludeFilter).value match { + val filter = (unmanagedSources / excludeFilter).value match // Hidden files are already filtered out by the FileStamps method case NothingFilter | HiddenFileFilter => include case exclude => include -- exclude - } val baseSources = - if (sourcesInBase.value) Globs(baseDirectory.value.toPath, recursive = false, filter) :: Nil + if sourcesInBase.value then + Globs(baseDirectory.value.toPath, recursive = false, filter) :: Nil else Nil unmanagedSourceDirectories.value .map(d => Globs(d.toPath, recursive = true, filter)) ++ baseSources @@ -652,11 +643,10 @@ object Defaults extends BuildCommon with DefExtra { .value, unmanagedResources / fileInputs := { val include = (unmanagedResources / includeFilter).value - val filter = (unmanagedResources / excludeFilter).value match { + val filter = (unmanagedResources / excludeFilter).value match // Hidden files are already filtered out by the FileStamps method case NothingFilter | HiddenFileFilter => include case exclude => include -- exclude - } unmanagedResourceDirectories.value.map(d => Globs(d.toPath, recursive = true, filter)) }, unmanagedResources := Def.uncached( @@ -732,23 +722,22 @@ object Defaults extends BuildCommon with DefExtra { ) ++ compileBaseGlobal ++ Seq( useScalaReplJLine :== false, scalaInstanceTopLoader := { - val topLoader = if (!useScalaReplJLine.value) { + val topLoader = if !useScalaReplJLine.value then // the JLineLoader contains the SbtInterfaceClassLoader classOf[org.jline.terminal.Terminal].getClassLoader - } else classOf[Compilers].getClassLoader // the SbtInterfaceClassLoader + else classOf[Compilers].getClassLoader // the SbtInterfaceClassLoader // Scala 2.10 shades jline in the console so we need to make sure that it loads a compatible // jansi version. Because of the shading, console does not work with the thin client for 2.10.x. - if (scalaVersion.value.startsWith("2.10.")) new ClassLoader(topLoader) { - override protected def loadClass(name: String, resolve: Boolean): Class[?] = { - if (name.startsWith("org.fusesource")) throw new ClassNotFoundException(name) - super.loadClass(name, resolve) - } - } + if scalaVersion.value.startsWith("2.10.") then + new ClassLoader(topLoader): + override protected def loadClass(name: String, resolve: Boolean): Class[?] = + if name.startsWith("org.fusesource") then throw new ClassNotFoundException(name) + super.loadClass(name, resolve) else topLoader }, scalaInstanceConfig := Def.uncached(Compiler.scalaInstanceConfigTask(None).value), scalaInstance := Def.uncached(Compiler.scalaInstanceTask(scalaInstanceConfig).value), - crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled), + crossVersion := (if crossPaths.value then CrossVersion.binary else CrossVersion.disabled), pluginCrossBuild / sbtBinaryVersion := binarySbtVersion( (pluginCrossBuild / sbtVersion).value ), @@ -761,18 +750,17 @@ object Defaults extends BuildCommon with DefExtra { val sv = (pluginCrossBuild / sbtVersion).value val scalaV = (pluginCrossBuild / scalaVersion).value val binVersion = (pluginCrossBuild / scalaBinaryVersion).value - val cross = id.crossVersionedValue match { + val cross = id.crossVersionedValue match case CrossValue.Disabled => Disabled() case CrossValue.Full => CrossVersion.full case CrossValue.Binary => CrossVersion.binary - } val base = ModuleID(id.groupID, id.name, sv).withCrossVersion(cross).platform(Platform.jvm) CrossVersion(scalaV, binVersion)(base).withCrossVersion(Disabled()) }, crossSbtVersions := Vector((pluginCrossBuild / sbtVersion).value), crossTarget := target.value, clean := { - try { + try val store = AnalysisUtil.staticCachedStore( analysisFile = (Compile / compileAnalysisFile).value.toPath, useTextAnalysis = false, @@ -780,18 +768,16 @@ object Defaults extends BuildCommon with DefExtra { ) // TODO: Uncomment after Zinc update // store.clearCache() - } catch { - case NonFatal(_) => () - } + catch case NonFatal(_) => () clean.value (ThisBuild / publish / clean).value }, scalaCompilerBridgeBin := Def.uncached { - if { + if val sv = scalaVersion.value val hasSbtBridge = ScalaArtifacts.isScala3(sv) || ScalaArtifacts.hasScala2SbtBridge(sv) hasSbtBridge - } then Compiler.compilerBridgeFromUpdate.value + then Compiler.compilerBridgeFromUpdate.value else Vector.empty }, scalaCompilerBridgeJars := (Def.taskDyn { @@ -803,7 +789,7 @@ object Defaults extends BuildCommon with DefExtra { scalaCompilerBridgeSource := ZincLmUtil .getDefaultBridgeSourceModule(scalaOrganization.value, scalaVersion.value), auxiliaryClassFiles ++= { - if (ScalaArtifacts.isScala3(scalaVersion.value)) List(TastyFiles.instance) + if ScalaArtifacts.isScala3(scalaVersion.value) then List(TastyFiles.instance) else Nil }, consoleProject / scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule( @@ -898,40 +884,35 @@ object Defaults extends BuildCommon with DefExtra { sv: String, epochVersion: String, cross: Boolean - ): Seq[File] = { - if (cross) + ): Seq[File] = + if cross then Seq( scalaSrcDir, scalaSrcDir.getParentFile / s"${scalaSrcDir.name}-$sv", scalaSrcDir.getParentFile / s"${scalaSrcDir.name}-$epochVersion", javaSrcDir, ).distinct - else - Seq(scalaSrcDir, javaSrcDir) - } + else Seq(scalaSrcDir, javaSrcDir) def makeCrossSources( scalaSrcDir: File, javaSrcDir: File, sv: String, cross: Boolean - ): Seq[File] = { - if (cross) + ): Seq[File] = + if cross then Seq(scalaSrcDir.getParentFile / s"${scalaSrcDir.name}-$sv", scalaSrcDir, javaSrcDir) - else - Seq(scalaSrcDir, javaSrcDir) - } + else Seq(scalaSrcDir, javaSrcDir) def makePluginCrossSources( isPlugin: Boolean, scalaSrcDir: File, sbtBinaryV: String, cross: Boolean - ): Seq[File] = { - if (cross && isPlugin) + ): Seq[File] = + if cross && isPlugin then Vector(scalaSrcDir.getParentFile / s"${scalaSrcDir.name}-sbt-$sbtBinaryV") else Vector() - } def makeCrossTarget( t: File, @@ -940,14 +921,13 @@ object Defaults extends BuildCommon with DefExtra { sbtv: String, plugin: Boolean, cross: Boolean - ): File = { + ): File = val scalaSuffix = - if (ScalaArtifacts.isScala3(scalaVersion)) scalaVersion else scalaBinaryVersion - val scalaBase = if (cross) t / ("scala-" + scalaSuffix) else t - if (plugin) scalaBase / ("sbt-" + sbtv) else scalaBase - } + if ScalaArtifacts.isScala3(scalaVersion) then scalaVersion else scalaBinaryVersion + val scalaBase = if cross then t / ("scala-" + scalaSuffix) else t + if plugin then scalaBase / ("sbt-" + sbtv) else scalaBase - def compilersSetting = { + def compilersSetting = compilers := Def.uncached { val st = state.value val g = BuildPaths.getGlobalBase(st) @@ -985,17 +965,14 @@ object Defaults extends BuildCommon with DefExtra { scalac ) val classLoaderCache = state.value.classLoaderCache - if (java.lang.Boolean.getBoolean("sbt.disable.interface.classloader.cache")) compilers - else { + if java.lang.Boolean.getBoolean("sbt.disable.interface.classloader.cache") then compilers + else compilers.withScalac( - compilers.scalac match { + compilers.scalac match case x: AnalyzingCompiler => x.withClassLoaderCache(classLoaderCache) case x => x - } ) - } } - } lazy val configTasks: Seq[Setting[?]] = docTaskSettings(doc) ++ inTask(compile)(compileInputsSettings) ++ @@ -1045,13 +1022,13 @@ object Defaults extends BuildCommon with DefExtra { }, compileOutputs := Def.uncached(compileOutputs.triggeredBy(compile).value), tastyFiles := Def.taskIf { - if (ScalaArtifacts.isScala3(scalaVersion.value)) { + if ScalaArtifacts.isScala3(scalaVersion.value) then val _ = compile.value val c = fileConverter.value val dir = c.toPath(backendOutput.value).toFile val tastyFiles = dir.**("*.tasty").get() tastyFiles.map(_.getAbsoluteFile) - } else Nil + else Nil }.value, tastyFilesVF := Def.uncached { val conv = fileConverter.value @@ -1068,13 +1045,11 @@ object Defaults extends BuildCommon with DefExtra { val promise = earlyOutputPing.value val mn = moduleName.value val c = configuration.value - new CompileProgress { - override def afterEarlyOutput(isSuccess: Boolean): Unit = { - if (isSuccess) s.log.debug(s"[$mn / $c] early output is success") + new CompileProgress: + override def afterEarlyOutput(isSuccess: Boolean): Unit = + if isSuccess then s.log.debug(s"[$mn / $c] early output is success") else s.log.debug(s"[$mn / $c] early output can't be made because of macros") promise.complete(Result.Value(isSuccess)) - } - } }, compileEarly := Def.uncached(compileEarlyTask.value), compile := Def.uncached(compileTask.value), @@ -1082,7 +1057,7 @@ object Defaults extends BuildCommon with DefExtra { compileJava := Def.uncached(compileJavaTask.value), compileSplit := { // conditional task - if (incOptions.value.pipelining) Def.uncached(compileJava.value) + if incOptions.value.pipelining then Def.uncached(compileJava.value) else Def.uncached(compileScalaBackend.value) }, internalDependencyConfigurations := InternalDependencies.configurations.value, @@ -1093,7 +1068,7 @@ object Defaults extends BuildCommon with DefExtra { // to the analysis cache, so we keep the scala versions separated. val binVersion = scalaBinaryVersion.value val extra = - if (crossPaths.value) s"_$binVersion" + if crossPaths.value then s"_$binVersion" else "" s"inc_compile$extra.zip" }, @@ -1127,12 +1102,12 @@ object Defaults extends BuildCommon with DefExtra { }, scalacOptions := { val old = scalacOptions.value - if (exportPipelining.value) { + if exportPipelining.value then val sv = scalaVersion.value val shouldApplyFlags = !ScalaArtifacts.isScala3(sv) || VersionNumber(sv).matchesSemVer( SemanticSelector(">=3.5.0") ) - if (shouldApplyFlags) + if shouldApplyFlags then Def.uncached( Vector( "-Ypickle-java", @@ -1141,7 +1116,7 @@ object Defaults extends BuildCommon with DefExtra { ) ++ old ) else Def.uncached(old) - } else Def.uncached(old) + else Def.uncached(old) }, scalacOptions := { val old = scalacOptions.value @@ -1195,13 +1170,11 @@ object Defaults extends BuildCommon with DefExtra { case s if isRunOrTestCommand(s) => true case r => // Handle both "/" (new syntax like Test/run) and ":" (old syntax like test:run) - r.split("[/:]") match { + r.split("[/:]") match case Array(parts*) => - parts.lastOption match { + parts.lastOption match case Some(s) if isRunOrTestCommand(s) => true case _ => false - } - } }) pickMainClassOrWarn(discoveredMainClasses.value, streams.value.log, logWarning) }, @@ -1232,13 +1205,12 @@ object Defaults extends BuildCommon with DefExtra { def generate(generators: SettingKey[Seq[Task[Seq[File]]]]): Initialize[Task[Seq[File]]] = generators { _.join.map(_.flatten) } - def transitiveUpdateTask: Initialize[Task[Seq[UpdateReport]]] = { + def transitiveUpdateTask: Initialize[Task[Seq[UpdateReport]]] = import ScopeFilter.Make.* val selectDeps = ScopeFilter(inDependencies(ThisProject, includeRoot = false)) val allUpdates = update.?.all(selectDeps) // If I am a "build" (a project inside project/) then I have a globalPluginUpdate. Def.task { allUpdates.value.flatten ++ globalPluginUpdate.?.value } - } // Returns the ScalaInstance only if it was not constructed via `update` // This is necessary to prevent cycles between `update` and `scalaInstance` @@ -1345,6 +1317,7 @@ object Defaults extends BuildCommon with DefExtra { case e: TestsFailedException if e.taskName.isEmpty => throw new TestsFailedException(taskName, e.testOutput) finally close(testLoader.value) + end try }, testSelected := { try inputTests(testSelected).evaluated @@ -1361,11 +1334,10 @@ object Defaults extends BuildCommon with DefExtra { test := testQuick.evaluated, ) - private def close(sbtLoader: ClassLoader): Unit = sbtLoader match { + private def close(sbtLoader: ClassLoader): Unit = sbtLoader match case u: AutoCloseable => u.close() case c: ClasspathFilter => c.close() case _ => - } /** * A scope whose task axis is set to Zero. @@ -1430,22 +1402,21 @@ object Defaults extends BuildCommon with DefExtra { private def closeableTestLogger(manager: Streams, baseKey: Scoped, buffered: Boolean)( tdef: TestDefinition - ): TestLogger.PerTest = { + ): TestLogger.PerTest = val scope = baseKey.scope - val extra = scope.extra match { case Select(x) => x; case _ => AttributeMap.empty } + val extra = scope.extra match + case Select(x) => x; + case _ => AttributeMap.empty val key = ScopedKey(scope.copy(extra = Select(testExtra(extra, tdef))), baseKey.key) val s = manager(key) new TestLogger.PerTest(s.log, () => s.close(), buffered) - } - def testExtra(extra: AttributeMap, tdef: TestDefinition): AttributeMap = { - val mod = tdef.fingerprint match { + def testExtra(extra: AttributeMap, tdef: TestDefinition): AttributeMap = + val mod = tdef.fingerprint match case f: SubclassFingerprint => f.isModule case f: AnnotatedFingerprint => f.isModule case _ => false - } extra.put(name.key, tdef.name).put(isModule, mod) - } def singleTestGroup(key: Scoped): Initialize[Task[Seq[Tests.Group]]] = inTask(key, singleTestGroupDefault) @@ -1457,7 +1428,7 @@ object Defaults extends BuildCommon with DefExtra { new Tests.Group( "", tests, - if (fk) Tests.SubProcess(opts) else Tests.InProcess, + if fk then Tests.SubProcess(opts) else Tests.InProcess, Seq.empty ) ) @@ -1467,7 +1438,7 @@ object Defaults extends BuildCommon with DefExtra { inTask(key, splitTestGroupDefault) lazy val splitTestGroupDefault: Initialize[Task[Seq[Tests.Group]]] = Def.taskIf { - if { + if val tests = definedTests.value val byName = tests.groupBy(_.name).toVector.sortBy(_._1) val reqSplit = TestTopology.requestedSplit(testTopology.value) @@ -1476,7 +1447,7 @@ object Defaults extends BuildCommon with DefExtra { math.min(math.min(workerMaxInstances.value, byName.size), reqSplit) ) fork.value && n > 1 - } then + then val tests = definedTests.value val opts = forkOptions.value val byName = tests.groupBy(_.name).toVector.sortBy(_._1) @@ -1490,8 +1461,7 @@ object Defaults extends BuildCommon with DefExtra { buckets.toVector.zipWithIndex.collect { case (b, i) if b.result().nonEmpty => new Tests.Group(s"", b.result(), Tests.SubProcess(opts), Seq.empty) - } - else singleTestGroupDefault.value + } else singleTestGroupDefault.value } def forkOptionsTask: Initialize[Task[ForkOptions]] = @@ -1536,7 +1506,7 @@ object Defaults extends BuildCommon with DefExtra { def inputTests(key: InputKey[?]): Initialize[InputTask[TestResult]] = inputTests0.mapReferenced(Def.mapScope((s) => s.rescope(key.key))) - private lazy val inputTests0: Initialize[InputTask[TestResult]] = { + private lazy val inputTests0: Initialize[InputTask[TestResult]] = val parser = loadForParser(definedTestNames)((s, i) => testOnlyParserWithOption(s, i getOrElse Nil)) ParserGen(parser).flatMapTask { parsed => @@ -1616,13 +1586,13 @@ object Defaults extends BuildCommon with DefExtra { case e: TestsFailedException if e.taskName.isEmpty => throw new TestsFailedException(taskName, e.testOutput) } - } + end inputTests0 def createTestRunners( frameworks: Map[TestFramework, Framework], loader: ClassLoader, config: Tests.Execution - ): Map[TestFramework, Runner] = { + ): Map[TestFramework, Runner] = import Tests.Argument val opts = config.options.toList frameworks.map { (tf, f) => @@ -1633,7 +1603,6 @@ object Defaults extends BuildCommon with DefExtra { val mainRunner = f.runner(args.toArray, Array.empty[String], loader) tf -> mainRunner } - } private[sbt] def allTestGroupsTask( s: TaskStreams, @@ -1643,7 +1612,7 @@ object Defaults extends BuildCommon with DefExtra { config: Tests.Execution, cp: Classpath, converter: FileConverter, - ): Task[Tests.Output] = { + ): Task[Tests.Output] = allTestGroupsTask( s, frameworks, @@ -1658,7 +1627,6 @@ object Defaults extends BuildCommon with DefExtra { projectId = "", converter = converter, ) - } private[sbt] def allTestGroupsTask( s: TaskStreams, @@ -1669,7 +1637,7 @@ object Defaults extends BuildCommon with DefExtra { cp: Classpath, converter: FileConverter, forkedParallelExecution: Boolean, - ): Task[Tests.Output] = { + ): Task[Tests.Output] = allTestGroupsTask( s, frameworks, @@ -1684,7 +1652,6 @@ object Defaults extends BuildCommon with DefExtra { projectId = "", converter = converter, ) - } // Binary compatibility overload for sbt 2.0.0-RC7 private[sbt] def allTestGroupsTask( @@ -1699,7 +1666,7 @@ object Defaults extends BuildCommon with DefExtra { strategy: ClassLoaderLayeringStrategy, projectId: String, converter: FileConverter, - ): Task[Tests.Output] = { + ): Task[Tests.Output] = allTestGroupsTask( s, frameworks, @@ -1714,7 +1681,6 @@ object Defaults extends BuildCommon with DefExtra { projectId, converter, ) - } private[sbt] def allTestGroupsTask( s: TaskStreams, @@ -1729,7 +1695,7 @@ object Defaults extends BuildCommon with DefExtra { strategy: ClassLoaderLayeringStrategy, projectId: String, converter: FileConverter, - ): Task[Tests.Output] = { + ): Task[Tests.Output] = val processedOptions: Map[Tests.Group, Tests.ProcessedOptions] = groups .map(group => group -> Tests.processOptions(config, group.tests.toVector, s.log)) @@ -1749,7 +1715,7 @@ object Defaults extends BuildCommon with DefExtra { val runners = createTestRunners(filteredFrameworks, loader, config) val groupTasks = groups map { group => - group.runPolicy match { + group.runPolicy match case Tests.SubProcess(opts) => s.log.debug(s"javaOptions: ${opts.runJVMOptions}") val forkedConfig = config.copy(parallel = config.parallel && forkedParallelExecution) @@ -1769,9 +1735,8 @@ object Defaults extends BuildCommon with DefExtra { Vector((Tags.ForkedTestGroup, 1)) ++ config.tags ++ group.tags* ) case Tests.InProcess => - if (javaOptions.nonEmpty) { + if javaOptions.nonEmpty then s.log.warn("javaOptions will be ignored, fork is set to false") - } Tests( frameworks, loader, @@ -1780,15 +1745,13 @@ object Defaults extends BuildCommon with DefExtra { config.copy(tags = config.tags ++ group.tags), s.log ) - } } val output = Tests.foldTasks(groupTasks, config.parallel) val result = output map { out => out.events.foreachEntry { (suite, e) => - if ( - strategy != ClassLoaderLayeringStrategy.Flat || + if strategy != ClassLoaderLayeringStrategy.Flat || strategy != ClassLoaderLayeringStrategy.ScalaLibrary - ) { + then (e.throwables ++ e.throwables.flatMap(t => Option(t.getCause))) .find { t => t.isInstanceOf[NoClassDefFoundError] || @@ -1823,7 +1786,6 @@ object Defaults extends BuildCommon with DefExtra { + "See ClassLoaderLayeringStrategy.scala for the full list of options." ) } - } } val summaries = runners.map: (tf, r) => @@ -1832,7 +1794,7 @@ object Defaults extends BuildCommon with DefExtra { } // Def.value[Task[Tests.Output]] { result - } + end allTestGroupsTask def detectTests: Initialize[Task[Seq[TestDefinition]]] = Def.task { @@ -1844,7 +1806,7 @@ object Defaults extends BuildCommon with DefExtra { val max = EvaluateTask.SystemProcessors val maxWorker = workerMaxInstances.value if maxWorker < 1 then sys.error("workerMaxInstances must be >= 1") - Tags.limitAll(if (par) max else 1) :: + Tags.limitAll(if par then max else 1) :: Tags.limit(Tags.ForkedTestGroup, maxWorker) :: Tags.exclusiveWithin(Tags.ExclusiveTestGroup, Tags.Test) :: Tags.exclusiveGroup(Tags.Clean) :: @@ -2079,17 +2041,15 @@ object Defaults extends BuildCommon with DefExtra { } val combined = cPart.toList ++ classifier.toList val configurations = cOpt.map(c => ConfigRef(c.name)).toVector - if (combined.isEmpty) a.withClassifier(None).withConfigurations(configurations) - else { + if combined.isEmpty then a.withClassifier(None).withConfigurations(configurations) + else val a1 = a .withClassifier(Some(combined.mkString("-"))) .withConfigurations(configurations) // use "source" as opposed to "foo-source" to retrieve the type - classifier match { + classifier match case Some(c) => a1.withType(Artifact.classifierType(c)) case None => a1 - } - } } def packageTaskSettings( @@ -2134,23 +2094,24 @@ object Defaults extends BuildCommon with DefExtra { def askForMainClass(classes: Seq[String]): Option[String] = sbt.SelectMainClass( - if (classes.length >= 10) Some(SimpleReader(ITerminal.get).readLine(_)) + if classes.length >= 10 then Some(SimpleReader(ITerminal.get).readLine(_)) else - Some(s => { - def print(st: String) = { scala.Console.out.print(st); scala.Console.out.flush() } + Some(s => + def print(st: String) = + scala.Console.out.print(st); scala.Console.out.flush() print(s) ITerminal.get.withRawInput { try - ITerminal.get.inputStream.read match { + ITerminal.get.inputStream.read match case -1 | -2 => None case b => val res = b.toChar.toString println(res) Some(res) - } - catch { case e: InterruptedException => None } + catch case e: InterruptedException => None } - }), + ) + , classes ) @@ -2161,16 +2122,14 @@ object Defaults extends BuildCommon with DefExtra { classes: Seq[String], logger: Logger, logWarning: Boolean - ): Option[String] = { - classes match { + ): Option[String] = + classes match case multiple if multiple.size > 1 && logWarning => val msg = "multiple main classes detected: run 'show discoveredMainClasses' to see the list" logger.warn(msg) case _ => - } pickMainClass(classes) - } /** Implements `cleanFiles` task. */ private[sbt] def cleanFilesTask: Initialize[Task[Vector[File]]] = Def.task { Vector.empty[File] } @@ -2178,7 +2137,7 @@ object Defaults extends BuildCommon with DefExtra { def runMainTask( classpath: Initialize[Task[Classpath]], scalaRun: Initialize[Task[ScalaRun]] - ): Initialize[InputTask[Unit]] = { + ): Initialize[InputTask[Unit]] = val parser = loadForParser(discoveredMainClasses)((s, names) => runMainParser(s, names getOrElse Nil)) Def.inputTask { @@ -2193,7 +2152,6 @@ object Defaults extends BuildCommon with DefExtra { .run(mainClass, cp.files, appArgs, log) .get } - } def runTask( classpath: Initialize[Task[Classpath]], @@ -2212,17 +2170,15 @@ object Defaults extends BuildCommon with DefExtra { val opts = forkOptions.value val options = javaOptions.value val trap = trapExit.value - if (fork.value) { + if fork.value then s.log.debug(s"javaOptions: $options") new ForkRun(opts) - } else { - new Run(si, trap, tmp) - } + else new Run(si, trap, tmp) } private def foreachJobTask( f: (BackgroundJobService, JobHandle) => Unit - ): Initialize[InputTask[Unit]] = { + ): Initialize[InputTask[Unit]] = val parser: Initialize[State => Parser[Seq[JobHandle]]] = Def.setting { (s: State) => val extracted = Project.extract(s) val service = extracted.get(bgJobService) @@ -2232,11 +2188,8 @@ object Defaults extends BuildCommon with DefExtra { } Def.inputTask { val handles = parser.parsed - for (handle <- handles) { - f(bgJobService.value, handle) - } + for handle <- handles do f(bgJobService.value, handle) } - } def psTask: Initialize[Task[Seq[JobHandle]]] = Def.task { @@ -2275,10 +2228,10 @@ object Defaults extends BuildCommon with DefExtra { val sv = scalaVersion.value val config = configuration.value val projectName = name.value - if (ScalaArtifacts.isScala3(sv)) { - val project = if (config == Compile) projectName else s"$projectName-$config" + if ScalaArtifacts.isScala3(sv) then + val project = if config == Compile then projectName else s"$projectName-$config" Seq("-project", project) - } else Seq.empty + else Seq.empty }, (TaskZero / key) := Def.uncached(Compiler.docTask(key).value), (TaskZero / docVF) := Def.uncached { @@ -2289,10 +2242,9 @@ object Defaults extends BuildCommon with DefExtra { ) ++ compilersSetting ) - def discoverMainClasses(analysis: CompileAnalysis): Seq[String] = analysis match { + def discoverMainClasses(analysis: CompileAnalysis): Seq[String] = analysis match case analysis: Analysis => analysis.infos.allInfos.values.map(_.getMainClasses).flatten.toSeq.sorted - } def consoleProjectTask = ConsoleProject.consoleProjectTask def consoleTask: Initialize[Task[Unit]] = @@ -2313,12 +2265,11 @@ object Defaults extends BuildCommon with DefExtra { // Save analysis midway if pipelining is enabled val store = analysisStore(compileAnalysisFile.value.toPath(), c) val contents = store.unsafeGet() - if (exportP) { + if exportP then // this stores the early analysis (again) in case the subproject contains a macro setup.earlyAnalysisStore.toScala map { earlyStore => earlyStore.set(contents) } - } CompileResult.of( contents.getAnalysis(), contents.getMiniSetup(), @@ -2331,20 +2282,17 @@ object Defaults extends BuildCommon with DefExtra { * via `compileProgress` implementation. */ private[sbt] def compileEarlyTask: Initialize[Task[CompileAnalysis]] = Def.task { - if ({ + if streams.value.log .debug(s"${name.value}: compileEarly: blocking on earlyOutputPing") earlyOutputPing.await.value - }) { + then val c = fileConverter.value val store = analysisStore(earlyCompileAnalysisFile.value.toPath(), c) - store.get.toScala match { + store.get.toScala match case Some(contents) => contents.getAnalysis case _ => Analysis.empty - } - } else { - compile.value - } + else compile.value } def compileTask: Initialize[Task[CompileAnalysis]] = Def.task { @@ -2353,10 +2301,9 @@ object Defaults extends BuildCommon with DefExtra { val store = analysisStore(compileAnalysisFile.value.toPath(), c) // TODO - expose bytecode manipulation phase. val analysisResult: CompileResult = manipulateBytecode.value - if (analysisResult.hasModified) { + if analysisResult.hasModified then val contents = AnalysisContents.create(analysisResult.analysis(), analysisResult.setup()) store.set(contents) - } val map = managedFileStampCache.value val analysis = analysisResult.analysis import scala.jdk.CollectionConverters.* @@ -2400,6 +2347,7 @@ object Defaults extends BuildCommon with DefExtra { reporter.sendFailureReport(ci.options.sources, compileFailed) bspTask.notifyFailure(compileFailed) throw cause + end match }, ) ) @@ -2410,10 +2358,9 @@ object Defaults extends BuildCommon with DefExtra { case _ => false private def projectIdFromScope(s: TaskStreams): String = - s.key.scope.project match { + s.key.scope.project match case Select(ref: ProjectRef) => ref.project case _ => "root" - } private val cachedCompileIncrementalTask = Def .cachedTask { @@ -2456,7 +2403,7 @@ object Defaults extends BuildCommon with DefExtra { val reporter = (compile / bspReporter).value val inputs = (compileJava / compileInputs).value val c = fileConverter.value - result match { + result match case Result.Value(hasModified) => val store = analysisStore(compileAnalysisFile.value.toPath(), c) val contents = store.unsafeGet() @@ -2469,7 +2416,6 @@ object Defaults extends BuildCommon with DefExtra { case Result.Inc(cause) => reporter.sendFailureReport(inputs.options.sources) throw cause - } } private val cachedCompileJavaTask = Def @@ -2501,7 +2447,7 @@ object Defaults extends BuildCommon with DefExtra { ci: Inputs, promise: PromiseWrap[Boolean], projectId: String - ): CompileResult = { + ): CompileResult = lazy val x = s.text(ExportStream) def onArgs(cs: Compilers) = cs.withScalac( @@ -2524,7 +2470,7 @@ object Defaults extends BuildCommon with DefExtra { ConcurrentRestrictions.cancelAllSentinels() throw e finally x.close() // workaround for #937 - } + end compileIncrementalTaskImpl def compileIncSetupTask = Def.task { val cp = dependencyPicklePath.value @@ -2560,7 +2506,7 @@ object Defaults extends BuildCommon with DefExtra { def compileInputsSettings: Seq[Setting[?]] = compileInputsSettings(dependencyPicklePath) - def compileInputsSettings(classpathTask: TaskKey[Classpath]): Seq[Setting[?]] = { + def compileInputsSettings(classpathTask: TaskKey[Classpath]): Seq[Setting[?]] = Seq( compileOptions := Def.uncached { val c = fileConverter.value @@ -2572,7 +2518,7 @@ object Defaults extends BuildCommon with DefExtra { c.toVirtualFile(c.toPath(x)) val eo = CompileOutput(c.toPath(earlyOutput.value)) val eoOpt = - if (exportPipelining.value) Some(eo) + if exportPipelining.value then Some(eo) else None CompileOptions.of( cp.toArray, @@ -2636,25 +2582,21 @@ object Defaults extends BuildCommon with DefExtra { ) ) ) - } private[sbt] def foldMappers( mappers: Seq[Position => Option[Position]], reportAbsolutePath: Boolean, fc: FileConverter - ) = { + ) = def withAbsoluteSource(p: Position): Position = - if (reportAbsolutePath) toAbsoluteSource(fc)(p) else p + if reportAbsolutePath then toAbsoluteSource(fc)(p) else p mappers.foldRight({ (p: Position) => withAbsoluteSource(p) // Fallback if sourcePositionMappers is empty - }) { (mapper, previousPosition) => - { (p: Position) => - // To each mapper we pass the position with the absolute source (only if reportAbsolutePath = true of course) - mapper(withAbsoluteSource(p)).getOrElse(previousPosition(p)) - } + }) { (mapper, previousPosition) => (p: Position) => + // To each mapper we pass the position with the absolute source (only if reportAbsolutePath = true of course) + mapper(withAbsoluteSource(p)).getOrElse(previousPosition(p)) } - } private[sbt] def none[A]: Option[A] = (None: Option[A]) private[sbt] def jnone[A]: Optional[A] = none[A].toJava @@ -2663,13 +2605,12 @@ object Defaults extends BuildCommon with DefExtra { val setup = compileIncSetup.value val c = fileConverter.value val store = analysisStore(compileAnalysisFile.value.toPath(), c) - val prev = store.get().toScala match { + val prev = store.get().toScala match case Some(contents) => val analysis = Option(contents.getAnalysis).toJava val setup = Option(contents.getMiniSetup).toJava PreviousResult.of(analysis, setup) case None => PreviousResult.of(jnone[CompileAnalysis], jnone[MiniSetup]) - } prev } ) @@ -2679,7 +2620,8 @@ object Defaults extends BuildCommon with DefExtra { def printWarningsTask: Initialize[Task[Unit]] = Def.task { - val analysis = compile.value match { case a: Analysis => a } + val analysis = compile.value match + case a: Analysis => a val max = maxErrors.value val spms = sourcePositionMappers.value val problems = @@ -2733,11 +2675,10 @@ object Defaults extends BuildCommon with DefExtra { mappings } - def runMainParser: (State, Seq[String]) => Parser[(String, Seq[String])] = { + def runMainParser: (State, Seq[String]) => Parser[(String, Seq[String])] = import DefaultParsers.* (state, mainClasses) => Space ~> token(NotSpace.examples(mainClasses.toSet)) ~ spaceDelimited("") - } private def testOnlyParserWithOption : (State, Seq[String]) => Parser[(Seq[String], Seq[String], Seq[Tests.AdhocOption])] = @@ -2750,15 +2691,14 @@ object Defaults extends BuildCommon with DefExtra { (t, f, o1.toList ::: o2.toList) } - def testOnlyParser: (State, Seq[String]) => Parser[(Seq[String], Seq[String])] = { + def testOnlyParser: (State, Seq[String]) => Parser[(Seq[String], Seq[String])] = (state, tests) => import DefaultParsers.* val selectTests = distinctParser(tests.toSet, true) val options = (token(Space) ~> token("--") ~> spaceDelimited("