Tk Source Code

View Ticket
Login
Ticket UUID: 57b821d2db2a75bc6d2ae1c01dd33827d5c9a844
Title: text index {insert wordstart} fails at 0 and 1 word start positions
Type: Bug Version: >=8.6, revised_text
Submitter: anonymous Created on: 2024-01-26 12:19:10
Subsystem: 18. [text] Assigned To: fvogel
Priority: 5 Medium Severity: Minor
Status: Closed Last Modified: 2024-02-08 20:53:54
Resolution: Fixed Closed By: fvogel
    Closed on: 2024-02-08 20:53:54
Description:
Perhaps I miss something, but I don't understand the outputs of the below code.

It's about [text index {insert wordstart}] command.
I'd tried it with and without "display" modifier - results are the same.

What I don't understand is:

- when the cursor is set at 1.5 and 1.7 positions, the output is 1.5 and 1.7 - OK
- when the cursor is set at 1.2 and 1.3 positions, the output is 1.1 - OK
- when the cursor is set at 1.1, the output is 1.0 - WHAT? I want 1.1 as 1.5 and 1.7 above

The same with 3rd line.

But most amazing things occur on 5th line:

- when the cursor goes 5.0 through 5.3 positions, the outputs are:
4.0
5.0
4.0
4.0

WHAT? I'd prefer 5.0 everywhere in this word.

  #_______________________

  package require Tk

  pack [text .txt -height 10 -width 50]

  .txt insert 1.0 { 123 5 789000

 123 5 789000

0123 5 789000}
  focus .txt
  #_______________________

  bind .txt <ButtonRelease> ::butpress
  bind .txt <KeyRelease> ::butpress

  #_______________________

  proc ::butpress {} {
    after 1000 [list after idle {puts [.txt index {insert display wordstart}]}]
  }

#==============================================================================

I've modified the previous code, see below the result.

And now I don't understand at all what's happening when I click or press a key in the text.

Looks like clicks and key presses on the 3rd and 5th lines can result in different outputs, i.e. sporadic sometimes.

I've intentionally set 1 second wait to let Tk settle down.

But I has to note that all problems are related to *.0 and *.1 positions of word starts (1st, 3rd and 5th lines).

Seemingly, all is OK with the results of wordend modifier.
At least, I've not found the problems with [text index {insert wordend}].

Also, it seems like the fixing this ticket can break some existing Tk codes, highly likely.

And as such it's rather embarrassing ticket to fix, for Tk 8 at any rate.

  # ________________________ BOF _________________________ #

  puts "Tk v[package require Tk]"
  #_______________________

  proc ::pressingButton {} {
    # After a moderate pause to let Tk settle down,
    # outputs the result of clicking or keypressing.

    after 1000 {after idle ::putResults}
  }

  proc ::putResults {} {
    # Outputs the result of clicking or keypressing
    # checking the Tk wordstart against to the waited.
    #
    # If the clicking/pressing position isn't found
    # in the list of awaited results (::list_pos_wstart)
    # just outputs "position + wordstart + wordend".

    set pos [.txt index insert]
    set wstart2 [.txt index {insert display wordstart}]
    set i [lsearch -index 0 $::list_pos_wstart $pos]
    if {$i>=0} {
      set wstart [lindex $::list_pos_wstart $i 1]
      putResult $pos $wstart $wstart2
    } else {
      set wend [wordEnd $pos]
      puts "pos:$pos wordstart:$wstart2 $wend"
    }
  }

  proc ::putResult {pos wstart wstart2} {
    # Outputs "position + waited wordstart + wordstart of Tk".
    #   pos - position
    #   wstart - waited wordstart
    #   wstart2 - wordstart returned by Tk
    # If the waited wordstart is equal to the Tk's,
    # outputs OK, otherwise ERROR.

    puts -nonewline "pos:$pos wordstart:$wstart"
    set wend [wordEnd $pos]
    if {$wstart == $wstart2} {
      puts " $wend - OK"
    } else {
      puts " (got $wstart2) $wend - ERROR"
    }
  }

  proc ::wordEnd {pos} {
    # Returns the info of the wordend position
    # for a position inside a word.
    #   pos - the position inside the word

    return wordend:[.txt index "$pos wordend"]
  }
  #_______________________

  pack [text .txt -height 10 -width 50]

  # create the text widget with 6 lines
  # to be clicked/keypressed only (not edited)
  .txt insert 1.0 { 123 5 789012  LINE-1

 123 5 789012  LINE-3

0123 5 789012  LINE-5
  23 5 789012  LINE-6}
  focus .txt

  # just to be sure that all settled down
  update
  #_______________________

  # list of pairs "cursor position + waited wordstart"
  set list_pos_wstart [list \
   {1.0 1.0} {1.1 1.1} {1.2 1.1} {1.3 1.1} {1.5 1.5} {1.7 1.7} \
   {3.0 3.0} {3.1 3.1} {3.2 3.1} {3.3 3.1} {3.5 3.5} {3.7 3.7} \
   {5.0 5.0} {5.1 5.0} {5.2 5.0} {5.3 5.0} {5.5 5.5} {5.7 5.7} \
   {6.0 6.0} {6.1 6.1} {6.2 6.2} {6.3 6.2} {6.5 6.5} {6.7 6.7}]

  # scan the list to output "position + waited wordstart + wordstart of Tk"
  foreach pos_wstart $list_pos_wstart {
    lassign $pos_wstart pos wstart
    set wstart2 [.txt index "$pos wordstart"]
    putResult $pos $wstart $wstart2
  }
  puts #_______________________

  # bind clickings & keypressings to ::pressingButton procedure
  bind .txt <ButtonRelease> {+ ::pressingButton}
  bind .txt <KeyRelease> {+ ::pressingButton}

  # ...
  # ... here only clickings
  # ... keypressings - LEFT ARROW, RIGHT ARROW, HOME, END
  # ...

  # ________________________ EOF _________________________ #
User Comments: fvogel added on 2024-02-08 20:53:54:
Thanks. I decided to follow your wise advice and have merged the fix.

anonymous added on 2024-02-08 12:59:42:

Francois, the only result I got from my testing is the proposal for you to use a bit modified test of yours, where the problematic positions (0 & 1) of beginning, middle and ending lines of text are all covered.

Perhaps you'll like it:

test textIndex-22.16 {text index wordstart, bug [57b821d2db]} {
    catch {destroy .t}
    text .t
    .t insert 1.0 " 123 5 789012  LINE-1\n\n 123 5 789000 LINE-3\n\n0123 5 789012  LINE-5"
    set res [list]
    foreach pos {1.0 1.1 3.0 3.1 5.0 5.1} {
        lappend res [.t index "$pos wordstart"]
        .t mark set insert $pos
        lappend res [.t index "insert wordstart"]
    }
    set res
} {1.0 1.0 1.1 1.1 3.0 3.0 3.1 3.1 5.0 5.0 5.0 5.0}

I run my tests on Debian 11 and Windows 10, where they show absolutely identical (though erroneous in Tk8.6) results.

So, if the test works OK for you, why don't close the ticket? Ignore me.


fvogel added on 2024-02-08 08:23:53:
Binaries now available for your testing. Warning: these builds did not go through any quality gate. Use them for the sole purpose of testing the fix for the present ticket. Thanks!

fvogel added on 2024-02-06 20:59:51:

Your test textIndex-22.17 passes as is with my fix. After careful analysis it does not exercise anything different from textIndex-22.16.

To help you testing, I have made arrangements in [2bdfc8bb74] so that binaries will get (nightly) built at Github for the bugfix branch. If I didn't screw all this up, they should be available in approximately 11 hours from now here.


anonymous added on 2024-02-06 17:40:35:

OK, I've modified the test, just to show the difference.

Result (in Tk 8.6.10):

---- Result was:
3.1 3.0 3.1 4.0 5.0 4.0
---- Result should have been (exact matching):
3.1 3.1 3.1 5.0 5.0 5.0
==== textIndex-22.17 FAILED

Note the 3.1 3.0 of the initial

[.t index "3.1 wordstart"]
[.t index "insert wordstart"]

Anyhow, as the difference occurs on 3.1 position you might add it to your test.

#_____________________________________________________________________

test textIndex-22.17 {text index wordstart with tk::TextSetCursor, bug [57b821d2db]} {
    catch {destroy .t}
    text .t
    .t insert 1.0 " 123 5 789012  LINE-1\n\n 123 5 789000 LINE-3\n\n0123 5 789012  LINE-5"
    set res [.t index "3.1 wordstart"]
    ::tk::TextSetCursor .t 3.1
    lappend res [.t index "insert wordstart"]
    ::tk::TextSetCursor .t 3.2
    lappend res [.t index "insert wordstart"]
    ::tk::TextSetCursor .t 5.0
    lappend res [.t index "insert wordstart"]
    ::tk::TextSetCursor .t 5.1
    lappend res [.t index "insert wordstart"]
    ::tk::TextSetCursor .t 5.2
    lappend res [.t index "insert wordstart"]
} {3.1 3.1 3.1 5.0 5.0 5.0}

fvogel added on 2024-02-06 13:38:09:
My fix already addresses both cases (the "list" case and the interactive case).

And textIndex-22.16 already exercises both cases as well.

But OK, I'll have a look at your test case below to determine whether it provides an added value or not.

anonymous added on 2024-02-06 13:23:23:
> Oh, really? Please show them in this case.

That's (2) problem already discussed: when you deals not with a list of positions, but set them with clicks and key presses. Interactively. This is provided by tk::TextSetCursor.

I.e. the additional test could be:

test textIndex-22.17 {text index wordstart with tk::TextSetCursor, bug [57b821d2db]} {
    catch {destroy .t}
    text .t
    .t insert 1.0 " 123 5 789012  LINE-1\n\n 123 5 789000 LINE-3\n\n0123 5 789012  LINE-5"
    ::tk::TextSetCursor .t 3.1
    set res [.t index "insert wordstart"]
    ::tk::TextSetCursor .t 3.2
    lappend res [.t index "insert wordstart"]
    ::tk::TextSetCursor .t 5.0
    lappend res [.t index "insert wordstart"]
    ::tk::TextSetCursor .t 5.1
    lappend res [.t index "insert wordstart"]
    ::tk::TextSetCursor .t 5.2
    lappend res [.t index "insert wordstart"]
} {3.1 3.1 5.0 5.0 5.0}

fvogel added on 2024-02-05 20:22:41:

    > there are additional problems with clicks/key presses.

Oh, really? Please show them in this case.

    > I still can't test your fix, since am waiting for new builds.

The builds you refer to at Github are currently not made for 8.6. What platform are you using? Are you really, absolutely, totally, unable to build branch bug-57b821d2db from source?

    > [...] supply the "mark" base with the link to MARKS section

Please see [c21dbc1347].

BTW, in the meantime I have found the fix for the revised_text version also.


anonymous added on 2024-02-05 17:07:41:

Hi Francois,

I use for testing

::tk::TextSetCursor $txt $pos

which is quite identical to the manual clicks & key presses.

These calls can be used in your test suite textIndex-22.16, because there are additional problems with clicks/key presses.

Also, I compared test results for a text containing four languages (de, es, ru, uk), all are identical to the English text's results.

Regretfully, I still can't test your fix, since am waiting for new builds.

#_______________________

As for the text man page, I'd overlooked the marks indeed. But you said "yes" along with "no" and possibly you meant the following.

As there is MARKS section, it would be fine to supply the "mark" base with the link to MARKS section.

Two profits:

  1. highlighting "mark" for nearsighted readers like me

  2. making the powerful hypertext mark from the simple mark :))


fvogel added on 2024-02-03 14:56:08:

    >  "insert" base is absent in INDICES section of the text man page.

Yes and no. In the "Indices" section the documentation tells about marks as possible base indices. And in the "Marks" section, the "insert" mark is documented.


anonymous added on 2024-02-03 14:41:16:

Hi Francois,

Of course, I will test your fix, if it's included into the Tk 9.0 build I use, i.e. (thanks to Andreas) from:

https://github.com/tcltk/tk/actions?query=workflow%3A%22Build+Binaries%22+branch%3Amain

I will test it as scrupulously as I can, especially since it's not the final issue with "insert".

#_______________________

Take e.g. its documentation. Namely:

"insert" base is absent in INDICES section of the text man page.

There is only “insert wordstart - 1 c” example which is far from satisfactory (sort of "guess what's that").

#_______________________

Thank you for the fix, Tk text widget is great whatever happens :))


fvogel added on 2024-02-03 11:07:39:

New test (textIndex-22.16) and fix committed in branch bug-57b821d2db. Applies to the legacy text widget only.


fvogel added on 2024-02-03 08:15:14:

The following patch works for me (fixes both problems, and does not create failures in the test suite) for core-8-6-branch:

Index: generic/tkTextIndex.c
==================================================================
--- generic/tkTextIndex.c
+++ generic/tkTextIndex.c
@@ -2436,10 +2436,13 @@
 			    segPtr->body.chars));
 		}
 		firstChar = 0;
 	    }
             if (offset == 0) {
+		if (indexPtr->byteIndex == 0) {
+		    goto done;
+		}
                 if (modifier == TKINDEX_DISPLAY) {
                     TkTextIndexBackChars(textPtr, indexPtr, 1, indexPtr,
                         COUNT_DISPLAY_INDICES);
                 } else {
                     TkTextIndexBackChars(NULL, indexPtr, 1, indexPtr,
@@ -2448,13 +2451,10 @@
             } else {
                 indexPtr->byteIndex -= chSize;
             }
             offset -= chSize;
 	    if (offset < 0) {
-		if (indexPtr->byteIndex == 0) {
-		    goto done;
-		}
 		segPtr = TkTextIndexToSeg(indexPtr, &offset);
 	    }
 	}
 
 	if (!firstChar) {

Could you (the OP) please test it extensively and confirm?

In the meantime I'll push this in a fossil branch with a test case. I'll also look at the revised_text case.

Thanks!


fvogel added on 2024-02-01 21:50:00:
Sorry, I made a mistake. Both problems (1) and (2) also affect the revised text widget.

fvogel added on 2024-02-01 21:46:14:

Problem (1), i.e. [.txt index {5.2 wordstart}] ==> 4.0 can be fixed by the following patch I think:

Index: generic/tkTextIndex.c
==================================================================
--- generic/tkTextIndex.c
+++ generic/tkTextIndex.c
@@ -2436,10 +2436,13 @@
 			    segPtr->body.chars));
 		}
 		firstChar = 0;
 	    }
             if (offset == 0) {
+		if (indexPtr->byteIndex == 0) {
+		    goto done;
+		}
                 if (modifier == TKINDEX_DISPLAY) {
                     TkTextIndexBackChars(textPtr, indexPtr, 1, indexPtr,
                         COUNT_DISPLAY_INDICES);
                 } else {
                     TkTextIndexBackChars(NULL, indexPtr, 1, indexPtr,

Note: Problem (1) does NOT affect the revised text widget. However problem (2) does affect both the legacy text widget and the revised text widget.


anonymous added on 2024-01-31 05:28:39:
Also, please turn your attention to the second example above. It puts out [.txt index "$pos wordstart"] with $pos taken from a list,
not from user's clicks & key presses where {insert wordstart} participates.

And these outputs DIFFER from ones initiated by a user!

You can try it yourself with
  puts [.txt index {5.2 wordstart}]
  puts [.txt index {3.1 wordstart}]

So, in fact the ticket contains two problems:

  - why [.txt index {5.2 wordstart}] ==> 4.0 ?

  - why [.txt index {3.1 wordstart}] => 3.1 (when by the script) and 3.0 (when by a user)

Obviously in (1) wordstart modifier fails by itself,
and in (2) insert modifier does too, additionally.

Has it to be split in two tickets?

anonymous added on 2024-01-31 04:39:49:
Perhaps. You know better.

But for the world outside Tk, this snag may present rather serious problem, that can fire in most unexpected cases.
Especially if some workarounds were made at using Tk 8.

Ignoring this issue in Tk 9 would be unreasonable at any rate, imho.

jan.nijtmans added on 2024-01-30 17:51:48:

Even though it's strange behavior, the only place where {insert wordend} is used (in Tk itself) is in the console (library/console.tcl). That's hardly considered "Critial".

In case someone wants to look at the bug, the handling is here: https://core.tcl-lang.org/tk/file?name=generic/tkTextIndex.c&ci=97e7e839f69b6b60&ln=2378-2420