"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Omar Polo <op@omarpolo.com>
Subject:
Re: fix diff+blame view search for horizontal scrolling with unicode
To:
Stefan Sperling <stsp@stsp.name>
Cc:
gameoftrees@openbsd.org
Date:
Thu, 16 Jun 2022 22:45:00 +0200

Download raw body.

Thread
Stefan Sperling <stsp@stsp.name> wrote:
> This makes horizontal scrolling work on lines matched while searching,
> even when matched lines contain unicode.
>  
> The regex matches on the underlying bytestring. Since a valid pattern is
> unlikely to match multi-byte characters in the middle, we can split the
> line into 3 segments, before, within, and after the matched region, and
> then convert each segment to wide characters separately.
> 
> In my testing I can succesfully match and highlight english text as well
> as copy-pasted japanese text. Horizontal scrolling seems to work correctly
> in either case.
> 
> ok?

simple strategy but works well :)

ok op@

> diff 2e5be6f6aff8a33d8189b76f25c2bec556de60c3 eb4ee4d26869424aab2f102bd9b156fdab1a39e7
> blob - bdff5844ccda157277e95325887def00e1f69e73
> blob + 7abd8ac2deaa36fa7cc9dc16462f7acb50b71f78
> --- tog/tog.c
> +++ tog/tog.c
> @@ -3122,56 +3122,107 @@ match_color(struct tog_colors *colors, const char *lin
>  
>  static const struct got_error *
>  add_matched_line(int *wtotal, const char *line, int wlimit, int col_tab_align,
> -    WINDOW *window, int skip, regmatch_t *regmatch)
> +    WINDOW *window, int skipcol, regmatch_t *regmatch)
>  {
>  	const struct got_error *err = NULL;
> -	wchar_t *wline;
> -	int rme, rms, n, width;
> +	wchar_t *wline = NULL;
> +	int rme, rms, n, width, scrollx;
> +	int width0 = 0, width1 = 0, width2 = 0;
> +	char *seg0 = NULL, *seg1 = NULL, *seg2 = NULL;
>  
>  	*wtotal = 0;
> +
>  	rms = regmatch->rm_so;
>  	rme = regmatch->rm_eo;
>  
> -	err = format_line(&wline, &width, NULL, line, 0, wlimit + skip,
> -	    col_tab_align, 1);
> -	if (err)
> -		return err;
> +	/* Split the line into 3 segments, according to match offsets. */
> +	seg0 = strndup(line, rms);
> +	if (seg0 == NULL)
> +		return got_error_from_errno("strndup");
> +	seg1 = strndup(line + rms, rme - rms);
> +	if (seg1 == NULL) {
> +		err = got_error_from_errno("strndup");
> +		goto done;
> +	}
> +	seg2 = strdup(line + rme);
> +	if (seg1 == NULL) {
> +		err = got_error_from_errno("strndup");
> +		goto done;
> +	}
>  
>  	/* draw up to matched token if we haven't scrolled past it */
> -	n = MAX(rms - skip, 0);
> +	err = format_line(&wline, &width0, NULL, seg0, 0, wlimit,
> +	    col_tab_align, 1);
> +	if (err)
> +		goto done;
> +	n = MAX(width0 - skipcol, 0);
>  	if (n) {
> -		waddnwstr(window, wline + skip, n);
> -		wlimit -= n;
> -		*wtotal += n;
> +		free(wline);
> +		err = format_line(&wline, &width, &scrollx, seg0, skipcol,
> +		    wlimit, col_tab_align, 1);
> +		if (err)
> +			goto done;
> +		waddwstr(window, &wline[scrollx]);
> +		wlimit -= width;
> +		*wtotal += width;
>  	}
>  
>  	if (wlimit > 0) {
> -		int len = rme - rms;
> -		n = 0;
> -		if (skip > rms) {
> -			n = skip - rms;
> -			len = MAX(len - n, 0);
> +		int i = 0, w = 0;
> +		size_t wlen;
> +
> +		free(wline);
> +		err = format_line(&wline, &width1, NULL, seg1, 0, wlimit,
> +		    col_tab_align, 1);
> +		if (err)
> +			goto done;
> +		wlen = wcslen(wline);
> +		while (i < wlen) {
> +			width = wcwidth(wline[i]);
> +			if (width == -1) {
> +				/* should not happen, tabs are expanded */
> +				err = got_error(GOT_ERR_RANGE);
> +				goto done;
> +			}
> +			if (width0 + w + width > skipcol)
> +				break;
> +			w += width; 
> +			i++;
>  		}
>  		/* draw (visible part of) matched token (if scrolled into it) */
> -		if (len) {
> +		if (width1 - w > 0) {
>  			wattron(window, A_STANDOUT);
> -			waddnwstr(window, wline + rms + n, len);
> +			waddwstr(window, &wline[i]);
>  			wattroff(window, A_STANDOUT);
> -			wlimit -= len;
> -			*wtotal += len;
> +			wlimit -= (width1 - w);
> +			*wtotal += (width1 - w);
>  		}
>  	}
>  
> -	if (wlimit > 0 && skip < width) {  /* draw rest of line */
> -		n = 0;
> -		if (skip > rme)
> -			n = MIN(skip - rme, width - rme);
> -		waddnwstr(window, wline + rme + n, wlimit);
> +	if (wlimit > 0) {  /* draw rest of line */
> +		free(wline);
> +		if (skipcol > width0 + width1) {
> +			err = format_line(&wline, &width2, &scrollx, seg2,
> +			    skipcol - (width0 + width1), wlimit,
> +			    col_tab_align, 1);
> +			if (err)
> +				goto done;
> +			waddwstr(window, &wline[scrollx]);
> +		} else {
> +			err = format_line(&wline, &width2, NULL, seg2, 0,
> +			    wlimit, col_tab_align, 1);
> +			if (err)
> +				goto done;
> +			waddwstr(window, wline);
> +		}
> +		*wtotal += width2;
>  	}
> -
> -	*wtotal = width;
> +done:
>  	free(wline);
> -	return NULL;
> +	free(seg0);
> +	free(seg1);
> +	free(seg2);
> +	return err;
>  }
>  
>  static const struct got_error *
> @@ -3237,6 +3288,17 @@ draw_file(struct tog_view *view, const char *header)
>  			return got_ferror(s->f, GOT_ERR_IO);
>  		}
>  
> +		/* Set view->maxx based on full line length. */
> +		err = format_line(&wline, &width, NULL, line, 0, INT_MAX, 0,
> +		    view->x ? 1 : 0);
> +		if (err) {
> +			free(line);
> +			return err;
> +		}
> +		view->maxx = MAX(view->maxx, width);
> +		free(wline);
> +		wline = NULL;
> +
>  		tc = match_color(&s->colors, line);
>  		if (tc)
>  			wattr_on(view->window,
> @@ -3249,24 +3311,8 @@ draw_file(struct tog_view *view, const char *header)
>  				free(line);
>  				return err;
>  			}
> -			view->maxx = MAX(view->maxx, width);
>  		} else {
>  			int skip;
> -
> -			/* Set view->maxx based on full line length. */
> -			err = format_line(&wline, &width, NULL, line,
> -			    0, INT_MAX, 0, view->x ? 1 : 0);
> -			if (err) {
> -				free(line);
> -				return err;
> -			}
> -			view->maxx = MAX(view->maxx, width);
> -
> -			/*
> -			 * Get a new version of the line for display.
> -			 * This will be scrolled and/or trimmed in length.
> -			 */
> -			free(wline);
>  			err = format_line(&wline, &width, &skip, line,
>  			    view->x, view->ncols, 0, view->x ? 1 : 0);
>  			if (err) {
> @@ -4355,6 +4401,16 @@ draw_blame(struct tog_view *view)
>  		if (++lineno < s->first_displayed_line)
>  			continue;
>  
> +		/* Set view->maxx based on full line length. */
> +		err = format_line(&wline, &width, NULL, line, 0, INT_MAX, 9, 1);
> +		if (err) {
> +			free(line);
> +			return err;
> +		}
> +		free(wline);
> +		wline = NULL;
> +		view->maxx = MAX(view->maxx, width);
> +
>  		if (view->focussed && nprinted == s->selected_line - 1)
>  			wstandout(view->window);
>  
> @@ -4398,12 +4454,6 @@ draw_blame(struct tog_view *view)
>  
>  		if (view->ncols <= 9) {
>  			width = 9;
> -			wline = wcsdup(L"");
> -			if (wline == NULL) {
> -				err = got_error_from_errno("wcsdup");
> -				free(line);
> -				return err;
> -			}
>  		} else if (s->first_displayed_line + nprinted ==
>  		    s->matched_line &&
>  		    regmatch->rm_so >= 0 && regmatch->rm_so < regmatch->rm_eo) {
> @@ -4413,25 +4463,9 @@ draw_blame(struct tog_view *view)
>  				free(line);
>  				return err;
>  			}
> -			view->maxx = MAX(view->maxx, width);
>  			width += 9;
>  		} else {
>  			int skip;
> -
> -			/* Set view->maxx based on full line length. */
> -			err = format_line(&wline, &width, NULL, line,
> -			    0, INT_MAX, 9, 1);
> -			if (err) {
> -				free(line);
> -				return err;
> -			}
> -			view->maxx = MAX(view->maxx, width);
> -
> -			/*
> -			 * Get a new version of the line for display.
> -			 * This will be scrolled and/or trimmed in length.
> -			 */
> -			free(wline);
>  			err = format_line(&wline, &width, &skip, line,
>  			    view->x, view->ncols - 9, 9, 1);
>  			if (err) {