diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 54af3c33b..640581ffc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,8 +19,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Fetch LintRatchet - run: git fetch origin LintRatchet:LintRatchet - name: Setup Java uses: actions/setup-java@v3 with: diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index f445bea37..22b5aea24 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -12,8 +12,12 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Fetch LintRatchet - run: git fetch origin LintRatchet:LintRatchet + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + - name: Run Linter + run: python format_whitespace.py - name: Setup Java uses: actions/setup-java@v3 with: @@ -29,8 +33,6 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle', 'gradle.properties', '**/*.accesswidener') }} restore-keys: | ${{ runner.os }}-gradle- - - name: Run Spotless - run: ./gradlew spotlessApply - name: Run Gradle run: ./gradlew && ./gradlew jar - name: Upload build @@ -38,7 +40,7 @@ jobs: with: name: Grasscutter path: grasscutter-*.jar - - name: Commit any Spotless changes + - name: Commit any Linter changes uses: EndBug/add-and-commit@v9 with: add: '-u' diff --git a/build.gradle b/build.gradle index 520f27ab4..6872c1e49 100644 --- a/build.gradle +++ b/build.gradle @@ -34,9 +34,6 @@ plugins { // Maven id 'maven-publish' id 'signing' - - // Spotless formatter - id "com.diffplug.spotless" version "6.8.0" } compileJava.options.encoding = "UTF-8" @@ -241,30 +238,6 @@ eclipse { } } -spotless { - // optional: limit format enforcement to just the files changed by this feature branch - ratchetFrom 'LintRatchet' - - format 'misc', { - // define the files to apply `misc` to - target '*.gradle', '*.md', '.gitignore' - - // define the steps to apply to those files - trimTrailingWhitespace() - indentWithSpaces() // or spaces. Takes an integer argument if you don't like 4 - endWithNewline() - } - java { - // don't need to set target, it is inferred from java - // define the steps to apply to those files - trimTrailingWhitespace() - indentWithSpaces() // or spaces. Takes an integer argument if you don't like 4 - endWithNewline() - replaceRegex('Force one space between if/etc. and ( or {', '(?<=\\b(?:if|for|while|switch|try|else|catch|finally|synchronized)) *(?=[\\(\\{])', ' ') - replaceRegex('Force one space between ) and {', '\\) *\\{(?!\\})', ') {') - } -} - signing { if(!version.endsWith('-dev')) { sign publishing.publications.mavenJava diff --git a/format_whitespace.py b/format_whitespace.py new file mode 100644 index 000000000..7e4844d72 --- /dev/null +++ b/format_whitespace.py @@ -0,0 +1,70 @@ +import re +import subprocess + + +UPSTREAM = 'https://github.com/Grasscutters/Grasscutter.git' +RATCHET = 'LintRatchet' +RATCHET_FALLBACK = '8e3deb9' + + +re_leading_whitespace = re.compile(r'^[ \t]+', re.MULTILINE) # Replace with \1.replace('\t', ' ') +re_trailing_whitespace = re.compile(r'[ \t]+$', re.MULTILINE) # Replace with '' +# Replace 'for (foo){bar' with 'for (foo) {bar' +re_bracket_space = re.compile(r'\) *\{(?!\})') # Replace with ') {' +# Replace 'for(foo)' with 'foo (bar)' +re_keyword_space = re.compile(r'(?<=\b)(if|for|while|switch|try|else|catch|finally|synchronized) *(?=[\(\{])') # Replace with '\1 ' + + +def get_changed_filelist(): + subprocess.run(['git', 'fetch', UPSTREAM, f'{RATCHET}:{RATCHET}']) # Ensure LintRatchet ref is matched to upstream + result = subprocess.run(['git', 'diff', RATCHET, '--name-only'], capture_output=True, text=True) + if result.returncode != 0: + print(f'{RATCHET} not found, trying fallback {RATCHET_FALLBACK}') + result = subprocess.run(['git', 'diff', RATCHET_FALLBACK, '--name-only'], capture_output=True, text=True) + if result.returncode != 0: + print('Fallback is also missing, aborting.') + exit(1) + return result.stdout.strip().split('\n') + + +def format_string(data: str): + data = re_leading_whitespace.sub(lambda m: m.group(0).replace('\t', ' '), data) + data = re_trailing_whitespace.sub('', data) + data = re_bracket_space.sub(') {', data) + data = re_keyword_space.sub(r'\1 ', data) + if not data.endswith('\n'): # Enforce trailing \n + data = data + '\n' + return data + + +def format_file(filename: str) -> bool: + try: + with open(filename, 'r') as file: + data = file.read() + data = format_string(data) + with open(filename, 'w') as file: + file.write(data) + return True + except FileNotFoundError: + print(f'File not found, probably deleted: {filename}') + return False + + +def main(): + filelist = [f for f in get_changed_filelist() if f.endswith('.java')] + replaced = 0 + not_found = 0 + if not filelist: + print('No changed files due for formatting!') + return + print('Changed files due for formatting: ', filelist) + for file in filelist: + if format_file(file): + replaced += 1 + else: + not_found += 1 + print(f'Format complete! {replaced} formatted, {not_found} missing.') + + +if __name__ == '__main__': + main()