Skip to content
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

Meson format #12318

Merged
merged 8 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions docs/markdown/Commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,82 @@ format should be used. There are currently 3 formats supported:
seems to be properly supported by vscode.

{{ devenv_arguments.inc }}


### format

*(since 1.5.0)*

{{ format_usage.inc }}

Format specified `meson.build` documents. For compatibility with `muon`, `fmt`
is an alias to `format`.

{{ format_arguments.inc }}

The configuration file is a `.ini` file. If a `meson.format` file exists
beside the provided build file to analyze, and no configuration file is
provided on the command line, the `meson.format` file is automatically used.

If no build file is provided on the command line, the `meson.build` file in
current directory is analyzed.

The following options are recognized:

- max_line_length (int): When an array, a dict, a function or a method
would be longer that this, it is formatted one argument per line
(Default is 80).
- indent_by (str): Indentation to use (Default is four spaces `' '`).
- space_array (bool): Whether to add spaces between `[]` and array
arguments (default is false).
- kwargs_force_multiline (bool): If true, arguments are formatted one per
line as soon as there is a keyword argument (default is false).
- wide_colon (bool): If true, a space is placed before colon in dict
and in keyword arguments (default is false).
- no_single_comma_function (bool): If true, a comma is never appended
to function arguments if there is only one argument, even if
using multiline arguments (default is false).
- end_of_line ('cr', 'lf', 'crlf', 'native'): Line ending to use
(applied when using `--output` or `--inline` argument) (default
is 'native).
- indent_before_comments (str): Indentation to use before inline comments
(default is two spaces `' '`).
- simplify_string_literals (bool): When true, multiline strings are
converted to single line strings if they don't contain newlines.
Formatted strings are converted to normal strings if they don't
contain substitutions (default is true).
- insert_final_newline (bool): If true, force the `meson.build` file
to end with a newline character (default is true).
- tab_width (int): Width of tab stops, used to compute line length
when `indent_by` uses tab characters (default is 4).
- sort_files (bool): When true, arguments of `files()` function are
sorted alphabetically (default is false).
- group_arg_value (bool): When true, string argument with `--` prefix
followed by string argument without `--` prefix are grouped on the
same line, in multiline arguments (default is false).
bruchar1 marked this conversation as resolved.
Show resolved Hide resolved
- use_editor_config (bool): When true, also uses config from .editorconfig .

The first six options are the same than for the `muon fmt` command.

It is also possible to use a `.editorconfig` file, by providing
the `--editor-config` option on the command line, or with the
`use_editor_config` option in the config file.

When `--recursive` option is specified, `meson.build` files from
`subdir` are also analyzed (must be used in conjunction with `--inplace`
or `--check-only` option).


#### Differences with `muon fmt`

The `meson format` command should be compatible with the `muon fmt` command.
However, it has more features, and some differences:

- By default, `meson format` put two spaces before inline comments,
while `muon fmt` only puts one.
- `muon fmt` can potentially mix crlf and lf end-of-lines, as it is not aware
of them. `meson format` will always be consistent in the output it produces.
- `muon fmt` only recognize the `indent_by` option from .editorconfig files.
`meson format` also recognizes `max_line_length`, `end_of_line`,
`insert_final_newline` and `tab_width` options.
- `meson format` has many additional format rules (see option list above).
4 changes: 4 additions & 0 deletions docs/markdown/snippets/meson_format_cmd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## New meson format command

This command is similar to `muon fmt` and allows to format a `meson.build`
document.
6 changes: 3 additions & 3 deletions mesonbuild/ast/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ def func_subdir(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[str
def method_call(self, node: BaseNode) -> bool:
return True

def evaluate_fstring(self, node: mparser.FormatStringNode) -> str:
assert isinstance(node, mparser.FormatStringNode)
def evaluate_fstring(self, node: mparser.StringNode) -> str:
assert isinstance(node, mparser.StringNode)
return node.value

def evaluate_arraystatement(self, cur: mparser.ArrayNode) -> TYPE_var:
Expand All @@ -231,7 +231,7 @@ def evaluate_ternary(self, node: TernaryNode) -> None:

def evaluate_dictstatement(self, node: mparser.DictNode) -> TYPE_nkwargs:
def resolve_key(node: mparser.BaseNode) -> str:
if isinstance(node, mparser.BaseStringNode):
if isinstance(node, mparser.StringNode):
return node.value
return '__AST_UNKNOWN__'
arguments, kwargs = self.reduce_arguments(node.args, key_resolver=resolve_key)
Expand Down
10 changes: 5 additions & 5 deletions mesonbuild/ast/introspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from ..compilers import detect_compiler_for
from ..interpreterbase import InvalidArguments, SubProject
from ..mesonlib import MachineChoice, OptionKey
from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, BaseStringNode
from ..mparser import BaseNode, ArithmeticNode, ArrayNode, ElementaryNode, IdNode, FunctionNode, StringNode
from .interpreter import AstInterpreter

if T.TYPE_CHECKING:
Expand Down Expand Up @@ -118,7 +118,7 @@ def func_project(self, node: BaseNode, args: T.List[TYPE_var], kwargs: T.Dict[st

if not self.is_subproject() and 'subproject_dir' in kwargs:
spdirname = kwargs['subproject_dir']
if isinstance(spdirname, BaseStringNode):
if isinstance(spdirname, StringNode):
assert isinstance(spdirname.value, str)
self.subproject_dir = spdirname.value
if not self.is_subproject():
Expand Down Expand Up @@ -165,7 +165,7 @@ def _add_languages(self, raw_langs: T.List[TYPE_var], required: bool, for_machin
for l in self.flatten_args(raw_langs):
if isinstance(l, str):
langs.append(l)
elif isinstance(l, BaseStringNode):
elif isinstance(l, StringNode):
langs.append(l.value)

for lang in sorted(langs, key=compilers.sort_clink):
Expand Down Expand Up @@ -254,7 +254,7 @@ def traverse_nodes(inqueue: T.List[BaseNode]) -> T.List[BaseNode]:
# Pop the first element if the function is a build target function
if isinstance(curr, FunctionNode) and curr.func_name.value in BUILD_TARGET_FUNCTIONS:
arg_nodes.pop(0)
elementary_nodes = [x for x in arg_nodes if isinstance(x, (str, BaseStringNode))]
elementary_nodes = [x for x in arg_nodes if isinstance(x, (str, StringNode))]
inqueue += [x for x in arg_nodes if isinstance(x, (FunctionNode, ArrayNode, IdNode, ArithmeticNode))]
if elementary_nodes:
res += [curr]
Expand Down Expand Up @@ -369,6 +369,6 @@ def extract_subproject_dir(self) -> T.Optional[str]:
assert isinstance(kw, IdNode), 'for mypy'
if kw.value == 'subproject_dir':
# mypy does not understand "and isinstance"
if isinstance(val, BaseStringNode):
if isinstance(val, StringNode):
return val.value
return None
41 changes: 25 additions & 16 deletions mesonbuild/ast/postprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# or an interpreter-based tool
from __future__ import annotations

from .visitor import AstVisitor
from .visitor import AstVisitor, FullAstVisitor
import typing as T

if T.TYPE_CHECKING:
Expand Down Expand Up @@ -78,32 +78,41 @@ def visit_default_func(self, node: mparser.BaseNode) -> None:
node.ast_id = name + '#' + str(self.counter[name])
self.counter[name] += 1

class AstConditionLevel(AstVisitor):
class AstConditionLevel(FullAstVisitor):
def __init__(self) -> None:
self.condition_level = 0

def visit_default_func(self, node: mparser.BaseNode) -> None:
def enter_node(self, node: mparser.BaseNode) -> None:
node.condition_level = self.condition_level

def visit_ForeachClauseNode(self, node: mparser.ForeachClauseNode) -> None:
self.visit_default_func(node)
self.condition_level += 1
self.enter_node(node)
node.foreach_.accept(self)
for varname in node.varnames:
varname.accept(self)
for comma in node.commas:
comma.accept(self)
node.colon.accept(self)
node.items.accept(self)
self.condition_level += 1
node.block.accept(self)
self.condition_level -= 1

def visit_IfClauseNode(self, node: mparser.IfClauseNode) -> None:
self.visit_default_func(node)
for i in node.ifs:
i.accept(self)
if node.elseblock:
self.condition_level += 1
node.elseblock.accept(self)
self.condition_level -= 1
node.endforeach.accept(self)
self.exit_node(node)

def visit_IfNode(self, node: mparser.IfNode) -> None:
self.visit_default_func(node)
self.condition_level += 1
self.enter_node(node)
node.if_.accept(self)
node.condition.accept(self)
self.condition_level += 1
node.block.accept(self)
self.condition_level -= 1
self.exit_node(node)

def visit_ElseNode(self, node: mparser.ElseNode) -> None:
self.enter_node(node)
node.else_.accept(self)
self.condition_level += 1
node.block.accept(self)
self.condition_level -= 1
self.exit_node(node)
Loading
Loading