00001 # TKE - Advanced Programmer's Editor
00002 # Copyright (C) 2014-2019 Trevor Williams (phase1geo@gmail.com)
00003 #
00004 # This program is free software; you can redistribute it and/or modify
00005 # it under the terms of the GNU General Public License as published by
00006 # the Free Software Foundation; either version 2 of the License, or
00007 # (at your option) any later version.
00008 #
00009 # This program is distributed in the hope that it will be useful,
00010 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00012 # GNU General Public License for more details.
00013 #
00014 # You should have received a copy of the GNU General Public License along
00015 # with this program; if not, write to the Free Software Foundation, Inc.,
00016 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00017
00018 ######################################################################
00019 # Name: search.tcl
00020 # Author: Trevor Williams (phase1geo@gmail.com)
00021 # Date: 9/9/2013
00022 # Brief: Namespace for all things related to editor searching.
00023 ######################################################################
00024
00025 namespace eval search {
00026
00027 variable lengths {}
00028
00029 array set data {
00030 find,hist {}
00031 find,hist_ptr 0
00032 find,current {}
00033 replace,hist {}
00034 replace,hist_ptr 0
00035 replace,current {}
00036 fif,hist {}
00037 fif,hist_ptr 0
00038 fif,current {}
00039 docsearch,hist {}
00040 docsearch,hist_ptr 0
00041 docsearch,current {}
00042 }
00043
00044 ######################################################################
00045 # Performs string search
00046 proc do_find {txt search_data} {
00047
00048 array set data_array $search_data
00049
00050 # If the user has specified a new search value, find all occurrences
00051 if {$data_array(find) ne ""} {
00052
00053 # Gather any search options
00054 switch $data_array(method) {
00055 "glob" {
00056 set search_opts [list -regexp]
00057 set data_array(find) [string map {* .* ? . {(} {\(} {)} {\)}} $data_array(find)]
00058 }
00059 "exact" {
00060 set search_opts [list -exact]
00061 }
00062 "regexp" {
00063 set search_opts [list -regexp]
00064 set data_array(find) [string map {{(} {\(} {)} {\)}} $data_array(find)]
00065 }
00066 }
00067
00068 if {!$data_array(case)} {
00069 lappend search_opts -nocase
00070 }
00071
00072 # Test the regular expression, if it is invalid, let the user know
00073 if {($data_array(method) ne "exact") && [catch { regexp $data_array(find) "" } rc]} {
00074 after 100 [list gui::set_info_message $rc -win $txt]
00075 return
00076 }
00077
00078 # Save the find text to history
00079 add_history find $search_data
00080
00081 # Clear the search class
00082 catch { $txt syntax delete classes search search_curr }
00083
00084 # Create a highlight class for the given search string
00085 $txt syntax addclass search_curr -fgtheme search_foreground -bgtheme marker -priority high
00086 $txt syntax addclass search -fgtheme search_foreground -bgtheme search_background -priority 1
00087 $txt syntax search search $data_array(find) $search_opts
00088
00089 }
00090
00091 }
00092
00093 ######################################################################
00094 # Performs a search of the current text widget in the given direction
00095 # with the text specified in the specified entry widget.
00096 proc find_start {direction} {
00097
00098 variable data
00099
00100 # Get the current text widget
00101 set txt [gui::current_txt]
00102
00103 # Get the search information
00104 do_find $txt [gui::get_search_data find]
00105
00106 # Select the search term
00107 if {$direction eq "next"} {
00108 find_next $txt
00109 } else {
00110 find_prev $txt
00111 }
00112
00113 # Close the search panel
00114 gui::close_search
00115
00116 }
00117
00118 ######################################################################
00119 # Performs a resilient find operation in the given directory. Resilient
00120 # searches keep the search panel visible and are started by clicking
00121 # either the next or previous buttons.
00122 proc find_resilient {dir {type find}} {
00123
00124 variable data
00125
00126 set txt [gui::current_txt]
00127
00128 # Get the search data
00129 set search_data [gui::get_search_data $type]
00130
00131 # Get the search information
00132 if {$search_data ne [lindex $data(find,hist) end]} {
00133 do_find $txt $search_data
00134 }
00135
00136 if {$dir eq "next"} {
00137 find_next $txt
00138 } else {
00139 find_prev $txt
00140 }
00141
00142 }
00143
00144 ######################################################################
00145 # Clears the current search text.
00146 proc find_clear {} {
00147
00148 # Get the current text widget
00149 set txt [gui::current_txt]
00150
00151 # Clear the highlight class
00152 catch { $txt syntax delete classes search search_curr }
00153
00154 # Clear the UI
00155 gui::search_clear
00156
00157 }
00158
00159 ######################################################################
00160 # Returns true if the Find Next/Previous menu options should be enabled;
00161 # otherwise, returns false.
00162 proc enable_find_view {txt} {
00163
00164 return [expr {[$txt syntax ranges search] ne ""}]
00165
00166 }
00167
00168 ######################################################################
00169 # Searches for the next occurrence of the search item.
00170 proc find_next {txt} {
00171
00172 set wrapped 0
00173
00174 # Search the text widget from the current insertion cursor forward.
00175 lassign [$txt syntax nextrange search "insert+1c"] startpos endpos
00176
00177 # We need to wrap on the search item
00178 if {$startpos eq ""} {
00179 lassign [$txt syntax nextrange search 1.0] startpos endpos
00180 set wrapped 1
00181 }
00182
00183 # Clear the search_curr
00184 $txt syntax clear search_curr
00185
00186 # Select the next match
00187 if {$startpos ne ""} {
00188 ::tk::TextSetCursor $txt.t $startpos
00189 $txt syntax add search_curr $startpos $endpos
00190 if {$wrapped} {
00191 gui::set_info_message [msgcat::mc "Search wrapped to beginning of file"] -win $txt
00192 } else {
00193 gui::set_info_message "" -win $txt
00194 }
00195 } else {
00196 gui::set_info_message [msgcat::mc "No search results found"] -win $txt
00197 }
00198
00199 }
00200
00201 ######################################################################
00202 # Searches for the previous occurrence of the search item.
00203 proc find_prev {txt} {
00204
00205 set wrapped 0
00206
00207 # Search the text widget from the current insertion cursor forward.
00208 lassign [$txt syntax prevrange search insert] startpos endpos
00209
00210 # We need to wrap on the search item
00211 if {$startpos eq ""} {
00212 lassign [$txt syntax prevrange search end] startpos endpos
00213 set wrapped 1
00214 }
00215
00216 # Clear the search_curr
00217 $txt syntax clear search_curr
00218
00219 # Select the next match
00220 if {$startpos ne ""} {
00221 ::tk::TextSetCursor $txt.t $startpos
00222 $txt syntax add search_curr $startpos $endpos
00223 if {$wrapped} {
00224 gui::set_info_message [msgcat::mc "Search wrapped to end of file"] -win $txt
00225 } else {
00226 gui::set_info_message "" -win $txt
00227 }
00228 } else {
00229 gui::set_info_message [msgcat::mc "No search results found"] -win $txt
00230 }
00231
00232 }
00233
00234 ######################################################################
00235 # Returns true if the insertion cursor is currently located on a found
00236 # search item.
00237 proc enable_select_current {txt} {
00238
00239 return [$txt syntax contains search insert]
00240
00241 }
00242
00243 ######################################################################
00244 # Appends the current search text to the selection.
00245 proc select_current {txt} {
00246
00247 # If the insertion cursor is not currently on a search item, return immediately
00248 if {![enable_select_current $txt]} {
00249 return
00250 }
00251
00252 # Add the search term to the selection
00253 $txt tag add sel {*}[$txt syntax prevrange search "insert+1c"]
00254
00255 }
00256
00257 ######################################################################
00258 # Selects all of the found text occurrences.
00259 proc select_all {txt} {
00260
00261 # Clear the selection
00262 $txt tag remove sel 1.0 end
00263
00264 # Get the search ranges
00265 if {[set ranges [$txt syntax ranges search]] ne ""} {
00266
00267 # Add the ranges to the selection
00268 $txt tag add sel {*}$ranges
00269
00270 # Make the last matched item viewable
00271 $txt mark set insert [lindex $ranges end]
00272 $txt see [lindex $ranges end]
00273
00274 }
00275
00276 }
00277
00278 ######################################################################
00279 # Performs a search and replace operation based on the GUI element
00280 # settings.
00281 proc replace_start {replace_all} {
00282
00283 array set data_array [set search_data [gui::get_search_data replace]]
00284
00285 # Perform the search and replace
00286 replace_do_raw 1.0 end $data_array(find) $data_array(replace) $data_array(method) [expr !$data_array(case)] $replace_all
00287
00288 # Add the search data to history
00289 add_history replace $search_data
00290
00291 # Close the search and replace bar
00292 gui::close_search_and_replace
00293
00294 }
00295
00296 ######################################################################
00297 # Performs a search and replace given the expression,
00298 proc replace_do_raw {sline eline search replace search_method ignore_case all} {
00299
00300 variable lengths
00301
00302 # Get the current text widget
00303 set txt [gui::current_txt]
00304
00305 # Clear the selection
00306 $txt tag remove sel 1.0 end
00307
00308 # Create regsub arguments
00309 if {$search_method eq "glob"} {
00310 set rs_args [list -regexp]
00311 set search [string map {* .* ? .} $search]
00312 } else {
00313 set rs_args [list -$search_method]
00314 }
00315
00316 if {$ignore_case} {
00317 lappend rs_args -nocase
00318 }
00319
00320 # Get the list of items to replace
00321 set indices [$txt search -all -count search::lengths {*}$rs_args -- $search $sline $eline]
00322 set matches [list]
00323
00324 if {$all} {
00325 foreach index [lreverse $indices] length [lreverse $lengths] {
00326 lappend matches $index [$txt index "$index+${length}c"]
00327 }
00328 } else {
00329 set last_line 0
00330 foreach index $indices length $lengths {
00331 set curr_line [lindex [split $index .] 0]
00332 if {$curr_line != $last_line} {
00333 lappend matches [$txt index "$index+${length}c"] $index
00334 set last_line $curr_line
00335 }
00336 }
00337 set matches [lreverse $matches]
00338 }
00339
00340 # Make sure that newline characters and tabs are replaced properly
00341 set replace [string map {{\n} \n {\t} \t} $replace]
00342
00343 if {$matches ne [list]} {
00344
00345 # Perform replacement
00346 do_replace $txt $matches $search $replace
00347
00348 # Specify the number of substitutions that we did
00349 gui::set_info_message [format "%d %s" [llength $matches] [msgcat::mc "substitutions done"]] -win $txt
00350
00351 } else {
00352
00353 gui::set_info_message [msgcat::mc "No search results found"] -win $txt
00354
00355 }
00356
00357 }
00358
00359 ######################################################################
00360 # Performs the replacement operation for the given indices.
00361 proc do_replace {txt matches search replace} {
00362
00363 set do_tags [list]
00364 set replace_len [string length $replace]
00365
00366 # Make sure that the text widget is editable
00367 $txt configure -state normal
00368
00369 # Replace the text (perform variable substitutions if necessary)
00370 foreach {startpos endpos} $matches {
00371 set rendpos [$txt index $startpos+${replace_len}c]
00372 if {[llength $do_tags] == 0} {
00373 ctext::comments_chars_deleted $txt $startpos $endpos do_tags
00374 }
00375 $txt fastreplace -update 0 $startpos $endpos [regsub $search [$txt get $startpos $endpos] $replace]
00376 if {[llength $do_tags] == 0} {
00377 ctext::comments_do_tag $txt $startpos $rendpos do_tags
00378 }
00379 }
00380
00381 # Perform the highlight
00382 $txt syntax highlight -dotags $do_tags -insert 1 -modified 1 {*}$matches
00383
00384 # Set the insertion cursor to the last match and make that line visible
00385 ::tk::TextSetCursor $txt [lindex $matches 0]
00386
00387 # Make sure that the insertion cursor is valid
00388 vim::adjust_insert $txt
00389
00390 }
00391
00392 ######################################################################
00393 # Replaces the matched item that exists on the insertion cursor with the
00394 # string that is in the replace field in the GUI.
00395 proc replace_one {} {
00396
00397 gui::get_info {} current txt
00398
00399 # Get the string to replace the current value with
00400 array set data_array [gui::get_search_data replace]
00401
00402 # Get the range to replace
00403 lassign [$txt syntax prevrange search "insert+1c"] startpos endpos
00404
00405 # Perform the replacement
00406 $txt replace $startpos $endpos [regsub $data_array(find) [$txt get $startpos $endpos] $data_array(replace)]
00407
00408 # Make sure that the insertion cursor is valid
00409 vim::adjust_insert $txt
00410
00411 # Find the next match
00412 find_next $txt
00413
00414 }
00415
00416 ######################################################################
00417 # Performs an egrep-like search in a user-specified list of files/directories.
00418 proc fif_start {} {
00419
00420 variable data
00421
00422 set rsp_list [list]
00423
00424 # Display the find UI to the user and get input
00425 if {[gui::fif_get_input rsp_list]} {
00426
00427 array set rsp $rsp_list
00428
00429 # Add the rsp(find) value to the history list
00430 add_history fif $rsp_list
00431
00432 # Convert directories into files
00433 array set files {}
00434 foreach file $rsp(in) {
00435 if {[file isdirectory $file]} {
00436 foreach sfile [glob -nocomplain -directory $file -types {f r} *] {
00437 if {![sidebar::ignore_file $sfile 1]} {
00438 set files($sfile) 1
00439 }
00440 }
00441 } elseif {![sidebar::ignore_file $file 1]} {
00442 set files($file) 1
00443 }
00444 }
00445
00446 # Convert the pattern based on the given search method
00447 switch $rsp(method) {
00448 glob {
00449 set rsp(find) [string map {* .* ? . . \\. \{ \\\{ \} \\\} ( \\( ) \\) ^ \\^ - \\- + \\+} $rsp(find)]
00450 }
00451 exact {
00452 set rsp(find) [string map {* \\* ? \\? . \\. \{ \\\{ \} \\\} ( \\( ) \\) ^ \\^ - \\- + \\+} $rsp(find)]
00453 }
00454 }
00455
00456 # Figure out any search options
00457 set egrep_opts [list]
00458 if {!$rsp(case)} {
00459 lappend egrep_opts -i
00460 }
00461
00462 # Perform egrep operation (test)
00463 if {[array size files] > 0} {
00464 if {$::tcl_platform(platform) eq "windows"} {
00465 search::fif_callback $rsp(find) [array size files] 0 [utils::egrep $rsp(find) [lsort [array names files]] [preferences::get Find/ContextNum] $egrep_opts]
00466 } else {
00467 bgproc::system find_in_files "egrep -a -H -C[preferences::get Find/ContextNum] -n $egrep_opts -s {$rsp(find)} [lsort [array names files]]" -killable 1 \
00468 -callback "search::fif_callback [list $rsp(find)] [array size files]"
00469 }
00470 } else {
00471 gui::set_info_message [msgcat::mc "No files found in specified directories"]
00472 }
00473
00474 }
00475
00476 }
00477
00478 ######################################################################
00479 # Called when the egrep operation has completed.
00480 proc fif_callback {find_expr num_files err data} {
00481
00482 # Add the file to the viewer
00483 gui::add_buffer end "FIF Results" "" -readonly 1 -other [preferences::get View/ShowFindInFileResultsInOtherPane]
00484
00485 # Inserts the results into the current buffer
00486 fif_insert_results $find_expr $num_files $err $data
00487
00488 }
00489
00490 ######################################################################
00491 # Inserts the results from the find in files egrep execution into the
00492 # newly created buffer.
00493 proc fif_insert_results {find_expr num_files err result} {
00494
00495 # Get the current text widget
00496 set txt [gui::current_txt]
00497
00498 # Change the text state to allow text to be inserted
00499 $txt configure -state normal
00500
00501 # Get the last index of the text widget
00502 set last_line [$txt index end]
00503
00504 # Insert a starting mark
00505 $txt insert -moddata ignore end "----\n"
00506
00507 if {!$err || ($num_files == 0)} {
00508
00509 # Append the results to the text widget
00510 $txt insert -moddata ignore end [fif_format $result]
00511
00512 # Modify find_expr so that information in results window will match
00513 if {[string index $find_expr 0] eq "^"} {
00514 set find_expr [string range $find_expr 1 end]
00515 }
00516
00517 # Highlight and bind the matches
00518 $txt tag configure fif -underline 1 -borderwidth 1 -relief raised -foreground black -background yellow
00519 set i 0
00520 foreach index [$txt search -regexp -all -count find_counts -- $find_expr $last_line] {
00521 $txt tag add fif $index "$index + [lindex $find_counts $i]c"
00522 $txt tag bind fif <Enter> [list %W configure -cursor [ttk::cursor link]]
00523 $txt tag bind fif <Leave> [list %W configure -cursor [$txt cget -cursor]]
00524 $txt tag bind fif <ButtonRelease-1> [list search::fif_handle_click %W %x %y]
00525 incr i
00526 }
00527
00528 bind $txt <Key-space> { if {[search::fif_handle_space %W]} break }
00529
00530 } else {
00531
00532 $txt insert -moddata ignore end "No matches were found for \"$find_expr\"\n\n\n"
00533
00534 }
00535
00536 # Make sure that the beginning of the inserted text is in view
00537 $txt see end
00538 ::tk::TextSetCursor $txt $last_line
00539
00540 # Change the state back to disabled
00541 $txt configure -state disabled
00542
00543 }
00544
00545 ######################################################################
00546 # Formats the raw egrep data to make it more readable.
00547 proc fif_format {data} {
00548
00549 set results ""
00550 set file_results [list]
00551 set last_linenum ""
00552 set first_separator 1
00553 array set indices {}
00554 array set fnames {}
00555 set index 0
00556 set matches 0
00557
00558 foreach line [split $data \n] {
00559 if {[regexp {^(.*?)([:-])(\d+)[:-](.*)$} $line -> fname type linenum content]} {
00560 set first_separator 1
00561 if {![info exists fnames($fname)]} {
00562 set fnames($fname) 1
00563 if {[llength $file_results] > 0} {
00564 if {[string trim [lindex $file_results end]] eq "..."} {
00565 set file_results [lrange $file_results 0 end-1]
00566 }
00567 append results "[join $file_results \n]\n\n"
00568 set file_results [list]
00569 }
00570 lappend file_results " [file normalize $fname]:\n"
00571 set last_linenum ""
00572 array unset indices
00573 }
00574 if {$type eq ":"} {
00575 if {($last_linenum eq "") || ($linenum > $last_linenum)} {
00576 lappend file_results [format " %6d: %s" $linenum $content]
00577 set indices($linenum) $index
00578 set last_linenum $linenum
00579 incr index
00580 } else {
00581 lset file_results $indices($linenum) [string replace [lindex $file_results $indices($linenum)] 11 11 ":"]
00582 }
00583 incr matches
00584 } else {
00585 if {($last_linenum eq "") || ($linenum > $last_linenum)} {
00586 lappend file_results [format " %6d %s" $linenum $content]
00587 set indices($linenum) $index
00588 set last_linenum $linenum
00589 incr index
00590 }
00591 }
00592 } elseif {[string trim $line] eq "--"} {
00593 if {$first_separator} {
00594 set first_separator 0
00595 } else {
00596 lappend file_results " ..."
00597 }
00598 }
00599 }
00600
00601 # Append the last files information to the results string
00602 append results "[join $file_results \n]\n\n"
00603
00604 return "Found $matches [expr {($matches != 1) ? {matches} : {match}}] in [array size fnames] [expr {([array size fnames] != 1) ? {files} : {file}}]\n\n$results"
00605
00606 }
00607
00608 ######################################################################
00609 # Handles a left-click on a matched pattern in the given text widget.
00610 # Causes the matching file to be opened and we jump to the matching line.
00611 proc fif_handle_selection {W index} {
00612
00613 # Get the line number from the beginning of the line
00614 regexp {^\s*(\d+)} [$W get "$index linestart" $index] -> linenum
00615
00616 # Get the filename of the line that is clicked
00617 set findex [$W search -regexp -backwards -count fif_count -- {^\s*(\w+:)?/.*:$} $index]
00618 set fname [$W get $findex "$findex+[expr $fif_count - 1]c"]
00619
00620 # Add the file to the file viewer (if necessary)
00621 gui::add_file end [string trim $fname]
00622
00623 # Jump to the line and set the cursor to the beginning of the line
00624 set txt [gui::current_txt]
00625 ::tk::TextSetCursor $txt $linenum.0
00626
00627 }
00628
00629 ######################################################################
00630 # Handles a left-click on a matched pattern in the given text widget.
00631 proc fif_handle_click {W x y} {
00632
00633 fif_handle_selection $W [$W index @$x,$y]
00634
00635 }
00636
00637 ######################################################################
00638 # Handles a space bar key hit on a matched pattern in the given text
00639 # widget.
00640 proc fif_handle_space {W} {
00641
00642 # Get the current insertion index
00643 set insert [$W index insert]
00644
00645 # Check to see if the space bar was hit inside of a tag
00646 foreach {first last} [$W tag ranges fif] {
00647 if {[$W compare $first <= $insert] && [$W compare $insert < $last]} {
00648 fif_handle_selection $W [$W index insert]
00649 return 1
00650 }
00651 }
00652
00653 return 0
00654
00655 }
00656
00657 ######################################################################
00658 # Adds the given string to the find history list.
00659 proc add_history {type hist_info} {
00660
00661 variable data
00662
00663 # Check to see if the search string exists within the history
00664 if {[set index [lsearch -exact -index 1 $data($type,hist) [lindex $hist_info 1]]] != -1} {
00665 set data($type,hist) [lreplace $data($type,hist) $index $index]
00666
00667 # Otherwise, reduce the size of the find history if adding another element will cause it to overflow
00668 } else {
00669 foreach index [lrange [lreverse [lsearch -index end -all $data($type,hist) 0]] [preferences::get {Find/MaxHistory}] end] {
00670 set data($type,hist) [lreplace $data($type,hist) $index $index]
00671 }
00672 }
00673
00674 # Save the find text to history
00675 lappend data($type,hist) $hist_info
00676
00677 # Clear the history pointer
00678 set data($type,hist_ptr) [llength $data($type,hist)]
00679
00680 # Clear the current find text
00681 set data($type,current) ""
00682
00683 }
00684
00685 ######################################################################
00686 # Updates the save state of the current search item, saving the item
00687 # to the current sessions file.
00688 proc update_save {type} {
00689
00690 variable data
00691
00692 # Get the current search information
00693 set search_data [gui::get_search_data $type]
00694
00695 # Find the matching item in history and update its save status
00696 set i 0
00697 foreach item $data($type,hist) {
00698 if {[lrange $item 0 end-1] eq [lrange $search_data 0 end-1]} {
00699 lset data($type,hist) $i end [lindex $search_data end]
00700 sessions::save find [sessions::current]
00701 break
00702 }
00703 incr i
00704 }
00705
00706 }
00707
00708 ######################################################################
00709 # Moves backwards or forwards through search history, populating the given
00710 # entry widget with the history search result. If we are moving forward
00711 # in history such that we fall into the present, the entry field will be
00712 # set to any text that was entered prior to traversing history.
00713 proc traverse_history {type dir} {
00714
00715 variable data
00716
00717 # Get the length of the find history list
00718 set hlen [llength $data($type,hist)]
00719
00720 # If the history pointer is -1, save the current text entered
00721 if {$data($type,hist_ptr) == $hlen} {
00722 if {$dir == 1} {
00723 return
00724 }
00725 set data($type,current) [gui::get_search_data $type]
00726 }
00727
00728 # Update the current pointer
00729 if {($data($type,hist_ptr) == 0) && ($dir == -1)} {
00730 return
00731 }
00732
00733 incr data($type,hist_ptr) $dir
00734
00735 # If the new history pointer is -1, restore the current text value
00736 if {$data($type,hist_ptr) == $hlen} {
00737 gui::set_search_data $type $data($type,current)
00738 } else {
00739 gui::set_search_data $type [lindex $data($type,hist) $data($type,hist_ptr)]
00740 }
00741
00742 }
00743
00744 ######################################################################
00745 # Searches the current language documentation for the given search string.
00746 # Returns 1 if the search was opened; otherwise, returns a value of 0.
00747 proc search_documentation {args} {
00748
00749 array set opts {
00750 -str ""
00751 -url ""
00752 }
00753 array set opts $args
00754
00755 # Get the current language
00756 gui::get_info {} current lang
00757
00758 # Get the list of searchable documents
00759 set docs [lsearch -all -inline -index 1 [list {*}[syntax::get_references $lang] {*}[preferences::get Documentation/References]] "*{query}*"]
00760
00761 # Initialize the rsp array
00762 array set rsp [list str $opts(-str) url $opts(-url)]
00763
00764 # If a search string was not specified, prompt the user
00765 if {($opts(-str) eq "") || ($opts(-url) eq "")} {
00766 if {![gui::docsearch_get_input $docs rsplist -str $opts(-str) -url $opts(-url)]} {
00767 return
00768 }
00769 array set rsp $rsplist
00770 add_history docsearch [list find $rsp(str) name $rsp(name) save $rsp(save)]
00771 }
00772
00773 # Substitute any space characters with %20
00774 set str [string range [http::formatQuery {} $rsp(str)] 1 end]
00775
00776 if {$rsp(url) eq ""} {
00777 foreach item $docs {
00778 lassign $item name url
00779 set url [string map [list "{query}" $str] $rsp(url)]
00780 if {[utils::test_url $url]} {
00781 utils::open_file_externally $url 0
00782 break
00783 }
00784 }
00785 } else {
00786 set url [string map [list "{query}" $str] $rsp(url)]
00787 if {[utils::test_url $url]} {
00788 utils::open_file_externally $url 0
00789 }
00790 }
00791
00792 }
00793
00794 ######################################################################
00795 # Loads the given session data.
00796 proc load_session {session_data} {
00797
00798 variable data
00799
00800 # Get the data
00801 array set data $session_data
00802
00803 # Clear the history pointers
00804 foreach type [list find replace fif docsearch] {
00805 set data($type,hist_ptr) [llength $data($type,hist)]
00806 }
00807
00808 }
00809
00810 ######################################################################
00811 # Returns find data to be saved in session history.
00812 proc save_session {} {
00813
00814 variable data
00815
00816 # Only save history items with the save indicator set
00817 foreach type [list find replace fif docsearch] {
00818 set saved($type,hist) [list]
00819 foreach item $data($type,hist) {
00820 if {[lindex $item end]} {
00821 lappend saved($type,hist) $item
00822 }
00823 }
00824 }
00825
00826 return [array get saved]
00827
00828 }
00829
00830 }