#!/afs/ece/usr/tcl/bin/wish -f
# The next line is executed by most shells, but not Tcl \
wish $0 $*


set Bind_Keyword [file tail [info script]]
source "[file dirname [info script]]/../util/frame.tcl"

# Help text.
set Help "" ; append Help {Completh -- Teach various string completion techniques to widgets

Many programs, including shells like tcsh offer string completion of things like
file names. This tool teaches textual widgets (currently text, entry, and canvas
widgets) how to complete strings in many different ways.

} $TH_Bindings_Help {

Widgets of Completh

File for Completion Entry / Add Button (Word from File:.. menu item)

Enter a filename onto this entry, and press the button, and a new menu item
will appear on the Completion Functions menu. This menu item will allow words
to be completed using words from the file specified on the entry.


List for Completion Entry / Add Button (Item from List:.. menu item)

Putting a list of items on this entry and pressing the button creates a new menu
item on the Completion Functions menu. This menu item will allow lines to be
completed using the list specified. For example, if the entry contains the items
{True False}, then you can complete 'T' to True.


'Use Widget for Completion' button

When this option is selected, completh allows you to select a widget in any
application. Then it adds a menu option to the Completion Functions menu to
allow you to use that widget as a source for completion. When teaching
completion to a program using a widget for source, that widget must be in the
same program learning completion. The type of completion varies on the type of
widget used as source, and are described below.


Completion Functions Menu

This is a menu showing all the types of completion available. Each type has a
checkbutton associated with it, if an item's checkbutton is on when a widget is
taught completion, that item's function will be included. For example, a widget
will be taught to expand glob patterns, if the 'Expand Glob Patterns' menuentry
is turned on.

Whenever completion is invoked, the functions that are available are called in
descending order. If a function yields no completions, the next one is called.
The program beeps if no functions yielded any available completions. For
example, if the 'Complete Command' and 'Complete Filename' options are turned
on, and completion is invoked when the text before the cursor reads: "[inf", the
command completion will yield the completion "[info", since that is a legitimate
Tcl command. If completion is invoked when the text reads "[x", and there is no
Tcl procedure starting with 'x', but there just happens to be a file called
"[xfigs]" (which is usually a tricky feat in itself), then the completion will
yield "[xfigs]". If no file exists and no Tcl command exists, then the program
will beep.

There is a set of functions that get put on the completion menu upon starting up
completh. More completion functions can be added using the other widgets. But
first, let's examine the functions immediately available.

  Complete Filename

To use this function, enter a filename preceded by a space, and this function
will attempt to complete it. For example " ma" might return " makefile". You
can also use this as a simple filter. For example, if your directory contains
the files foo.c, bar.c, and foobar.h, then " *.h" completes to foobar.h, but 
"*.c" doesn't complete to anything, since there are multiple completions
possible. However, you can then list the .c files (foo.c and bar.c) by
requesting for possible completions.

  Complete Variable

To use this function, enter a Tcl global variable, preceded by a $ in usual Tcl
fashion, and this function will attempt to complete the variable. Or you can
enter an array and part of an index, and this function will complete the index.
For exmple: $en can complete to $env and $env(D can complete to $env(DISPLAY))

  Complete Command

To use this function, enter an open bracket and part of a Tcl command. This
function will complete the command, and if the string is ambiguous, you can view
all the possible Tcl commands. For example, [in will beep because both [incr and
[info are legitimate completions.

  Expand Glob Patterns

To use this function, enter a glob pattern after a space. This function will
replace the glob pattern with all the files it expands to. For example, " *.c"
might return " foo.c bar.c".

  Value of Variable

To use this function, enter a Tcl global variable preceded by a $ in the usual
Tcl fashion, or array. This function will replace the variable with its value.
If you enter an array variable, this function will append an '(' to it, so you
can then do array index completion. For example, $argc might return 2, and
$env(DISPLAY) might return ":0.0", and $env might return $env(.

  Result of Tcl Command

To use this function, enter a Tcl command between a pair of brackets, and after
hitting the close bracket, do completion, and the command will be replaced with
the Tcl command's result. The program will beep if an error results from the
code. Invoking the dialog will then bring up an information window showing the
error message. For example, [info tclversion] might return '7.4'.

  History Substitution

This feature uses !-syntax, as in csh, along with a widget's history list. So
if an entry once contained, for example, "foo", "f g", and "bar", !1 expands to
'foo', !f expands to 'f g', and !-1 and !! expand to 'bar'. Unlike csh,
however, this feature does not substitute the last event in the list if the
completion is ambiguous, but it allows you to list the possible completions.
So, for example, you could complete on '!' to view all events in the current
history list.

  Filename Completion

This takes the text from the line's beginning to the cursor. This means all the
text on an Entry widget, or a Canvas item, or all the text on one line of a
Text widget before te cursor. It interprets that text as the beginning of a
file or path name, and tries to complete it, just like the 'Complete Filename'
option described above. It has one extra feature over the 'Complete Filename'
option, which is, if it is used when the text is empty (an empty Entry widget,
for example), it inserts the present working directory.

  Command Completion

This takes the text from the line's beginning to the cursor, assumes it is part
of a Tcl command and tries to complete that command, just like the 'Complete
Command' option described above. Unlike the 'Complete Command' option, this
option expands an empty string (in fashion similar to 'Filename Completion') to
the widget name of the widget with the current input focus.

  Abbreviation Lexicon

This function allows words to be completed. A word is any set of letters,
numbers, or undescores, or any single character that is not a letter, number, or
underscore. This function is associated with an abbreviation list, consisting of
abbreviations, such as 1st, 2nd, 3rd, and words, such as first, second, third.
If the text before the cursor is an abbreviation, the appropriate word is
substituted, so you can type 1st, and replace it with first. You must type the
abbreviation exactly as it is stored to expand it.

Right now completh does not provide a way to specify what abbreviations are
available...see the Bugs section for how to add them.

  Text Widgets (Word from: ...)

This, and other subsequent menu entries don't exist upon startup, but if you
select the 'Use Widget for Completion' button, and select a text widget, this
entry gets added, using that text widget. The text widget is searched for a word
that begins the same way as the word just typed in the widget with completion
(which may be the same text widget!). As soon is it finds a match, the
incomplete string is completed to the same word. Example, 'th' might complete to
this, the, then, etc. depending on the contents of the widget. Unlike most
functions, this one does not search for all completions immediately, but returns
upon finding one. You can invoke completion again to find a different
completion, or you can invoke the completion dialog to see all possible
completions. If the text widget contains a lot of text, this might take a while
to generate.

  Entry Widgets (Word from: ...)

This works the same way, except that an entry widget is used as the source text
instead.

  Message Widgets (Word from: ...)

This also works the same, except that the text from a message widget is used as
the source instead.

  Menu Widget (Entry from: ...)

If you select a menu (not a menubutton!) that menu is searched for an entry
whose text label starts with the line before the cursor. No submenus are
searched.

  Menubutton Widget (Entry from: ...)

If you select a menubutton, its menu, as well as all of its submenus are
searched for an entry whose text label starts with the line before the cursor.

  Listbox Widget (Line from listbox: ...)

The listbox is searched for an item that matches the text on the line before the
cursor.

  Canvas Widget (Text from canvas: ...)

The canvas is searched for a text item whose text matches the text on the line
before the cursor.

} $TH_Frame_Help {

This program uses only one set of global variables per application. This can
cause confusion if you have two widgets with completions defined, and switch
between them often; they may get confused about each other's outstanding
completion possibilities. To avoid this, only request the completion listbox
immediately after invoking a completion in the same widget (which is the natural
thing to do anyway).

Right now there is no way to specify the abbreviation lexicon for the
abbreviation function. The lexicon must be specified in the remote application
by some explicitly code. Here is an example:

set TH(Completion,Wordlist) {{first} {second} {third} {fourth} 
	{Internal Revenue Service} {government}}
set TH(Completion,Abbrevs) {{1st} {2nd} {3rd} {4th} {IRS} {govt}}

If a widget has multiple binding tags bound to the completion bindings, only
the completions bound to the first bindtag get executed, whether they find
a legitimate completion or not.

Beware of doing things like '!l<Return>'. In csh, this brings up & executes the
last command starting with 'l', but completh will not complete on this (since
you did not request it), and so this would generate some kind of error.}


# Gives app all the code necessary to do our functions.
proc teach_code {} {
  global Class App Widget Bindings
  if {[lsearch "Entry Text Canvas" $Class] < 0} {return ""}

  include_files {complete.Misc.tcl th_string_complete_multiple} \
	[list "complete.$Class.tcl" "th_[set Class]_complete_multiple"]

  foreach cf [form_menu_selected Completions] {
    if {[lindex $cf 0] == "th_word_replace"} {
      include_files {complete.word.tcl th_word_replace}
    } else {
      include_files {complete.line.tcl th_string_global_value}
  }}

  upvar 1 class_flag cl
  if {![catch "set cl"] && !$cl} {set w $Widget} else {set w $Class}

  foreach binding $Bindings(Completion) {teach_binding_code [lindex $binding 0]}

  do_cmd_set TH(Completions,$w) [form_menu_selected Completions]
}

proc widget_bindings {} {
  global Class App Menu_Flag Bindings
  if {[lsearch "Entry Text Canvas" $Class] < 0} {
    return ""}
  set completions [list [form_menu_selected Completions]]
  if {$completions == "{}"} {return ""}

  return $Bindings(Completion)
}


# Adds completion from a file
proc add_file_completion {} {
  global File_For_Completion
  if {!(($File_For_Completion != "") && [file exists $File_For_Completion] && \
      [file readable $File_For_Completion])} {
    bell ; return}
  add_to_form_menu Completions [list "Word from file: $File_For_Completion" \
	[list th_word_replace $File_For_Completion ""]]
}

proc completion_source_widget {} {
  global App Widget Class
  switch $Class {
    "Text" - "Entry" - "Message" {
      set option [list "Word from text in $Widget" \
	[list th_word_replace $Widget ""] $App]
    } "Canvas" {
      set option [list "Text from canvas: $Widget" \
	[list th_line_complete [list th_canvas_text_entries $Widget] none] $App]
    } "Listbox" {
      set option [list "Line from listbox: $Widget" \
	[list th_line_complete [list th_listbox_entries $Widget] none] $App]
    } "Menubutton" {
      set option [list "Entry from menus in: $Widget" [list th_line_complete \
		[list th_cascading_menu_entries $Widget] none] $App]
    } "Menu" {
      set option [list "Entry from menu: $Widget" \
	[list th_line_complete [list th_menu_entries $Widget] none] $App]
    } detault {set option "" ; bell}}

  if {$option != ""} { add_to_form_menu Completions $option }
}


create_form_menu Completions \
 {"Complete Filename" {th_substring_complete th_filter_glob { }}} \
 {"Complete Variable" {th_substring_complete th_filter_vars {$}}} \
 {"Complete Command" {th_substring_complete th_filter_cmds {[}}} \
 {"Expand Glob Patterns" {th_substring_replace th_string_glob_files { }}} \
 {"Value of Variable" {th_substring_replace th_string_global_value {$}}} \
 {"Result of Tcl Command" {th_substring_replace th_string_tcl_result {[}}} \
 {"History Substitution" {th_line_complete th_filter_history none}} \
 {"Filename Completion" {th_line_complete th_filter_glob_pwd none}} \
 {"Command Completion" {th_line_complete th_filter_cmds_focus none}} \
 {"Abbreviation Lexicon" {th_word_replace "" ""}}
for {set inc 8} {$inc <= 10} {incr inc} {set Menu(completions,$inc) 0}

create_form_entry .filecomp "File for Completion" File_For_Completion ""
button .filecomp.use -text "Add to Menu" -command add_file_completion
pack .filecomp.use -side right -before .filecomp.e
lappend TH(Completions,.filecomp.e) {th_line_complete th_filter_glob none}

create_form_entry .listcomp "List for Completion" List_For_Completion ""
button .listcomp.use -text "Add to Menu" -com {add_to_form_menu Completions \
	[list "Item from List: [lrange $List_For_Completion 0 1]..." [list \
	th_line_complete [list th_list_completions $List_For_Completion] none]]}
pack .listcomp.use -side right -before .listcomp.e

pack [button .widgetcomp -text "Use Widget for Completion" -command {
	if {![get_widget]} {bell} else {completion_source_widget}}] -fill x
