Subject: bug#23461: perl-mode: Displaying HERE-docs as strings instead of comments [PATCH]

From: Stefan Monnier <[email-masked]>
Date: Wed, 23 Dec 2020 14:04:27 -0500

>> Indeed, terminating the comment just before the newline is a problem if
>> "just before the newline" is the comment starter.  I see that in that
>> case, you mark the char before the # but that can also be a problem with
>> things like:
>>
>>     foo <<'BAR' "baz"#
>
> ...or, as I've discovered in the meantime, also bad,
>
>       foo << BAR#
>
>       foo << BAR;#
>
> (The latter breaks indentation after the HERE-doc because the ";"
> became a comment)
>
> So, back to the drawing board.  If that edge case of an "empty comment"
> is just ignored, then the single # loses its comment-face (that is, by
> the way, the treatment of HERE-docs in sh-script.el), which is ugly but
> as far as I can tell harmless.  Maybe this can be covered by assigning a
> font-lock-face property to these single comment starters... I'll check
> that.

I suspect that shifting the "here-doc start" to the next line (in the
"#\n" case only) and placing a `syntax-multiline` property on all three
chars (#, \n, and the first char of the next line) will be our least
bad option.

> This points to the deeper problem: In Perl, we have some occasions where
> one character has two different roles.  A line end ends a comment *and*
> starts a here-doc, and in s/foo/bar/ge the middle slash ends the search
> string *and* starts the replacement string.  The programming modes seem
> to treat this by distributing the roles on two neighbouring characters,
> which comes with some ... inaccuracies if the characters nearby have
> roles of their own.

Yes, this doesn't occur very often, but when it does there's no
really satisfactory solution currently.  I also encountered such
situations in a few other modes (sorry, can't remember where, offhand).
I've often thought about trying to add some way to "cram several syntax
elements on a single character" or have some way to place an arbitrary
"state change function" on a character which would take an PPSS and
return a new one).  But then I end up concluding that a completely new
system would be preferable (e.g. one based on a DFA so that we don't
need ad-hoc "multi-char comment markers" and so that fewer cases need
to resort to text-property crutches).

>>>  (defun perl-font-lock-syntactic-face-function (state)
>>>    (cond
>>> +   ((and (eq 2 (nth 7 state)) ; c-style comment
>>> +         (cdr-safe (get-text-property (nth 8 state) 'syntax-table))) ; HERE doc
>>> +    'font-lock-string-face)
>>
>> I think some people won't like the string-face property for it.
>> How 'bout we (require 'sh-script) and use the `sh-heredoc` face?
>
> I'd hesitate to do that.  I think that in shell-script mode it is well
> justified to use a different face for HERE-docs, but in Perl it isn't.
> In Perl, a HERE-doc is just a string and can be used wherever a string
> can be used.  So, string-face seems quite appropriate.  In Shell, a
> HERE-doc is sort of an I/O redirection.  You can't, for example, assign
> a HERE-doc to a shell variable.  So, a different face seems appropriate.
>
> BTW: CPerl also mode uses string-face for HERE-docs.

I must admit I don't use Perl very much these days, but when I used it,
I used it as a "better shell", so I thought of Perl's here docs in
exactly the same way as sh's here docs.

So maybe a compromise is to add a new `perl-heredoc` face and make it
inherit from `font-lock-string-face` by default?

        Stefan