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 }