Formatter
Civet should either gain a "formatter" mode, or its AST should become rich enough to support a separate formatter tool (probably in the same repo still, because AST changes need to be closely tied). Here are possible specific formatting rules we might aim for:
- [ ] Consistent indentation, configurable to be a specific number of spaces or tabs. This should be easy, especially once we add
StatementAST nodes. (Currently they are arrays[indent, exp, delim]at specific layers of the AST.) - [ ] No extra spacing, e.g.
x + 2→x + 2. Some of this can be done by processing_nodes. - [ ] Collapsing multiple blank lines as in Prettier
- [ ] Single vs. double vs. back quotes, as in Prettier
- [ ] Single vs. triple quoted strings
- [ ] Line length limits / line wrapping. This is easy to measure, but tricky to modify... See Prettier print width.
- [ ] No unnecessary parentheses, e.g.
((x)+y)→x+y. This might require us to actually parse binary expressions into proper ASTs, instead of the current flat representation. - [ ] Preferred call style:
f(x)vs.f x. Obviously can only use the latter when the parens are unnecessary. - [ ] Preferred "no implicit return" style:
:voidvs. trailing ~~comma~~ semicolon. - [ ] Preferred code blocks: braces vs. unbraced.
- [ ] Preferred
ifconditions: parenthesized vs. not. - [ ] Preferred object style: braced vs. unbraced. Probably preferring braced when using features not supported by unbraced.
- [ ] Preferred method syntax for objects (
{ f() {} }vs.f: ->) - [ ] Preferred multiline array style:
[...]vs. bullets (once that exists). - [ ] Preferred arrow style (
->vs.=>) for functions that don't usethis.thischecking would need to be recursive for nested=>but not nested-> - [ ] Preferred function declaration style (
function f()vs.f := ->) - [ ] Preferred declaration style (
const/letvs.:=/.=) - [ ] Preferred
importstyle (import ... fromvs.... fromvs.from ... import) - [ ] Preferred operator names:
andvs.&&,===vs.is,!==vs.is not/isnt - [ ] Preferred Unicode vs. ASCII operators
- [ ] Preferred conditional expressions:
?:vs.if - [ ] Preferred
lengthvs.# - [ ] Preferred JSX style: closing tags or not, remove
<>...</>when implicit, remove extra{}s, preferred code block syntax - [ ] Preferred Civet compile flags. This might be a way to offer (limited) automatic changes to compiler flags. For example, adding or removing
"civet coffeeForLoops"is fairly challenging but plausibly automatable.
Detecting issues should be doable with the AST, possibly once it's fleshed out more. (Ideally we can do this piece by piece.) There are two possible ways to do the emit process:
- Keep track of the entire Civet source in the AST somehow, in particular no longer throwing away compiled-away tokens like
then. We've discussed this for other purposes too (#40?). Maybe something parallel totokenlikesource... The trouble with this is that it requires a lot of work before we can get anything working. - Use source maps to translate all edits of surviving tokens back to modifications to the Civet code. So e.g. when converting
f(x)tof x, we find the location of(and replace it with, and find the location of)and remove it. These edits can be applied to the original source code all at once at the end, and then the file can be re-emitted. (Somewhat similar to LSP auto completions.) This is more annoying to work with, and will still require adding some Civet-only tokens that don't normally get emitted, but it's easier to get started with.
By contrast, I don't think Civet should gain a full "lint" mode that tests for bugs. See Prettier on formatting vs. code-quality rules. I think ideally the output of Civet would be compatible with most eslint code-quality rules, so we can use eslint directly for those. But formatting is fundamentally tied to the Civet source, not the generated JS/TS.
This post distills Discord discussion with @bbrk24.
Preferred "no implicit return" style: :void vs. trailing comma.
Comma? Not semicolon?
A couple other rules I thought of:
- Preferred arrow style (
->vs=>) for functions that don't usethis.thischecking would need to be recursive for nested=>but not nested-> - Preferred function declaration style (
function f()vsf := ->) - Preferred method syntax for objects (
{ f() {} }vsf: ->)
Are you conceiving of the rule list posted here as a list of options that the user of the formatter can go either way on, or are you seeking a One True Civet Format in which the formatter would embody one specific choice on each option (i.e., be "opinionated" in the sense of Prettier)? I ask because in one project I worked on, we all hated one or two of Prettier's choices enough that we invested a fair amount of time in tooling that ran Prettier and then eslint -fix to undo what we saw as a couple bad choices by the creators of Prettier. That was a pain.
Some of these would have to be configurable (maximum line length and :void vs ; for example). Ideally, it would be possible to turn all of these off individually, just like with ESLint.
One True Civet Format
Definitely not that. There are so many ways to do things in Civet, and different people have different preferences on what they want to use. The goal is to make these choices fully configurable, to the extent possible (unlike Prettier). We will try to choose reasonable defaults, though. Of course, it's all speculation at the moment. :-)
Civet should either gain a "formatter" mode, or its AST should become rich enough to support a separate formatter tool
Preferably the latter, and with ESLint support (https://github.com/DanielXMoore/Civet/discussions/852) ideally work with eslint --fix.
See also ESLint Stylistic, and https://antfu.me/posts/why-not-prettier.
I doubt we'd be able to make an AST that's eslint compatible, given how different Civet is from JS. This is the approach taken by CoffeeScript, and in my experience, it's rather fragile (only works on old eslint versions), so I'd hesitate to even try here.
For this reason, I think we need to do formatting ourselves from scratch. We're planning to make it highly configurable, taking best ideas from eslint and Prettier.
On the other hand, we'd like to use existing eslint rules for semantic checks, because that could hopefully be done just on the transformed JS. Hence why this issue is about formatting only. (Of course, it's a blurry line, so maybe one day our formatter does semantic checks too. But this is all hypothetical at the moment.)
Are there any new developments in this discussion. When I try to use Civet in some projects I migrate a lot of code from the original TS. The lack of a formatting tool makes this process extremely difficult. I spend a lot of time tweaking formatting errors to fit Civet.
No, no progress yet. (If there were, it would be reported here.)
If you're converting from TS, you might consider running Prettier first, and/or check out ts2civet.