Subject: bug#75154: 31.0.50; java-ts-mode. Issues with Indentation

From: Yuan Fu <[email-masked]>
Date: Sun, 16 Mar 2025 22:47:48 -0700

> On Mar 9, 2025, at 1:54 AM, Eli Zaretskii <[email-masked]> wrote:
> 
> Ping! Yuan, could you please respond, so we could make progress with
> these issues?

Yeah, to move this forward, I applied the patch I submitted and made some more changes suggested by Artem. Now everything in this report should be handled. And honestly I don’t want to put more work in java-ts-mode anymore. I don’t know java very well and there’s a million other tree-sitter things I need to do.

>> From: Artem Bliznetsov <[email-masked]>
>> Cc: Theodor Thornhill <[email-masked]>, [email-masked], Eli
>> Zaretskii <[email-masked]>, Stefan Kangas <[email-masked]>
>> Date: Sun, 02 Mar 2025 01:34:05 +0300
>> 
>>> Ok, I attached a patch-set, if you apply this patch-set on top of _latest master_, most of the fixable problems in this report should be fixed/improved. Now, let me reply each item:
>> 
>> Thank you for your patch. I have tested your changes.
>> During compilation, I encountered the following warning:
>> 
>> In java-ts-mode--first-line-on-multi-line-string:
>> java-ts-mode.el:105:68: Warning: Unused lexical argument ‘bol’
>> 
>> However, everything seems to be working fine. 
>> 
>>> 1. In the patch I added java-ts-mode-method-chaining-indent-offset,
>>>   defaults to 8
>> 
>> Thank you! That’s great. Most users accustomed to 8-space indentation
>> will find the default setting comfortable. Those who prefer
>> 4 spaces are also taken into account.
>> 
>>> 2. Set electric-indent-chars to nil so it doesn’t indent the current
>>>   line when you press RET.
>> 
>> Actually, setting electric-indent-chars to nil disables all automatic
>> indentation. Now, I’m reconsidering whether the initial example I
>> provided was perhaps misguided and beyond what should be expected.
>> 
>> 2) Inner Classes
>> Example 1:
>> public class Outer {
>> class Inner {| <-- cursor here moves Inner class unexpectedly
>> 
>>    }
>> }
>> Example 2:
>> public class Outer {
>>    class Inner { // ???
>>        | <-- cursor here.           
>>    }
>> }
>> 
>> Again, I compared this behavior with IntelliJ IDEA and
>> wondered why it shouldn't work the same way here. Essentially,
>> what are we violating if we write
>> 
>> public class Outer {
>> class Inner {}
>> }
>> 
>> There are no syntax errors, but class Inner is not indented.
>> Yes, the indentation appears when pressing RET after {, but if we
>> then remove the spaces before class Inner and press RET again
>> after {, electric-indent corrects the indentation.

On my machine this code snippet is indented correctly:

public class Outer {
    class Inner {}
}

Might have to do with different tree-sitter grammar versions. But let’s focus on the bigger issues and move forward at the moment.

>> On one hand, this seems reasonable. On the other hand, shouldn’t this
>> be handled by an external formatter? This is a minor issue and may
>> not require any changes.

You can definitely use an external formatter. If you really just want indent behavior of other editors, try stupid-indent-mode (you can install it from MELPA I think).

>> 
>>> 3. The indentation is fixed once you type a valid statement, this is
>>>   because tree-sitter needs something to generate a parse-tree. We
>>>   can add some heuristics that gives a more intuitive indentation
>>>   when the line is empty, eg, "if prev line is while and current line
>>>   is empty, indent one level", etc. But that’s a more complicated
>>>   feature and I’ll defer to Theo.
>> 
>>> 
>>> 4.
>>> - Triple quote support in electric-pair-mode -> let’s open a separate
>>>  bug report for it and have electric-pair-mode maintainer take a
>>>  look.
>> 
>> Should I classify this as a bug report? I didn’t notice any feature
>> request category in the bug tracker. This is a small improvement that
>> would add some convenience. Triple-quoted text blocks are not
>> exclusive to Java; many other languages use them as well.

Sure, I don’t think the category matter too much.

>> 
>>> - In general, TAB in Emacs prog modes indent to a fixed point, rather
>>>  than just inserting a tab.
>> 
>> I wasn’t aware of how this works. I briefly tested python-ts-mode and
>> noticed that TAB behaves more freely there.

Because python’s semantics depends on indentation, so it’s impossible to know what’s the correct indentation. Anyway, as I said, try stupid-indent-mode.

>> 
>>> I added a indent rule such that aligns a
>>>  line in a string block to the previous line, for the first line, it indents one level.
>>> 
>> 
>>> 6. For the parameter indentation, I recently added
>>>   c-ts-common-baseline-indent-rules that can handle it correctly. So
>>>   if we remove the existing indent rule for the parameters and add
>>>   c-ts-common-baseline-indent-rules at the end so the indentation
>>>   falls back to it, the indentation would look like this:
>>> 
>>> public class TextBlocks {
>>>    public record StudentRecord(String firstName,
>>>                                String lastName,
>>>                                Long studentId,
>>>                                String email) {
>>> 
>>>    }
>>> 
>>> 
>>>    public String filterData(@RequestParam(required = false) String name,
>>>                             @RequestParam(required = false) String name,
>>>                             @RequestParam(required = false) Integer age
>>>    )
>>> }
>>> 
>>> Seems fair? Theo, WDYT?
>> 
>> I tested it, and it works. But do you see how it behaves?
>> I’m not sure how to describe it correctly, but it feels a bit odd.
>> If issue #3 gets resolved, everything should look much better.

Stupid-indent-mode should solve your problem. So I’ll leave it as-is for now.

>> 
>>>> - Annotations (@Annotations)
>>> It seems to work fine? What’s the problem that you see?
>> 
>> That was my mistake. I didn’t check which face was being used—or
>> whether there was one at all. By default, java-mode uses
>> c-annotation-face,while java-ts-mode uses font-lock-constant-face.
>> One inherits from the other. I use the Modus theme, so something may
>> have changed there.I only noticed it because, without any additional
>> configuration, java-mode highlighted annotations by default.
>> 
>> Anyway, it’s not that important now.
>> 
>>>> - Diamond Brackets (<>)
>>> Same as above, what’s the desired behavior?
>> 
>> I just tested it with emacs -q and didn’t see any specific face for <>
>> in java-mode. I use the popular package rainbow-delimiters.el,
>> which does highlight <> in java-mode, but it hasn’t been updated in
>> a while and doesn’t work with java-ts-mode.Currently, java-ts-mode
>> applies font-lock-operator-face to <>, =, ->, &&, and possibly other
>> symbols.That doesn’t seem quite right—should all operators really be
>> highlighted this way? Some users might prefer extensive syntax
>> highlighting, but it feels excessive to me.

What’s your treesit-font-lock-level? It’s 3 by default, and you shouldn’t get highlighting on operators unless the level is set to 4.

>> For reference, IntelliJ IDEA also doesn’t highlight <> by default.
>> It requires a third-party plugin for that. So I’m not sure whether
>> this should be added or not. If it is, that would be a nice improvement.

>> 
>>>> - Constants, Static Variables, Enum Variables should be highlighted with
>>>> distinct colors and optionally italic font
>>> 
>>> For constants, they aren’t highlighted in constant face because rules
>>> for ‘definition’ and ‘expression’ feature overrides them with
>>> variable-name face. We can fix this by either moving the ‘constant’
>>> face after ‘definition’ and ‘expression’ feature, or remove the
>>> :override flag for ‘definition’ and ‘expression’ feature. Theo, any
>>> suggestions here?
>> 
>> That would be great to implement! I’m not sure how to show you exactly
>> how it "should" look. Is it possible to attach images here?

Yes, just attach it to the email. And I moved the font-lock rule for constant below definition and expression. Now constants should be fontified correctly.

>> Although, Theo might have IntelliJ IDEA CE installed, so he likely
>> already knows how it can look visually appealing.
>> 
>>>> - Unused Variables or Classes (Grayed Out)
>>>> - Unused variables, unused classes, etc., highlighted in gray. Not
>>>    sure if this can be achieved
>>> 
>>> Tree-sitter can’t do this. So the only option is to use eglot for it.
>> 
>> Thanks for the clarification! I’ll try to look into this.
>> 
>> ----
>> By the way, I discovered something else today. In
>> java-ts-mode--keywords, the following keywords are missing:
>> boolean, byte, char, const, double, float,
>> goto, int, long, short, super, this, void, permits, var, when, yield, _.
>> According to the Java Language Specification
>> https://docs.oracle.com/javase/specs/jls/se23/html/jls-3.html#jls-3.9,
>> the keyword @interface should be removed since annotations are already
>> handled separately, and interface is already in the list.

I added the non-type keywords in. Types like boolean and byte are fontified as type, so they don’t need to be in the list. And this and super are handled differently. I removed @interface.

>>> Finally, some suggestions on communication. As you said on reddit,
>>> you’re not a native English speaker, so I can’t blame you, but some
>>> minor changes to wording can make your message sound a lot kinder :)
>>> For example, short and imperative sentences like "you understand?"
>>> sounds harsh and condescending; OTOH something like "I hope you can
>>> understand/get that..." is a lot better. As a rule of thumb, the less
>>> certain and the longer your expression is, the softer it sounds ;-)
>>> 
>>> Yuan
>> 
>> Yes, it is true that English is not my native language. However, upon
>> reviewing my messages,I understand that in certain instances, I was not
>> sufficiently polite. I will make an effort to improve this.
>> I regret that this occurred.

Thanks for you consideration :-)