-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for ktfmt #196
Changes from 5 commits
932f9b7
106dcde
4bdaf67
3095389
d1bf17d
43ef761
889525a
9c2d33a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
0.46 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,7 @@ | |
from language_formatters_pre_commit_hooks.utils import run_command | ||
|
||
|
||
def _download_kotlin_formatter_jar(version: str) -> str: # pragma: no cover | ||
def _download_ktlint_formatter_jar(version: str) -> str: # pragma: no cover | ||
def get_url(_version: str) -> str: | ||
# Links extracted from https://github.com/pinterest/ktlint/ | ||
return "https://github.com/pinterest/ktlint/releases/download/{version}/ktlint".format( | ||
|
@@ -30,6 +30,18 @@ def get_url(_version: str) -> str: | |
) | ||
|
||
|
||
def _download_ktfmt_formatter_jar(version: str) -> str: # pragma: no cover | ||
url ="https://repo1.maven.org/maven2/com/facebook/ktfmt/{version}/ktfmt-{version}-jar-with-dependencies.jar".format( | ||
version=version, | ||
) | ||
try: | ||
return download_url(url) | ||
except: # noqa: E722 (allow usage of bare 'except') | ||
raise RuntimeError( | ||
f"Failed to download {url}. Probably the requested version, {version}" | ||
", is not valid or you have some network issue." | ||
) | ||
|
||
def _fix_paths(paths: typing.Iterable[str]) -> typing.Iterable[str]: | ||
# Starting from KTLint 0.41.0 paths cannot contain backward slashes as path separator | ||
# Odd enough the error messages reported by KTLint contain `\` :( | ||
|
@@ -52,14 +64,55 @@ def pretty_format_kotlin(argv: typing.Optional[typing.List[str]] = None) -> int: | |
default=_get_default_version("ktlint"), | ||
help="KTLint version to use (default %(default)s)", | ||
) | ||
|
||
parser.add_argument( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to think a little bit about the arguments, the PR adds 2 arguments that would only be honoured if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. I guess what's important is that autofix works in the same way for both ktlint and ktfmt. I'm not sure we can do much about therest. |
||
"--ktfmt-version", | ||
dest="kftmt_version", | ||
default=_get_default_version("ktfmt"), | ||
help="ktfmt version to use (default %(default)s)", | ||
) | ||
parser.add_argument( | ||
"--ktfmt", | ||
action="store_true", | ||
dest="ktfmt", | ||
help="Use ktfmt", | ||
) | ||
parser.add_argument( | ||
"--ktfmt-style", | ||
choices=['dropbox', 'google', 'kotlinlang'], | ||
dest="ktfmt_style", | ||
help="Which style to use", | ||
) | ||
parser.add_argument("filenames", nargs="*", help="Filenames to fix") | ||
args = parser.parse_args(argv) | ||
|
||
ktlint_jar = _download_kotlin_formatter_jar( | ||
args.ktlint_version, | ||
print(args) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please remove the print of the arguments (possibly a manual testing leftover) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed |
||
if args.ktfmt: | ||
return run_ktfmt(args.kftmt_version, args.filenames, args.ktfmt_style, args.autofix) | ||
else: | ||
return run_ktlint(args.ktlint_version,args.filenames, args.autofix) | ||
|
||
|
||
def run_ktfmt(ktfmt_version: str, filenames: typing.Iterable[str], ktfmt_style: typing.Optional[str], autofix: bool) -> int: | ||
jar = _download_ktfmt_formatter_jar(ktfmt_version) | ||
ktfmt_args = ["--set-exit-if-changed"] | ||
if ktfmt_style is not None: | ||
ktfmt_args.append(f"--{ktfmt_style}-style") | ||
if not autofix: | ||
ktfmt_args.append("--dry-run") | ||
filenames = filenames if filenames else ["./"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pre-commit hooks will always pass the filenames. Furthermore, an empty iterable (depending on the actual type) might or might now be evaluated as
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed. |
||
return_code, _, _ = run_command( | ||
"java", | ||
"-jar", | ||
jar, | ||
"ktfmt", | ||
*ktfmt_args, | ||
*filenames, | ||
) | ||
return return_code | ||
|
||
|
||
def run_ktlint(ktlint_version: str, filenames:typing.Iterable[str], autofix: bool): | ||
|
||
ktlint_jar = _download_ktlint_formatter_jar(ktlint_version) | ||
jvm_args = ["--add-opens", "java.base/java.lang=ALL-UNNAMED"] | ||
|
||
# ktlint does not return exit-code!=0 if we're formatting them. | ||
|
@@ -79,14 +132,14 @@ def pretty_format_kotlin(argv: typing.Optional[typing.List[str]] = None) -> int: | |
"--reporter=json", | ||
"--relative", | ||
"--", | ||
*_fix_paths(args.filenames), | ||
*_fix_paths(filenames), | ||
) | ||
|
||
not_pretty_formatted_files: typing.Set[str] = set() | ||
if return_code != 0: | ||
not_pretty_formatted_files.update(item["file"] for item in json.loads(stdout)) | ||
|
||
if args.autofix: | ||
if autofix: | ||
print("Running ktlint format on {}".format(not_pretty_formatted_files)) | ||
run_command( | ||
"java", | ||
|
@@ -106,7 +159,7 @@ def pretty_format_kotlin(argv: typing.Optional[typing.List[str]] = None) -> int: | |
status = 1 | ||
print( | ||
"{}: {}".format( | ||
"The following files have been fixed by ktlint" if args.autofix else "The following files are not properly formatted", | ||
"The following files have been fixed by ktlint" if autofix else "The following files are not properly formatted", | ||
", ".join(sorted(not_pretty_formatted_files)), | ||
), | ||
) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ | |
def run_command(*command: str) -> typing.Tuple[int, str, str]: | ||
print( | ||
"[cwd={cwd}] Run command: {command}".format( | ||
command=command, | ||
command=' '.join(command), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as this Still I would suggest not to apply this change because assuming that the input filenames have spaces then "copy-pasting" the command would not work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense, I've rolled back. |
||
cwd=os.getcwd(), | ||
), | ||
file=sys.stderr, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fun main(args: Array<String>) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for adding the end-to-end tests and examples. I'm wondering if it would be better to have the ktfmt fixtures in a separate directories.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not too sure about this one, since we share some files between the two tests. Also I'd have to change some of the helper code to look in different directory based on the formatter. I'm happy to do it if you really insist. |
||
println("Hello, world!") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fun main(args: Array<String>) { | ||
println("Hello, world!") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
fun main(args: Array<String>) { | ||
println("Hello, world!") | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -46,6 +46,7 @@ def run_autofix_test( | |||||
method: typing.Callable[[typing.List[str]], int], | ||||||
not_pretty_formatted_path: str, | ||||||
formatted_path: str, | ||||||
extra_parameters: list[str], | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding this parameter is changing the function signature and effectively breaking tests.
Suggested change
and while using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed. |
||||||
) -> None: | ||||||
tmpdir.mkdir("src") | ||||||
not_pretty_formatted_tmp_path = tmpdir.join("src").join(basename(not_pretty_formatted_path)) | ||||||
|
@@ -55,14 +56,14 @@ def run_autofix_test( | |||||
|
||||||
copyfile(not_pretty_formatted_path, not_pretty_formatted_tmp_path) | ||||||
with change_dir_context(tmpdir.strpath): | ||||||
parameters = ["--autofix", not_pretty_formatted_tmp_strpath] | ||||||
parameters = extra_parameters + [not_pretty_formatted_tmp_strpath] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test is specific to run
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
status_code = method(parameters) | ||||||
if status_code != 1: | ||||||
raise UnexpectedStatusCode(parameters=parameters, expected_status_code=1, actual_status_code=status_code) | ||||||
|
||||||
# file was formatted (shouldn't trigger linter again) | ||||||
with change_dir_context(tmpdir.strpath): | ||||||
parameters = ["--autofix", not_pretty_formatted_tmp_strpath] | ||||||
parameters = extra_parameters + [not_pretty_formatted_tmp_strpath] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as above
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
status_code = method(parameters) | ||||||
if status_code != 0: | ||||||
raise UnexpectedStatusCode(parameters=parameters, expected_status_code=0, actual_status_code=status_code) | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,8 @@ | |
import pytest | ||
|
||
from language_formatters_pre_commit_hooks import _get_default_version | ||
from language_formatters_pre_commit_hooks.pretty_format_kotlin import _download_kotlin_formatter_jar | ||
from language_formatters_pre_commit_hooks.pretty_format_kotlin import \ | ||
_download_ktlint_formatter_jar, _download_ktfmt_formatter_jar | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most likely you want to run pre-commit hooks on your PR. You would have test failure otherwise. |
||
from language_formatters_pre_commit_hooks.pretty_format_kotlin import pretty_format_kotlin | ||
from tests import change_dir_context | ||
from tests import run_autofix_test | ||
|
@@ -45,8 +46,21 @@ def undecorate_method(): | |
) | ||
@pytest.mark.integration | ||
def test__download_kotlin_formatter_jar(ensure_download_possible, version): # noqa: F811 | ||
_download_kotlin_formatter_jar(version) | ||
_download_ktlint_formatter_jar(version) | ||
|
||
@pytest.mark.parametrize( | ||
"version", | ||
sorted( | ||
{ | ||
_get_default_version("ktfmt"), | ||
"0.45", | ||
"0.46", | ||
} | ||
), | ||
) | ||
@pytest.mark.integration | ||
def test__download_kotlin_formatter_jar(ensure_download_possible, version): # noqa: F811 | ||
_download_ktfmt_formatter_jar(version) | ||
|
||
@pytest.mark.parametrize( | ||
("filename", "expected_retval"), | ||
|
@@ -57,9 +71,56 @@ def test__download_kotlin_formatter_jar(ensure_download_possible, version): # n | |
("NotPrettyFormattedFixed.kt", 0), | ||
), | ||
) | ||
def test_pretty_format_kotlin(undecorate_method, filename, expected_retval): | ||
def test_pretty_format_kotlin_ktlint(undecorate_method, filename, expected_retval): | ||
assert undecorate_method([filename]) == expected_retval | ||
|
||
|
||
def test_pretty_format_kotlin_autofix(tmpdir, undecorate_method): | ||
run_autofix_test(tmpdir, undecorate_method, "NotPrettyFormatted.kt", "NotPrettyFormattedFixed.kt") | ||
@pytest.mark.parametrize( | ||
("filename", "expected_retval", "extra_parameters"), | ||
( | ||
("Invalid.kt", 1, []), | ||
("PrettyFormatted.kt", 0, []), | ||
("NotPrettyFormatted.kt", 1, []), | ||
("NotPrettyFormattedFixedKtlint.kt", 0, []), | ||
# ktfmt dropbox style (same as kotlinlang in this example) | ||
("NotPrettyFormattedFixedKtfmtDropbox.kt", 1, ["--ktfmt"]), | ||
("NotPrettyFormattedFixedKtfmtDropbox.kt", 0, ["--ktfmt", "--ktfmt-style=dropbox"]), | ||
("NotPrettyFormattedFixedKtfmtDropbox.kt", 1, ["--ktfmt", "--ktfmt-style=google"]), | ||
("NotPrettyFormattedFixedKtfmtDropbox.kt", 0, ["--ktfmt", "--ktfmt-style=kotlinlang"]), | ||
# ktfmt google style | ||
("NotPrettyFormattedFixedKtfmtGoogle.kt", 0, ["--ktfmt"]), | ||
("NotPrettyFormattedFixedKtfmtGoogle.kt", 1, ["--ktfmt", "--ktfmt-style=dropbox"]), | ||
("NotPrettyFormattedFixedKtfmtGoogle.kt", 0, ["--ktfmt", "--ktfmt-style=google"]), | ||
("NotPrettyFormattedFixedKtfmtGoogle.kt", 1, ["--ktfmt", "--ktfmt-style=kotlinlang"]), | ||
# ktfmt kotlinlang style (same as dropbox in this example) | ||
("NotPrettyFormattedFixedKtfmtKotlinlang.kt", 1, ["--ktfmt"]), | ||
("NotPrettyFormattedFixedKtfmtKotlinlang.kt", 0, ["--ktfmt", "--ktfmt-style=dropbox"]), | ||
("NotPrettyFormattedFixedKtfmtKotlinlang.kt", 1, ["--ktfmt", "--ktfmt-style=google"]), | ||
("NotPrettyFormattedFixedKtfmtKotlinlang.kt", 0, ["--ktfmt", "--ktfmt-style=kotlinlang"]), | ||
# ktfmt other files | ||
("NotPrettyFormatted.kt", 1, ["--ktfmt"]), | ||
("Invalid.kt", 1, ["--ktfmt"]), | ||
) | ||
) | ||
def test_pretty_format_kotlin(undecorate_method, filename, expected_retval, extra_parameters): | ||
assert undecorate_method(extra_parameters + [filename]) == expected_retval | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be worth splitting this test in
Tests are going to be somewhat easier to handle and more importantly you won't need to pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, I agree this was a mouthful |
||
|
||
|
||
@pytest.mark.parametrize( | ||
("filename", "fixed_filename", "extra_parameters"), | ||
( | ||
("NotPrettyFormatted.kt", "NotPrettyFormattedFixedKtlint.kt", ["--autofix"]), | ||
("NotPrettyFormatted.kt", "NotPrettyFormattedFixedKtfmtGoogle.kt", ["--autofix", "--ktfmt"]), | ||
("NotPrettyFormatted.kt", "NotPrettyFormattedFixedKtfmtGoogle.kt", ["--autofix", "--ktfmt", "--ktfmt-style=google"]), | ||
("NotPrettyFormatted.kt", "NotPrettyFormattedFixedKtfmtDropbox.kt", ["--autofix", "--ktfmt", "--ktfmt-style=dropbox"]), | ||
("NotPrettyFormatted.kt", "NotPrettyFormattedFixedKtfmtKotlinlang.kt", ["--autofix", "--ktfmt", "--ktfmt-style=kotlinlang"]), | ||
), | ||
) | ||
def test_pretty_format_kotlin_autofix(tmpdir, undecorate_method, filename, fixed_filename, extra_parameters): | ||
run_autofix_test( | ||
tmpdir, | ||
undecorate_method, | ||
filename, | ||
fixed_filename, | ||
extra_parameters | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to have no strong opinions on the tools offered in the documentation.
The main suggestions are:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed, I've just put the available format instead (google/dropbox/kotlinlang).