Ctext

Documentation at [L1 ]

Ctext is

  • part of tklib.
  • a megawidget that is built upon the Tk text widget. It provides syntax highlighting, and can be used to highlight a variety of languages. It provides a simple API and more radical changes (other than color) can be made by modifying the tag attributes. By default matching pairs of characters are flashed on the screen, like the popular Emacs and Lisp editors can do.
  • licensed like Tcl (BSDish), but copyrighted

See also TkTextPlus and TkScintilla for alternative approaches.


 What: custom text widget
 Where: http://tcllib.sf.net/ (within the tklib module)
 Description: Fast syntax highlighting text megawidget.
        Provides a way to control the highlighting colors for classes
        of keywords.  Latest version has electric braces/quotes/brackets
        and a new edit modified instance command.
        Has been tested with Tcl/Tk 8.3.2 and 8.4b1.
 Updated: 05/2004
 Contact: tklib maintainers

RLH - I just looked at the docs (which I somehow missed before). What a great widget!

AK: The docs were actually added last week, so you could not find them before.

KJN: Thanks for the docs. In case anyone else is looking, they were added to CVS on 2005-04-08, and are not yet in a released version of Tcllib. See the link at the top of the page for the HTML version.


TR: A real-life example using ctext and BWidget for interactive debugging and introspection is here: a debugger with syntax highlighting using ctext


MSH: 2006-05-05 I have just discovered a major drawback with Ctext. Using the linemap to display linenumbers all works OK till I change the size of the font used (I have highlighting using 4 fonts, normal, bold, italic and bolditalic). When the font increases (CTRL-Mousewheel), the numbers no longer line up !! After investigation I found that the -linespace font metrics is not identical for a given point size on all four fonts !!

I am working on a proc to adjust the sizes of the fonts individually to maintain a nominal linespacing UNLESS someone knows better ? ;-)

George Peter Staplin Unfortunately that's a known problem. The linemap wasn't designed for varying fonts, and different offsets for lines. I have some ideas relating to this, but they involve lots of redesign.


MSH: 2006-06-21 Me again, Font resizing is implemented but under linux the fixed fonts are reeealy ugly when scaled up. I have since discovered another problem, I have an application which uses two text widgets one for a help system with images and another with a fixed font line numbered ctext widget. I saw that the text widget of 8.5 does pixel scrolling with images so I repacked with 8.5a2 and the help is ok but now the fixed text can scroll by a few pixels and the line numbers no longer line up!! Is there any way to force the new text widget to only scroll by whole text lines? Or has someone found another way to update the numbers in ctext ?

George Peter Staplin I think a better approach would be to make ctext actually insert line numbers into the actual text widget, rather than having a separate widget for the linemap. If we then iterate the dump results we could extract the text, and trim these line numbers (perhaps with a special tag).

Martyn Smith 2009-06-12 I have finally had a few spare hours to update things to 8.5 and found that all that was needed in ctext for the linenumbers was a few lines

    # Calculate the pixel offset of the first line of text
    set off [lindex [$win._t dlineinfo @0,0] 1]
    # Adjust the offset
    set off [expr {abs($off-3)}]
    # Yscroll the text widget by the number of pixels calculated
    $win.l yview scroll $off pixels

which shift the linenumbers up the correct number of pixels

LV Don't forget to submit a patch report to tklib.sf.net so that your changes make it into tklib!


MB: 2006-07-27 I would like to use ctext but I am failing to figure out how to use the undo feature. This is my example :

 package require ctext
 ctext .t -undo 1
 pack .t
 .t insert 1.0 "This is a sentence."
 .t edit undo
 if {[.t edit modified]==1} then {
     puts "Ctext Failed"
 } else {
     puts "Ctext Pass"
 }
 destroy .t

With the current version of ctext (3.1), this prints out "Ctext Failed". Am I wrong or there is a bug in line 475 of ctext.tcl where the ar(modified) value is not updated even if a ".t edit undo" is called? This is the wrong version :

 return [uplevel 1 [linsert $args 0 $self._t $cmd]]

This is what I think fixes it:

 set result [uplevel 1 [linsert $args 0 $self._t $cmd]]
 set ar(modified) [$self._t edit modified]
 return $result

Am I wrong ?

George Peter Staplin You're probably not wrong. I don't use the undo feature very often. I may look into the problem, unless someone has already applied a patch to tklib/ctext.


Gong Ding: The edit modified command should be fixed like this:

                        if {"modified" == $subCmd} {
                                if {$argsLength == 1} {
                                        return $ar(modified)
                                } elseif {$argsLength == 2} {
                                        set value [lindex $args 1]
                                        set ar(modified) $value
                                        $self._t edit modified $value
                                } else {
                                        return -code error "invalid arg(s) to $self edit modified: $args"
                                }
                        }  else {
                                #Tk 8.4 has other edit subcommands that I don't want to emulate.
                                #return [uplevel 1 [linsert $args 0 $self._t $cmd]]
                                set result [uplevel 1 [linsert $args 0 $self._t $cmd]]
                                set ar(modified) [$self._t edit modified]
                                return $result
                        }

Paul Danielson: Almost. To get the <<Modified>> event notification to work properly, I had to add event generation to the instanceCmd. Here's a proc that fixes it for me in ctext 3.1:

    proc patchCtext {} {
      # fixes problem with modified notification in ctext widget v3.1

      if { [catch {package require -exact ctext 3.1}] } {
        return "patch only works for ctext 3.1"
      } 
      set b [info body ctext::instanceCmd]

      set pat1    {set ar\(modified\) \$value}
      set rep1    {set ar(modified) $value}
      append rep1 {\n$self._t edit modified $value}
      append rep1 {\nevent generate $self <<Modified>>}
      append rep1 {\nputs "instance 1: $value"}
      set rep1    [subst -novariables $rep1]
      set b1 [regsub $pat1 $b $rep1]

      set pat2    {return \[uplevel 1 \[linsert \$args 0 \$self._t \$cmd]]}
      set rep2    {set result [uplevel 1 [linsert $args 0 $self._t $cmd]]}
      append rep2 {\nset ar(modified) [$self._t edit modified]}
      append rep2 {\nevent generate $self <<Modified>>}
      append rep2 {\nputs "instance 2: $ar(modified)"}
      append rep2 {\nreturn $result}
      set rep2    [subst -nocommands -novariables $rep2]
      set b2 [regsub $pat2 $b1 $rep2]

      proc boo { self cmd args } $b2
      rename ::ctext::instanceCmd {}
      rename boo ::ctext::instanceCmd
    }

SeS - 2009-08-05 09:37:33

Hi all, I am working on a major private project, i hope to share it with you all some day... I was working on my source editor (part of the project), i stumbled upon the fact that insert/overwrite mode is not readily available, or did i miss something (again?). Anyways, here is a solution:

  bind ${module1_path} <KeyPress> {
    if {%k == 45} {
      set insertMode [expr {!$insertMode}]
    }
    if {$insertMode} {
      ${module1_path}.info tag remove sel 0.0 end
      set curIndex [${module1_path}.info index insert]
      catch {${module1_path}.info tag add \
        sel $curIndex [lindex [split $curIndex .] 0].[expr {[lindex [split $curIndex .] 1] + 1}]}
    }
  }

You all are great, i learned a lot form this site! Selam/Groeten/Grussen/Regards


SeS - 2011-05-12

I realize the code above is not functional without some adaptations, it was my second or third (can't remember) contribution to wiki.tcl.tk back then and I did not anticipate on how one is supposed to test this, so, here another try:

  # .ct is the pathName of the ctext widget.
  # pressing the Insert key toggles overwrite/insert mode
  set ::overwriteMode 0
  bind .ct <KeyPress> {
    if {%k == 45} {set overwriteMode [expr {!$overwriteMode}]}
    if {$overwriteMode} {
      .ct tag remove sel 0.0 end
      set curIndex [.ct index insert]
      catch {.ct tag add sel $curIndex [lindex [split $curIndex .] 0].[expr {[lindex [split $curIndex .] 1] + 1}]}
    }
  }

And with this oppertunity, another contribution I would like to make by sharing with you a quick way to toggle the hash character ("#"), as a speciall feature for the ctext widget. I implemented this feature into the source editor of tG² and I use it frequently, bullet proof and provides fast (de)commenting. See Easy Ctext Commenting


AlexD - 2012-07-12 08:35:54

Hello,

is there a way to have case insensitive highlighting with ctext?

Thank you very much in advance.

AMG: You can make your regular expressions case-insensitive by starting them with (?i).


SeS - August 4th, 2012

Enabling mousebutton sensitivity to a ctext widget's highlighted texts


mb88 - 2021-11-23 20:28:07

Line numbers are not aligned with the content of the text box.

When I was using the IDE that I wrote using it, I saw that the line numbers are are not aligned with the content, and I can't publish an issue on the Github repo of tklib.

chw Please see how to create a bug report in tklib's github mirror.


prep - May 17th, 2023

The electric braces/blinking text was bothering me, ideally there would be a widget option to disable it but I couldn't find one. Here's a hack around that, add it before using the command, it just makes the blinking functions do nothing:

proc ctext::matchPair {win str1 str2 escape} {;}
proc ctext::matchQuote {win} {;}