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: indent.tcl 00020 # Author: Trevor Williams (phase1geo@gmail.com) 00021 # Date: 5/13/2013 00022 # Brief: Namespace for text bindings to handle proper indentations 00023 ###################################################################### 00024 00025 namespace eval indent { 00026 00027 variable current_indent "IND+" 00028 00029 array set tabstops {} 00030 array set data {} 00031 array set indent_mode_map { 00032 "OFF" "OFF" 00033 "IND" "IND" 00034 "IND+" "IND+" 00035 "0" "OFF" 00036 "1" "IND+" 00037 } 00038 00039 trace variable preferences::prefs(Editor/SpacesPerTab) w [list indent::handle_spaces_per_tab] 00040 trace variable preferences::prefs(Editor/IndentSpaces) w [list indent::handle_indent_spaces] 00041 00042 ###################################################################### 00043 # Sets the tabstop value to match the value of Editor/SpacesPerTab and 00044 # updates all text widgets to match, cleaning up any non-existent windows 00045 # along the way. 00046 proc handle_spaces_per_tab {name1 name2 op} { 00047 00048 variable tabstops 00049 00050 foreach txtt [array names tabstops] { 00051 if {[winfo exists $txtt]} { 00052 set_tabstop $txtt [preferences::get Editor/SpacesPerTab] 00053 } else { 00054 unset tabstops($txtt) 00055 } 00056 } 00057 00058 } 00059 00060 ###################################################################### 00061 # Sets the shiftwidth value to match the value of Editor/IndentSpaces. 00062 # Updates all text widgets to match, cleaning up any non-existent 00063 # windows along the way. 00064 proc handle_indent_spaces {name1 name2 op} { 00065 00066 variable shiftwidths 00067 00068 foreach txtt [array names shiftwidths] { 00069 if {[winfo exists $txtt]} { 00070 set_shiftwidth $txtt [preferences::get Editor/IndentSpaces] 00071 } else { 00072 unset shiftwidths($txtt) 00073 } 00074 } 00075 00076 } 00077 00078 ###################################################################### 00079 # Adds indentation bindings for the given text widget. 00080 proc add_bindings {txt} { 00081 00082 # Initialize the tabstop 00083 set_tabstop $txt.t [preferences::get Editor/SpacesPerTab] 00084 set_shiftwidth $txt.t [preferences::get Editor/IndentSpaces] 00085 00086 bind indent$txt <Any-Key> { indent::check_indent %W insert 1 } 00087 bind indent$txt <Return> { indent::newline %W insert 1 } 00088 bind indent$txt <BackSpace> { indent::backspace %W insert 1 } 00089 00090 # Add the indentation tag into the bindtags list just after Text 00091 set text_index [lsearch [bindtags $txt.t] Text] 00092 bindtags $txt.t [linsert [bindtags $txt.t] [expr $text_index + 1] indent$txt] 00093 00094 } 00095 00096 ###################################################################### 00097 # Sets the tabstop value for the given text widget. 00098 proc set_tabstop {txtt value} { 00099 00100 variable tabstops 00101 00102 # Check to make sure that the value is an integer 00103 if {![string is integer $value]} { 00104 return -code error "Tabstop value is not an integer" 00105 } 00106 00107 # Save the tabstop value 00108 set tabstops($txtt) $value 00109 00110 # Set the text widget tabstop value 00111 $txtt configure -tabs [list [expr $value * [font measure [$txtt cget -font] 0]] left] 00112 00113 } 00114 00115 ###################################################################### 00116 # Returns the tabstop value for the given text widget. 00117 proc get_tabstop {txtt} { 00118 00119 variable tabstops 00120 00121 if {[info exists tabstops($txtt)]} { 00122 return $tabstops($txtt) 00123 } 00124 00125 return -code error "Tabstop information for $txtt does not exist" 00126 00127 } 00128 00129 ###################################################################### 00130 # Sets the shiftwidth value for the given text widget. 00131 proc set_shiftwidth {txtt value} { 00132 00133 variable shiftwidths 00134 00135 # Check to make sure that the value is an integer 00136 if {![string is integer $value]} { 00137 return -code error "Shiftwidth value is not an integer" 00138 } 00139 00140 # Save the shiftwidth value 00141 set shiftwidths($txtt) $value 00142 00143 } 00144 00145 ###################################################################### 00146 # Returns the shiftwidth value for the given text widget. 00147 proc get_shiftwidth {txtt} { 00148 00149 variable shiftwidths 00150 00151 if {[info exists shiftwidths($txtt)]} { 00152 return $shiftwidths($txtt) 00153 } 00154 00155 return -code error "Shiftwidth information for $txtt does not exist" 00156 00157 } 00158 00159 ###################################################################### 00160 # Sets the indentation mode for the current text widget. 00161 proc set_current_indent_mode {mode} { 00162 00163 set_indent_mode [gui::current_txt] $mode 00164 00165 } 00166 00167 ###################################################################### 00168 # Sets the indentation mode for the given text widget. 00169 proc set_indent_mode {txt mode} { 00170 00171 variable data 00172 variable indent_mode_map 00173 variable current_indent 00174 00175 # Set the current mode 00176 set data($txt.t,mode) $indent_mode_map($mode) 00177 set current_indent $indent_mode_map($mode) 00178 00179 # Set the text widget's indent mode 00180 folding::add_folds $txt 1.0 end 00181 00182 # Update the menu button 00183 $gui::widgets(info_indent) configure -text $mode 00184 00185 # Set the focus back to the text widget 00186 catch { gui::set_txt_focus [gui::last_txt_focus] } 00187 00188 } 00189 00190 ###################################################################### 00191 # Returns the value of the indentation mode for the given text widget. 00192 proc get_indent_mode {txt} { 00193 00194 variable data 00195 00196 if {![info exists data($txt.t,mode)]} { 00197 return "OFF" 00198 } else { 00199 return [lindex $data($txt.t,mode) 0] 00200 } 00201 00202 } 00203 00204 ###################################################################### 00205 # Returns true if auto-indentation is available; otherwise, returns false. 00206 proc is_auto_indent_available {txt} { 00207 00208 variable data 00209 00210 return $data($txt.t,auto,avail) 00211 00212 } 00213 00214 ###################################################################### 00215 # Returns true if the reindent symbol is not the first in the parent statement. 00216 proc check_reindent_for_unindent {txtt index} { 00217 00218 if {[set spos [lindex [$txtt tag prevrange __reindentStart $index] 0]] ne ""} { 00219 00220 # If the starting reindent is also an indent, return 1 00221 if {[lsearch [$txtt tag names $spos] __indent*] != -1} { 00222 return 2 00223 } 00224 00225 # Get the starting position of the previous reindent string 00226 set rpos [lindex [$txtt tag prevrange __reindent $index] 0] 00227 00228 if {($rpos ne "") && [$txtt compare $rpos > $spos]} { 00229 00230 # Find the indent symbol that is just before the reindentStart symbol 00231 while {([lassign [$txtt tag prevrange __indent $index] ipos] ne "") && [$txtt compare $ipos > $spos]} { 00232 set index $ipos 00233 } 00234 00235 return [$txtt compare $index < $rpos] 00236 00237 } 00238 00239 } 00240 00241 return 0 00242 00243 } 00244 00245 ###################################################################### 00246 # Checks the given text prior to the insertion marker to see if it 00247 # matches the unindent expressions. Increment/decrement 00248 # accordingly. 00249 proc check_indent {txtt index do_update} { 00250 00251 variable data 00252 00253 # If the auto-indent feature was disabled, we are in vim start mode, or 00254 # the current language doesn't have an indent expression, quit now 00255 if {($data($txtt,mode) ne "IND+") || [vim::in_vim_mode $txtt]} { 00256 return $index 00257 } 00258 00259 # If the current line contains an unindent expression, is not within a comment or string, 00260 # and is preceded in the line by only whitespace, replace the whitespace with the proper 00261 # indentation whitespace. 00262 if {([set endpos [lassign [$txtt tag prevrange __unindent $index] startpos]] ne "") && [$txtt compare $endpos >= $index]} { 00263 00264 if {[string trim [set space [$txtt get "$index linestart" $startpos]]] eq ""} { 00265 00266 # Find the matching indentation index 00267 if {[set tindex [get_match_indent $txtt $startpos]] ne ""} { 00268 set indent_space [get_start_of_line $txtt $tindex] 00269 } else { 00270 set indent_space [get_start_of_line $txtt $index] 00271 } 00272 00273 # Replace the whitespace with the appropriate amount of indentation space 00274 if {$indent_space ne $space} { 00275 $txtt fastreplace -update $do_update "$index linestart" $startpos $indent_space 00276 set offset [expr [lindex [split $index .] 1] + ([string length $indent_space] - [lindex [split $startpos .] 1])] 00277 return [$txtt index "$index linestart+${offset}c"] 00278 } 00279 00280 } 00281 00282 } elseif {(([set endpos [lassign [$txtt tag prevrange __reindent $index] startpos]] ne "") && [$txtt compare $endpos == $index]) && [set type [check_reindent_for_unindent $txtt $startpos]]} { 00283 00284 if {[string trim [set space [$txtt get "$index linestart" $startpos]]] eq ""} { 00285 00286 if {$type == 1} { 00287 00288 # Get the starting whitespace of the previous line 00289 set indent_space [get_start_of_line $txtt [$txtt index "$index-1l lineend"]] 00290 00291 # Check to see if the previous line contained a reindent 00292 if {[$txtt compare "$index-1l linestart" > [lindex [$txtt tag prevrange __reindent "$index linestart"] 0]]} { 00293 set indent_space [string range $indent_space [get_shiftwidth $txtt] end] 00294 } 00295 00296 } else { 00297 00298 # Set the indentation space to the same as the reindentStart line 00299 set indent_space [get_start_of_line $txtt [lindex [$txtt tag prevrange __reindentStart $index] 0]] 00300 00301 } 00302 00303 # Replace the whitespace with the appropriate amount of indentation space 00304 if {$indent_space ne $space} { 00305 $txtt fastreplace -update $do_update "$index linestart" $startpos $indent_space 00306 set offset [expr [lindex [split $index .] 1] + ([string length $indent_space] - [lindex [split $startpos .] 1])] 00307 return [$txtt index "$index linestart+${offset}c"] 00308 } 00309 00310 } 00311 00312 } 00313 00314 return $index 00315 00316 } 00317 00318 ###################################################################### 00319 # Returns 1 if the given line contains an indentation. 00320 proc line_contains_indentation {txtt index} { 00321 00322 # Ignore whitespace 00323 if {[lsearch [$txtt tag names "$index linestart"] __prewhite] == -1} { 00324 if {[set range [$txtt tag prevrange __prewhite "$index lineend"]] ne ""} { 00325 set index [$txtt index "[lindex $range 1] lineend"] 00326 } else { 00327 set index 1.0 00328 } 00329 } 00330 00331 # Check to see if the current line contains an indentation symbol towards the end of the line 00332 if {[lassign [$txtt tag prevrange __indent $index "$index linestart"] ipos] ne ""} { 00333 return [expr {([lassign [$txtt tag prevrange __unindent $index] upos] eq "") || [$txtt compare $ipos > $upos]}] 00334 } 00335 00336 # Returns true if we have a reindent symbol in the current line 00337 return [expr {[lassign [$txtt tag prevrange __reindent $index "$index linestart"] ipos] ne ""}] 00338 00339 } 00340 00341 ###################################################################### 00342 # Get the matching indentation marker. 00343 proc get_match_indent {txtt index} { 00344 00345 set count 1 00346 00347 lassign [$txtt tag prevrange __indent $index] sfirst slast 00348 lassign [$txtt tag prevrange __unindent $index] ofirst olast 00349 00350 if {($olast ne "") && [$txtt compare $olast >= $index]} { 00351 set olast $index 00352 } 00353 00354 while {($ofirst ne "") && ($sfirst ne "")} { 00355 if {[$txtt compare $sfirst > $ofirst]} { 00356 if {[incr count -1] == 0} { 00357 return $sfirst 00358 } 00359 lassign [$txtt tag prevrange __indent $sfirst] sfirst slast 00360 } else { 00361 incr count 00362 lassign [$txtt tag prevrange __unindent $ofirst] ofirst olast 00363 } 00364 } 00365 00366 while {$sfirst ne ""} { 00367 if {[incr count -1] == 0} { 00368 return $sfirst 00369 } 00370 lassign [$txtt tag prevrange __indent $sfirst] sfirst slast 00371 } 00372 00373 return "" 00374 00375 } 00376 00377 ###################################################################### 00378 # Returns the whitespace found at the beginning of the specified logical 00379 # line. 00380 proc get_start_of_line {txtt index} { 00381 00382 # Ignore whitespace 00383 if {[lsearch [$txtt tag names "$index linestart"] __prewhite] == -1} { 00384 if {[set range [$txtt tag prevrange __prewhite "$index lineend"]] ne ""} { 00385 set index [$txtt index "[lindex $range 1] lineend"] 00386 } else { 00387 set index 1.0 00388 } 00389 } 00390 00391 # Find an ending bracket on the current line 00392 set win_type "none" 00393 set startpos(none) "$index linestart" 00394 foreach type [list curlyR parenR squareR angledR] { 00395 if {([lassign [$txtt tag prevrange __$type $index] startpos($type)] ne "") && \ 00396 [$txtt compare $startpos($type) >= "$index linestart"] && \ 00397 [$txtt compare $startpos($type) >= $startpos($win_type)]} { 00398 set win_type $type 00399 } 00400 } 00401 00402 # If we could not find a right bracket, we have found the line that we are looking for 00403 if {$win_type eq "none"} { 00404 if {[lsearch [$txtt tag names "$index linestart"] __prewhite] != -1} { 00405 return [string range [$txtt get {*}[$txtt tag nextrange __prewhite "$index linestart"]] 0 end-1] 00406 } else { 00407 return "" 00408 } 00409 00410 # Otherwise, jump the insertion cursor to the line containing the matching bracket and 00411 # do the search again. 00412 } else { 00413 array set other_type [list curlyR curlyL parenR parenL squareR squareL angledR angledL] 00414 if {[set match_index [ctext::getMatchBracket [winfo parent $txtt] $other_type($win_type) $startpos($win_type)]] ne ""} { 00415 return [get_start_of_line $txtt $match_index] 00416 } elseif {[lsearch [$txtt tag names "$index linestart"] __prewhite] != -1} { 00417 return [string range [$txtt get {*}[$txtt tag nextrange __prewhite "$index linestart"]] 0 end-1] 00418 } else { 00419 return "" 00420 } 00421 } 00422 00423 } 00424 00425 ###################################################################### 00426 # Handles a newline character. Returns the character position of the 00427 # first line of non-space text. 00428 proc newline {txtt index do_update} { 00429 00430 variable data 00431 00432 # If the auto-indent feature was disabled, we are in vim start mode, 00433 # or the current language doesn't have an indent expression, quit now 00434 if {($data($txtt,mode) eq "OFF") || [vim::in_vim_mode $txtt]} { 00435 if {[$txtt cget -autoseparators]} { 00436 $txtt edit separator 00437 } 00438 return $index 00439 } 00440 00441 # If we do not need smart indentation, use the previous space 00442 if {$data($txtt,mode) eq "IND"} { 00443 00444 set indent_space [get_previous_indent_space $txtt $index] 00445 00446 # Otherwise, do smart indentation 00447 } else { 00448 00449 # Get the current indentation level 00450 set indent_space [get_start_of_line $txtt [$txtt index "$index-1l lineend"]] 00451 00452 # If the previous line indicates an indentation is required, 00453 if {[line_contains_indentation $txtt "$index-1l lineend"]} { 00454 append indent_space [string repeat " " [get_shiftwidth $txtt]] 00455 } 00456 00457 } 00458 00459 # Create an index to restore the insertion cursor, if necessary 00460 set restore_insert "" 00461 00462 # Remove any leading whitespace and update indentation level 00463 # (if the first non-whitespace char is a closing bracket) 00464 if {[lsearch [$txtt tag names "$index linestart"] __prewhite] != -1} { 00465 00466 lassign [$txtt tag nextrange __prewhite "$index linestart"] startpos endpos 00467 00468 # If the first non-whitespace characters match an unindent pattern, 00469 # lessen the indentation by one 00470 if {[lsearch [$txtt tag names "$endpos-1c"] __unindent*] != -1} { 00471 $txtt fastinsert -update 0 insert "$indent_space\n" 00472 set startpos [$txtt index $startpos+1l] 00473 set endpos [$txtt index $endpos+1l] 00474 set restore_insert [$txtt index insert-1c] 00475 if {$data($txtt,mode) eq "IND+"} { 00476 set indent_space [string range $indent_space [get_shiftwidth $txtt] end] 00477 } 00478 00479 # Otherwise, if the first non-whitepace characters match a reindent pattern, lessen the 00480 # indentation by one 00481 } elseif {([lsearch [$txtt tag names "$endpos-1c"] __reindent*] != -1) && [check_reindent_for_unindent $txtt [lindex [$txtt tag prevrange __reindent $endpos] 0]]} { 00482 # $txtt insert insert "$indent_space\n" 00483 # set restore_insert [$txtt index insert-1c] 00484 if {$data($txtt,mode) eq "IND+"} { 00485 set indent_space [string range $indent_space [get_shiftwidth $txtt] end] 00486 } 00487 } 00488 00489 # See if we are deleting a multicursor 00490 set mcursor [lsearch [$txtt tag names $index] "mcursor"] 00491 00492 # Delete the whitespace 00493 $txtt fastdelete -update [expr {($do_update && ($indent_space eq "")) ? 1 : 0}] $startpos "$endpos-1c" 00494 00495 # If the newline was from a multicursor, we need to re-add the tag since we have deleted it 00496 if {$mcursor != -1} { 00497 $txtt tag add mcursor $index 00498 } 00499 00500 } 00501 00502 # Insert leading whitespace to match current indentation level 00503 if {$indent_space ne ""} { 00504 $txtt fastinsert -update $do_update "$index linestart" $indent_space 00505 } 00506 00507 # If we need to restore the insertion cursor, do it now 00508 if {$restore_insert ne ""} { 00509 ::tk::TextSetCursor $txtt $restore_insert 00510 } 00511 00512 # If autoseparators are called for, add it now 00513 if {[$txtt cget -autoseparators]} { 00514 $txtt edit separator 00515 } 00516 00517 return [$txtt index "$index+[string length $indent_space]c"] 00518 00519 } 00520 00521 ###################################################################### 00522 # Handles the backspace key. If we are 00523 proc backspace {txtt index do_update} { 00524 00525 variable data 00526 00527 # If the auto-indent feature was disabled, we are in vim start mode, or 00528 # the current language doesn't have an indent expression, quit now 00529 if {($data($txtt,mode) eq "OFF") || [vim::in_vim_mode $txtt]} { 00530 return $index 00531 } 00532 00533 # Figure out the leading space 00534 set space "" 00535 if {[set endpos [lassign [$txtt tag prevrange __prewhite $index "$index linestart"] startpos]] ne ""} { 00536 if {[$txtt compare $endpos == "$index+1c"]} { 00537 set space [$txtt get $startpos $index] 00538 } else { 00539 return $index 00540 } 00541 } else { 00542 set space [$txtt get "$index linestart" "$index lineend"] 00543 } 00544 00545 # If the leading whitespace only consists of spaces, attempt to delete to the previous tab 00546 if {([string map {{ } {}} $space] eq "")} { 00547 00548 # Calculate the new indentation 00549 set shiftwidth [get_shiftwidth $txtt] 00550 set tab_count [expr [string length $space] / $shiftwidth] 00551 set indent_space [string repeat " " [expr $tab_count * $shiftwidth]] 00552 00553 # Replace the whitespace with the appropriate amount of indentation space 00554 if {$indent_space ne $space} { 00555 $txtt fastreplace -update $do_update "$index linestart" $index $indent_space 00556 set offset [string length $indent_space] 00557 return [$txtt index "$index linestart+${offset}c"] 00558 } 00559 00560 } 00561 00562 return $index 00563 00564 } 00565 00566 ###################################################################### 00567 # Returns the whitespace of the previous (non-empty) line of text. 00568 proc get_previous_indent_space {txtt index} { 00569 00570 variable data 00571 00572 if {($data($txtt,mode) eq "OFF") || \ 00573 [vim::in_vim_mode $txtt] || \ 00574 ([lindex [split $index .] 0] == 1)} { 00575 return 0 00576 } 00577 00578 if {[set range [$txtt tag prevrange __prewhite "$index-1l lineend"]] ne ""} { 00579 return [string range [$txtt get {*}$range] 0 end-1] 00580 } else { 00581 return "" 00582 } 00583 00584 } 00585 00586 ###################################################################### 00587 # This procedure counts the number of tags in the given range. 00588 proc get_tag_count {txtt tag start end} { 00589 00590 variable data 00591 00592 # Initialize the indent_level 00593 set count 0 00594 00595 # Count all tags that are not within comments or are escaped 00596 while {[set range [$txtt tag nextrange __$tag $start $end]] ne ""} { 00597 incr count 00598 set start [lindex $range 1] 00599 } 00600 00601 return $count 00602 00603 } 00604 00605 ###################################################################### 00606 # Formats the given str based on the indentation information of the text 00607 # widget at the current insertion cursor. 00608 proc format_text {txtt startpos endpos {add_separator 1}} { 00609 00610 variable data 00611 00612 # Create a separator 00613 if {$add_separator} { 00614 $txtt edit separator 00615 } 00616 00617 # If we are the first line containing non-whitespace, preserve the indentation 00618 if {([$txtt tag prevrange __prewhite "$startpos linestart"] eq "") || \ 00619 ([string trim [$txtt get "$startpos linestart" $startpos]] ne "")} { 00620 set curpos [$txtt index "$startpos+1l linestart"] 00621 } else { 00622 set curpos [$txtt index "$startpos linestart"] 00623 } 00624 00625 set endpos [$txtt index $endpos] 00626 set indent_space "" 00627 set shiftwidth [get_shiftwidth $txtt] 00628 00629 while {[$txtt compare $curpos < $endpos]} { 00630 00631 if {$curpos ne "1.0"} { 00632 00633 # If the current line contains an unindent expression, is not within a comment or string, 00634 # and is preceded in the line by only whitespace, replace the whitespace with the proper 00635 # indentation whitespace. 00636 if {[set epos [lassign [$txtt tag nextrange __unindent $curpos "$curpos lineend"] spos]] ne ""} { 00637 if {[set tindex [get_match_indent $txtt $spos]] ne ""} { 00638 if {[$txtt compare "$tindex linestart" == "$spos linestart"]} { 00639 set indent_space [get_start_of_line $txtt "$tindex-1l lineend"] 00640 if {[line_contains_indentation $txtt "$tindex-1l lineend"]} { 00641 append indent_space [string repeat " " $shiftwidth] 00642 } 00643 } else { 00644 set indent_space [get_start_of_line $txtt $tindex] 00645 } 00646 } else { 00647 set indent_space [get_start_of_line $txtt $epos] 00648 } 00649 00650 } elseif {([set epos [lassign [$txtt tag nextrange __reindent $curpos "$curpos lineend"] spos]] ne "") && [check_reindent_for_unindent $txtt $spos]} { 00651 set indent_space [get_start_of_line $txtt [$txtt index "$curpos-1l lineend"]] 00652 if {[string trim [$txtt get "$curpos linestart" $spos]] eq ""} { 00653 if {[$txtt compare "$curpos-1l linestart" > [lindex [$txtt tag prevrange __reindent "$curpos linestart"] 1]]} { 00654 set indent_space [string range $indent_space $shiftwidth end] 00655 } 00656 } 00657 00658 } else { 00659 set indent_space [get_start_of_line $txtt [$txtt index "$curpos-1l lineend"]] 00660 if {[line_contains_indentation $txtt "$curpos-1l lineend"]} { 00661 append indent_space [string repeat " " $shiftwidth] 00662 } 00663 } 00664 00665 } 00666 00667 # Remove any leading whitespace and update indentation level 00668 # (if the first non-whitespace char is a closing bracket) 00669 set whitespace "" 00670 if {[lsearch [$txtt tag names $curpos] __prewhite] != -1} { 00671 set whitespace [string range [$txtt get {*}[$txtt tag nextrange __prewhite $curpos]] 0 end-1] 00672 } 00673 00674 # Replace the leading whitespace with the calculated amount of indentation space 00675 if {$whitespace ne $indent_space} { 00676 $txtt replace $curpos "$curpos+[string length $whitespace]c" $indent_space 00677 } 00678 00679 # Adjust the startpos 00680 set curpos [$txtt index "$curpos+1l linestart"] 00681 00682 } 00683 00684 # Create a separator 00685 $txtt edit separator 00686 00687 # Perform syntax highlighting 00688 $txtt syntax highlight $startpos $endpos 00689 00690 } 00691 00692 ###################################################################### 00693 # Sets the indentation expressions for the given text widget. 00694 proc set_indent_expressions {txtt indent unindent reindent} { 00695 00696 variable data 00697 00698 # Update the auto-indent settings 00699 set data($txtt,auto,avail) [expr {$indent ne ""}] 00700 set data($txtt,auto,enable) 1 00701 00702 # Set the default indentation mode 00703 if {[preferences::get Editor/EnableAutoIndent]} { 00704 if {($indent ne "") && [$txtt cget -highlight]} { 00705 set data($txtt,mode) "IND+" 00706 } else { 00707 set data($txtt,mode) "IND" 00708 } 00709 } else { 00710 set data($txtt,mode) "OFF" 00711 } 00712 00713 } 00714 00715 ###################################################################### 00716 # This will be caused if the associated file is not able to be automatically 00717 # indented due to syntax highlighting being disabled. 00718 proc update_auto_indent {txtt w} { 00719 00720 variable data 00721 variable current_indent 00722 00723 set data($txtt,auto,enable) [expr [$txtt cget -highlight] && $data($txtt,auto,avail)] 00724 set state [expr {$data($txtt,auto,enable) ? "normal" : "disabled"}] 00725 00726 if {!$data($txtt,auto,enable) && ($data($txtt,mode) eq "IND+")} { 00727 set data($txtt,mode) "IND" 00728 } 00729 00730 set current_indent $data($txtt,mode) 00731 00732 ${w}Menu entryconfigure [msgcat::mc "Smart Indent"] -state $state 00733 00734 } 00735 00736 ###################################################################### 00737 # Repopulates the specified syntax selection menu. 00738 proc populate_indent_menu {mnu} { 00739 00740 variable langs 00741 00742 # Clear the menu 00743 $mnu delete 0 end 00744 00745 # Populate the menu with the available languages 00746 foreach {lbl mode} [list [msgcat::mc "No Indent"] "OFF" [msgcat::mc "Auto-Indent"] "IND" [msgcat::mc "Smart Indent"] "IND+"] { 00747 $mnu add radiobutton -label $lbl -variable indent::current_indent \ 00748 -value $mode -command [list indent::set_current_indent_mode $mode] 00749 } 00750 00751 return $mnu 00752 00753 } 00754 00755 ###################################################################### 00756 # Creates the menubutton to control the indentation mode for the current 00757 # editor. 00758 proc create_menu {w} { 00759 00760 # Create the menubutton menu 00761 set mnu [menu ${w}Menu -tearoff 0] 00762 00763 # Populate the indent menu 00764 populate_indent_menu $mnu 00765 00766 # Register the menu 00767 theme::register_widget $mnu menus 00768 00769 return $mnu 00770 00771 } 00772 00773 ###################################################################### 00774 # Updates the menubutton to match the current mode. 00775 proc update_button {w} { 00776 00777 variable data 00778 variable current_indent 00779 00780 # Get the current text widget 00781 set txtt [gui::current_txt].t 00782 00783 # Configure the menubutton 00784 if {[info exists data($txtt,mode)]} { 00785 $w configure -text [set current_indent $data($txtt,mode)] 00786 } 00787 00788 # Update the selectable state of the button 00789 ${w}Menu entryconfigure [msgcat::mc "Smart Indent"] -state [expr {[$txtt cget -highlight] ? "normal" : "disabled"}] 00790 00791 } 00792 00793 } 00794 00795 ###################################################################### 00796 # Disable the Text action for Return so that when we are not in Vim 00797 # mode, an auto-separator is not automatically added. 00798 bind Text <Return> { tk::TextInsert %W \n }