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 }