feature: highlight patterns like vim's hlsearch#2861
Conversation
|
@daviewales @frosencrantz You two have contributed to past discussions and work on visidata's colorizers. Like those, pattern highlighting is a visual cue. Perhaps you have some feedback? |
|
Is there a way to clear the highlighting? For example, vim-sensible maps this to Ctrl+L: |
|
There's no command/binding to clear it now. The only way to clear it is to set a new nonexistent pattern. Having one is a good suggestion. On thinking about it more, allowing multiple simultaneous highlight patterns (one for each column, plus one for the entire sheet) is too complicated. Instead there should only be one at a time. That's what users expect from |
|
Thank you for doing this. I think this is great. Having only one highlighting format at a time is reasonable. Having multiple different highlights can get too complicated. Would the highlighting be per column or per sheet depending on the last type of search performed? Or would it be useful for it to control that by option? At some point it might be interesting to have a way do some sort of syntax highlight like I think the special cases where the matches are at the edge of the cell or screen should eventually be fixed. |
|
I agree that it makes sense just to have one highlight type at a time. In Vim at least, the highlight is based on the most recent search. I'd likely expect similar in Visidata. For example, I know that when I jump to the next match, it will be the next highlighted item. |
|
@frosencrantz I've pushed a change. The last search sets the highlight type. So
For So the remaining issues now are just 3, 4, and 5 from my initial list: fixing undo for |
|
This is a neat feature. My main concern is that it has to be a core feature. If it could be segmented into a self-contained file in |
|
Hopefully there is a way to resolve the feedback for this useful feature. It would be difficult to make this a separate feature without making of the cliptext code more generic. If the request is to make it smaller, maybe for now it would be limited to either per column or per sheet? Perhaps full sheet highlighting is the slightly more useful feature? |
|
@frosencrantz I'm glad you find this feature useful. It's good motivation for me to revisit this. The reason for delay here is that I ran into a troublesome edge case when handling strings that get automatically truncated by I'll push a set of commits here with the current state of what I've got, for more discussion. I'll squash the previous commits together with some new work into 1 commit, that contains just the basics of the highlight feature. Then I'll tack on a few more commits that trigger the troublesome edge case, for discussion on whether to keep them or exclude them. |
|
Last week, I wrote here:
But it's not a problem any more, I found a workaround. So I've force-pushed a new set of commits. It uses the previous set as a foundation, and fixed some bugs, and squashed it all into 1 commit. Then I added several commits that add highlighting of patterns that don't fit in the cell. When the highlighted match is fully left or right of the visible cell, the ellipses on the left or right side of the cell are highlighted. When the match is partly visible and partly outside the cell, the visible partial match is highlighted, and so are the ellipses where the match continues offscreen. I left them as multiple small commits in case we want to rework details of them. In particular, 207255f feels a bit awkward and repetitive. Any ideas on a better solution? |
|
Also, there is one known flaw with this PR right now: highlighting has problems in multiline mode. Matches that cross lines fail to be highlighted, though matches that fully fit within a line are fine. And when matches are fully offscreen to the right of the cell, the ellipses fail to be highlighted. This is because Fixing these problems would require changes to And multiline mode has had flaws of its own for a while. For example, multiline mode does not properly word-wrap full-width symbols. And multiline mode causes display oddities with So I'm proposing we take this PR. I think the highlight feature is helpful enough that it's worth it. |
visidata/sheets.py
Outdated
|
|
||
| pre = disp_truncator if hoffset != 0 else disp_column_fill | ||
| prechunks = [] | ||
| def _highlight(chunks): |
There was a problem hiding this comment.
This is a lot of code! Can we move this functionality into a separate file like features/hlsearch.py, such that importing the file implements it, but everything works fine as it currently does without it? The other functions should be trivial to move (with the implementation being enabled by command replacements in that file), but I'm not sure how easy it would be for this code in draw(). I can help figure out a strategy if you want.
There was a problem hiding this comment.
Sure, separating it should be possible. I should have made _highlight(chunks) take hp as a parameter, I just missed the dependence on hp. Once I do that, it can be extracted from drawRow().
How does features/ handle the default initialization of instance variables, like self.highlight_regex = None for TableSheet? Right now it's done in TableSheet.__init__() in sheets.py. Should that be moved to hlsearch.py too?
There was a problem hiding this comment.
This is the purpose of the TableSheet.init('highlight_regex', lambda: None, copy=False), so that these variables can be instantiated within the same .py that they are used (and won't exist if that file is removed).
The copy kwarg is whether the value should be copied if the sheet is copied (for instance, to make a subsheet). Up to you in this instance, if the search highlighting should persist when you use " to dup-selected.
visidata/sheets.py
Outdated
| BaseSheet.addCommand('zZ', 'splitwin-input', 'vd.options.disp_splitwin_pct = input("% height for split window: ", value=vd.options.disp_splitwin_pct)', 'set split pane to specific size') | ||
|
|
||
| BaseSheet.addCommand('Ctrl+L', 'redraw', 'sheet.refresh(); vd.redraw(); vd.draw_all()', 'Refresh screen') | ||
| BaseSheet.addCommand('Ctrl+L', 'redraw', 'highlight_clear(); sheet.refresh(); vd.redraw(); vd.draw_all()', 'Refresh screen') |
There was a problem hiding this comment.
Do we really want to clear the highlights? My model for Ctrl+L is that it simply redraws the screen as it was (after e.g. extra-terminal corruption) and doesn't change anything.
There was a problem hiding this comment.
I found it a convenient binding. But that behavior is different from vim, where Ctrl+L leaves the pattern visible. So I'm fine with taking it out.
Are there keys free to bind to highlight-clear? Without a good key, people are likely to just do / asdf to run another search for a nonexistent pattern. That's bad because it pollutes the search history.
There was a problem hiding this comment.
Hm, I see. The only key that feels possibly intuitive is Esc (though that implies a larger modality which is not the case here). Let me think about Ctrl+L having a side-effect like this.
There was a problem hiding this comment.
I suggested Ctrl+L, because that's the binding in the vim-sensible plugin to clear highlights:
https://github.com/tpope/vim-sensible/blob/master/plugin/sensible.vim#L55
It also appears to have been adopted by Neovim for the same purpose:
https://neovim.io/doc/user/various.html#CTRL-L
But you're right that in Vim, without plugins, it doesn't clear the search highlight:
https://vimhelp.org/various.txt.html#CTRL-L
|
Okay, I've made the requested changes. What's the right way to make the standalone There are still a couple of places where the highlight features are used outside of |
|
@saulpw Just a bump to let you know this PR is currently tagged |
This is fine for now. I use VisiData.api, Sheet.api, and Column.api as places to stick utility functions like this, depending on whether their primary context is a Sheet, a Column, or neither. As long as the function has a reasonably non-conflicting name and is relegated to a module that can be not imported without breaking things, then that's fine.
Using |
saulpw
left a comment
There was a problem hiding this comment.
Just a couple of notes. Let's consider the option name, and make the highlight_clear() function more generic and give it a stub so that nothing breaks if hlsearch.py is no longer imported. Then I think we can merge!
Reading the option that doesn't exist produces a How should I handle that? A |
Hmm...it seems like it should give a warning rather than an exception. For now, move the option (default False) to sheets.py so it always exists, with a comment next to it indicating that the entire implementation is in features/hlsearch.py. I'll look into how best to deal with checking unknown options, and I can move it then. |
|
Okay, that's a good temporary solution. Done! |
…light_search saulpw#2861 Apply hoffset slicing when highlight_search is active but no regex is set, fixing horizontal scrolling. Use options.get() so the option can live solely in features/hlsearch.py without a stub in sheets.py. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…unks This should change no current behavior, as clipdraw_chunks was only being called with cattr that were dicts or empty strings.
instead of silently failing
Even if features/hlsearch.py is ever removed. Otherwise, reading the nonexistent option would raise ValueError.
…light_search saulpw#2861 Apply hoffset slicing when highlight_search is active but no regex is set, fixing horizontal scrolling. Use options.get() so the option can live solely in features/hlsearch.py without a stub in sheets.py. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
okay, I made a few small changes. We'll set the option to True by default for now, to give the feature visibility on develop. Depending on response we may turn it to False before the next release. Thanks again! |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Traced through code and confirmed clear_search() always wipes both scopes before setting a new pattern, so the or in drawRow never chooses between two live patterns. Left a comment for future readers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This is a draft of a feature to highlight substrings within a column, or in every column. It looks like this, highlighting
\d+in theSKUcolumn, and highlightingcateverywhere.I'm floating this to get feedback. By default, highlighting is used after every search with
/and?andg/andg?.options.highlightcan be toggled to turn highlighting off.highlight-sheetwill do the same highlighting asg/andhighlight-colwill do the same as/, while keeping the cursor in place.In the screenshot you can see that partial offscreen matches are highlighted for strings that don't fully fit in the cell, like where
Catis shown asC…. (But partial offscreen data will not be highlighted for collections like tuples, lists, and dictionaries. That treatment of offscreen contents is the same as the current behavior for/.)Remaining work
|set the highlight pattern like/does? I'm not sure.SKUcolumn, you can see that the column\d+pattern is lit, but the sheet patterncatis not.search-*does not undo highlighting. (undo ofhighlight-*works properly, butsearch-*is innonLoggedso it is more complicated)