4

I still use Perl, some new code, and maintaining old code. I use emacs and cperl-mode. I like syntax coloring.

At first (many years ago) I disliked cperl-mode's special coloring of arrays and hashes, but it has grown on me. To the point where I will sometimes prefer to use a hash rather than a hash reference, just to get the special coloring. That may not sound so bad - but if I admit to occasionally using a global %hash or $hash{key} rather than an object member $hashref->{key}, just to get the coloring, well, it is bad. I.e. syntax coloring is making me want to follow bad programming practices.

So, my question is: does anyone have emacs/elisp configuration code to get cperl-mode or perl-mode to colorize a hash reference like $hashref->{key} in the same or similar to $hash{key}?

Let me use bold to indicate the places that might be colored:

  • cperl-mode does now: $hash{key}
  • what I would like: $hash->{key}

I have done extensive customization of coloring (faces) in emacs - e.g. colorizing to distinguish DEBUG code from non-debug code, TEST from non-test, etc. - but I have not managed to get this syntax coloring in cperl-mode working. (FOLLOW-ON: I eventually got font-lock-add-keywords working, as shown in my answer to my own question below.)

In the example below, you can see that $hashref->{key} is not colored, while $hash{key} is.

example of cperl-mode syntax coloring

Similarly for array refs, and perhaps other refs.

I realize that coloring refs will only apply to derefs like $hashref->{key}, and not to other stuff like $hashref1 = $hashref2. I think that I can live with that.

Krazy Glew
  • 7,210
  • 2
  • 49
  • 62
  • 1
    What I do in cperl mode is to set `cperl-highlight-variables-indiscriminately` to `t` so that `$ref` has the same colour as in `my $ref` (i.e. scalars are coloured when used). Using the same colour for `$var[0]` and `$var->[0]` might obscure the fact they are two different variables. – choroba May 30 '18 at 16:25
  • @choroba I agree about the risk in coloring $h{k} and $h->{k} the same. This being emacs, I would assume that we would have hash face and hashref face allowing distinction. – Krazy Glew May 30 '18 at 16:28
  • But you can't tell whether `$hash_ref = function()` is a hash ref or not, so it again will be confusing when used close to `exists $hash_ref->{a}`. – choroba May 30 '18 at 16:32
  • 1
    Your image needs more free-hand circles! – simbabque May 30 '18 at 16:34
  • @choroba agreed - not coloring non-derefs is a loss. But I think I can live with that. – Krazy Glew May 30 '18 at 16:34

3 Answers3

4

You can set cperl-highlight-variables-indiscriminately to t (via customizing it) to get scalar variables coloured not only when declared but always. enter image description here

Using the same colour for @ref and $ref is confusing, as they are different variable types (and different variables); similarly, it's confusing to use the scalar colour for $ref but array colour for $ref->[0] as they are the same variable.

Also, Perl being Perl, would you use all three colours here?

if (ref $ref eq 'ARRAY') {
    return $ref->[0]
} elsif (ref $ref eq 'HASH') {
    return $ref->{key}
}
choroba
  • 231,213
  • 25
  • 204
  • 289
  • Thanks, but what I want to emphasize is the similarity between $hash{key} and $href->{key} - both being used to access hashes, one directly, the other indirectly. NOT the similarly between (scalar) variables that contain numbers, strings, hashrefs or arrayrefs. // yes, I would use all three colors in your second example - in fact the fruit salad effect would probably highlight the fact that something tricky is going on. – Krazy Glew May 30 '18 at 18:09
4

I dislike answering my own question, but the wild goose chase answer suggested annoyed me enough to figure out what my attempts were doing wrong.

(I hate it when I ask for X, somebody answers Y, and disses X. Especially when X is doable, as here.)

Here is working code from my .emacs:

(defun ag-extend-cperl-font-lock-keywords ()
  (interactive)
  (font-lock-add-keywords
    'cperl-mode
    '(
       (
         "\\($[a-zA-Z_][a-zA-Z_0-9]*->\\){"
         1 'cperl-hash-face t
         )
       (
         "\\($[a-zA-Z_][a-zA-Z_0-9]*->\\)\\["
         1 'cperl-array-face t
         )
       (
         "\\($[a-zA-Z_][a-zA-Z_0-9]*->\\)("
         1 'font-lock-function-name-face t
         )
       )
    t
    )
  )

(ag-extend-cperl-font-lock-keywords)

giving

enter image description here

Just for grins, @choroba's example of multiple types:

enter image description here

I haven't decided if I should create separate faces for hashrefs, arrayrefs, and coderefs. For now, just using the same face as their non-ref counterparts. Including -> as part of the text colored provides some distinction between non-ref and ref.

Nor have I yet decided if I want to extend to the various other Perl syntaxes. From https://perldoc.perl.org/perlref.html:

enter image description here

But now that I have the font-lock-add-keywords invocation, those details I can fix at my leisure.

Krazy Glew
  • 7,210
  • 2
  • 49
  • 62
1

You can't do what you want without extending cperl-mode. cperl-mode doesn't understand references. There's no reference "face" for you to customize, and no "thing" to apply that face to. If you want to render a hash reference like a hash (a la your example) I'd start with modifying the second regex in the definition of t-font-lock-keywords-1 in cperl-mode.el. That should take care of hash and array refs. Beware of cperl-highlight-variables-indiscriminately overriding your changes. If you want to do something fancier, like have a "reference face", you'll have to

  1. define a face
  2. add the face to customize (if you want)
  3. hack t-font-lock-keywords-1 and apply the face to a regex match

Of course it might just be easier to send a feature request upstream. cperl-mode is ancient and could definitely use some modernization.

nega
  • 2,526
  • 20
  • 25
  • In fact I did it without hacking `cperl-mode` itself. – Krazy Glew May 31 '18 at 00:40
  • sorry for downvoting your answer at first. I was a bit grumpy when you started with a statement that I had just shown to be wrong. But thanks for looking, and fortunately stackoverflow allowed me to change my -1 to a 0. – Krazy Glew May 31 '18 at 00:46
  • not a problem. "without hacking cperl-mode itself" really should be something like "without hacking some elisp yourself". – nega May 31 '18 at 01:07