Skip to content

Commit d89f89c

Browse files
committed
mformat: sort files() arguments naturally
1 parent e0ea239 commit d89f89c

File tree

7 files changed

+48
-16
lines changed

7 files changed

+48
-16
lines changed

docs/markdown/Commands.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,9 @@ The following options are recognized:
473473
- tab_width (int): Width of tab stops, used to compute line length
474474
when `indent_by` uses tab characters (default is 4).
475475
- sort_files (bool): When true, arguments of `files()` function are
476-
sorted alphabetically (default is true).
476+
sorted (default is true). *Since 1.10.0*, arguments are sorted
477+
[naturally](Style-guide.md#sorting-source-paths) rather than
478+
alphabetically.
477479
- group_arg_value (bool): When true, string argument with `--` prefix
478480
followed by string argument without `--` prefix are grouped on the
479481
same line, in multiline arguments (default is false).
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## `meson format` sorts `files()` arguments naturally
2+
3+
If the `sort_files` option is enabled, as it is by default, `meson format`
4+
now sorts `files()` arguments [naturally](Style-guide.md#sorting-source-paths)
5+
rather than alphabetically.

mesonbuild/mformat.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import sys
1313

1414
from . import mparser
15-
from .mesonlib import MesonException
15+
from .mesonlib import MesonException, pathname_sort_key
1616
from .ast.postprocess import AstConditionLevel
1717
from .ast.printer import RawPrinter
1818
from .ast.visitor import FullAstVisitor
@@ -301,13 +301,12 @@ def dedent(self, value: str) -> str:
301301
return value
302302

303303
def sort_arguments(self, node: mparser.ArgumentNode) -> None:
304-
# TODO: natsort
305304
def sort_key(arg: mparser.BaseNode) -> str:
306305
if isinstance(arg, mparser.StringNode):
307306
return arg.raw_value
308307
return getattr(node, 'value', '')
309308

310-
node.arguments.sort(key=sort_key)
309+
node.arguments.sort(key=lambda arg: pathname_sort_key(sort_key(arg)))
311310

312311
def visit_EmptyNode(self, node: mparser.EmptyNode) -> None:
313312
self.enter_node(node)
@@ -1064,5 +1063,5 @@ def run(options: argparse.Namespace) -> int:
10641063
# - Option to simplify string literals
10651064
# - Option to recognize and parse meson.build in subdirs
10661065
# - Correctly compute line length when using tabs
1067-
# - By default, arguments in files() are sorted alphabetically
1066+
# - By default, arguments in files() are sorted naturally
10681067
# - Option to group '--arg', 'value' on same line in multiline arguments

mesonbuild/rewriter.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from .ast.interpreter import IntrospectionBuildTarget, IntrospectionDependency, _symbol
1414
from .interpreterbase import UnknownValue, TV_func
1515
from .interpreterbase.helpers import flatten
16-
from mesonbuild.mesonlib import MesonException, setup_vsenv, relpath
16+
from mesonbuild.mesonlib import MesonException, pathname_sort_key, relpath, setup_vsenv
1717
from . import mlog, environment
1818
from functools import wraps
1919
from .mparser import Token, ArrayNode, ArgumentNode, ArithmeticNode, AssignmentNode, BaseNode, StringNode, BooleanNode, ElementaryNode, IdNode, FunctionNode, PlusAssignmentNode
@@ -951,15 +951,6 @@ def rel_source(src: str) -> str:
951951

952952
# Sort files
953953
for i in to_sort_nodes:
954-
def convert(text: str) -> T.Union[int, str]:
955-
return int(text) if text.isdigit() else text.lower()
956-
957-
def alphanum_key(key: str) -> T.List[T.Union[int, str]]:
958-
return [convert(c) for c in re.split('([0-9]+)', key)]
959-
960-
def path_sorter(key: str) -> T.List[T.Tuple[bool, T.List[T.Union[int, str]]]]:
961-
return [(key.count('/') <= idx, alphanum_key(x)) for idx, x in enumerate(key.split('/'))]
962-
963954
if isinstance(i, FunctionNode) and i.func_name.value in BUILD_TARGET_FUNCTIONS:
964955
src_args = i.args.arguments[1:]
965956
target_name = [i.args.arguments[0]]
@@ -968,7 +959,7 @@ def path_sorter(key: str) -> T.List[T.Tuple[bool, T.List[T.Union[int, str]]]]:
968959
target_name = []
969960
unknown: T.List[BaseNode] = [x for x in src_args if not isinstance(x, StringNode)]
970961
sources: T.List[StringNode] = [x for x in src_args if isinstance(x, StringNode)]
971-
sources = sorted(sources, key=lambda x: path_sorter(x.value))
962+
sources = sorted(sources, key=lambda x: pathname_sort_key(x.value))
972963
i.args.arguments = target_name + unknown + T.cast(T.List[BaseNode], sources)
973964

974965
def process(self, cmd: T.Dict[str, T.Any]) -> None:

mesonbuild/utils/universal.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class _VerPickleLoadable(Protocol):
138138
'listify_array_value',
139139
'partition',
140140
'path_is_in_root',
141+
'pathname_sort_key',
141142
'pickle_load',
142143
'Popen_safe',
143144
'Popen_safe_logged',
@@ -2494,3 +2495,17 @@ def __get__(self, instance: object, cls: T.Type) -> _T:
24942495
value = self.__func(instance)
24952496
setattr(instance, self.__name, value)
24962497
return value
2498+
2499+
2500+
def pathname_sort_key(key: str) -> tuple[tuple[bool, tuple[int | str, ...]], ...]:
2501+
'''Sort key for natural pathname sort, as defined in the Meson style guide.
2502+
Use as the key= argument to sort() or sorted().'''
2503+
2504+
def convert(text: str) -> int | str:
2505+
return int(text) if text.isdigit() else text.lower()
2506+
2507+
def alphanum_key(key: str) -> tuple[int | str, ...]:
2508+
return tuple(convert(c) for c in re.split('([0-9]+)', key))
2509+
2510+
return tuple((key.count('/') <= idx, alphanum_key(x))
2511+
for idx, x in enumerate(key.split('/')))

test cases/format/1 default/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ meson_files = {
88
'comments': files('crazy_comments.meson'),
99
'indentation': files('indentation.meson'),
1010
'gh13242': files('gh13242.meson'),
11+
'sort': files('sort_files.meson'),
1112
}
1213

1314
# Ensure empty function are formatted correctly on long lines
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Based on the example in the Meson style guide
2+
3+
sources = files(
4+
'aaa/a1.c',
5+
'aaa/A2.c',
6+
'aaa/a3.c',
7+
'bbb/subdir1/b1.c',
8+
'bbb/subdir2/b2.c',
9+
'bbb/subdir10/b3.c',
10+
'bbb/subdir20/b4.c',
11+
'bbb/b5.c',
12+
'bbb/b6.c',
13+
'c/a111.c',
14+
'c/ab0.c',
15+
'f1.c',
16+
'f2.c',
17+
'f10.c',
18+
'f20.c',
19+
)

0 commit comments

Comments
 (0)