Skip to content

Conversation

@OpeOginni
Copy link
Contributor

feat: Allowing for wildcards in formatter extenstions, utilizing already existing Wildcards utils

Solved issue #3431

Tested with this config and it works

{
    "$schema": "https://opencode.ai/config.json",
    "formatter": {
    "prettier": {
        "extensions": [".ts"],
        "command": ["npx", "prettier", "--write", "$FILE"],
        "enabled": true
      },
      "biome": {
        "extensions": ["*.test.ts"],
        "command": ["npx", "@biomejs/biome", "format", "--write", "$FILE"],
        "enabled": true
      }
    }
}

@rekram1-node I want to ask,

  • If a user mistakely enables two formatters for a single file type what should be the system's response?

@rekram1-node
Copy link
Collaborator

@OpeOginni I think it should just run both of them in that case.

I was using a python codebase a while ago and we wanted to run pylint and black on any file edit + save, and then both would run and adjust the code

@rekram1-node rekram1-node self-assigned this Nov 4, 2025
@OpeOginni
Copy link
Contributor Author

Okay cool, the this fix should work. Ill do some more testing and maybe make some docs update as well?

@rekram1-node
Copy link
Collaborator

@OpeOginni I tested your changes but now formatters don't run in certain cases, ex:

.test.ts won't be formatted.

I don't think this should be fixed by just adding ".test.ts" to the array either.

@OpeOginni
Copy link
Contributor Author

@rekram1-node Hmm thats weird, in this case is it that you didnt explicitly add *.test.ts to the extensions, only adding .ts without a wildcard?

@rekram1-node
Copy link
Collaborator

@OpeOginni im talking about our defaults in formatter.ts

But this would also apply to any existing users we dont wanna push breaking changes if we can help it.

What do u think makes sense given that constraint?

@OpeOginni
Copy link
Contributor Author

@rekram1-node Maybe, having a system that checks for any explicit extension combination such as *.test.ts if none exists we treat the whole system as previous where a normal .ts would be like using *.ts and would work for all files.

Ig this way only users who want to make use of these specific formatter feature, will be given it, but if a normal user doesnt see the need (not in their config), formatting works as it did before?

@rekram1-node
Copy link
Collaborator

I guess we could also have some exclusion...

Can we derive the behavior from how editors do this? I'm sure there is an existing UX for best practice if you can point to some examples of how other people do it we should follow one of those "standards"

@OpeOginni
Copy link
Contributor Author

OpeOginni commented Nov 4, 2025

I tried to get a working logic

  async function getFormatter(ext: string, fullExt: string) {
    const formatters = await state().then((x) => x.formatters)
    const possibleFormatters = []

    // Step 1: Collect all formatters that could apply to this file
    // Include if: direct ext match OR wildcard pattern matches fullExt
    for (const item of Object.values(formatters)) {
      log.info("checking", { name: item.name, ext, fullExt })
      
      const matches = item.extensions.some(pattern => {
        if (pattern.includes("*") || pattern.includes("?")) {
          return Wildcard.match(fullExt, pattern)
        }
        return pattern === ext
      })
      
      if (!matches) continue
      if (!(await isEnabled(item))) continue
      possibleFormatters.push(item)
    }

    // Step 2: Apply precedence - prefer formatters with explicit fullExt wildcard matches
    // Strong: formatters with wildcard patterns that match fullExt (e.g., "*.test.ts")
    // Weak: formatters that only match base ext (e.g., ".ts")
    // If any strong matches exist, use only those; otherwise use all possible formatters
    const strongFormatters = possibleFormatters.filter(formatter =>
      formatter.extensions.some(pattern => 
        (pattern.includes("*") || pattern.includes("?")) && 
        Wildcard.match(fullExt, pattern)
      )
    )
    if (strongFormatters.length) return strongFormatters
    return possibleFormatters
  }

Let me know what you think?

Ill try to check if any editor "standards" exists now.

@OpeOginni
Copy link
Contributor Author

@rekram1-node Was looking at how it is handled by Biome and some prettier plugins, the new logic works, only thing we can add is some sort of "longest matching pattern wins" that Biome implements.

In this case if two formatters have extensions as *.test.ts and *.ts, it will only take the formatter with *.test.ts for a file like example.test.ts.

@OpeOginni I think it should just run both of them in that case.

But based on your answer here, I don't know if thats what we want.

In the case of a user not having any wildcards at all, normal .ts would work for all files that ends with .ts as long as there was no other strong matches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants