From 73c78d1cf42840bd5bbbdfa20254a0c38547b951 Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Sat, 2 Aug 2025 10:24:19 -0400 Subject: [PATCH 01/20] Formatting: upgrade to v3.9.9 --- .scalafmt.conf | 2 +- project/Dependencies.scala | 2 +- .../src/main/scala/org/scalafmt/internal/FormatOps.scala | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 575d698e43..604540ace3 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version=3.9.6 +version=3.9.9 runner.dialect = scala213 project { git = true diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 06a088b87a..f3c2d96cd0 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -15,7 +15,7 @@ object Dependencies { private def smorg(pkg: => String, v: String) = Def.setting("org.scalameta" %%% pkg % v) - val munit = smorg("munit", munitV) + val munit = smorg("munit", munitV) val scalameta = Def.setting( smorg("scalameta", scalametaV).value .excludeAll("com.thesamet.scalapb" % s"scalapb-runtime_${scalaBinaryVersion.value}"), diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala index a33f986114..87f38adba2 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatOps.scala @@ -125,10 +125,9 @@ class FormatOps( (style.newlines.getSelectChains eq Newlines.keep) => null case _ => start } - case _: T.Colon if { - !style.newlines.sometimesBeforeColonInMethodReturnType && - colonDeclType(start.rightOwner).isDefined - } => tokens(start, 2) // can't break after colon either + case _: T.Colon + if !style.newlines.sometimesBeforeColonInMethodReturnType && + colonDeclType(start.rightOwner).isDefined => tokens(start, 2) // can't break after colon either case _: T.Comment if start.noBreak => val nft = nextNonCommentSameLineAfter(start) if (!start.left.is[T.LeftParen] || nft.hasBreakOrEOF) return nft // RETURN!!! From 9de224392c58ba63db2aa28a5e01d657a714b99d Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Sat, 2 Aug 2025 10:25:20 -0400 Subject: [PATCH 02/20] git blame: ignore scalafmt-3.9.9 reformat commits --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 42751a7e20..cbccc2072f 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -20,3 +20,5 @@ da8534d89378be7d9b1db4032195abfea73e9619 b713da2d0fcee76fd27f6d2e6dbfacb15e2a1150 # v3.8.6 6489c56053a833ef10f4c9ab86e0974acceebc79 +# v3.9.9 +73c78d1cf42840bd5bbbdfa20254a0c38547b951 From e0f25396d4183df28cee8c486a48569a6e0baa54 Mon Sep 17 00:00:00 2001 From: Scalameta Bot Date: Mon, 4 Aug 2025 02:00:22 +0000 Subject: [PATCH 03/20] Update typesafe:config to 1.4.4 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 55730db8ff..46456f46e2 100644 --- a/build.sbt +++ b/build.sbt @@ -75,7 +75,7 @@ lazy val dynamic = crossProject(JVMPlatform) // don't build for NativePlatform buildInfoSettings("org.scalafmt.dynamic", "BuildInfo"), libraryDependencies ++= List( "io.get-coursier" % "interface" % "1.0.28", - "com.typesafe" % "config" % "1.4.3", + "com.typesafe" % "config" % "1.4.4", ), sharedTestSettings, scalacOptions ++= scalacJvmOptions.value, From 80fb2c19fa2f954d0f0f33845e377df053b85e20 Mon Sep 17 00:00:00 2001 From: Scalameta Bot Date: Mon, 4 Aug 2025 02:00:36 +0000 Subject: [PATCH 04/20] Update sbt, scripted-plugin to 1.11.3 --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index bbb0b608ca..c02c575fdb 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.11.2 +sbt.version=1.11.3 From 64cf9a277e1ec297ec0d9de01105f4e98a5e4be6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 03:12:26 +0000 Subject: [PATCH 05/20] Bump actions/download-artifact from 4 to 5 Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release-native.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-native.yml b/.github/workflows/release-native.yml index 31274e75c1..cb32f09d77 100644 --- a/.github/workflows/release-native.yml +++ b/.github/workflows/release-native.yml @@ -76,7 +76,7 @@ jobs: images: scalameta/scalafmt tags: type=raw,value=${{ github.event_name == 'release' && github.event.release.tag_name || github.event.inputs.version }} - name: Downloading scalafmt for Docker Build - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: scalafmt-x86_64-pc-linux.zip path: tmp/scalafmt-docker-build # Dockerfile uses this From 4e12aa65d2702f2947999f1fe02abaded623b29d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 03:54:15 +0000 Subject: [PATCH 06/20] Bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/release-native.yml | 6 +++--- .github/workflows/release.yml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c017f79c7c..6516aea155 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: - '2.13.16' runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - name: Set up JVM @@ -58,7 +58,7 @@ jobs: group: [ Scala2, Scala3, Spark, Intellij, Other ] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - name: Set up JVM @@ -72,7 +72,7 @@ jobs: formatting: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-java@v4 with: java-version: '11' @@ -107,7 +107,7 @@ jobs: NATIVE_IMAGE_STATIC: musl env: ${{ matrix.env }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up GraalVM uses: graalvm/setup-graalvm@v1 with: diff --git a/.github/workflows/release-native.yml b/.github/workflows/release-native.yml index cb32f09d77..e148b5d112 100644 --- a/.github/workflows/release-native.yml +++ b/.github/workflows/release-native.yml @@ -31,7 +31,7 @@ jobs: ] runs-on: ${{ matrix.deploy.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - uses: actions/setup-java@v4 @@ -68,7 +68,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Docker Meta id: meta uses: docker/metadata-action@v5 @@ -125,7 +125,7 @@ jobs: NATIVE_IMAGE_STATIC: musl env: ${{ matrix.env }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up GraalVM uses: graalvm/setup-graalvm@v1 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d834f0dd31..a3ce47ea76 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - uses: actions/setup-java@v4 @@ -28,7 +28,7 @@ jobs: publish-docusaurus: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 - uses: actions/setup-java@v4 From 2ef98691bf5477645705ca5bb8abcc9344adc769 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Aug 2025 03:55:06 +0000 Subject: [PATCH 07/20] Bump actions/setup-java from 4 to 5 Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 6 +++--- .github/workflows/release-native.yml | 2 +- .github/workflows/release.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6516aea155..e9c0d26ed8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: with: fetch-depth: 0 - name: Set up JVM - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: ${{ matrix.java }} distribution: 'temurin' @@ -62,7 +62,7 @@ jobs: with: fetch-depth: 0 - name: Set up JVM - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: ${{ matrix.java }} distribution: 'temurin' @@ -73,7 +73,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: '11' distribution: 'temurin' diff --git a/.github/workflows/release-native.yml b/.github/workflows/release-native.yml index e148b5d112..ce82e442af 100644 --- a/.github/workflows/release-native.yml +++ b/.github/workflows/release-native.yml @@ -34,7 +34,7 @@ jobs: - uses: actions/checkout@v5 with: fetch-depth: 0 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: '21' distribution: 'temurin' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3ce47ea76..69142a9172 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v5 with: fetch-depth: 0 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: '11' distribution: 'temurin' @@ -31,7 +31,7 @@ jobs: - uses: actions/checkout@v5 with: fetch-depth: 0 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: java-version: '11' distribution: 'temurin' From f59b16659dde5d8b2b444e4fcd10dea9965db26c Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Thu, 4 Sep 2025 10:29:49 -0700 Subject: [PATCH 08/20] CoursierDependencyDownloader: remove legacy OSS Also, use `addRepositories` instead of `withRepositories`, as that will preserve the default repositories so we can skip adding them explicitly. --- .../dynamic/CoursierDependencyDownloader.scala | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/scalafmt-dynamic/jvm/src/main/scala/org/scalafmt/dynamic/CoursierDependencyDownloader.scala b/scalafmt-dynamic/jvm/src/main/scala/org/scalafmt/dynamic/CoursierDependencyDownloader.scala index ea73e31e6b..9af8765519 100644 --- a/scalafmt-dynamic/jvm/src/main/scala/org/scalafmt/dynamic/CoursierDependencyDownloader.scala +++ b/scalafmt-dynamic/jvm/src/main/scala/org/scalafmt/dynamic/CoursierDependencyDownloader.scala @@ -12,7 +12,7 @@ import coursierapi.{Dependency => CoursierDependency, _} private class CoursierDependencyDownloader( downloadProgressWriter: OutputStreamWriter, - customRepositories: Seq[Repository], + repositories: Seq[Repository], ) extends DependencyDownloader { override def download(dependencies: Seq[Dependency]): Try[Seq[URL]] = Try { @@ -22,18 +22,11 @@ private class CoursierDependencyDownloader( val cache = Cache.create() .withLogger(Logger.progressBars(downloadProgressWriter)) val settings = Fetch.create().withCache(cache) - .withRepositories(repositories: _*) + .addRepositories(repositories: _*) .withDependencies(coursierDependencies: _*) settings.fetch().asScala.map(_.toURI.toURL).toList } - private def repositories: Seq[Repository] = - // Default repositories are ivy2local, central and also anything in COURSIER_REPOSITORIES overrides - customRepositories ++ Repository.defaults().asScala ++ Seq( - "https://oss.sonatype.org/content/repositories/snapshots", - "https://oss.sonatype.org/content/repositories/public", - ).map(MavenRepository.of) - } object CoursierDependencyDownloader extends DependencyDownloaderFactory { From 4e043cbaf82d3e059b864291307826d78bd50d70 Mon Sep 17 00:00:00 2001 From: Scalameta Bot Date: Fri, 5 Sep 2025 01:16:42 +0000 Subject: [PATCH 09/20] Update sbt-native-packager to 1.11.3 --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 15237dd8fc..06058dbc8b 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -8,7 +8,7 @@ addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.11.1") -addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.11.1") +addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.11.3") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4") From c1e2d3d7a59de6cfe27eb4780256ab32475ca0a3 Mon Sep 17 00:00:00 2001 From: Scalameta Bot Date: Fri, 5 Sep 2025 01:16:54 +0000 Subject: [PATCH 10/20] Update jline to 3.30.5 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 46456f46e2..83cf1d6d08 100644 --- a/build.sbt +++ b/build.sbt @@ -41,7 +41,7 @@ inThisBuild { resolvers += Resolver.sonatypeCentralSnapshots, testFrameworks += new TestFramework("munit.Framework"), // causes native image issues - dependencyOverrides += "org.jline" % "jline" % "3.30.4", + dependencyOverrides += "org.jline" % "jline" % "3.30.5", ) } From c7d40bafc6dcb799005237318cf6eeabea94dc5d Mon Sep 17 00:00:00 2001 From: Scalameta Bot Date: Fri, 5 Sep 2025 01:16:58 +0000 Subject: [PATCH 11/20] Update sbt, scripted-plugin to 1.11.5 --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index c02c575fdb..e480c675f2 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.11.3 +sbt.version=1.11.5 From 97bef57ade1f3daa293b794a2f61cdb6f1d2d108 Mon Sep 17 00:00:00 2001 From: scalameta-bot <50175807+scalameta-bot@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:49:18 +0200 Subject: [PATCH 12/20] Update sbt-ci-release to 1.11.2 (#4963) --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 06058dbc8b..053bc57c88 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -7,7 +7,7 @@ val crossProjectV = "1.3.2" addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1") -addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.11.1") +addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.11.2") addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.11.3") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4") From 8de177f903fa953d93f51b29629e6d103940148c Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Thu, 4 Sep 2025 10:59:46 -0700 Subject: [PATCH 13/20] CoursierDependencyDownloader: support credentials This requires using the full coursier library as it loads credentials from COURSIER_CREDENTIALS env var, just like any default repositories have been loaded from COURSIER_REPOSITORIES. --- build.sbt | 2 +- .../CoursierDependencyDownloader.scala | 25 ++++++++----------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/build.sbt b/build.sbt index 83cf1d6d08..de3e6206bd 100644 --- a/build.sbt +++ b/build.sbt @@ -74,7 +74,7 @@ lazy val dynamic = crossProject(JVMPlatform) // don't build for NativePlatform description := "Implementation of scalafmt-interfaces", buildInfoSettings("org.scalafmt.dynamic", "BuildInfo"), libraryDependencies ++= List( - "io.get-coursier" % "interface" % "1.0.28", + "io.get-coursier" %% "coursier" % coursier, "com.typesafe" % "config" % "1.4.4", ), sharedTestSettings, diff --git a/scalafmt-dynamic/jvm/src/main/scala/org/scalafmt/dynamic/CoursierDependencyDownloader.scala b/scalafmt-dynamic/jvm/src/main/scala/org/scalafmt/dynamic/CoursierDependencyDownloader.scala index 9af8765519..410f53d004 100644 --- a/scalafmt-dynamic/jvm/src/main/scala/org/scalafmt/dynamic/CoursierDependencyDownloader.scala +++ b/scalafmt-dynamic/jvm/src/main/scala/org/scalafmt/dynamic/CoursierDependencyDownloader.scala @@ -1,14 +1,12 @@ package org.scalafmt.dynamic -import org.scalafmt.CompatCollections.JavaConverters._ - import java.io.OutputStreamWriter import java.net.URI import java.net.URL import scala.util.Try -import coursierapi.{Dependency => CoursierDependency, _} +import coursier.{Dependency => _, MavenRepository => Repository, _} private class CoursierDependencyDownloader( downloadProgressWriter: OutputStreamWriter, @@ -16,15 +14,12 @@ private class CoursierDependencyDownloader( ) extends DependencyDownloader { override def download(dependencies: Seq[Dependency]): Try[Seq[URL]] = Try { - val coursierDependencies = dependencies - .map(x => CoursierDependency.of(x.group, x.artifact, x.version)) - // TODO: ttl is unavailable, see https://github.com/coursier/interface/issues/57 - val cache = Cache.create() - .withLogger(Logger.progressBars(downloadProgressWriter)) - val settings = Fetch.create().withCache(cache) - .addRepositories(repositories: _*) - .withDependencies(coursierDependencies: _*) - settings.fetch().asScala.map(_.toURI.toURL).toList + val fileCache = cache.FileCache() // this ctor preserves COURSIER_CREDENTIALS + .withLogger(cache.loggers.RefreshLogger.create(downloadProgressWriter)) + Fetch(fileCache).addDependencies(dependencies.map { dep => + val mod = Module(Organization(dep.group), ModuleName(dep.artifact)) + core.Dependency(mod, dep.version) + }: _*).addRepositories(repositories: _*).run().map(_.toURI.toURL).toList } } @@ -35,9 +30,11 @@ object CoursierDependencyDownloader extends DependencyDownloaderFactory { val writer = properties.reporter.downloadOutputStreamWriter() val repositories = properties.repositories.map { x => val host = new URI(x).getHost - val repo = MavenRepository.of(x) + val repo = Repository(x) properties.repositoryCredentials.find(_.host == host).fold(repo)(cred => - repo.withCredentials(Credentials.of(cred.username, cred.password)), + repo.withAuthentication(Some( + core.Authentication(cred.username, cred.password), + )), ) } new CoursierDependencyDownloader(writer, repositories) From 82ec39180954974dbb611e2a4afdeb8f27e96ce8 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Fri, 5 Sep 2025 18:42:00 +0200 Subject: [PATCH 14/20] improvement: Remove the edit button (#4967) It can easily get out of date and doesn't allow for quickly creating a fork. --- website/pages/en/users.js | 7 ++----- website/siteConfig.js | 2 -- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/website/pages/en/users.js b/website/pages/en/users.js index fbe4c03bcd..2fd438fb71 100644 --- a/website/pages/en/users.js +++ b/website/pages/en/users.js @@ -19,7 +19,6 @@ class Users extends React.Component { return null; } - const editUrl = `${siteConfig.repoUrl}/edit/master/website/siteConfig.js`; const showcase = siteConfig.users.map(user => ( {user.caption} @@ -35,10 +34,8 @@ class Users extends React.Component {

This project is used by many folks

{showcase}
-

Are you using this project?

-
- Add your company - +

Are you using this project? Add your company to the list!

+ diff --git a/website/siteConfig.js b/website/siteConfig.js index 90a12cd279..142344f691 100644 --- a/website/siteConfig.js +++ b/website/siteConfig.js @@ -74,8 +74,6 @@ const siteConfig = { ogImage: "img/scalameta-logo.png", twitterImage: "img/scalameta-logo.png", - editUrl: `${repoUrl}/edit/master/docs/`, - repoUrl }; From 3aaf48a8efd56f5d53fc17a94b005f9e1e01ae9f Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Fri, 12 Sep 2025 22:23:32 -0700 Subject: [PATCH 15/20] PlatformRunOps: return `Seq[String]` in `runArgv` (#4970) --- .../scala/org/scalafmt/cli/TermDisplay.scala | 2 +- .../org/scalafmt/sysops/PlatformRunOps.scala | 4 ++-- .../org/scalafmt/sysops/PlatformRunOps.scala | 24 +++++++++++-------- .../scala/org/scalafmt/sysops/GitOps.scala | 19 +++++++-------- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/TermDisplay.scala b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/TermDisplay.scala index 7ba8bd0136..d1572d9847 100644 --- a/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/TermDisplay.scala +++ b/scalafmt-cli/shared/src/main/scala/org/scalafmt/cli/TermDisplay.scala @@ -23,7 +23,7 @@ object Terminal { else if (!new File("/dev/tty").exists()) None else PlatformRunOps .runArgv(Seq("bash", "-c", s"$pathedTput $s 2> /dev/tty"), None) - .map(_.trim.toInt).toOption + .map(_.headOption.map(_.trim.toInt)).toOption.flatten implicit class Ansi(val output: Writer) extends AnyVal { private def control(n: Int, c: Char) = output.write("\u001b[" + n + c) diff --git a/scalafmt-sysops/js/src/main/scala/org/scalafmt/sysops/PlatformRunOps.scala b/scalafmt-sysops/js/src/main/scala/org/scalafmt/sysops/PlatformRunOps.scala index 781e0aa406..a867e554d5 100644 --- a/scalafmt-sysops/js/src/main/scala/org/scalafmt/sysops/PlatformRunOps.scala +++ b/scalafmt-sysops/js/src/main/scala/org/scalafmt/sysops/PlatformRunOps.scala @@ -22,13 +22,13 @@ private[scalafmt] object PlatformRunOps { implicit def parasiticExecutionContext: ExecutionContext = GranularDialectAsyncOps.parasiticExecutionContext - def runArgv(cmd: Seq[String], cwd: Option[Path]): Try[String] = { + def runArgv(cmd: Seq[String], cwd: Option[Path]): Try[Seq[String]] = { val options = cwd.fold(js.Dictionary[js.Any]())(cwd => js.Dictionary[js.Any]("cwd" -> cwd.toString), ) val result = SpawnSync(cmd.head, cmd.tail.toJSArray, options) if (result.status.asInstanceOf[Int] == 0) - Success(result.stdout.toString.trim) + Success(result.stdout.toString.trim.linesIterator.toSeq) else { val msg = s"Failed to run '${cmd.mkString(" ")}'. Error:\n${result.stderr}\n" diff --git a/scalafmt-sysops/jvm-native/src/main/scala/org/scalafmt/sysops/PlatformRunOps.scala b/scalafmt-sysops/jvm-native/src/main/scala/org/scalafmt/sysops/PlatformRunOps.scala index 15406c6547..e9e8432dc8 100644 --- a/scalafmt-sysops/jvm-native/src/main/scala/org/scalafmt/sysops/PlatformRunOps.scala +++ b/scalafmt-sysops/jvm-native/src/main/scala/org/scalafmt/sysops/PlatformRunOps.scala @@ -8,6 +8,7 @@ import java.util.concurrent.TimeUnit import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContextExecutorService +import scala.sys.process.Process import scala.sys.process.ProcessLogger import scala.util.Failure import scala.util.Success @@ -38,21 +39,24 @@ private[scalafmt] object PlatformRunOps { implicit def parasiticExecutionContext: ExecutionContext = GranularDialectAsyncOps.parasiticExecutionContext - def runArgv(cmd: Seq[String], cwd: Option[Path]): Try[String] = { + def runArgv(cmd: Seq[String], cwd: Option[Path]): Try[Seq[String]] = { + val out = Seq.newBuilder[String] val err = new StringBuilder() - val logger = ProcessLogger(_ => (), x => err.append("\n> ").append(x)) + val logger = ProcessLogger(out += _, err.append("\n> ").append(_)) val argv = if (PlatformCompat.isNativeOnWindows) cmd.map(arg => '"' + arg + '"') else cmd - try { - val proc = sys.process.Process(argv, cwd.map(_.toFile)) - Success(proc.!!(logger).trim) - } catch { - case e: Throwable => - val msg = - s"Failed to run '${cmd.mkString(" ")}'. Error:${err.result()}\n" - Failure(new IllegalStateException(msg, e)) + def failed(e: Throwable) = { + val msg = cmd + .addString(new StringBuilder(), "Failed to run '", " ", "'. Error: ") + .append(err).append('\n') + Failure(new IllegalStateException(msg.toString(), e)) } + try { + val exit = Process(argv, cwd.map(_.toFile)).!(logger) + if (exit != 0) failed(new RuntimeException("exit code " + exit)) + else Success(out.result()) + } catch { case e: Throwable => failed(e) } } def exit(code: Int): Nothing = sys.exit(code) diff --git a/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/GitOps.scala b/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/GitOps.scala index cb970f99db..b402b75fb3 100644 --- a/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/GitOps.scala +++ b/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/GitOps.scala @@ -49,11 +49,8 @@ trait GitOps { private class GitOpsImpl(val workingDirectory: AbsoluteFile) extends GitOps { - private[scalafmt] def tryExec(cmd: Seq[String]): Try[String] = PlatformRunOps - .runArgv(cmd, Some(workingDirectory.path)) - private[scalafmt] def tryExecLines(cmd: Seq[String]): Try[Seq[String]] = - tryExec(cmd).map(_.linesIterator.toSeq) + PlatformRunOps.runArgv(cmd, Some(workingDirectory.path)) private[scalafmt] def exec(cmd: Seq[String]): Seq[String] = tryExecLines(cmd) .get @@ -67,11 +64,13 @@ private class GitOpsImpl(val workingDirectory: AbsoluteFile) extends GitOps { private lazy val tryRoot: Try[AbsoluteFile] = { val cmd = Seq("git", "rev-parse", "--show-toplevel") - tryExec(cmd).flatMap { x => - val file = AbsoluteFile(x) - if (file.isDirectory) Success(file) - else Failure(new GitOps.GitException(s"not a directory: $x")) - } + def failed(msg: String) = Failure(new GitOps.GitException(msg)) + tryExecLines(cmd).flatMap(_.headOption match { + case Some(x) => + val file = AbsoluteFile(x) + if (file.isDirectory) Success(file) else failed(s"not a directory: $x") + case _ => failed("unable to determine git root") + }) } override def diff(branch: String, dir: AbsoluteFile*): Seq[AbsoluteFile] = { @@ -88,7 +87,7 @@ private class GitOpsImpl(val workingDirectory: AbsoluteFile) extends GitOps { override def getAutoCRLF: Option[String] = { val cmd = Seq("git", "config", "--get", "core.autocrlf") - tryExec(cmd).toOption + tryExecLines(cmd).toOption.flatMap(_.headOption) } private final val renameStatusCode = "R" From 645985414f8768fbaa728997675f49739476b99e Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Sat, 13 Sep 2025 00:35:05 -0700 Subject: [PATCH 16/20] GitOps: call "git" centrally, args as sequences (#4971) --- .../scala/org/scalafmt/sysops/GitOps.scala | 55 ++++++++++--------- .../org/scalafmt/sysops/GitOpsTest.scala | 14 ++--- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/GitOps.scala b/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/GitOps.scala index b402b75fb3..af4a7c4ce8 100644 --- a/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/GitOps.scala +++ b/scalafmt-sysops/shared/src/main/scala/org/scalafmt/sysops/GitOps.scala @@ -49,23 +49,26 @@ trait GitOps { private class GitOpsImpl(val workingDirectory: AbsoluteFile) extends GitOps { - private[scalafmt] def tryExecLines(cmd: Seq[String]): Try[Seq[String]] = - PlatformRunOps.runArgv(cmd, Some(workingDirectory.path)) + private[scalafmt] def tryExec(args: Any*): Try[Seq[String]] = { + val argv = Seq.newBuilder[String] + argv += "git" + args.foreach { + case x: Seq[_] => x.foreach(argv += _.toString) + case x => argv += x.toString + } + PlatformRunOps.runArgv(argv.result(), Some(workingDirectory.path)) + } - private[scalafmt] def exec(cmd: Seq[String]): Seq[String] = tryExecLines(cmd) - .get + private[scalafmt] def exec(args: Any*): Seq[String] = tryExec(args: _*).get - override def lsTree(dir: AbsoluteFile*): Seq[AbsoluteFile] = { - val cmd = Seq("git", "ls-files", "--full-name") ++ dir.map(_.toString()) - withRoot(exec(cmd)).filter(_.isRegularFileNoLinks) - } + override def lsTree(dir: AbsoluteFile*): Seq[AbsoluteFile] = + withRoot(exec("ls-files", "--full-name", dir)).filter(_.isRegularFileNoLinks) override def rootDir: Option[AbsoluteFile] = tryRoot.toOption private lazy val tryRoot: Try[AbsoluteFile] = { - val cmd = Seq("git", "rev-parse", "--show-toplevel") def failed(msg: String) = Failure(new GitOps.GitException(msg)) - tryExecLines(cmd).flatMap(_.headOption match { + tryExec("rev-parse", "--show-toplevel").flatMap(_.headOption match { case Some(x) => val file = AbsoluteFile(x) if (file.isDirectory) Success(file) else failed(s"not a directory: $x") @@ -73,22 +76,22 @@ private class GitOpsImpl(val workingDirectory: AbsoluteFile) extends GitOps { }) } - override def diff(branch: String, dir: AbsoluteFile*): Seq[AbsoluteFile] = { - val cmd = Seq("git", "diff", "--name-only", "--diff-filter=d", branch) ++ - (if (dir.isEmpty) Seq.empty else "--" +: dir.map(_.toString())) - withRoot(exec(cmd)) - } - - override def status(dir: AbsoluteFile*): Seq[AbsoluteFile] = { - val cmd = Seq("git", "status", "--porcelain") ++ - (if (dir.isEmpty) Seq.empty else "--" +: dir.map(_.toString())) - withRoot(exec(cmd).map(getFileFromGitStatusLine)).filter(_.exists) - } - - override def getAutoCRLF: Option[String] = { - val cmd = Seq("git", "config", "--get", "core.autocrlf") - tryExecLines(cmd).toOption.flatMap(_.headOption) - } + override def diff(branch: String, dir: AbsoluteFile*): Seq[AbsoluteFile] = + withRoot(exec( + "diff", + "--name-only", + "--diff-filter=d", + branch, + if (dir.isEmpty) Seq.empty else "--" +: dir, + )) + + override def status(dir: AbsoluteFile*): Seq[AbsoluteFile] = withRoot( + exec("status", "--porcelain", if (dir.isEmpty) Seq.empty else "--" +: dir) + .map(getFileFromGitStatusLine), + ).filter(_.exists) + + override def getAutoCRLF: Option[String] = + tryExec("config", "--get", "core.autocrlf").toOption.flatMap(_.headOption) private final val renameStatusCode = "R" private final val renameStatusArrowDelimiter = "-> " diff --git a/scalafmt-sysops/shared/src/test/scala/org/scalafmt/sysops/GitOpsTest.scala b/scalafmt-sysops/shared/src/test/scala/org/scalafmt/sysops/GitOpsTest.scala index 87b4e3a411..dc0d2c33b3 100644 --- a/scalafmt-sysops/shared/src/test/scala/org/scalafmt/sysops/GitOpsTest.scala +++ b/scalafmt-sysops/shared/src/test/scala/org/scalafmt/sysops/GitOpsTest.scala @@ -271,24 +271,20 @@ private object GitOpsTest { def rmfs(file: AbsoluteFile): Unit = file.delete() // Git commands - def git(cmd: String, args: String*)(implicit - ops: GitOpsImpl, - loc: Location, - ): Seq[String] = ops.tryExecLines("git" +: cmd +: args) - .fold(ex => Assertions.fail(s"Failed git command. Got: $ex"), identity) + def git(args: Any*)(implicit ops: GitOpsImpl, loc: Location): Seq[String] = + ops.tryExec(args: _*) + .fold(ex => Assertions.fail(s"Failed git command. Got: $ex"), identity) def init(implicit ops: GitOpsImpl, loc: munit.Location): Unit = git("init", "-b", defaultBranch) def add( file: AbsoluteFile*, - )(implicit ops: GitOpsImpl, loc: munit.Location): Unit = - git("add", file.map(_.toString()): _*) + )(implicit ops: GitOpsImpl, loc: munit.Location): Unit = git("add", file) def rm( file: AbsoluteFile*, - )(implicit ops: GitOpsImpl, loc: munit.Location): Unit = - git("rm", file.map(_.toString()): _*) + )(implicit ops: GitOpsImpl, loc: munit.Location): Unit = git("rm", file) def commit(implicit ops: GitOpsImpl, loc: munit.Location): Unit = git("commit", "-m", "'some-message'") From f8a4b984b6e6c38bf66206b5338f6ed55765a201 Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Sun, 14 Sep 2025 11:17:34 -0700 Subject: [PATCH 17/20] FormatWriter: refactor appendWhitespace (#4972) Do not concatenate newlines with spaces to append to string buffer, and DRY with respect to delayedAlign usage. Also, avoid using `return`. --- .../org/scalafmt/internal/FormatWriter.scala | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala index cbc06e0600..47e56eeb53 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala @@ -439,25 +439,26 @@ class FormatWriter(formatOps: FormatOps) { sb: StringBuilder, ): Int = { val mod = state.mod - def currentAlign = tokenAligns.get(i).fold(0)(_ + alignOffset) - val ws = mod match { + def align = tokenAligns.get(i).fold(0)(_ + alignOffset) + delayedAlign + mod match { case nl: NewlineT => val extraBlanks = if (i == locations.length - 1) 0 else extraBlankTokens.getOrElse(i, if (nl.isDouble) 1 else 0) - val newlines = getNewlines(extraBlanks) - if (nl.noIndent) newlines - else newlines + getIndentation(state.indentation) + sb.append(getNewlines(extraBlanks)) + if (!nl.noIndent) sb.append(getIndentation(state.indentation)) + 0 - case p: Provided => p.betweenText + case p: Provided => + sb.append(p.betweenText) + 0 - case NoSplit if style.align.delayUntilSpace => - return delayedAlign + currentAlign // RETURNING! + case NoSplit if style.align.delayUntilSpace => align // delay - case _ => getIndentation(mod.length + currentAlign + delayedAlign) + case _ => + sb.append(getIndentation(mod.length + align)) + 0 } - sb.append(ws) - 0 } def formatWhitespace(delayedAlign: Int)(implicit sb: StringBuilder): Int = { From 4e9de61ace7683acc31bfc8c5c73ec987cfea8db Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Sun, 14 Sep 2025 07:58:55 -0700 Subject: [PATCH 18/20] Test multiline strings after aligned tokens --- .../resources/align/DefaultWithAlign.stat | 21 +++++++++++++++++++ .../test/scala/org/scalafmt/FormatTests.scala | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/scalafmt-tests/shared/src/test/resources/align/DefaultWithAlign.stat b/scalafmt-tests/shared/src/test/resources/align/DefaultWithAlign.stat index 0a0d6c8f5d..5e49d2e848 100644 --- a/scalafmt-tests/shared/src/test/resources/align/DefaultWithAlign.stat +++ b/scalafmt-tests/shared/src/test/resources/align/DefaultWithAlign.stat @@ -2308,3 +2308,24 @@ object a { ddd %% dd %% d e %%% eeeeee % eee } +<<< align with trailing multiline string +align.multiline = true +align.stripMargin = true +align.tokens."+" = [ { code = "&&" } ] +assumeStandardLibraryStripMargin = true +=== +object a { +val xy = xy && xy && xy +val x = x && x && """|x + |x""".stripMargin +val y = y && y && s"""|y + |y""".stripMargin +} +>>> +object a { + val xy = xy && xy && xy + val x = x && x && """|x + |x""".stripMargin + val y = y && y && s"""|y + |y""".stripMargin +} diff --git a/scalafmt-tests/shared/src/test/scala/org/scalafmt/FormatTests.scala b/scalafmt-tests/shared/src/test/scala/org/scalafmt/FormatTests.scala index 874f6f7988..5f879d7a08 100644 --- a/scalafmt-tests/shared/src/test/scala/org/scalafmt/FormatTests.scala +++ b/scalafmt-tests/shared/src/test/scala/org/scalafmt/FormatTests.scala @@ -148,7 +148,7 @@ class FormatTests extends FunSuite with CanRunTests with FormatAssertions { val explored = Debug.explored.get() logger.debug(s"Total explored: $explored") if (!onlyUnit && !onlyManual) - assertEquals(explored, 2597792, "total explored") + assertEquals(explored, 2597948, "total explored") if (!sys.env.contains("CI")) PlatformFileOps.writeFileAsync( FileOps.getPath("target", "index.html"), Report.heatmap(debugResults.result()), From 1ce4606378553ed4368cf8896b7d1ea6cc274ac8 Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Sun, 14 Sep 2025 10:15:07 -0700 Subject: [PATCH 19/20] FormatWriter: add align shift to multiline strings --- .../org/scalafmt/internal/FormatWriter.scala | 29 ++++++++++++++----- .../resources/align/DefaultWithAlign.stat | 4 +-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala index 47e56eeb53..26a998d2ac 100644 --- a/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala +++ b/scalafmt-core/shared/src/main/scala/org/scalafmt/internal/FormatWriter.scala @@ -37,6 +37,7 @@ class FormatWriter(formatOps: FormatOps) { styleMap.init.runner.event(FormatEvent.Written(locations)) var delayedAlign = 0 + var totalAlignShift = 0 locations.foreach { entry => val location = entry.curr implicit val style: ScalafmtConfig = location.style @@ -47,8 +48,8 @@ class FormatWriter(formatOps: FormatOps) { ft.left match { case _ if entry.previous.formatToken.meta.formatOff => sb.append(ltext) // checked the state for left case _: T.Comment => entry.formatComment - case _: T.Interpolation.Part | _: T.Constant.String => - entry.formatMarginized + case _: T.Interpolation.Part | _: T.Constant.String => entry + .formatMarginized(totalAlignShift) case _: T.Constant.Int => LiteralOps.prettyPrintInteger(ltext) case _: T.Constant.Long => LiteralOps.prettyPrintInteger(ltext) case _: T.Constant.Float => LiteralOps.prettyPrintFloat(ltext) @@ -85,7 +86,16 @@ class FormatWriter(formatOps: FormatOps) { } else if (ft.right.is[T.RightParen]) skipWs = true } else if (location.missingBracesOpenOrTuck) sb.append(" {") - if (!skipWs) delayedAlign = entry.formatWhitespace(delayedAlign) + if (!skipWs) { + val alignShift = entry.formatWhitespace(delayedAlign) + if (location.state.mod.isNL) { + delayedAlign = 0 + totalAlignShift = 0 + } else if (alignShift > 0) { + delayedAlign = 0 + totalAlignShift += alignShift + } else delayedAlign = -alignShift + } } sb.toString() @@ -453,11 +463,12 @@ class FormatWriter(formatOps: FormatOps) { sb.append(p.betweenText) 0 - case NoSplit if style.align.delayUntilSpace => align // delay + case NoSplit if style.align.delayUntilSpace => -align // delay case _ => - sb.append(getIndentation(mod.length + align)) - 0 + val alignShift = align + sb.append(getIndentation(mod.length + alignShift)) + alignShift } } @@ -533,7 +544,9 @@ class FormatWriter(formatOps: FormatOps) { } } - def formatMarginized(implicit sb: StringBuilder): Unit = { + def formatMarginized( + alignShift: Int, + )(implicit sb: StringBuilder): Unit = { val text = tok.meta.left.text val tupleOpt = tok.left match { case _ if !style.assumeStandardLibraryStripMargin => None @@ -559,7 +572,7 @@ class FormatWriter(formatOps: FormatOps) { } tupleOpt match { case Some((pipe, indent)) => - val spaces = getIndentation(indent) + val spaces = getIndentation(indent + alignShift) val matcher = RegexCompat.getStripMarginPattern(pipe).matcher(text) var pos = 0 while (matcher.find()) { diff --git a/scalafmt-tests/shared/src/test/resources/align/DefaultWithAlign.stat b/scalafmt-tests/shared/src/test/resources/align/DefaultWithAlign.stat index 5e49d2e848..5b598829e1 100644 --- a/scalafmt-tests/shared/src/test/resources/align/DefaultWithAlign.stat +++ b/scalafmt-tests/shared/src/test/resources/align/DefaultWithAlign.stat @@ -2325,7 +2325,7 @@ val y = y && y && s"""|y object a { val xy = xy && xy && xy val x = x && x && """|x - |x""".stripMargin + |x""".stripMargin val y = y && y && s"""|y - |y""".stripMargin + |y""".stripMargin } From f95850d438503c990c7c068e531096a10faa3669 Mon Sep 17 00:00:00 2001 From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:50:59 -0700 Subject: [PATCH 20/20] Scalameta: upgrade to v4.13.10 --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f3c2d96cd0..545b05d188 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -7,7 +7,7 @@ import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._ object Dependencies { val metaconfigV = "0.15.0" - val scalametaV = "4.13.9" + val scalametaV = "4.13.10" val scalacheckV = "1.18.1" val coursier = "2.1.24" val munitV = "1.1.0"