Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support an 'owner' key in region_highlight. #57

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 28 additions & 8 deletions Doc/Zsh/zle.yo
Original file line number Diff line number Diff line change
Expand Up @@ -974,34 +974,54 @@ of the non-editable parts of the command line in tt(PREDISPLAY)
and tt(POSTDISPLAY) are possible, but note that the tt(P) flag
is needed for character indexing to include tt(PREDISPLAY).

Each string consists of the following parts:
Each string consists of the following whitespace-separated parts:

startitemize()
itemiz(Optionally, a `tt(P)' to signify that the start and end offset that
follow include any string set by the tt(PREDISPLAY) special parameter;
this is needed if the predisplay string itself is to be highlighted.
Whitespace may follow the `tt(P)'.)
itemiz(A start offset in the same units as tt(CURSOR), terminated by
whitespace.)
itemiz(An end offset in the same units as tt(CURSOR), terminated by
whitespace.)
Whitespace between the `tt(P)' and the start offset is optional.)
itemiz(A start offset in the same units as tt(CURSOR).)
itemiz(An end offset in the same units as tt(CURSOR).)
itemiz(A highlight specification in the same format as
used for contexts in the parameter tt(zle_highlight), see
ifnzman(noderef(Character Highlighting))\
ifzman(the section `Character Highlighting' below);
for example, tt(standout) or tt(fg=red,bold)).
for example, tt(standout) or tt(fg=red,bold).)
itemiz(Optionally, a string of the form `tt(memo=)var(token)'.
The var(token) consists of everything between the `tt(=)' and the next
whitespace, comma, NUL, or the end of the string.
The var(token) is preserved verbatim but not parsed in any way.

Plugins may use this to identify array elements they have added: for example,
a plugin might set var(token) to its (the plugin's) name and then use
`tt(region_highlight=+LPAR() ${region_highlight:#*memo=)var(token)tt(} +RPAR())'
in order to remove array elements it have added.

(This example uses the `tt(${)var(name)tt(:#)var(pattern)tt(})' array-grepping
syntax described in
ifzman(the section `Parameter Expansion' in zmanref(zshexpn))\
ifnzman(noderef(Parameter Expansion)).))
enditemize()

For example,

example(region_highlight=("P0 20 bold"))
example(region_highlight=("P0 20 bold memo=foobar"))

specifies that the first twenty characters of the text including
any predisplay string should be highlighted in bold.

Note that the effect of tt(region_highlight) is not saved and disappears
as soon as the line is accepted.

Note that zsh 5.8 and older do not support the `tt(memo=)var(token)' field
and may misparse the third (highlight specification) field when a memo
is given.
COMMENT(The syntax `tt(0 20 bold, memo=foobar)' (with an auxiliary comma)
happens to work on both zsh <=5.8 and zsh 5.9-to-be, but that seems to be more of
an accident of implementation than something we should make a first-class-citizen
API promise. It's mentioned in the "Incompatibilities" section of README.)

The final highlighting on the command line depends on both tt(region_highlight)
and tt(zle_highlight); see
ifzman(the section CHARACTER HIGHLIGHTING below)\
Expand Down
9 changes: 9 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ affects you, make the implied colons in the first pattern explicit, as in:
zstyle ':foo:*:baz:*' style value2
This will use value1 in both 5.8 and 5.9.

Elements of the region_highlight array have gained a fourth space-separated
field. Code written against 5.9 that sets the new field may break under 5.8:
for example, the element "0 20 bold memo=foo", which is valid under 5.9, would
not work under 5.8. (Under the hood, 5.8 does not recognize the space as
terminating the highlighting specification.) On the other hand, code that does
not set the new, fourth field will continue to work under both 5.8 and 5.9.
(As it happens, adding a comma after "bold" will make both 5.8 and 5.9 do the
right thing, but this should be viewed as an unsupported hack.)

Incompatibilities between 5.7.1 and 5.8
---------------------------------------

Expand Down
4 changes: 4 additions & 0 deletions Src/Zle/zle.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,10 @@ struct region_highlight {
* Any of the flags defined above.
*/
int flags;
/*
* User-settable "memo" key. Metafied.
*/
const char *memo;
};

/*
Expand Down
62 changes: 56 additions & 6 deletions Src/Zle/zle_refresh.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ static zattr default_atr_on, special_atr_on;

/*
* Array of region highlights, no special termination.
* The first element (0) always describes the region between
* point and mark. Any other elements are set by the user
* via the parameter region_highlight.
* The first N_SPECIAL_HIGHLIGHTS elements describe special uses of
* highlighting, documented under N_SPECIAL_HIGHLIGHTS.
* Any other elements are set by the user via the parameter region_highlight.
*/

/**/
Expand Down Expand Up @@ -414,16 +414,19 @@ get_region_highlight(UNUSED(Param pm))
arrsize--;
rhp++, arrp++) {
char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE];
int atrlen = 0, alloclen;
int atrlen, alloclen;
const char memo_equals[] = "memo=";

sprintf(digbuf1, "%d", rhp->start);
sprintf(digbuf2, "%d", rhp->end);

atrlen = output_highlight(rhp->atr, NULL);
alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) +
3; /* 2 spaces, 1 0 */
3; /* 2 spaces, 1 terminating NUL */
if (rhp->flags & ZRH_PREDISPLAY)
alloclen += 2; /* "P " */
if (rhp->memo)
alloclen += 1 /* space */ + strlen(memo_equals) + strlen(rhp->memo);
*arrp = (char *)zhalloc(alloclen * sizeof(char));
/*
* On input we allow a space after the flags.
Expand All @@ -436,6 +439,12 @@ get_region_highlight(UNUSED(Param pm))
(rhp->flags & ZRH_PREDISPLAY) ? "P" : "",
digbuf1, digbuf2);
(void)output_highlight(rhp->atr, *arrp + strlen(*arrp));

if (rhp->memo) {
strcat(*arrp, " ");
strcat(*arrp, memo_equals);
strcat(*arrp, rhp->memo);
}
}
*arrp = NULL;
return retarr;
Expand All @@ -460,6 +469,8 @@ set_region_highlight(UNUSED(Param pm), char **aval)
/* no null termination, but include special highlighting at start */
int newsize = len + N_SPECIAL_HIGHLIGHTS;
int diffsize = newsize - n_region_highlights;

free_region_highlights_memos();
region_highlights = (struct region_highlight *)
zrealloc(region_highlights,
sizeof(struct region_highlight) * newsize);
Expand All @@ -476,6 +487,7 @@ set_region_highlight(UNUSED(Param pm), char **aval)
*aval;
rhp++, aval++) {
char *strp, *oldstrp;
const char memo_equals[] = "memo=";

oldstrp = *aval;
if (*oldstrp == 'P') {
Expand All @@ -502,7 +514,44 @@ set_region_highlight(UNUSED(Param pm), char **aval)
while (inblank(*strp))
strp++;

match_highlight(strp, &rhp->atr);
strp = (char*) match_highlight(strp, &rhp->atr);

while (inblank(*strp))
strp++;

if (strpfx(memo_equals, strp)) {
const char *memo_start = strp + strlen(memo_equals);
const char *i, *memo_end;

/*
* Forward compatibility: end parsing at a comma or whitespace to
* allow the following extensions:
*
* - A fifth field: "0 20 bold memo=foo bar".
*
* - Additional attributes in the fourth field: "0 20 bold memo=foo,bar"
* and "0 20 bold memo=foo\0bar".
*
* For similar reasons, we don't flag an error if the fourth field
* doesn't start with "memo=" as we expect.
*/
i = memo_start;

/* ### TODO: Consider optimizing the common case that memo_start to
* end-of-string is entirely ASCII */
while (1) {
int nbytes;
convchar_t c = unmeta_one(i, &nbytes);

if (c == '\0' || c == ',' || inblank(c)) {
memo_end = i;
break;
} else
i += nbytes;
}
rhp->memo = ztrduppfx(memo_start, memo_end - memo_start);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where's the corresponding free()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed.

} else
rhp->memo = NULL;
}

freearray(av);
Expand Down Expand Up @@ -2797,6 +2846,7 @@ zle_refresh_finish(void)

if (region_highlights)
{
free_region_highlights_memos();
zfree(region_highlights,
sizeof(struct region_highlight) * n_region_highlights);
region_highlights = NULL;
Expand Down
21 changes: 21 additions & 0 deletions Src/Zle/zle_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,22 @@ zlegetline(int *ll, int *cs)
}


/*
* free() the 'memo' elements of region_highlights.
*/

/**/
void
free_region_highlights_memos(void)
{
struct region_highlight *rhp;
for (rhp = region_highlights;
rhp < region_highlights + n_region_highlights;
rhp++) {
zsfree(rhp->memo);
}
}

/* Forward reference */
struct zle_region;

Expand All @@ -568,6 +584,7 @@ struct zle_region {
int start;
int end;
int flags;
const char *memo;
};

/* Forward reference */
Expand Down Expand Up @@ -632,6 +649,7 @@ zle_save_positions(void)
newrhp->next = NULL;
newrhp->atr = rhp->atr;
newrhp->flags = rhp->flags;
newrhp->memo = ztrdup(rhp->memo);
if (zlemetaline) {
newrhp->start = rhp->start_meta;
newrhp->end = rhp->end_meta;
Expand Down Expand Up @@ -682,6 +700,7 @@ zle_restore_positions(void)
nreg++, oldrhp = oldrhp->next)
;
if (nreg + N_SPECIAL_HIGHLIGHTS != n_region_highlights) {
free_region_highlights_memos();
n_region_highlights = nreg + N_SPECIAL_HIGHLIGHTS;
region_highlights = (struct region_highlight *)
zrealloc(region_highlights,
Expand All @@ -694,6 +713,7 @@ zle_restore_positions(void)

rhp->atr = oldrhp->atr;
rhp->flags = oldrhp->flags;
rhp->memo = oldrhp->memo; /* transferring ownership of the permanently-allocated memory */
if (zlemetaline) {
rhp->start_meta = oldrhp->start;
rhp->end_meta = oldrhp->end;
Expand All @@ -707,6 +727,7 @@ zle_restore_positions(void)
rhp++;
}
} else if (region_highlights) {
free_region_highlights_memos();
zfree(region_highlights, sizeof(struct region_highlight) *
n_region_highlights);
region_highlights = NULL;
Expand Down
2 changes: 1 addition & 1 deletion Src/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -1655,7 +1655,7 @@ free(FREE_ARG_T p)

/**/
mod_export void
zsfree(char *p)
zsfree(const char *p)
{
if (p)
zfree(p, strlen(p) + 1);
Expand Down
9 changes: 6 additions & 3 deletions Src/prompt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1724,10 +1724,11 @@ match_colour(const char **teststrp, int is_fg, int colour)
/*
* Match a set of highlights in the given teststr.
* Set *on_var to reflect the values found.
* Return a pointer to the first character not consumed.
*/

/**/
mod_export void
mod_export const char *
match_highlight(const char *teststr, zattr *on_var)
{
int found = 1;
Expand All @@ -1745,7 +1746,7 @@ match_highlight(const char *teststr, zattr *on_var)
atr = match_colour(&teststr, is_fg, 0);
if (*teststr == ',')
teststr++;
else if (*teststr)
else if (*teststr && *teststr != ' ')
break;
found = 1;
/* skip out of range colours but keep scanning attributes */
Expand All @@ -1758,7 +1759,7 @@ match_highlight(const char *teststr, zattr *on_var)

if (*val == ',')
val++;
else if (*val)
else if (*val && *val != ' ')
break;

*on_var |= hl->mask_on;
Expand All @@ -1769,6 +1770,8 @@ match_highlight(const char *teststr, zattr *on_var)
}
}
}

return teststr;
}

/*
Expand Down
44 changes: 44 additions & 0 deletions Test/X04zlehighlight.ztst
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,50 @@
0:basic region_highlight with 8 colors
>0m27m24mCDE|32|trueCDE|39|

zpty_start
zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=someplugin" ); typeset -p region_highlight }'
zpty_input 'zle -N rh_widget'
zpty_input 'bindkey "\C-a" rh_widget'
zpty_enable_zle
zpty_input $'\C-a'
zpty_line
zpty_stop
0:region_highlight memo information round trips
>typeset -a region_highlight=( '0 4 fg=green memo=someplugin' )

zpty_start
zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=someplugin,futureattribute=futurevalue" ); typeset -p region_highlight }'
zpty_input 'zle -N rh_widget'
zpty_input 'bindkey "\C-a" rh_widget'
zpty_enable_zle
zpty_input $'\C-a'
zpty_line
zpty_stop
0:region_highlight memo information forward compatibility, #1
>typeset -a region_highlight=( '0 4 fg=green memo=someplugin' )

zpty_start
zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=someplugin futurefifthfield" ); typeset -p region_highlight }'
zpty_input 'zle -N rh_widget'
zpty_input 'bindkey "\C-a" rh_widget'
zpty_enable_zle
zpty_input $'\C-a'
zpty_line
zpty_stop
0:region_highlight memo information forward compatibility, #2
>typeset -a region_highlight=( '0 4 fg=green memo=someplugin' )

zpty_start
zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=some'$'\0''plugin" ); typeset -p region_highlight }'
zpty_input 'zle -N rh_widget'
zpty_input 'bindkey "\C-a" rh_widget'
zpty_enable_zle
zpty_input $'\C-a'
zpty_line
zpty_stop
0:region_highlight memo information forward compatibility, #3: NULs
>typeset -a region_highlight=( '0 4 fg=green memo=some' )

zpty_start
zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=#040810" ); }'
zpty_input 'zle -N rh_widget'
Expand Down