Designing configuration for coala's aspect
This post is part of my GSOC journey in coala.
With the emerge of aspect project in coala, come the need to redefine how configuration works and define various new parameter that should control how the aspect should run.
Currently, the mockup for those configuration could be found on cEP-0005 and look like this
1[all]
2# Values can be added to inherited ones only, not within the section because
3# a configuration should describe a state and not involve operations.
4# A system wide coafile can define venv, .git, ... and we would recommend to
5# always use += for ignore even in the default section to inherit those values.
6# += always concatenates with a comma.
7ignore += ./vendor
8
9max_line_length = 80
10
11# This inherits all settings from the `all` section. There is no implicit
12# inheritance.
13[all.python]
14language = Python
15files = **.py
16aspects = smell, redundancy.clone # Inclusive
17
18[all.c]
19language = C
20files = **.(c|h)
21ignore_aspects = redundancy # Exclusive
This mockup provide basic example on how aspect configuration could work. Its key feature is:
- New parameter
aspect
andignore_aspects
to define list of aspect that coala should analyze. - New parameter
language
to help coala choose the right bear that could analyze a specific language. - A taste could defined by writing its name directly (
max_line_length = 80
). - Specifying
bears
manually is still possible. - Explicit section inheritance (
[parent.child]
).
Again, this configuration is still a mockup. I think, there are still a few way to further enhance this configuration.
Default behaviour
One important aspect of coala and its usability is that the configuration of a new language is as easy as possible.
IMO, usability is a very crucial factor on making coala more widely used in open source project. People like to explore new tools, but if configuring these tools take too much time or too complex, they tend to pass it and get the next tools on the list. coala must shallow its learning curve.
In that regard, I want to make the configuration powerfull and compact, but still provide option to tweak it in detail. It’s should be possible for people to run coala with the most minimal line of configuration, or even with no explicit configuration nor initial setup beforehand.
For coala to analyze aspect properly, we need to know:
- What aspect is choosen
- Which files should analyzed
- What languages it is written in, and
- (optional) Defining aspect’s taste, how a correct aspect should look like
This could be detailed further…
Defining aspect
An aspect could be written by its fully qualified name (Root.Some.MyAspect
) or
partially qualified name (MyAspect
or Some.MyAspect
). Defining a parent node
mean all of its children node will be included too.
Defining taste could be done with writing the taste name as the parameter name
and assign a value to it. In case of multiple aspects have the same taste name,
ambiguity must be resolved by prefixing the taste name with its aspect name.
For example, max_length
is a taste of Shortlog
and LineLength
. Thus the
Shortlog
taste would be defined like Shortlog.max_length = 50
.
Defining language
While defining language, we could use coalang (could be found in module
coalib.bearlib.languages.definitions
) to provide flexibility on writting the
language name. For example, C++
could also written by CPP
, CXX
, or even
CPlusPlus
and they will still refer to the same progamming language.
Other advantadge is in the case of language that have multiple version (like
python2 vs python3), coalang could provide a default version number or users
could define it themself with keyword version
. Note that defining version for
language is not mandatory. Its neccessary in case of project that should be
analyzed under specific version, like python3 project that should analyzed
by python3 only bear.
Defining files
IMO explicitly defining file in section is not always necessary because we can
get list of file extension from coalang. By defining language = CPP
, then it
also mean implicitly files = **.c, **.cpp, **.h, **.hpp
. Of course this could
be overwritten by explicitly define files
parameter in configuration.
Backward compatibility for choosing bear by its name
There is some unresolved problem on defining bear. The problem is gathering the option configuration for those bear. In aspect based configuration, bear option will collected through taste, but in old configuration, option is given as a kind of “global” parameter in section. It will be a enormous task to write a mapping function from old parameter and its taste pair.
For the time being, I think the most easy approach is to make conditionally collection on each bear with something like this:
1class LineLengthBear(LocalBear, aspects=[
2 'detect': LineLength
3]):
4 def run(self, filename, file, aspects, min_length):
5 if LineLength in aspects:
6 # Overwrite with the aspect's taste if it exist
7 min_length = aspects.get(LineLength).min_length
8
9 # do something with min_length
Conclusion
To write a aspect configuration, we at least need to know what aspect we want to analyze and what language it’s written in. We can configure it further by defining list of files and list of option (aspect’s taste) for each aspect.
This is my first time trying to write a configuration format and it will be far from perferct or have some unforseen edge case. Hopefully, all the bugs and weird edge cases could be tackled in the future while the the coding is in progress.