forked from DistributedProofreaders/guiguts-py
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
be87783
commit 1ba3009
Showing
5 changed files
with
317 additions
and
180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
# https://tkdocs.com/tutorial/text.html | ||
# | ||
# https://stackoverflow.com/a/3781773 | ||
# The basic concept is, you assign properties to tags, and you apply tags to | ||
# ranges of text in the widget. You can use the text widget's search command to | ||
# find strings that match your pattern, which will return you enough information | ||
# apply a tag to the range that matched. | ||
# | ||
# https://stackoverflow.com/a/3781773 | ||
# This answer seems to have a way to do a search/match with a regex? Also Nigel | ||
# probably has done a lot of work here that can be used as an example of even | ||
# re-used. | ||
# | ||
# Relevant tickets (all from GG1 search menu): | ||
# | ||
# Highlight Character, String or Regex #41 - https://github.com/DistributedProofreaders/guiguts-py/issues/41 | ||
# Behavior: | ||
# - opens a dialog | ||
# - accepts a string input | ||
# - has either "exact" or "regex" mode - a toggle | ||
# - works on a selection if present; or else lets you choose "prev selection" or else "select whole file" | ||
# - and then "apply highlights" or "remove highlight" | ||
|
||
# Highlight surrounding quotes/brackets #42 - https://github.com/DistributedProofreaders/guiguts-py/issues/42 | ||
# Behavior: | ||
# - Wherever the cursor is place, surrounding markers are highlighted | ||
# - it can match left to right brackets i think but quotes just any pair. | ||
# - it doesn't necessarily do only the innermost like for ({foo bar}) it would | ||
# highlight both of the bracket pairs ... have to analyze what it's doing in GG1 | ||
# - colors: | ||
# - gray for ' ‘’ (single quote) | ||
# - green for " “ ” (double quote) | ||
# - pink for ( ) | ||
# - purple for [ ] | ||
# - blue for { } | ||
# - it doesn't seem to match < > | ||
# - not sure about guillemet | ||
# - check what guiguts1 does. | ||
|
||
# Highlight Alignment Column #43 - https://github.com/DistributedProofreaders/guiguts-py/issues/43 | ||
# Behavior: | ||
# - identify column at cursor | ||
# - find all characters at the column PRIOR, so to left of the bar cursor position | ||
# - highlight that character | ||
# - so if cursor is at column 3, highlight any character in column 2 on any line | ||
# - it only can highlight lines with a character; lines with no char in that column | ||
# get no highlight there | ||
# - THIS COLUMN DOESN'T MOVE but as the text may move or change, on each change, | ||
# or maybe firing on some timer, it re-highlights so that even if text change, | ||
# the highlight column stays in the same location. | ||
# - this seems to be something that has to be done on each redraw / cursor position move? | ||
# is there a central hook to hang that on? In GG1 it can lag, which makes me think it is | ||
# firing on 250ms timer or something instead of "on each change" ? have to investigate. | ||
|
||
# Other things, not ticketed, but related: | ||
# Search -> Highlight single quotes in selection | ||
# Search -> Highlight double quotes in selection | ||
|
||
# These are all defined in GG1 in MenuStructure.pm lines 337+ | ||
|
||
# Order of these by how easy they seem: | ||
# 1. Highlight surrounding quotes/brackets #42 | ||
# this is a CheckButton which then has a command tied to it... | ||
# Highlight.pm :: highlight_quotbrac() | ||
# 2. Highlight Alignment Column #43 (must be done on each redraw...) | ||
# Highlight.pm :: it calls a lambda that runs hilite_alignment_{start,stop}() | ||
# this is a CheckButton | ||
# 3. Highlight Character, String or Regex #41 (needs a dialog) | ||
# Highlight.pm :: hilitepopup() | ||
# 4. Search -> Highlight single quotes in selection | ||
# Highlight.pm :: hilitesinglequotes() -> calls hilite() | ||
# 5. Search -> Highlight double quotes in selection | ||
# Highlight.pm :: hilitedoublequotes() -> calls hilite() | ||
|
||
# [ | ||
# 'command', 'Highlight ~Double Quotes in Selection', | ||
# -accelerator => 'Ctrl+.', | ||
# -command => \&::hilitedoublequotes, | ||
# ], | ||
# [ | ||
# 'command', 'Highlight ~Single Quotes in Selection', | ||
# -accelerator => 'Ctrl+,', | ||
# -command => \&::hilitesinglequotes, | ||
# ], | ||
# [ | ||
# 'command', '~Highlight Character, String or Regex...', | ||
# -accelerator => "Ctrl+$::altkeyname+h", | ||
# -command => \&::hilitepopup, | ||
# ], | ||
# [ | ||
# Checkbutton => 'Highlight S~urrounding Quotes & Brackets', | ||
# -variable => \$::nohighlights, | ||
# -onvalue => 1, | ||
# -offvalue => 0, | ||
# -accelerator => "Ctrl+;", | ||
# -command => \&::highlight_quotbrac | ||
# ], | ||
# [ | ||
# Checkbutton => 'Highlight Al~ignment Column', | ||
# -accelerator => 'Ctrl+Shift+a', | ||
# -variable => \$::lglobal{highlightalignment}, | ||
# -onvalue => 1, | ||
# -offvalue => 0, | ||
# -command => sub { | ||
# if ( $::lglobal{highlightalignment} ) { | ||
# ::hilite_alignment_start(); | ||
# } else { | ||
# ::hilite_alignment_stop(); | ||
# } | ||
# $textwindow->focus; | ||
# } | ||
# ], | ||
# [ | ||
# 'command', 'Re~move Highlights', | ||
# -accelerator => 'Ctrl+0', | ||
# -command => \&::hiliteremove, | ||
# ], | ||
|
||
# Things I need to work out: | ||
# | ||
# - these two are menu items in the Search menu. seems like it's just a "take this action" thing. | ||
# - look at GG1 "highlight single quotes in selection" algorithm | ||
# - look at GG1 "highlight double quotes in selection" algorithm | ||
# - make a function to highlight quotes (either kind) to use for both of the above 2 | ||
# | ||
# - Search menu - this is a checkbox / toggle thing. as you move the cursor it CHANGES the surround that's highlighted. | ||
# - so this is another thing that is on every cursor move / redraw? have to re-evaluate it | ||
# - look at GG1 "highlight surrounding quote/bracket" algorithm | ||
# - look at the colors used by the different things and map it in that function | ||
# - make generic function to highlight surrounding (anything) to use for the above | ||
# | ||
# - Search menu - this is a checkbox / toggle thing. as you move cursor if changes with every redraw. | ||
# - make a function to highlight the alignment column | ||
# - look to see if there's an "every change" function that can be used for "on redraw" already | ||
# - implement that if it's not already there. ask nigel first to be sure. | ||
# | ||
# - create dialog box for the char/str/re function | ||
# - work on the implementation(s) behind that |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
"""Highlighter functionality""" | ||
|
||
import logging | ||
import tkinter as tk | ||
# from tkinter import ttk | ||
from typing import Any, Tuple, Optional | ||
|
||
# import regex as re | ||
|
||
# from guiguts.checkers import CheckerDialog | ||
from guiguts.maintext import maintext | ||
# from guiguts.maintext import maintext, TclRegexCompileError | ||
from guiguts.preferences import preferences, PersistentBoolean, PrefKey | ||
from guiguts.utilities import ( | ||
# sound_bell, | ||
# IndexRowCol, | ||
IndexRange, | ||
# process_accel, | ||
# is_mac, | ||
# sing_plur, | ||
) | ||
# from guiguts.widgets import ToplevelDialog, Combobox, mouse_bind, ToolTip | ||
|
||
logger = logging.getLogger(__package__) | ||
|
||
# MARK_FOUND_START = "FoundStart" | ||
# MARK_FOUND_END = "FoundEnd" | ||
# MARK_END_RANGE = "SearchRangeEnd" | ||
|
||
def poc_highlighter(word="", disable=False) -> None: | ||
""" | ||
Only a proof-of-concept for now. Figuring out how to highlight text. | ||
""" | ||
|
||
# a tag name for our highlight thingamajig | ||
tag_name = "dmlhighlightword" | ||
|
||
if disable: | ||
maintext().tag_delete(tag_name) | ||
return | ||
|
||
# TODO: either augment the search dialog with a button for | ||
# this, or else create a new dialog? See how the GG1 flow works. | ||
search_string = word | ||
|
||
# Configure what this tag will look like | ||
# TODO: eventually let user control the colors in a pref or something | ||
maintext().tag_configure(tag_name, background="OliveDrab1", foreground="black") | ||
|
||
# TODO: improve on this with differing cases; see | ||
# search.py : SearchDialog.findall_clicked for examples | ||
|
||
# hack for now | ||
find_range = IndexRange(maintext().start(), maintext().end()) | ||
# find_range = (the_range, "in entire file") | ||
|
||
matches = maintext().find_matches( | ||
search_string, | ||
find_range, # what is this?? | ||
nocase=False, # figure out how to handle this | ||
regexp=False, # um handle later | ||
wholeword=False, # handle later | ||
) | ||
count = len(matches) | ||
print(f"Found {count} things to highlight") | ||
|
||
# Example of how to do highlight | ||
# matches should contain an array of FindMatch | ||
# so match.rowcol.index() should return the start position like 'x.y' | ||
# match.rowcol.count should return the length | ||
# and this code shows how to find from an index to +x chars: | ||
# rowcol_end = maintext().rowcol(match.rowcol.index() + f"+{match.count}c") | ||
|
||
# TODO: This doesn't currently behave in the way that GG1 behaves. | ||
# In GG1 if you highlight quotes they stay (unless overwritten.) | ||
# If you highlight a word/match it will highlight but if you insert anything | ||
# in the middle of a word it will split and NOT highlight that word anymore. | ||
# do I need to highlight each individual character to mimic that behavior?? | ||
# And if you highlight the alignment column, then it will re-highlight if you | ||
# insert, delete, or overwrite text involving that column. It must be in the | ||
# main loop checking on each redraw. | ||
|
||
# GG1 references: | ||
# The popup when you run "Highlight Chars, ..." is in Highlight.pm, sub hilitepopup | ||
# There is a search history here. And a whole dialog. | ||
# The "highlight surrounding brackets" menu item calls Highlight.pm sub highlight_quotbrac | ||
# The "highlight alignment column" menu item calls code in Highlight.pm: | ||
# to start: hilite_alignment_start, to stop: hilite_alignment_stop | ||
# it's all keyed off a global value, a bool `highlightalignment`. menu is a checkbox item. | ||
# (?) can menus in macos act as toggles too? (i think so) - does it work in tkinter? | ||
# and if so does it also work in linux? | ||
# the funcs note they are called every 200ms | ||
# in hilite_alignment_start, a repeating function is started that handles this. | ||
# $::lglobal{align_id} = $::top->repeat( 200, \&hilite_alignment ); | ||
# later on this is called with ->cancel to stop the repeating routine | ||
# so it just calls the actual hilight_alignment routine every 200ms | ||
|
||
for match in matches: | ||
for n in range(match.count): | ||
if n % 2 == 0: | ||
continue | ||
# maintext().tag_add(tag_name, match.rowcol.index(), match.rowcol.index() + f"+{match.count}c") | ||
__mystart = match.rowcol.index() # + f"+{n}c" | ||
__myend = match.rowcol.index() + f"+{n+1}c" | ||
print(f"Highlighter: from {__mystart} to {__myend}") | ||
maintext().tag_add(tag_name, __mystart, __myend) | ||
|
||
# Can I get from the above thing, a FindMatch, to somehow an IndexRange? | ||
# An IndexRange would have | ||
# _range.start.index(), _range.end.index() | ||
|
||
# TODO: the below is enough to: | ||
# - set the value | ||
# - save the value in preferences persistently | ||
# - store the value in a place we can easily find it | ||
# NEXT THINGS | ||
# - in the _on and _off routine need to either start, or stop, the 400ms loop | ||
# - on startup if the value is true then also start that loop up | ||
# - and somewhere the handler needs to live (this file?) which does the work every 400mx. | ||
# - steal the code from GG1 to do that. | ||
|
||
def toggle_highlight_quotbrac() -> None: | ||
"""Turn on/off highlighting for quotes and brackets""" | ||
|
||
pref = PrefKey.HIGHLIGHT_QUOTBRAC | ||
|
||
if preferences.get(pref): | ||
preferences.set(pref, False) | ||
print("Turn quote/bracket highlighting off.") | ||
|
||
else: | ||
preferences.set(pref, True) | ||
print("Turn quote/bracket highlighting on.") | ||
|
||
print(preferences.get(pref)) | ||
|
||
def highlight_quotbrac(): | ||
"""Action routine to highlight quotes/brackets that surround the cursor""" | ||
|
||
print("Firing highlight_quotbrac.") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.