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: vim.tcl
00020 # Author: Trevor Williams (phase1geo@gmail.com)
00021 # Date: 05/21/2013
00022 # Brief: Namespace containing special bindings to provide Vim-like
00023 # support. The Vim commands supported are not meant to be
00024 # a complete representation of its functionality.
00025 ######################################################################
00026
00027 namespace eval vim {
00028
00029 variable modelines
00030 variable seltype "inclusive"
00031
00032 array set command_entries {}
00033 array set mode {}
00034 array set search_dir {}
00035 array set column {}
00036 array set select_anchors {}
00037 array set last_selection {}
00038 array set modeline {}
00039 array set multicursor {}
00040
00041 array set multiplier {}
00042 array set operator {}
00043 array set motion {}
00044 array set number {}
00045
00046 array set recording {
00047 mode "none"
00048 curr_reg ""
00049 }
00050
00051 trace variable preferences::prefs(Editor/VimModelines) w [list vim::handle_vim_modelines]
00052
00053 ######################################################################
00054 # Handles any value changes to the Editor/VimModlines preference value.
00055 proc handle_vim_modelines {name1 name2 op} {
00056
00057 variable modelines
00058
00059 set modelines [preferences::get Editor/VimModelines]
00060
00061 }
00062
00063 ######################################################################
00064 # Enables/disables Vim mode for all text widgets.
00065 proc set_vim_mode_all {{value ""}} {
00066
00067 variable command_entries
00068
00069 if {$value ne ""} {
00070 set preferences::prefs(Editor/VimMode) $value
00071 }
00072
00073 # Set the Vim mode on all text widgets
00074 foreach txtt [array names command_entries] {
00075 set_vim_mode [winfo parent $txtt]
00076 }
00077
00078 }
00079
00080 ######################################################################
00081 # Enables/disables Vim mode for the specified text widget.
00082 proc set_vim_mode {txt} {
00083
00084 if {[preferences::get Editor/VimMode]} {
00085 add_bindings $txt
00086 } else {
00087 remove_bindings $txt
00088 }
00089
00090 # Update the position information in the status bar
00091 gui::update_position $txt
00092
00093 }
00094
00095 ######################################################################
00096 # Returns true if we are currently in multi-cursor move mode.
00097 proc in_multimove {txtt} {
00098
00099 variable multicursor
00100
00101 return [expr {[in_vim_mode $txtt] && (([get_edit_mode $txtt] ne "") || $multicursor($txtt))}]
00102
00103 }
00104
00105 ######################################################################
00106 # Returns the current edit mode type (insert or replace).
00107 proc get_edit_mode {txtt} {
00108
00109 variable mode
00110
00111 if {[info exists mode($txtt)]} {
00112 if {$mode($txtt) eq "edit"} {
00113 return "insert"
00114 } elseif {[string equal -length 7 $mode($txtt) "replace"]} {
00115 return "replace"
00116 }
00117 }
00118
00119 return ""
00120
00121 }
00122
00123 ######################################################################
00124 # Returns 1 if we are currently in non-edit vim mode; otherwise,
00125 # returns 0.
00126 proc in_vim_mode {txtt} {
00127
00128 variable mode
00129
00130 if {[preferences::get Editor/VimMode] && \
00131 [info exists mode($txtt)] && \
00132 ($mode($txtt) ne "edit")} {
00133 return 1
00134 } else {
00135 return 0
00136 }
00137
00138 }
00139
00140 ######################################################################
00141 # Returns the current Vim mode for the editor.
00142 proc get_mode {txt} {
00143
00144 variable mode
00145 variable recording
00146 variable multicursor
00147
00148 if {[preferences::get Editor/VimMode]} {
00149 set record ""
00150 if {[in_recording]} {
00151 set record ", REC\[ $recording(curr_reg) \]"
00152 }
00153 if {[info exists mode($txt.t)]} {
00154 switch $mode($txt.t) {
00155 "edit" { return [format "%s%s" [msgcat::mc "INSERT MODE"] $record] }
00156 "visual:char" { return [format "%s%s" [msgcat::mc "VISUAL MODE"] $record] }
00157 "visual:line" { return [format "%s%s" [msgcat::mc "VISUAL LINE MODE"] $record] }
00158 "visual:block" { return [format "%s%s" [msgcat::mc "VISUAL BLOCK MODE"] $record] }
00159 default {
00160 if {[info exists multicursor($txt.t)] && $multicursor($txt.t)} {
00161 return [msgcat::mc "MULTIMOVE MODE"]
00162 } else {
00163 return [format "%s%s" [msgcat::mc "COMMAND MODE"] $record]
00164 }
00165 }
00166 }
00167 }
00168 } else {
00169 return ""
00170 }
00171
00172 }
00173
00174 ######################################################################
00175 # Parses the first N lines of the given text widget for a Vim modeline.
00176 # Parses out the language (if specified) and/or indentation information.
00177 proc parse_modeline {txt} {
00178
00179 variable modeline
00180 variable modelines
00181
00182 if {$modelines && (![info exists modeline($txt.t)] || $modeline($txt.t))} {
00183
00184 foreach {startline endline} [list 1.0 "1.0+${modelines}l linestart" "end-${modelines}l linestart" end] {
00185 foreach line [split [$txt get $startline $endline] \n] {
00186 if {[regexp {(^|\s)(vi|vim|vim\d+|vim<\d+|vim>\d+|vim=\d+|ex):\s*(set?\s+(.*):.*|(.*)$)} $line -> dummy0 dummy1 dummy2 opts1 opts2]} {
00187 set opts [expr {(([string range $dummy2 0 2] eq "se ") || ([string range $dummy2 0 3] eq "set ")) ? $opts1 : $opts2}]
00188 set opts [string map {"\\:" {:} ":" { }} $opts]
00189 foreach opt $opts {
00190 if {[regexp {(\S+?)(([+-])?=(\S+))?$} $opt -> key dummy mod val]} {
00191 do_set_command $txt $key $val $mod 1
00192 }
00193 }
00194 return
00195 }
00196 }
00197 }
00198
00199 }
00200
00201 }
00202
00203 ######################################################################
00204 # Binds the given entry
00205 proc bind_command_entry {txt entry} {
00206
00207 variable command_entries
00208
00209 # Save the entry
00210 set command_entries($txt.t) $entry
00211
00212 bind $entry <Return> [list vim::handle_command_return %W]
00213 bind $entry <Escape> [list vim::handle_command_escape %W]
00214 bind $entry <BackSpace> [list vim::handle_command_backspace %W]
00215
00216 }
00217
00218 ######################################################################
00219 # Handles the command entry text.
00220 proc handle_command_return {w} {
00221
00222 variable recording
00223
00224 # Get the last txt widget that had the focus
00225 set txt [gui::last_txt_focus]
00226
00227 # Get the value from the command field
00228 set value [$w get]
00229
00230 # Save the value as a recording
00231 set recording(:,events) $value
00232
00233 # Delete the value in the command entry
00234 $w delete 0 end
00235
00236 # Execute the colon command
00237 set txt [handle_colon_command $txt $value]
00238
00239 # Remove the grab
00240 grab release $w
00241
00242 if {$txt ne ""} {
00243
00244 # Hide the command entry widget
00245 gui::panel_forget $w
00246
00247 # Set the focus back to the text widget
00248 gui::set_txt_focus $txt
00249
00250 }
00251
00252 }
00253
00254 ######################################################################
00255 # Parses and executes the colon command value.
00256 proc handle_colon_command {txt value} {
00257
00258 # Execute the command
00259 switch -- $value {
00260 w { gui::save_current }
00261 w! { gui::save_current -force 1 }
00262 wq { if {[gui::save_current]} { gui::close_current; set txt "" } }
00263 wq! { if {[gui::save_current -force 1]} { gui::close_current; set txt "" } }
00264 q { gui::close_current; set txt "" }
00265 q! { gui::close_current -force 1; set txt "" }
00266 cq { gui::close_all -force 1 -exiting 1; menus::exit_command }
00267 e! { gui::update_current }
00268 n { gui::next_tab }
00269 N { gui::previous_tab }
00270 p { after idle gui::next_pane }
00271 e\# { gui::last_tab }
00272 m { gui::remove_current_marker }
00273 default {
00274 catch {
00275
00276 # Perform search and replace
00277 if {[regexp {^((\d+|[.^$]|\w+),(\d+|[.^$]|\w+))?s/(.*)/(.*)/([giI]*)$} $value -> dummy from to search replace opts]} {
00278 set ranges [list]
00279 if {$dummy eq ""} {
00280 if {[set ranges [$txt tag ranges sel]] eq ""} {
00281 set ranges [list [$txt index "insert linestart"] [$txt index "insert lineend"]]
00282 }
00283 } else {
00284 set ranges [list [get_linenum $txt $from] [$txt index "[get_linenum $txt $to] lineend-1c"]]
00285 }
00286 foreach {from to} $ranges {
00287 search::replace_do_raw $from $to $search $replace "regexp" \
00288 [expr [string first "i" $opts] != -1] [expr [string first "g" $opts] != -1]
00289 }
00290
00291 # Delete/copy lines
00292 } elseif {[regexp {^(\d+|[.^$]|\w+),(\d+|[.^$]|\w+)([dy])$} $value -> from to cmd]} {
00293 set from [get_linenum $txt $from]
00294 set to [$txt index "[get_linenum $txt $to] lineend"]
00295 clipboard clear
00296 clipboard append [$txt get $from $to]
00297 if {$cmd eq "d"} {
00298 $txt delete $from $to
00299 adjust_insert $txt.t
00300 }
00301 cliphist::add_from_clipboard
00302
00303 # Jump to line
00304 } elseif {[regexp {^(\d+|[.^$]|\w+)$} $value]} {
00305 edit::jump_to_line $txt.t [get_linenum $txt $value]
00306
00307 # Add multicursors to a range of lines
00308 } elseif {[regexp {^(\d+|[.^$]|\w+),(\d+|[.^$]|\w+)c/(.*)/$} $value -> from to search]} {
00309 set from [get_linenum $txt $from]
00310 set to [$txt index "[get_linenum $txt $to] lineend"]
00311 multicursor::search_and_add_cursors $txt $from $to $search
00312
00313 # Handle code fold opening in range
00314 } elseif {[regexp {^(\d+|[.^$]|\w+),(\d+|[.^$]|\w+)foldo(pen)?(!?)$} $value -> from to dummy full_depth]} {
00315 set from [lindex [split [get_linenum $txt $from] .] 0]
00316 set to [lindex [split [get_linenum $txt $to] .] 0]
00317 folding::open_folds_in_range $txt $from $to [expr {$full_depth ne ""}]
00318
00319 # Handle code fold closing in range
00320 } elseif {[regexp {^(\d+|[.^$]|\w+),(\d+|[.^$]|\w+)foldc(lose)?(!?)$} $value -> from to dummy full_depth]} {
00321 set from [lindex [split [get_linenum $txt $from] .] 0]
00322 set to [lindex [split [get_linenum $txt $to] .] 0]
00323 folding::close_folds_in_range $txt $from $to [expr {$full_depth ne ""}]
00324
00325 # Handling code folding
00326 } elseif {[regexp {^(\d+|[.^$]|\w+),(\d+|[.^$]|\w+)fo(ld)?$} $value -> from to]} {
00327 set from [lindex [split [get_linenum $txt $from] .] 0]
00328 set to [lindex [split [get_linenum $txt $to] .] 0]
00329 folding::close_range $txt $from $to
00330
00331 # Save/quit a subset of lines as a filename
00332 } elseif {[regexp {^(\d+|[.^$]|\w+),(\d+|[.^$]|\w+)w(q)?(!)?\s+(.*)$} $value -> from to and_close overwrite fname]} {
00333 set from [$txt index "[get_linenum $txt $from] linestart"]
00334 set to [$txt index "[get_linenum $txt $to] lineend"]
00335 if {[edit::save_selection $txt $from $to [expr {$overwrite eq "!"}] $fname]} {
00336 if {$and_close ne ""} {
00337 gui::close_current
00338 set txt ""
00339 }
00340 }
00341
00342 # Open a new file
00343 } elseif {[regexp {^e\s+(.*)$} $value -> filename]} {
00344 set filename [normalize_filename [utils::perform_substitutions $filename]]
00345 if {[file exists $filename]} {
00346 gui::add_file end $filename
00347 } else {
00348 gui::add_new_file end -name $filename
00349 }
00350
00351 # Save/quit the entire file with a new name
00352 } elseif {[regexp {^w(q)?(!)?\s+(.*)$} $value -> and_close and_force filename]} {
00353 if {![file exists [file dirname [set filename [normalize_filename [utils::perform_substitutions $filename]]]]]} {
00354 gui::set_error_message [msgcat::mc "Unable to write"] [msgcat::mc "Filename directory does not exist"]
00355 } else {
00356 gui::save_current -force [expr {$and_force ne ""}] -save_as [normalize_filename [utils::perform_substitutions $filename]]
00357 if {$and_close ne ""} {
00358 gui::close_current -force [expr {($and_close eq "q") ? 0 : 1}]
00359 set txt ""
00360 }
00361 }
00362
00363 # Create/delete a marker for the current line
00364 } elseif {[regexp {^m\s+(.*)$} $value -> marker]} {
00365 set line [lindex [split [$txt index insert] .] 0]
00366 gui::get_info $txt txt tab
00367 if {$marker ne ""} {
00368 if {[set tag [ctext::linemapSetMark $txt $line]] ne ""} {
00369 markers::add $tab tag $tag $marker
00370 gui::update_tab_markers $tab
00371 }
00372 } else {
00373 markers::delete_by_line $tab $line
00374 ctext::linemapClearMark $txt $line
00375 }
00376
00377 # Insert the contents of a file after the current line
00378 } elseif {[regexp {^r\s+(.*)$} $value -> filename]} {
00379 if {[string index $filename 0] eq "!"} {
00380 edit::insert_file $txt "|[utils::perform_substitutions [string range $filename 1 end]]"
00381 } else {
00382 edit::insert_file $txt [normalize_filename [utils::perform_substitutions $filename]]
00383 }
00384
00385 # Change the working directory
00386 } elseif {[regexp {^cd\s+(.*)$} $value -> directory]} {
00387 if {[file isdirectory [utils::perform_substitutions $directory]]} {
00388 gui::change_working_directory $directory
00389 }
00390
00391 # Handle set commands
00392 } elseif {[regexp {^set?\s+(.*)$} $value -> opts]} {
00393 foreach opt [split $opts ": "] {
00394 if {[regexp {(\S+?)(([+-])?=(\S+))?$} $opt -> key dummy mod val]} {
00395 set txt [do_set_command $txt $key $val $mod]
00396 }
00397 }
00398
00399 }
00400
00401 }
00402 }
00403 }
00404
00405 return $txt
00406
00407 }
00408
00409 ######################################################################
00410 # Handles set command calls and modeline settings.
00411 proc do_set_command {txt opt val mod {ml 0}} {
00412
00413 switch $opt {
00414 autochdir -
00415 acd {
00416 if {$ml} { return $txt }
00417 do_set_autochdir 1
00418 }
00419 noautochdir -
00420 noacd {
00421 if {$ml} { return $txt }
00422 do_set_autochdir 0
00423 }
00424 autoindent -
00425 ai { do_set_indent_mode IND 1 }
00426 noautoindent -
00427 noai { do_set_indent_mode IND 0 }
00428 browsedir -
00429 bsdir { do_set_browse_dir $val }
00430 expandtab -
00431 et { do_set_expandtab 1 }
00432 noexpandtab -
00433 noet { do_set_expandtab 0 }
00434 fileformat -
00435 ff { do_set_fileformat $val }
00436 foldenable -
00437 fen { do_set_foldenable 1 }
00438 nofoldenable -
00439 nofen { do_set_foldenable 0 }
00440 foldmethod -
00441 fdm { do_set_foldmethod $val }
00442 matchpairs -
00443 mps { do_set_matchpairs $val $mod }
00444 modeline -
00445 ml { do_set_modeline 1 }
00446 nomodeline -
00447 noml { do_set_modeline 0 }
00448 modelines -
00449 mls {
00450 if {$ml} { return $txt }
00451 do_set_modelines $val
00452 }
00453 modifiable -
00454 ma { do_set_modifiable 1 }
00455 nomodifiable -
00456 noma { do_set_modifiable 0 }
00457 modified -
00458 mod { do_set_modified 1 }
00459 nomodified -
00460 nomod { do_set_modified 0 }
00461 number -
00462 nu { do_set_number 1 }
00463 nonumber -
00464 nonu { do_set_number 0 }
00465 numberwidth -
00466 nuw {
00467 if {$ml} { return $txt }
00468 do_set_numberwidth $val
00469 }
00470 relativenumber -
00471 rnu { do_set_relativenumber relative }
00472 norelativenumber -
00473 nornu { do_set_relativenumber absolute }
00474 selection -
00475 sel { do_set_selection $val }
00476 shiftwidth -
00477 sw { do_set_shiftwidth $val }
00478 showmatch -
00479 sm { do_set_showmatch 1 }
00480 noshowmatch -
00481 nosm { do_set_showmatch 0 }
00482 smartindent -
00483 si { do_set_indent_mode IND+ 1 }
00484 nosmartindent -
00485 nosi { do_set_indent_mode IND+ 0 }
00486 splitbelow -
00487 sb { do_set_split 1 }
00488 nosplitbelow -
00489 nosb { do_set_split 0; set txt [gui::current_txt] }
00490 syntax -
00491 syn { do_set_syntax $val }
00492 tabstop -
00493 ts { do_set_tabstop $val }
00494 default {
00495 gui::set_info_message [format "%s (%s)" [msgcat::mc "Unrecognized vim option"] $opt]
00496 }
00497 }
00498
00499 return $txt
00500
00501 }
00502
00503 ######################################################################
00504 # Causes the current working directory to automatically change to be
00505 # the directory of the currently opened file. This is a global setting.
00506 proc do_set_autochdir {value} {
00507
00508 gui::set_auto_cwd $value
00509
00510 }
00511
00512 ######################################################################
00513 # Sets the indentation mode based on the current value, the specified
00514 # type (IND, IND+) and the value (0 or 1).
00515 proc do_set_indent_mode {type value} {
00516
00517 array set newval {
00518 {OFF,IND,0} {OFF}
00519 {OFF,IND,1} {IND}
00520 {OFF,IND+,0} {OFF}
00521 {OFF,IND+,1} {IND+}
00522 {IND,IND,0} {OFF}
00523 {IND,IND,1} {IND}
00524 {IND,IND+,0} {IND}
00525 {IND,IND+,1} {IND+}
00526 {IND+,IND,0} {IND+}
00527 {IND+,IND,1} {IND+}
00528 {IND+,IND+,0} {OFF}
00529 {IND+,IND+,1} {IND+}
00530 }
00531
00532 set txt [gui::current_txt]
00533
00534 # Get the current mode
00535 set curr [indent::get_indent_mode $txt]
00536
00537 # If the indentation mode will change, set it to the new value
00538 if {$curr ne $newval($curr,$type,$value)} {
00539 indent::set_indent_mode $txt $newval($curr,$type,$value)
00540 }
00541
00542 }
00543
00544 ######################################################################
00545 # Sets the file browser directory default pathname.
00546 proc do_set_browse_dir {val} {
00547
00548 gui::set_browse_directory $val
00549
00550 }
00551
00552 ######################################################################
00553 # Sets the tab expansion mode for the current buffer to (use tabs or
00554 # translate tabs to spaces.
00555 proc do_set_expandtab {val} {
00556
00557 snippets::set_expandtabs [gui::current_txt] $val
00558
00559 }
00560
00561 ######################################################################
00562 # Set the EOL setting for the current buffer.
00563 proc do_set_fileformat {val} {
00564
00565 array set map {
00566 dos crlf
00567 unix lf
00568 mac cr
00569 }
00570
00571 # Set the current EOL translation
00572 if {[info exists map($val)]} {
00573 gui::set_current_eol_translation $map($val)
00574 } else {
00575 gui::set_info_message [format "%s (%s)" [msgcat::mc "File format unrecognized"] $val]
00576 }
00577
00578 }
00579
00580 ######################################################################
00581 # Perform a fold_all or unfold_all command call.
00582 proc do_set_foldenable {val} {
00583
00584 folding::set_vim_foldenable [gui::current_txt] $val
00585
00586 }
00587
00588 ######################################################################
00589 # Set the current code folding method.
00590 proc do_set_foldmethod {val} {
00591
00592 array set map {
00593 none 1
00594 manual 1
00595 syntax 1
00596 }
00597
00598 # Set the current folding method
00599 if {[info exists map($val)]} {
00600 folding::set_fold_method [gui::current_txt] $val
00601 } else {
00602 gui::set_info_message [format "%s (%s)" [msgcat::mc "Folding method unrecognized"] $val]
00603 }
00604
00605 }
00606
00607 ######################################################################
00608 # Set the matchpairs to the given value(s). The value of val is like
00609 # <:> and mod will be {}, + or -.
00610 proc do_set_matchpairs {val mod} {
00611
00612 # Get the current text widget
00613 set txt [gui::current_txt]
00614 set lang [ctext::getLang $txt insert]
00615
00616 # Get the current match characters
00617 set match_chars [ctext::getAutoMatchChars $txt $lang]
00618 set new_chars [list]
00619
00620 # Iterate through the match characters
00621 foreach pair [split $val ,] {
00622 switch $pair {
00623 \{:\} { lappend new_chars curly }
00624 \(:\) { lappend new_chars paren }
00625 \[:\] { lappend new_chars square }
00626 <:> { lappend new_chars angled }
00627 }
00628 }
00629
00630 # Handle the modification value
00631 switch $mod {
00632 {} { set match_chars $new_chars }
00633 \+ { set match_chars [::struct::set union $match_chars $new_chars] }
00634 \- { set match_chars [::struct::set difference $match_chars $new_chars] }
00635 }
00636
00637 # Set the AutoMatchChars to the given set
00638 ctext::setAutoMatchChars $txt $lang $match_chars
00639
00640 }
00641
00642 ######################################################################
00643 # Sets whether or not modeline information should be used for the current
00644 # buffer.
00645 proc do_set_modeline {val} {
00646
00647 variable modeline
00648
00649 set modeline([gui::current_txt].t) $val
00650
00651 }
00652
00653 ######################################################################
00654 # Sets the number of lines to parse for modeline information.
00655 proc do_set_modelines {val} {
00656
00657 variable modelines
00658
00659 if {[string is integer $val]} {
00660 set modelines $val
00661 } else {
00662 gui::set_info_message [msgcat::mc "Illegal modelines value"]
00663 }
00664
00665 }
00666
00667 ######################################################################
00668 # Set the locked status of the current buffer.
00669 proc do_set_modifiable {val} {
00670
00671 gui::set_current_file_lock [expr {$val ? 0 : 1}]
00672
00673 }
00674
00675 ######################################################################
00676 # Changes the modified state of the current buffer.
00677 proc do_set_modified {val} {
00678
00679 gui::set_current_modified $val
00680
00681 }
00682
00683 ######################################################################
00684 # Sets the visibility of the line numbers.
00685 proc do_set_number {val} {
00686
00687 gui::set_line_number_view $val
00688
00689 }
00690
00691 ######################################################################
00692 # Sets the minimum width of the line number gutter area to the specified
00693 # value.
00694 proc do_set_numberwidth {val} {
00695
00696 if {[string is integer $val]} {
00697 gui::set_line_number_width $val
00698 } else {
00699 gui::set_info_message [format "%s (%s)" [msgcat::mc "Number width not a number"] $val]
00700 }
00701
00702 }
00703
00704 ######################################################################
00705 # Sets the relative numbering mode to the given value.
00706 proc do_set_relativenumber {val} {
00707
00708 [gui::current_txt] configure -linemap_type $val
00709
00710 }
00711
00712 ######################################################################
00713 # Sets the selection value to either old, inclusive or exclusive.
00714 proc do_set_selection {val} {
00715
00716 variable seltype
00717
00718 switch $val {
00719 "inclusive" -
00720 "exclusive" {
00721 set seltype $val
00722 }
00723 default {
00724 gui::set_info_message [format "%s (%s)" [msgcat::mc "Selection value is unsupported"] $val]
00725 }
00726 }
00727
00728 }
00729
00730 ######################################################################
00731 # Specifies the number of spaces to use for each indentation.
00732 proc do_set_shiftwidth {val} {
00733
00734 if {[string is integer $val]} {
00735 indent::set_shiftwidth [gui::current_txt].t $val
00736 } else {
00737 gui::set_info_message [msgcat::mc "Shiftwidth value is not an integer"]
00738 }
00739
00740 }
00741
00742 ######################################################################
00743 # Sets the showmatch value in all of the text widgets.
00744 proc do_set_showmatch {val} {
00745
00746 gui::set_matching_char $val
00747
00748 }
00749
00750 ######################################################################
00751 # Shows or hides split view in the current buffer.
00752 proc do_set_split {val} {
00753
00754 if {$val} {
00755 gui::show_split_pane [gui::get_info {} current tab]
00756 } else {
00757 gui::hide_split_pane [gui::get_info {} current tab]
00758 }
00759
00760 }
00761
00762 ######################################################################
00763 # Run the set syntax command.
00764 proc do_set_syntax {val} {
00765
00766 syntax::set_current_language [syntax::get_vim_language $val]
00767
00768 }
00769
00770 ######################################################################
00771 # Specifies number of spaces that a TAB in the file counts for.
00772 proc do_set_tabstop {val} {
00773
00774 if {[string is integer $val]} {
00775 indent::set_tabstop [gui::current_txt].t $val
00776 } else {
00777 gui::set_info_message [msgcat::mc "Tabstop value is not an integer"]
00778 }
00779
00780 }
00781
00782 ######################################################################
00783 # Set the select anchor for visual mode.
00784 proc set_select_anchors {txtt indices} {
00785
00786 variable select_anchors
00787
00788 set select_anchors($txtt) $indices
00789
00790 }
00791
00792 ######################################################################
00793 # Normalizes the given filename string, performing any environment
00794 # variable substitutions.
00795 proc normalize_filename {file_str} {
00796
00797 while {[regexp -indices {(\$(\w+))} $file_str -> str var]} {
00798 set var [string range $file_str {*}$var]
00799 if {[info exists ::env($var)]} {
00800 set file_str [string replace $file_str {*}$str $::env($var)]
00801 } else {
00802 return -code error "Environment variable $var does not exist"
00803 }
00804 }
00805
00806 return [file normalize $file_str]
00807
00808 }
00809
00810 ######################################################################
00811 # Adjust the current selection if we are in visual mode.
00812 proc adjust_select {txtt index pos} {
00813
00814 variable mode
00815 variable select_anchors
00816 variable seltype
00817
00818 # Get the visual type from the mode
00819 set type [lindex [split $mode($txtt) :] 1]
00820
00821 # Get the anchor for the given selection
00822 set anchor [lindex $select_anchors($txtt) $index]
00823
00824 if {$type eq "block"} {
00825 if {$seltype eq "exclusive"} {
00826 select::handle_block_selection $txtt $anchor [$txtt index $pos]
00827 } else {
00828 select::handle_block_selection $txtt $anchor [$txtt index $pos+1c]
00829 }
00830 } elseif {[$txtt compare $anchor < $pos]} {
00831 if {$type eq "line"} {
00832 $txtt tag add sel "$anchor linestart" "$pos lineend"
00833 } elseif {$seltype eq "exclusive"} {
00834 $txtt tag add sel $anchor $pos
00835 } else {
00836 $txtt tag add sel $anchor $pos+1c
00837 }
00838 } else {
00839 if {$type eq "line"} {
00840 $txtt tag add sel "$pos linestart" "$anchor lineend"
00841 } elseif {$seltype eq "exclusive"} {
00842 $txtt tag add sel $pos $anchor
00843 } else {
00844 $txtt tag add sel $pos $anchor+1c
00845 }
00846 }
00847
00848 }
00849
00850 ######################################################################
00851 # Handles an escape key in the command entry widget.
00852 proc handle_command_escape {w} {
00853
00854 # Get the last text widget that had focus
00855 set txt [gui::last_txt_focus]
00856
00857 # Delete the value in the command entry
00858 $w delete 0 end
00859
00860 # Remove the grab and set the focus back to the text widget
00861 grab release $w
00862 gui::set_txt_focus $txt
00863
00864 # Hide the command entry widget
00865 gui::panel_forget $w
00866
00867 }
00868
00869 ######################################################################
00870 # Handles a backspace key in the command entry widget.
00871 proc handle_command_backspace {w} {
00872
00873 if {[$w get] eq ""} {
00874
00875 # Remove the grab and set the focus back to the text widget
00876 grab release $w
00877 gui::set_txt_focus [gui::last_txt_focus]
00878
00879 # Hide the command entry widget
00880 gui::panel_forget $w
00881
00882 }
00883
00884 }
00885
00886 ######################################################################
00887 # Returns the line number based on the given line number character.
00888 proc get_linenum {txt char} {
00889
00890 gui::get_info $txt txt tab
00891
00892 if {$char eq "."} {
00893 return [$txt index "insert linestart"]
00894 } elseif {$char eq "^"} {
00895 return "1.0"
00896 } elseif {$char eq "$"} {
00897 return [$txt index "end linestart"]
00898 } elseif {[set index [markers::get_index $tab $char]] ne ""} {
00899 return [$txt index "$index linestart"]
00900 } elseif {[regexp {^\d+$} $char]} {
00901 return "$char.0"
00902 } else {
00903 return -code error "$char is not a valid marker name"
00904 }
00905
00906 }
00907
00908 ######################################################################
00909 # Add Vim bindings
00910 proc add_bindings {txt} {
00911
00912 variable mode
00913 variable number
00914 variable multiplier
00915 variable search_dir
00916 variable column
00917 variable select_anchors
00918 variable modeline
00919 variable multicursor
00920 variable recording
00921 variable operator
00922 variable motion
00923
00924 # Change the cursor to the block cursor
00925 $txt configure -blockcursor true -insertwidth 1
00926
00927 # Put ourselves into start mode
00928 set mode($txt.t) "command"
00929 set number($txt.t) ""
00930 set multiplier($txt.t) ""
00931 set search_dir($txt.t) "next"
00932 set column($txt.t) ""
00933 set select_anchors($txt.t) [list]
00934 set modeline($txt.t) 1
00935 set multicursor($txt.t) 0
00936 set operator($txt.t) ""
00937 set motion($txt.t) ""
00938
00939 # Add bindings
00940 bind vim$txt <Escape> "if {\[vim::handle_escape %W\]} { break }"
00941 bind vim$txt <Key> "if {\[vim::handle_any %W %k %A %K\]} { break }"
00942 bind vim$txt <Control-Button-1> "vim::nil"
00943 bind vim$txt <Shift-Button-1> "vim::nil"
00944 bind vim$txt <Button-1> "vim::handle_button1 %W %x %y; break"
00945 bind vim$txt <Double-Shift-Button-1> "vim::nil"
00946 bind vim$txt <Double-Button-1> "vim::handle_double_button1 %W %x %y; break"
00947 bind vim$txt <Triple-Button-1> "vim::nil"
00948 bind vim$txt <Triple-Shift-Button-1> "vim::nil"
00949 bind vim$txt <B1-Motion> "vim::handle_motion %W %x %y; break"
00950
00951 # Insert the vim binding just after all
00952 set all_index [lsearch [bindtags $txt.t] all]
00953 bindtags $txt.t [linsert [bindtags $txt.t] [expr $all_index + 1] vim$txt]
00954
00955 # Put ourselves into start mode
00956 command_mode $txt.t
00957
00958 # Set autoseparator mode to false
00959 $txt configure -autoseparators 0
00960
00961 }
00962
00963 ######################################################################
00964 # Called whenever the given text widget is destroyed.
00965 proc handle_destroy_txt {txt} {
00966
00967 variable command_entries
00968 variable mode
00969 variable number
00970 variable multiplier
00971 variable search_dir
00972 variable column
00973 variable select_anchors
00974 variable modeline
00975
00976 unset -nocomplain command_entries($txt.t)
00977 unset -nocomplain mode($txt.t)
00978 unset -nocomplain number($txt.t)
00979 unset -nocomplain multiplier($txt.t)
00980 unset -nocomplain search_dir($txt.t)
00981 unset -nocomplain column($txt.t)
00982 unset -nocomplain select_anchors($txt.t)
00983 unset -nocomplain modeline($txt.t)
00984
00985 }
00986
00987 ######################################################################
00988 # This is a do-nothing procedure that is called by bindings that would
00989 # otherwise match other keybindings that we don't want to call.
00990 proc nil {} {
00991
00992 }
00993
00994 ######################################################################
00995 # Handles a left-click event when in Vim mode.
00996 proc handle_button1 {W x y} {
00997
00998 $W tag remove sel 1.0 end
00999
01000 set current [$W index @$x,$y]
01001 $W mark set [utils::text_anchor $W] $current
01002 ::tk::TextSetCursor $W $current
01003
01004 adjust_insert $W
01005
01006 focus $W
01007
01008 }
01009
01010 ######################################################################
01011 # Handles a double-left-click event when in Vim mode.
01012 proc handle_double_button1 {W x y} {
01013
01014 $W tag remove sel 1.0 end
01015
01016 set current [$W index @$x,$y]
01017 ::tk::TextSetCursor $W [$W index "$current wordstart"]
01018
01019 adjust_insert $W
01020
01021 $W tag add sel [$W index "$current wordstart"] [$W index "$current wordend"]
01022
01023 focus $W
01024
01025 }
01026
01027 ######################################################################
01028 # Handle left-button hold motion event when in Vim mode.
01029 proc handle_motion {W x y} {
01030
01031 $W tag remove sel 1.0 end
01032
01033 set current [$W index @$x,$y]
01034 ::tk::TextSetCursor $W $current
01035
01036 adjust_insert $W
01037
01038 # Add the selection
01039 set anchor [utils::text_anchor $W]
01040 if {[$W compare $anchor < $current]} {
01041 $W tag add sel $anchor $current
01042 } else {
01043 $W tag add sel $current $anchor
01044 }
01045
01046 focus $W
01047
01048 }
01049
01050 ######################################################################
01051 # Remove the Vim bindings on the text widget.
01052 proc remove_bindings {txt} {
01053
01054 # Remove the vim* bindings from the widget
01055 if {[set index [lsearch [bindtags $txt.t] vim$txt]] != -1} {
01056 bindtags $txt.t [lreplace [bindtags $txt.t] $index $index]
01057 }
01058
01059 # Remove the vimpre* bindings from the widget
01060 if {[set index [lsearch [bindtags $txt.t] vimpre$txt]] != -1} {
01061 bindtags $txt.t [lreplace [bindtags $txt.t] $index $index]
01062 }
01063
01064 # Move $txt.t <<Modified>> binding back to $txt
01065 bind $txt <<Modified>> ""
01066
01067 # Change the cursor to the insertion cursor and turn autoseparators on
01068 $txt configure -blockcursor false -autoseparators 1 -insertwidth [preferences::get Appearance/CursorWidth]
01069
01070 }
01071
01072 ######################################################################
01073 # Resets the operator/motion states.
01074 proc reset_state {txtt err} {
01075
01076 variable operator
01077 variable motion
01078 variable multiplier
01079 variable number
01080
01081 # Stop recording
01082 record_stop 0
01083
01084 # Clear the state information
01085 set operator($txtt) ""
01086 set motion($txtt) ""
01087 set multiplier($txtt) ""
01088 set number($txtt) ""
01089
01090 # Add a separator
01091 $txtt edit separator
01092
01093 }
01094
01095 ######################################################################
01096 # Sets the operator.
01097 proc set_operator {txtt op keysyms} {
01098
01099 variable operator
01100
01101 # Set operator
01102 set operator($txtt) $op
01103
01104 # Start recording
01105 record_start $txtt $keysyms
01106
01107 }
01108
01109 ######################################################################
01110 # Set the current mode to the "edit" mode.
01111 proc edit_mode {txtt} {
01112
01113 variable mode
01114 variable multicursor
01115
01116 # Set the mode to the edit mode
01117 set mode($txtt) "edit"
01118
01119 # Clear the multicursor mode (since we are not moving multicursors around)
01120 disable_multicursor $txtt
01121
01122 # Set the blockcursor to false
01123 $txtt configure -blockcursor false -insertwidth [preferences::get Appearance/CursorWidth]
01124
01125 # If the current cursor is on a dummy space, remove it
01126 set tags [$txtt tag names insert]
01127 if {([lsearch $tags "dspace"] != -1) && ([lsearch $tags "mcursor"] == -1)} {
01128 $txtt fastdelete -update 0 -undo 0 insert
01129 }
01130
01131 }
01132
01133 ######################################################################
01134 # Saves the last selection in case the user wants to use it again.
01135 proc set_last_selection {txtt} {
01136
01137 variable mode
01138 variable last_selection
01139
01140 if {[info exists mode($txtt)]} {
01141 set last_selection($txtt) [list $mode($txtt) [$txtt tag ranges sel]]
01142 }
01143
01144 }
01145
01146 ######################################################################
01147 # Set the current mode to the "command" mode.
01148 proc command_mode {txtt} {
01149
01150 variable mode
01151 variable multicursor
01152
01153 # If we are coming from visual mode, clear the selection and the anchors
01154 if {[$txtt tag ranges sel] ne ""} {
01155 set_last_selection $txtt
01156 $txtt tag remove sel 1.0 end
01157 }
01158
01159 # If were in the edit or replace_all state, move the insertion cursor back
01160 # one character.
01161 if {(($mode($txtt) eq "edit") || ([string compare -length 7 $mode($txtt) "replace"] == 0)) && \
01162 ([$txtt index insert] ne [$txtt index "insert linestart"])} {
01163 if {[multicursor::enabled $txtt]} {
01164 multicursor::move $txtt left
01165 } else {
01166 ::tk::TextSetCursor $txtt "insert-1c"
01167 }
01168 }
01169
01170 # Set the blockcursor to true
01171 $txtt configure -blockcursor true -insertwidth 1
01172
01173 # Set the current mode to the command mode
01174 set mode($txtt) "command"
01175
01176 # Clear multicursor mode
01177 disable_multicursor $txtt
01178
01179 # Reset the states
01180 reset_state $txtt 0
01181
01182 # Adjust the insertion marker
01183 adjust_insert $txtt
01184
01185 }
01186
01187 ######################################################################
01188 # Set the current mode to multicursor move mode.
01189 proc multicursor_mode {txtt} {
01190
01191 variable mode
01192 variable multicursor
01193
01194 set multicursor($txtt) 1
01195
01196 # Effectively make the insertion cursor disappear
01197 $txtt configure -blockcursor 0 -insertwidth 0
01198
01199 # Make the multicursors look like the normal cursor
01200 $txtt tag configure mcursor -background [$txtt cget -insertbackground]
01201
01202 # Make sure that the status bar is updated properly
01203 gui::update_position [winfo parent $txtt]
01204
01205 }
01206
01207 ######################################################################
01208 # Turns off multicursor moving mode.
01209 proc disable_multicursor {txtt} {
01210
01211 variable mode
01212 variable multicursor
01213
01214 set multicursor($txtt) 0
01215
01216 # Restore the insertion cursor width
01217 $txtt configure -blockcursor [expr {$mode($txtt) ne "edit"}] -insertwidth [preferences::get Appearance/CursorWidth]
01218
01219 # Make the multicursors look like normal
01220 $txtt tag configure mcursor -background ""
01221
01222 # Make sure that the status bar is updated properly
01223 gui::update_position [winfo parent $txtt]
01224
01225 }
01226
01227 ######################################################################
01228 # Set the current mode to the "visual" mode.
01229 proc visual_mode {txtt type} {
01230
01231 variable mode
01232 variable select_anchors
01233 variable multicursor
01234 variable seltype
01235 variable last_selection
01236
01237 # If we are called with the type of "last", set the selection
01238 if {$type eq "last"} {
01239 if {$last_selection($txtt) ne ""} {
01240 lassign $last_selection($txtt) vmode sel
01241 set mode($txtt) $vmode
01242 ::tk::TextSetCursor $txtt "[lindex $sel 1]-1c"
01243 $txtt tag remove sel 1.0
01244 $txtt tag add sel {*}$sel
01245 }
01246 return
01247 }
01248
01249 # Set the current mode
01250 set mode($txtt) "visual:$type"
01251
01252 # Clear the current selection
01253 $txtt tag remove sel 1.0 end
01254
01255 # Initialize the select range
01256 if {$multicursor($txtt)} {
01257 set select_anchors($txtt) [list]
01258 foreach {start end} [$txtt tag ranges mcursor] {
01259 lappend select_anchors($txtt) $start
01260 }
01261 } else {
01262 set select_anchors($txtt) [$txtt index insert]
01263 }
01264
01265 # If the selection type is inclusive or old, include the current insertion cursor in the selection
01266 if {$type eq "line"} {
01267 foreach anchor $select_anchors($txtt) {
01268 $txtt tag add sel "$anchor linestart" "$anchor+1l linestart"
01269 }
01270 } elseif {$seltype ne "exclusive"} {
01271 foreach anchor $select_anchors($txtt) {
01272 $txtt tag add sel $anchor $anchor+1c
01273 }
01274 }
01275
01276 # Make sure that the mode is updated in the status bar
01277 gui::update_position [winfo parent $txtt]
01278
01279 }
01280
01281 ######################################################################
01282 # Returns true if we are in visual mode.
01283 proc in_visual_mode {txtt} {
01284
01285 variable mode
01286
01287 return [expr {[lindex [split $mode($txtt) :] 0] eq "visual"}]
01288
01289 }
01290
01291 ######################################################################
01292 # Starts recording keystrokes.
01293 proc record_start {txtt {keysyms {}} {reg ""}} {
01294
01295 variable recording
01296 variable multiplier
01297
01298 if {$recording(mode) eq "none"} {
01299 set recording(mode) "record"
01300 set recording(num) $multiplier($txtt)
01301 set recording(events) $keysyms
01302 if {($recording(curr_reg) eq "") && ($reg ne "")} {
01303 set recording($reg,events) [list]
01304 set recording(curr_reg) $reg
01305 }
01306 }
01307
01308 }
01309
01310 ######################################################################
01311 # Stops recording keystrokes.
01312 proc record_stop {reg_stop} {
01313
01314 variable recording
01315
01316 set recording(mode) "none"
01317
01318 if {[set reg $recording(curr_reg)] ne ""} {
01319 lappend recording($reg,events) {*}$recording(events)
01320 if {$reg_stop} {
01321 set recording(curr_reg) ""
01322 }
01323 }
01324
01325 }
01326
01327 ######################################################################
01328 # Records a signal event and stops recording.
01329 proc record {txtt keysyms {reg ""}} {
01330
01331 variable recording
01332 variable multiplier
01333
01334 if {$recording(mode) eq "none"} {
01335 set recording(events) $keysyms
01336 set recording(num) $multiplier($txtt)
01337 if {$recording(curr_reg) ne ""} {
01338 lappend recording($reg,events) {*}$recording(events)
01339 }
01340 }
01341
01342 }
01343
01344 ######################################################################
01345 # Adds an event to the recording buffer if we are in record mode.
01346 proc record_add {keysym} {
01347
01348 variable recording
01349
01350 if {$recording(mode) eq "record"} {
01351 lappend recording(events) $keysym
01352 }
01353
01354 }
01355
01356 ######################################################################
01357 # Returns true if we are currently in a user-recording state.
01358 proc in_recording {} {
01359
01360 variable recording
01361
01362 return [expr {$recording(curr_reg) ne ""}]
01363
01364 }
01365
01366 ######################################################################
01367 # Plays back the record buffer.
01368 proc playback {txtt {reg ""}} {
01369
01370 variable recording
01371 variable multiplier
01372
01373 # Set the record mode to playback
01374 set recording(mode) "playback"
01375
01376 if {$reg eq ""} {
01377
01378 # Sets the number to use prior to the sequence
01379 set num [expr {($multiplier($txtt) ne "") ? $multiplier($txtt) : $recording(num)}]
01380
01381 # Clear the multiplier
01382 set multiplier($txtt) ""
01383
01384 # Add the numerical value
01385 foreach event [split $num {}] {
01386 event generate $txtt <Key> -keysym $event
01387 }
01388
01389 set events "events"
01390
01391 } else {
01392
01393 set events "$reg,events"
01394
01395 }
01396
01397 # Replay the recording buffer
01398 foreach event $recording($events) {
01399 event generate $txtt <Key> -keysym $event
01400 }
01401
01402 # Set the record mode to none
01403 set recording(mode) "none"
01404
01405 }
01406
01407 ######################################################################
01408 # Performs the last colon command operation.
01409 proc playback_colon {txtt} {
01410
01411 variable recording
01412
01413 if {[info exists recording(:,events)] && ($recording(:,events) ne "")} {
01414 handle_colon_command [winfo parent $txtt] $recording(:,events)
01415 }
01416
01417 }
01418
01419 ######################################################################
01420 # Stops recording and clears the recording array.
01421 proc record_clear {{reg ""}} {
01422
01423 variable recording
01424
01425 set recording(mode) "none"
01426 set recording(num) ""
01427 set recording(events) [list]
01428 set recording($reg,num) ""
01429 set recording($reg,events) [list]
01430
01431 }
01432
01433 ######################################################################
01434 # Adjust the insertion marker so that it never is allowed to sit on
01435 # the lineend spot.
01436 proc adjust_insert {txtt} {
01437
01438 variable mode
01439
01440 # If we are not running in Vim mode, don't continue
01441 if {![in_vim_mode $txtt]} {
01442 return
01443 }
01444
01445 # Remove any existing dspace characters
01446 remove_dspace [winfo parent $txtt]
01447
01448 # If the current line contains nothing, add a dummy space so that the
01449 # block cursor doesn't look dumb.
01450 if {[$txtt index "insert linestart"] eq [$txtt index "insert lineend"]} {
01451 $txtt fastinsert -update 0 -undo 0 insert " " dspace
01452 $txtt mark set insert "insert-1c"
01453 gui::update_position [winfo parent $txtt]
01454
01455 # Make sure that lineend is never the insertion point
01456 } elseif {[$txtt index insert] eq [$txtt index "insert lineend"]} {
01457 $txtt mark set insert "insert-1 display chars"
01458 gui::update_position [winfo parent $txtt]
01459 }
01460
01461 # Adjust the selection (if we are in visual mode)
01462 if {[in_visual_mode $txtt]} {
01463 adjust_select $txtt 0 insert
01464 }
01465
01466 }
01467
01468 ######################################################################
01469 # Returns the current number.
01470 proc get_number {txtt} {
01471
01472 variable number
01473 variable multiplier
01474
01475 set num [expr {($number($txtt) eq "") ? 1 : $number($txtt)}]
01476 set mult [expr {($multiplier($txtt) eq "") ? 1 : $multiplier($txtt)}]
01477
01478 return [expr $mult * $num]
01479
01480 }
01481
01482 ######################################################################
01483 # Removes dspace characters.
01484 proc remove_dspace {w} {
01485
01486 foreach {endpos startpos} [lreverse [$w tag ranges dspace]] {
01487 if {[lsearch [$w tag names $startpos] "mcursor"] == -1} {
01488 $w fastdelete -update 0 -undo 0 $startpos $endpos
01489 }
01490 }
01491
01492 }
01493
01494 ######################################################################
01495 # Removes the dspace tag from the current index (if it is set).
01496 proc cleanup_dspace {w} {
01497
01498 if {[lsearch [$w tag names insert] dspace] != -1} {
01499 $w tag remove dspace insert
01500 }
01501
01502 }
01503
01504 ######################################################################
01505 # Returns the contents of the given text widget without the injected
01506 # dspaces.
01507 proc get_cleaned_content {txt} {
01508
01509 set str ""
01510 set last_startpos 1.0
01511
01512 # Remove any dspace characters
01513 foreach {startpos endpos} [$txt tag ranges dspace] {
01514 append str [$txt get $last_startpos $startpos]
01515 set last_startpos $endpos
01516 }
01517
01518 append str [$txt get $last_startpos "end-1c"]
01519
01520 return $str
01521
01522 }
01523
01524 ######################################################################
01525 # Handles the escape-key when in Vim mode.
01526 proc handle_escape {txtt} {
01527
01528 variable mode
01529 variable recording
01530 variable multicursor
01531
01532 if {$mode($txtt) ne "command"} {
01533
01534 # Add to the recording if we are doing so
01535 record_add Escape
01536
01537 # Set the mode to command
01538 command_mode $txtt
01539
01540 } else {
01541
01542 # Clear the any selections
01543 $txtt tag remove sel 1.0 end
01544
01545 # If were in start mode, clear the auto recording buffer
01546 record_clear
01547
01548 # Clear any searches
01549 search::find_clear
01550
01551 # Clear the state
01552 reset_state $txtt 1
01553
01554 }
01555
01556 # Clear the multicursor indicator
01557 disable_multicursor $txtt
01558
01559 return 1
01560
01561 }
01562
01563 if {[tk windowingsystem] eq "aqua"} {
01564 proc get_keysym {keycode keysym} {
01565 return $utils::code2sym([expr $keycode & 0xffff])
01566 }
01567 } else {
01568 proc get_keysym {keycode keysym} { return $keysym }
01569 }
01570
01571 ######################################################################
01572 # Handles any single printable character.
01573 proc handle_any {txtt keycode char keysym} {
01574
01575 variable mode
01576 variable operator
01577 variable column
01578 variable recording
01579
01580 # Lookup the keysym
01581 if {[catch { get_keysym $keycode $keysym } keysym]} {
01582 return 0
01583
01584 # If the key does not have a printable char representation, quit now
01585 } elseif {([string compare -length 5 $keysym "Shift"] == 0) || \
01586 ([string compare -length 7 $keysym "Control"] == 0) || \
01587 ([string compare -length 3 $keysym "Alt"] == 0) || \
01588 ($keysym eq "??")} {
01589 return 1
01590 }
01591
01592 # Record the character
01593 record_add $keysym
01594
01595 # If the current character needs to be used by the current mode, handle it now
01596 if {[info procs do_mode_$mode($txtt)] ne ""} {
01597 if {![catch { do_mode_$mode($txtt) $txtt $keysym $char } rc]} {
01598 return $rc
01599 }
01600 }
01601
01602 # If we are handling a motion based on a character, handle it
01603 if {[handle_find_motion $txtt $char]} {
01604 return 1
01605 } elseif {[handle_between_motion $txtt $char]} {
01606 return 1
01607 }
01608
01609 # If the keysym is neither j or k, clear the column
01610 if {($keysym ne "j") && ($keysym ne "k")} {
01611 set column($txtt) ""
01612 }
01613
01614 # Handle the command
01615 if {[info procs handle_$keysym] ne ""} {
01616 if {![catch { handle_$keysym $txtt } rc] && $rc} {
01617 return 1
01618 }
01619 } elseif {[string is integer $keysym] && [handle_number $txtt $char]} {
01620 return 1
01621 }
01622
01623 # Reset the state
01624 reset_state $txtt 1
01625
01626 return 1
01627
01628 }
01629
01630 ######################################################################
01631 # Called by handle_any when the current mode is edit.
01632 proc do_mode_edit {txtt keysym char} {
01633
01634 return 0
01635
01636 }
01637
01638 ######################################################################
01639 # Called by handle_any when the current mode is replace.
01640 proc do_mode_replace {txtt keysym char} {
01641
01642 # Replace the current character with the given character
01643 do_replace $txtt $char
01644
01645 # Change our mode back to command mode
01646 command_mode $txtt
01647
01648 return 1
01649
01650 }
01651
01652 ######################################################################
01653 # Called by handle_any when the current mode is replace_all.
01654 proc do_mode_replace_all {txtt keysym char} {
01655
01656 # Replace the current character with the given character
01657 do_replace $txtt $char
01658
01659 return 1
01660
01661 }
01662
01663 ######################################################################
01664 # Called by handle_any when the current mode is record_reg. Parses
01665 # character for a valid recording register.
01666 proc do_mode_record_reg {txtt keysym char} {
01667
01668 # Set the mode to command
01669 command_mode $txtt
01670
01671 # Parse the given character to see if it is a matching register name
01672 if {[regexp {^[a-zA-Z\"]$} $keysym]} {
01673 record_start $txtt {} $keysym
01674 return 1
01675 }
01676
01677 return -code error "Unexpected recording register"
01678
01679 }
01680
01681 ######################################################################
01682 # Called by handle_any when the current mode is playback_reg. Parses
01683 # the character for a valid playback register.
01684 proc do_mode_playback_reg {txtt keysym char} {
01685
01686 # Set the mode to command
01687 command_mode $txtt
01688
01689 if {[regexp {^[a-z]$} $keysym]} {
01690 playback $txtt $keysym
01691 return 1
01692 } elseif {$keysym eq "at"} {
01693 if {$recording(curr_reg) ne ""} {
01694 playback $txtt $recording(curr_reg)
01695 }
01696 return 1
01697 } elseif {$keysym at "colon"} {
01698 for {set i 0} {$i < [get_number $txtt]} {incr i} {
01699 playback_colon $txtt
01700 }
01701 return 1
01702 }
01703
01704 return -code error "Unexpected playback register"
01705
01706 }
01707
01708 ######################################################################
01709 # Perform text replacement.
01710 proc do_replace {txtt char} {
01711
01712 if {[multicursor::enabled $txtt]} {
01713 multicursor::replace $txtt $char indent::check_indent
01714 } else {
01715 $txtt replace insert "insert+1c" $char
01716 $txtt syntax highlight "insert linestart" "insert lineend"
01717 }
01718
01719 }
01720
01721 ######################################################################
01722 # Performs the current motion-specific operation on the text range specified
01723 # by startpos/endpos.
01724 proc do_operation {txtt eposargs {sposargs {}} args} {
01725
01726 variable mode
01727 variable operator
01728 variable motion
01729 variable multiplier
01730 variable number
01731 variable multicursor
01732
01733 array set opts {
01734 -cursor "none"
01735 -object ""
01736 }
01737 array set opts $args
01738
01739 switch $operator($txtt) {
01740 "" {
01741 if {$multicursor($txtt)} {
01742 multicursor::move $txtt $eposargs
01743 } elseif {$opts(-object) ne ""} {
01744 lassign [edit::get_range $txtt $eposargs $sposargs $opts(-object) 1] spos epos
01745 if {$spos ne ""} {
01746 ::tk::TextSetCursor $txtt $spos
01747 visual_mode $txtt char
01748 ::tk::TextSetCursor $txtt $epos
01749 vim::adjust_insert $txtt
01750 }
01751 } else {
01752 ::tk::TextSetCursor $txtt [edit::get_index $txtt {*}$eposargs]
01753 vim::adjust_insert $txtt
01754 }
01755 reset_state $txtt 0
01756 return 1
01757 }
01758 "delete" {
01759 if {![multicursor::delete $txtt $eposargs $sposargs $opts(-object)]} {
01760 set copy [expr [lsearch [list spacestart spaceend] [lindex $eposargs 0]] == -1]
01761 edit::delete $txtt {*}[edit::get_range $txtt $eposargs $sposargs $opts(-object) 0] $copy 1
01762 }
01763 command_mode $txtt
01764 return 1
01765 }
01766 "change" {
01767 if {![multicursor::delete $txtt $eposargs $sposargs $opts(-object)]} {
01768 edit::delete $txtt {*}[edit::get_range $txtt $eposargs $sposargs $opts(-object) 0] 0 0
01769 }
01770 edit_mode $txtt
01771 set operator($txtt) ""
01772 set motion($txtt) ""
01773 set multiplier($txtt) ""
01774 set number($txtt) ""
01775 return 1
01776 }
01777 "yank" {
01778 lassign [edit::get_range $txtt $eposargs $sposargs $opts(-object) 0] startpos endpos
01779 clipboard clear
01780 clipboard append [$txtt get $startpos $endpos]
01781 if {$opts(-cursor) ne ""} {
01782 ::tk::TextSetCursor $txtt [edit::get_index $txtt {*}$opts(-cursor)]
01783 }
01784 vim::adjust_insert $txtt
01785 command_mode $txtt
01786 return 1
01787 }
01788 "swap" {
01789 if {![multicursor::toggle_case $txtt $eposargs $sposargs $opts(-object)]} {
01790 lassign [edit::get_range $txtt $eposargs $sposargs $opts(-object) 0] startpos endpos
01791 edit::transform_toggle_case $txtt $startpos $endpos [$txtt index [edit::get_index $txtt {*}$opts(-cursor)]]
01792 }
01793 command_mode $txtt
01794 return 1
01795 }
01796 "upper" {
01797 if {![multicursor::upper_case $txtt $eposargs $sposargs $opts(-object)]} {
01798 lassign [edit::get_range $txtt $eposargs $sposargs $opts(-object) 0] startpos endpos
01799 edit::transform_to_upper_case $txtt $startpos $endpos [$txtt index [edit::get_index $txtt {*}$opts(-cursor)]]
01800 }
01801 command_mode $txtt
01802 return 1
01803 }
01804 "lower" {
01805 if {![multicursor::lower_case $txtt $eposargs $sposargs $opts(-object)]} {
01806 lassign [edit::get_range $txtt $eposargs $sposargs $opts(-object) 0] startpos endpos
01807 edit::transform_to_lower_case $txtt $startpos $endpos [$txtt index [edit::get_index $txtt {*}$opts(-cursor)]]
01808 }
01809 command_mode $txtt
01810 return 1
01811 }
01812 "rot13" {
01813 if {![multicursor::rot13 $txtt $eposargs $sposargs $opts(-object)]} {
01814 lassign [edit::get_range $txtt $eposargs $sposargs $opts(-object) 0] startpos endpos
01815 edit::transform_to_rot13 $txtt $startpos $endpos [$txtt index [edit::get_index $txtt {*}$opts(-cursor)]]
01816 }
01817 command_mode $txtt
01818 return 1
01819 }
01820 "format" {
01821 if {![multicursor::format_text $txtt $eposargs $sposargs $opts(-object)]} {
01822 lassign [edit::get_range $txtt $eposargs $sposargs $opts(-object) 0] startpos endpos
01823 indent::format_text $txtt $startpos $endpos
01824 ::tk::TextSetCursor $txtt [edit::get_index $txtt firstchar -num 0 -startpos $startpos]
01825 }
01826 command_mode $txtt
01827 return 1
01828 }
01829 "lshift" {
01830 if {![multicursor::shift $txtt left $eposargs $sposargs $opts(-object)]} {
01831 lassign [edit::get_range $txtt $eposargs $sposargs $opts(-object) 0] startpos endpos
01832 edit::unindent $txtt $startpos $endpos
01833 ::tk::TextSetCursor $txtt [edit::get_index $txtt firstchar -num 0 -startpos $startpos]
01834 }
01835 command_mode $txtt
01836 return 1
01837 }
01838 "rshift" {
01839 if {![multicursor::shift $txtt right $eposargs $sposargs $opts(-object)]} {
01840 lassign [edit::get_range $txtt $eposargs $sposargs $opts(-object) 0] startpos endpos
01841 edit::indent $txtt $startpos $endpos
01842 ::tk::TextSetCursor $txtt [edit::get_index $txtt firstchar -num 0 -startpos $startpos]
01843 }
01844 command_mode $txtt
01845 return 1
01846 }
01847 }
01848
01849 return 0
01850
01851 }
01852
01853 ######################################################################
01854 # Perform the current operation on the given object.
01855 proc do_object_operation {txtt object} {
01856
01857 variable operator
01858 variable motion
01859
01860 # Execute the operation
01861 switch $motion($txtt) {
01862 "a" {
01863 if {[in_visual_mode $txtt] || ($operator($txtt) ne "")} {
01864 return [do_operation $txtt [list $object [get_number $txtt]] [list] -object "a"]
01865 }
01866 }
01867 "i" {
01868 if {[in_visual_mode $txtt] || ($operator($txtt) ne "")} {
01869 return [do_operation $txtt [list $object [get_number $txtt]] [list] -object "i"]
01870 }
01871 }
01872 }
01873
01874 reset_state $txtt 0
01875
01876 return 1
01877
01878 }
01879
01880 ######################################################################
01881 # Checks the current mode and if we are in a find character motion,
01882 # handle the action.
01883 proc handle_find_motion {txtt char} {
01884
01885 variable operator
01886 variable motion
01887
01888 # If the current mode does not pertain to us, return now
01889 if {[lsearch {t f} [string tolower $motion($txtt)]] == -1} {
01890 return 0
01891 }
01892
01893 # Get the motion information
01894 set dir [expr {[string is lower $motion($txtt)] ? "next" : "prev"}]
01895 set excl [expr {[string tolower $motion($txtt)] eq "t"}]
01896
01897 # Determine where to put the cursor
01898 set cursorargs "none"
01899 if {$dir eq "prev"} {
01900 set cursorargs [list findchar -dir prev -char $char -num [get_number $txtt] -exclusive $excl]
01901 }
01902
01903 if {($operator($txtt) eq "") || ($dir eq "prev")} {
01904 return [do_operation $txtt [list findchar -dir $dir -char $char -num [get_number $txtt] -exclusive $excl] {} -cursor $cursorargs]
01905 } else {
01906 return [do_operation $txtt [list findchar -dir $dir -char $char -num [get_number $txtt] -exclusive $excl -adjust "+1 display chars"]]
01907 }
01908
01909 }
01910
01911 ######################################################################
01912 # Checks the current mode and if we are in a between character motion,
01913 # handle the action.
01914 proc handle_between_motion {txtt char} {
01915
01916 variable motion
01917
01918 # TBD - We have some work to do here to support between characters
01919 return 0
01920
01921 return [do_operation $txtt [list betweenchar -dir prev -char $char] [list betweenchar -dir next -char $char]]
01922
01923 }
01924
01925 ######################################################################
01926 # If we are in "command" mode, the number is 0 and the current number
01927 # is empty, set the insertion cursor to the beginning of the line;
01928 # otherwise, append the number current to number value.
01929 proc handle_number {txtt num} {
01930
01931 variable motion
01932 variable number
01933 variable multiplier
01934
01935 if {($multiplier($txtt) eq "") && ($num eq "0")} {
01936 if {$motion($txtt) eq ""} {
01937 return [do_operation $txtt linestart]
01938 } elseif {$motion($txtt) eq "g"} {
01939 return [do_operation $txtt dispstart]
01940 }
01941 } else {
01942 append multiplier($txtt) $num
01943 return 1
01944 }
01945
01946 return 0
01947
01948 }
01949
01950 ######################################################################
01951 # If we are in the "command" mode, display the command entry field and
01952 # give it the focus.
01953 proc handle_colon {txtt} {
01954
01955 variable motion
01956 variable command_entries
01957
01958 if {$motion($txtt) eq ""} {
01959 $command_entries($txtt) configure \
01960 -background [$txtt cget -background] -foreground [$txtt cget -foreground] \
01961 -insertbackground [$txtt cget -insertbackground] -font [$txtt cget -font]
01962 gui::panel_place $command_entries($txtt)
01963 grab $command_entries($txtt)
01964 focus $command_entries($txtt)
01965 return 1
01966 }
01967
01968 return 0
01969
01970 }
01971
01972 ######################################################################
01973 # If we are in the "command" mode, move insertion cursor to the end of
01974 # the current line. If we are in "delete" mode, delete all of the
01975 # text from the insertion marker to the end of the line.
01976 proc handle_dollar {txtt} {
01977
01978 variable motion
01979
01980 if {$motion($txtt) eq ""} {
01981 return [do_operation $txtt [list lineend -num [get_number $txtt]]]
01982 } elseif {$motion($txtt) eq "g"} {
01983 return [do_operation $txtt [list dispend -num [get_number $txtt]]]
01984 }
01985
01986 return 0
01987
01988 }
01989
01990 ######################################################################
01991 # If we are in the "command" mode, move insertion cursor to the beginning
01992 # of the current line. If we are in "delete" mode, delete all of the
01993 # text between the beginning of the current line and the current
01994 # insertion marker.
01995 proc handle_asciicircum {txtt} {
01996
01997 variable motion
01998
01999 if {$motion($txtt) eq ""} {
02000 return [do_operation $txtt [list firstchar -num 0]]
02001 } elseif {$motion($txtt) eq "g"} {
02002 return [do_operation $txtt dispfirst]
02003 }
02004
02005 return 0
02006
02007 }
02008
02009 ######################################################################
02010 # If we are in "command" mode, display the search bar.
02011 proc handle_slash {txtt} {
02012
02013 variable motion
02014 variable search_dir
02015
02016 if {$motion($txtt) eq ""} {
02017 gui::search "next"
02018 set search_dir($txtt) "next"
02019 return 1
02020 }
02021
02022 return 0
02023
02024 }
02025
02026 ######################################################################
02027 # If we are in "command" mode, display the search bar for doing a
02028 # a previous search.
02029 proc handle_question {txtt} {
02030
02031 variable operator
02032 variable motion
02033 variable search_dir
02034
02035 switch $operator($txtt) {
02036 "" {
02037 if {$motion($txtt) eq ""} {
02038 gui::search "prev"
02039 set search_dir($txtt) "prev"
02040 } elseif {$motion($txtt) eq "g"} {
02041 if {[edit::transform_to_rot13_selected $txtt]} {
02042 command_mode $txtt
02043 } else {
02044 set_operator $txtt "rot13" {g question}
02045 set motion($txtt) ""
02046 }
02047 return 1
02048 }
02049 }
02050 "rot13" {
02051 return [do_operation $txtt [list lineend -num [get_number $txtt]] linestart -cursor linestart]
02052 }
02053 }
02054
02055 return 0
02056
02057 }
02058
02059 ######################################################################
02060 # If we are in "command" mode, invokes the buffered command at the current
02061 # insertion point.
02062 proc handle_period {txtt} {
02063
02064 variable motion
02065
02066 if {$motion($txtt) eq ""} {
02067 set start_index [$txtt index insert]
02068 playback $txtt
02069 set end_index [$txtt index insert]
02070 if {$start_index != $end_index} {
02071 if {[$txtt compare $start_index < $end_index]} {
02072 $txtt syntax highlight $start_index $end_index
02073 } else {
02074 $txtt syntax highlight $end_index $start_index
02075 }
02076 }
02077 reset_state $txtt 0
02078 return 1
02079 }
02080
02081 return 0
02082
02083 }
02084
02085 ######################################################################
02086 # If we are in "command" mode and the insertion point character has a
02087 # matching left/right partner, display the partner.
02088 proc handle_percent {txtt} {
02089
02090 variable multiplier
02091
02092 if {$multiplier($txtt) eq ""} {
02093 gui::show_match_pair
02094 } else {
02095 set lines [lindex [split [$txtt index end] .] 0]
02096 set line [expr int( ($multiplier($txtt) * $lines + 99) / 100 )]
02097 return [do_operation $txtt [list linenum -num $line]]
02098 }
02099
02100 return 0
02101
02102 }
02103
02104 ######################################################################
02105 # Handles the i-key when in Vim mode.
02106 proc handle_i {txtt} {
02107
02108 variable operator
02109 variable motion
02110
02111 switch $operator($txtt) {
02112 "" {
02113 if {![in_visual_mode $txtt]} {
02114 edit_mode $txtt
02115 record_start $txtt "i"
02116 } else {
02117 set motion($txtt) i
02118 }
02119 return 1
02120 }
02121 "folding" {
02122 if {![in_visual_mode $txtt]} {
02123 folding::set_vim_foldenable [winfo parent $txtt] [expr [folding::get_vim_foldenable [winfo parent $txtt]] ^ 1]
02124 }
02125 }
02126 default {
02127 if {$motion($txtt) eq ""} {
02128 set motion($txtt) "i"
02129 return 1
02130 }
02131 }
02132 }
02133
02134 return 0
02135
02136 }
02137
02138 ######################################################################
02139 # If we are in "command" mode, inserts at the beginning of the current
02140 # line.
02141 proc handle_I {txtt} {
02142
02143 variable operator
02144
02145 if {$operator($txtt) eq ""} {
02146 ::tk::TextSetCursor $txtt "insert linestart"
02147 edit_mode $txtt
02148 record_start $txtt "I"
02149 return 1
02150 }
02151
02152 return 0
02153
02154 }
02155
02156 ######################################################################
02157 # If we are in "command" mode, move the insertion cursor down one line.
02158 proc handle_j {txtt} {
02159
02160 variable column
02161 variable operator
02162
02163 # Move the insertion cursor down one line
02164 switch $operator($txtt) {
02165 "folding" {
02166 folding::jump_to [winfo parent $txtt] next [get_number $txtt]
02167 }
02168 "folding:range" {
02169 folding::close_range [winfo parent $txtt] insert "insert+[get_number $txtt] display lines"
02170 }
02171 default {
02172 return [do_operation $txtt [list down -num [get_number $txtt] -column vim::column($txtt)]]
02173 }
02174 }
02175
02176 return 0
02177
02178 }
02179
02180 ######################################################################
02181 # If we are in "command" mode, join the next line to the end of the
02182 # previous line.
02183 proc handle_J {txtt} {
02184
02185 variable mode
02186 variable operator
02187
02188 if {$mode($txtt) eq "command"} {
02189 if {$operator($txtt) eq ""} {
02190 edit::transform_join_lines $txtt [get_number $txtt]
02191 record $txtt J
02192 }
02193 }
02194
02195 return 0
02196
02197 }
02198
02199 ######################################################################
02200 # If we are in "command" mode, move the insertion cursor up one line.
02201 proc handle_k {txtt} {
02202
02203 variable column
02204 variable multicursor
02205 variable operator
02206
02207 set num [get_number $txtt]
02208
02209 # Move the insertion cursor up one line
02210 switch $operator($txtt) {
02211 "folding" {
02212 folding::jump_to [winfo parent $txtt] prev [get_number $txtt]
02213 }
02214 "folding:range" {
02215 folding::close_range [winfo parent $txtt] [edit::get_index $txtt up -num [get_number $txtt] -column vim::column($txtt)] insert
02216 ::tk::TextSetCursor $txtt "insert-1 display lines"
02217 adjust_insert $txtt
02218 }
02219 default {
02220 return [do_operation $txtt [list up -num [get_number $txtt] -column vim::column($txtt)]]
02221 }
02222 }
02223
02224 return 0
02225
02226 }
02227
02228 ######################################################################
02229 # If we are in start mode and multicursor is enabled, move all of the
02230 # cursors up one line.
02231 proc handle_K {txtt} {
02232
02233 variable operator
02234
02235 if {$operator($txtt) eq ""} {
02236 if {[set word [string trim [$txtt get "insert wordstart" "insert wordend"]]] ne ""} {
02237 search::search_documentation -str $word
02238 }
02239 }
02240
02241 return 0
02242
02243 }
02244
02245 ######################################################################
02246 # If we are in "command" mode, move the insertion cursor right one
02247 # character.
02248 proc handle_l {txtt} {
02249
02250 variable motion
02251
02252 # Move the insertion cursor right one character
02253 set startargs ""
02254 switch [lindex $motion($txtt) end] {
02255 "V" {
02256 set startargs linestart
02257 set endargs lineend
02258 }
02259 "v" {
02260 set endargs [list right -num [expr [get_number $txtt] + 1]]
02261 }
02262 default {
02263 set endargs [list right -num [get_number $txtt]]
02264 }
02265 }
02266
02267 return [do_operation $txtt $endargs $startargs]
02268
02269 }
02270
02271 ######################################################################
02272 # If we are in "command" mode and multicursor mode is enabled, adjust
02273 # all of the cursors to the right by one character. If we are only
02274 # in "command" mode, jump the insertion cursor to the bottom line.
02275 proc handle_L {txtt} {
02276
02277 variable motion
02278
02279 if {$motion($txtt) eq ""} {
02280 return [do_operation $txtt screenbot]
02281 }
02282
02283 return 0
02284
02285 }
02286
02287 ######################################################################
02288 # Returns the string containing the filename to open.
02289 proc get_filename {txtt pos} {
02290
02291 # Get the index of pos
02292 set index [lindex [split [$txtt index $pos] .] 1]
02293
02294 # Get the current line
02295 set line [$txtt get "$pos linestart" "$pos lineend"]
02296
02297 # Get the first space
02298 set first_space [string last " " $line $index]
02299
02300 # Get the last space
02301 if {[set last_space [string first " " $line $index]] == -1} {
02302 set last_space [string length $line]
02303 }
02304
02305 return [string range $line [expr $first_space + 1] [expr $last_space - 1]]
02306
02307 }
02308
02309 ######################################################################
02310 # If we are in "goto" mode, edit any filenames that are found under
02311 # any of the cursors.
02312 proc handle_f {txtt} {
02313
02314 variable operator
02315 variable motion
02316
02317 if {$operator($txtt) eq "folding"} {
02318 if {![folding::close_selected [winfo parent $txtt]]} {
02319 set operator($txtt) "folding:range"
02320 return 1
02321 }
02322 } else {
02323 if {$motion($txtt) eq ""} {
02324 set motion($txtt) "f"
02325 return 1
02326 } elseif {$motion($txtt) eq "g"} {
02327 if {[multicursor::enabled $txtt]} {
02328 foreach {startpos endpos} [$txtt tag ranges mcursor] {
02329 if {[file exists [set fname [get_filename $txtt $startpos]]]} {
02330 gui::add_file end $fname
02331 }
02332 }
02333 } else {
02334 if {[file exists [set fname [get_filename $txtt insert]]]} {
02335 gui::add_file end $fname
02336 }
02337 }
02338 }
02339 }
02340
02341 return 0
02342
02343 }
02344
02345 ######################################################################
02346 # Handles any previous find character motions.
02347 proc handle_F {txtt} {
02348
02349 variable operator
02350 variable motion
02351
02352 if {$operator($txtt) eq "folding"} {
02353 if {![folding::close_selected [winfo parent $txtt]]} {
02354 if {[set num [get_number $txtt]] > 1} {
02355 folding::close_range [winfo parent $txtt] insert "insert+[expr $num - 1] display lines"
02356 }
02357 }
02358 } else {
02359 if {$motion($txtt) eq ""} {
02360 set motion($txtt) "F"
02361 return 1
02362 }
02363 }
02364
02365 return 0
02366
02367 }
02368
02369 ######################################################################
02370 # Handles any next find character (non-inclusive) motions.
02371 proc handle_t {txtt} {
02372
02373 variable operator
02374 variable motion
02375
02376 switch $motion($txtt) {
02377 "" {
02378 set motion($txtt) "t"
02379 return 1
02380 }
02381 default {
02382 return [do_object_operation $txtt tag]
02383 }
02384 }
02385
02386 return 0
02387
02388 }
02389
02390 ######################################################################
02391 # Handles any next find character (non-inclusive) motions.
02392 proc handle_T {txtt} {
02393
02394 variable motion
02395
02396 if {$motion($txtt) eq ""} {
02397 set motion($txtt) "T"
02398 return 1
02399 }
02400
02401 return 0
02402
02403 }
02404
02405 ######################################################################
02406 # If we are in "command" mode, edit any filenames found under any of
02407 # the cursors.
02408 proc handle_g {txtt} {
02409
02410 variable motion
02411
02412 if {$motion($txtt) eq ""} {
02413 set motion($txtt) "g"
02414 return 1
02415 } elseif {$motion($txtt) eq "g"} {
02416 return [do_operation $txtt first]
02417 }
02418
02419 return 0
02420
02421 }
02422
02423 ######################################################################
02424 # If we are in "command" mode, move the insertion cursor left one
02425 # character.
02426 proc handle_h {txtt} {
02427
02428 variable motion
02429
02430 # Move the insertion cursor left one character
02431 set startargs ""
02432 set cursorargs [list left -num [get_number $txtt]]
02433 switch [lindex $motion($txtt) end] {
02434 "V" {
02435 set startargs "linestart"
02436 set endargs "lineend"
02437 # set cursorargs "none"
02438 }
02439 "v" {
02440 set startargs right
02441 set endargs [list left -num [get_number $txtt]]
02442 }
02443 default {
02444 set endargs [list left -num [get_number $txtt]]
02445 }
02446 }
02447
02448 return [do_operation $txtt $endargs $startargs -cursor $cursorargs]
02449
02450 }
02451
02452 ######################################################################
02453 # If we are in "command" mode and multicursor mode is enabled, move all
02454 # cursors to the left by one character. Otherwise, if we are just in
02455 # "command" mode, jump to the top line of the editor.
02456 proc handle_H {txtt} {
02457
02458 variable motion
02459
02460 if {$motion($txtt) eq ""} {
02461 return [do_operation $txtt screentop]
02462 }
02463
02464 return 0
02465
02466 }
02467
02468 ######################################################################
02469 # If we are in "command" mode, move the insertion cursor to the beginning
02470 # of previous word.
02471 proc handle_b {txtt} {
02472
02473 variable motion
02474
02475 switch $motion($txtt) {
02476 "" {
02477 return [do_operation $txtt [list wordstart -dir prev -num [get_number $txtt] -exclusive 1]]
02478 }
02479 default {
02480 return [do_object_operation $txtt paren]
02481 }
02482 }
02483
02484 return 0
02485
02486 }
02487
02488 ######################################################################
02489 # Move counts WORDs backward.
02490 proc handle_B {txtt} {
02491
02492 variable motion
02493
02494 switch $motion($txtt) {
02495 "" {
02496 return [do_operation $txtt [list WORDstart -dir prev -num [get_number $txtt] -exclusive 1]]
02497 }
02498 default {
02499 return [do_object_operation $txtt curly]
02500 }
02501 }
02502
02503 return 0
02504
02505 }
02506
02507 ######################################################################
02508 # If we are in "command" mode, change the state to "change" mode. If
02509 # we are in the "change" mode, delete the current line and put ourselves
02510 # into edit mode.
02511 proc handle_c {txtt} {
02512
02513 variable operator
02514
02515 switch $operator($txtt) {
02516 "" {
02517 if {[edit::delete_selected $txtt 0]} {
02518 edit_mode $txtt
02519 } else {
02520 set_operator $txtt "change" {c}
02521 return 1
02522 }
02523 }
02524 "change" {
02525 return [do_operation $txtt [list lineend -num [get_number $txtt]] linestart]
02526 }
02527 "folding" {
02528 folding::close_fold [get_number $txtt] [winfo parent $txtt] [lindex [split [$txtt index insert] .] 0]
02529 }
02530 }
02531
02532 return 0
02533
02534 }
02535
02536 ######################################################################
02537 # If we are in "command" mode, delete from the insertion cursor to the
02538 # end of the line and put ourselves into "edit" mode.
02539 proc handle_C {txtt} {
02540
02541 variable operator
02542
02543 if {$operator($txtt) eq ""} {
02544 if {[edit::delete_selected $txtt 1]} {
02545 edit_mode $txtt
02546 } else {
02547 $txtt delete insert "insert lineend"
02548 edit_mode $txtt
02549 record_start $txtt "C"
02550 }
02551 return 1
02552 } elseif {$operator($txtt) eq "folding"} {
02553 folding::close_fold 0 [winfo parent $txtt] [lindex [split [$txtt index insert] .] 0]
02554 }
02555
02556 return 0
02557
02558 }
02559
02560 ######################################################################
02561 # If we are in "change" mode, delete the current word and change to edit
02562 # mode.
02563 proc handle_w {txtt} {
02564
02565 variable operator
02566 variable motion
02567
02568 switch $motion($txtt) {
02569 "" {
02570 switch $operator($txtt) {
02571 "" { return [do_operation $txtt [list wordstart -dir next -num [get_number $txtt] -exclusive 1]] }
02572 "change" {
02573 set num [get_number $txtt]
02574 if {[string is space [$txtt get insert]]} {
02575 return [do_operation $txtt [list wordstart -dir next -num $num -exclusive 1]]
02576 } else {
02577 return [do_operation $txtt [list wordend -dir next -num $num -exclusive 0 -adjust +1c]]
02578 }
02579 }
02580 default { return [do_operation $txtt [list wordstart -dir next -num [get_number $txtt] -exclusive 0]] }
02581 }
02582 }
02583 default {
02584 return [do_object_operation $txtt word]
02585 }
02586 }
02587
02588 return 0
02589
02590 }
02591
02592 ######################################################################
02593 # If we are in "change" mode, delete the current WORD and change to edit
02594 # mode.
02595 proc handle_W {txtt} {
02596
02597 variable operator
02598 variable motion
02599
02600 switch $motion($txtt) {
02601 "" {
02602 switch $operator($txtt) {
02603 "" { return [do_operation $txtt [list WORDstart -dir next -num [get_number $txtt] -exclusive 1]] }
02604 "change" {
02605 set num [get_number $txtt]
02606 if {[string is space [$txtt index insert]]} {
02607 return [do_operation $txtt [list WORDstart -dir next -num $num -exclusive 1]]
02608 } else {
02609 return [do_operation $txtt [list WORDend -dir next -num $num -exclusive 0 -adjust +1c]]
02610 }
02611 }
02612 default { return [do_operation $txtt [list WORDstart -dir next -num [get_number $txtt] -exclusive 0]] }
02613 }
02614 }
02615 default {
02616 return [do_object_operation $txtt WORD]
02617 }
02618 }
02619
02620 return 0
02621
02622 }
02623
02624 ######################################################################
02625 # If we are in "command" mode, go to the last line.
02626 proc handle_G {txtt} {
02627
02628 variable multiplier
02629
02630 if {$multiplier($txtt) eq ""} {
02631 return [do_operation $txtt last]
02632 } else {
02633 return [do_operation $txtt [list linenum -num $multiplier($txtt)]]
02634 }
02635
02636 return 0
02637
02638 }
02639
02640 ######################################################################
02641 # If we are in "command" mode, transition the mode to the delete mode.
02642 # If we are in the "delete" mode, delete the current line.
02643 proc handle_d {txtt} {
02644
02645 variable operator
02646
02647 switch $operator($txtt) {
02648 "" {
02649 if {[edit::delete_selected $txtt 0]} {
02650 command_mode $txtt
02651 } else {
02652 set_operator $txtt "delete" {d}
02653 }
02654 return 1
02655 }
02656 "delete" {
02657 return [do_operation $txtt [list lineend -num [get_number $txtt] -adjust +1c] linestart]
02658 }
02659 "folding" {
02660 folding::delete_fold [winfo parent $txtt] [lindex [split [$txtt index insert] .] 0]
02661 }
02662 }
02663
02664 return 0
02665
02666 }
02667
02668 ######################################################################
02669 # If we are in "command" mode, deletes all text from the current
02670 # insertion cursor to the end of the line.
02671 proc handle_D {txtt} {
02672
02673 variable operator
02674
02675 switch $operator($txtt) {
02676 "" {
02677 if {[edit::delete_selected $txtt 1]} {
02678 command_mode $txtt
02679 return 1
02680 } else {
02681 set_operator $txtt "delete" {D}
02682 return [do_operation $txtt [list lineend -num [get_number $txtt]]]
02683 }
02684 }
02685 "folding" {
02686 folding::delete_folds [winfo parent $txtt] [lindex [split [$txtt index insert] .] 0]
02687 }
02688 }
02689
02690 return 0
02691
02692 }
02693
02694 ######################################################################
02695 # If we are in the "command" mode, move the insertion cursor ahead by
02696 # one character and set ourselves into "edit" mode.
02697 proc handle_a {txtt} {
02698
02699 variable mode
02700 variable operator
02701 variable motion
02702
02703 if {$mode($txtt) eq "command"} {
02704 switch $operator($txtt) {
02705 "" {
02706 if {[multicursor::enabled $txtt]} {
02707 multicursor::move $txtt right
02708 }
02709 cleanup_dspace $txtt
02710 ::tk::TextSetCursor $txtt "insert+1c"
02711 edit_mode $txtt
02712 record_start $txtt "a"
02713 return 1
02714 }
02715 "folding" {
02716 folding::toggle_fold [winfo parent $txtt] [lindex [split [$txtt index insert] .] 0] [get_number $txtt]
02717 }
02718 default {
02719 set motion($txtt) "a"
02720 return 1
02721 }
02722 }
02723 } elseif {[in_visual_mode $txtt]} {
02724 set motion($txtt) "a"
02725 return 1
02726 }
02727
02728 return 0
02729
02730 }
02731
02732 ######################################################################
02733 # If we are in "command" mode, insert text at the end of the current line.
02734 proc handle_A {txtt} {
02735
02736 variable mode
02737 variable operator
02738
02739 if {$mode($txtt) eq "command"} {
02740 switch $operator($txtt) {
02741 "" {
02742 ::tk::TextSetCursor $txtt "insert lineend"
02743 edit_mode $txtt
02744 record_start $txtt "A"
02745 return 1
02746 }
02747 "folding" {
02748 folding::toggle_fold [winfo parent $txtt] [lindex [split [$txtt index insert] .] 0] 0
02749 }
02750 }
02751 }
02752
02753 return 0
02754
02755 }
02756
02757 ######################################################################
02758 # If we are in the "command" mode, set ourselves to yank mode. If we
02759 # are in "yank" mode, copy the current line to the clipboard.
02760 proc handle_y {txtt} {
02761
02762 variable operator
02763
02764 switch $operator($txtt) {
02765 "" {
02766 if {[set ranges [$txtt tag ranges sel]] ne ""} {
02767 clipboard clear
02768 foreach {start end} $ranges {
02769 clipboard append [$txtt get $start $end]
02770 }
02771 ::tk::TextSetCursor $txtt $start
02772 command_mode $txtt
02773 } else {
02774 set_operator $txtt "yank" {y}
02775 }
02776 return 1
02777 }
02778 "yank" {
02779 return [do_operation $txtt [list lineend -num [get_number $txtt] -adjust +1c] linestart -cursor 0]
02780 }
02781 }
02782
02783 return 0
02784
02785 }
02786
02787 ######################################################################
02788 # Handles a paste operation from the menu (or keyboard shortcut).
02789 proc handle_paste {txt} {
02790
02791 variable mode
02792
02793 if {[preferences::get Editor/VimMode] && [info exists mode($txt.t)]} {
02794
02795 # If we are not currently in edit mode, temporarily set ourselves to edit mode
02796 if {$mode($txt.t) ne "edit"} {
02797 record_add i
02798 }
02799
02800 # Add the characters
02801 foreach c [split [clipboard get] {}] {
02802 record_add [utils::string_to_keysym $c]
02803 }
02804
02805 # If we were in command mode, escape out of edit mode
02806 if {$mode($txt.t) ne "edit"} {
02807 record_add Escape
02808 record_stop 0
02809 }
02810
02811 }
02812
02813 }
02814
02815 ######################################################################
02816 # Pastes the contents of the given clip to the text widget after the
02817 # current line.
02818 proc do_post_paste {txtt clip} {
02819
02820 # Create a separator
02821 $txtt edit separator
02822
02823 # Get the number of pastes that we need to perform
02824 set num [get_number $txtt]
02825
02826 if {[set nl_index [string last \n $clip]] != -1} {
02827 if {[expr ([string length $clip] - 1) == $nl_index]} {
02828 set clip [string replace $clip $nl_index $nl_index]
02829 }
02830 $txtt insert "insert lineend" [string repeat "\n$clip" $num]
02831 multicursor::paste $txtt "insert+1l linestart"
02832 ::tk::TextSetCursor $txtt "insert+1l linestart"
02833 edit::move_cursor $txtt firstchar -num 0
02834 } else {
02835 set clip [string repeat $clip $num]
02836 $txtt insert "insert+1c" $clip
02837 multicursor::paste $txtt "insert+1c"
02838 ::tk::TextSetCursor $txtt "insert+[string length $clip]c"
02839 }
02840 adjust_insert $txtt
02841
02842 # Create a separator
02843 $txtt edit separator
02844
02845 }
02846
02847 ######################################################################
02848 # If we are in the "command" mode, put the contents of the clipboard
02849 # after the current line.
02850 proc handle_p {txtt} {
02851
02852 variable motion
02853
02854 switch $motion($txtt) {
02855 "" {
02856 do_post_paste $txtt [set clip [clipboard get]]
02857 cliphist::add_from_clipboard
02858 record $txtt p
02859 }
02860 default {
02861 return [do_object_operation $txtt paragraph]
02862 }
02863 }
02864
02865 return 0
02866
02867 }
02868
02869 ######################################################################
02870 # Pastes the contents of the given clip prior to the current line
02871 # in the text widget.
02872 proc do_pre_paste {txtt clip} {
02873
02874 $txtt edit separator
02875
02876 # Calculate the number of clips to pre-paste
02877 set num [get_number $txtt]
02878
02879 if {[set nl_index [string last \n $clip]] != -1} {
02880 if {[expr ([string length $clip] - 1) == $nl_index]} {
02881 set clip [string replace $clip $nl_index $nl_index]
02882 }
02883 set startpos [$txtt index "insert linestart"]
02884 $txtt insert "insert linestart" [string repeat "$clip\n" $num]
02885 multicursor::paste $txtt $startpos
02886 ::tk::TextSetCursor $txtt $startpos
02887 edit::move_cursor $txtt firstchar -num 0
02888 } else {
02889 $txtt insert insert [string repeat $clip $num]
02890 multicursor::paste $txtt insert
02891 ::tk::TextSetCursor $txtt "insert-1c"
02892 }
02893 adjust_insert $txtt
02894
02895 # Create separator
02896 $txtt edit separator
02897
02898 }
02899
02900 ######################################################################
02901 # If we are in the "command" mode, put the contents of the clipboard
02902 # before the current line.
02903 proc handle_P {txtt} {
02904
02905 do_pre_paste $txtt [set clip [clipboard get]]
02906 cliphist::add_from_clipboard
02907 record $txtt P
02908
02909 return 0
02910
02911 }
02912
02913 ######################################################################
02914 # Performs an undo operation.
02915 proc undo {txtt} {
02916
02917 # Perform the undo operation
02918 catch { $txtt edit undo }
02919
02920 # Adjusts the insertion cursor
02921 adjust_insert $txtt
02922
02923 # Allow the UI to update its state
02924 gui::check_for_modified $txtt
02925
02926 }
02927
02928 ######################################################################
02929 # Performs a redo operation.
02930 proc redo {txtt} {
02931
02932 # Performs the redo operation
02933 catch { $txtt edit redo }
02934
02935 # Adjusts the insertion cursor
02936 adjust_insert $txtt
02937
02938 # Allow the UI to update its state
02939 gui::check_for_modified $txtt
02940
02941 }
02942
02943 ######################################################################
02944 # If we are in "command" mode, undoes the last operation.
02945 proc handle_u {txtt} {
02946
02947 variable operator
02948 variable motion
02949
02950 switch $operator($txtt) {
02951 "" {
02952 if {$motion($txtt) eq ""} {
02953 undo $txtt
02954 } elseif {$motion($txtt) eq "g"} {
02955 if {[edit::transform_to_lower_case_selected $txtt]} {
02956 command_mode $txtt
02957 } else {
02958 set_operator $txtt "lower" {g u}
02959 set motion($txtt) ""
02960 }
02961 return 1
02962 }
02963 }
02964 "lower" {
02965 return [do_operation $txtt [list lineend -num [get_number $txtt]] linestart -cursor linestart]
02966 }
02967 }
02968
02969 return 0
02970
02971 }
02972
02973 ######################################################################
02974 # If we are in "goto" mode, convert the mode to uppercase mode.
02975 proc handle_U {txtt} {
02976
02977 variable operator
02978 variable motion
02979
02980 switch $operator($txtt) {
02981 "" {
02982 if {$motion($txtt) eq "g"} {
02983 if {[edit::transform_to_upper_case_selected $txtt]} {
02984 command_mode $txtt
02985 } else {
02986 set_operator $txtt "upper" {g U}
02987 set motion($txtt) ""
02988 }
02989 return 1
02990 }
02991 }
02992 "upper" {
02993 return [do_operation $txtt [list lineend -num [get_number $txtt]] linestart -cursor linestart]
02994 }
02995 }
02996
02997 return 0
02998
02999 }
03000
03001 ######################################################################
03002 # If we are in "command" mode, deletes the current character.
03003 proc handle_x {txtt} {
03004
03005 if {[edit::delete_selected $txtt 0]} {
03006 command_mode $txtt
03007 return 1
03008 } else {
03009 set_operator $txtt "delete" {x}
03010 return [do_operation $txtt [list right -num [get_number $txtt]]]
03011 }
03012
03013 return 0
03014
03015 }
03016
03017 ######################################################################
03018 # If we are in "command" mode, deletes the current character (same as
03019 # the 'x' command).
03020 proc handle_Delete {txtt} {
03021
03022 if {[edit::delete_selected $txtt 0]} {
03023 command_mode $txtt
03024 return 1
03025 } else {
03026 set_operator $txtt "delete" {Delete}
03027 return [do_operation $txtt [list right -num [get_number $txtt]]]
03028 }
03029
03030 return 0
03031
03032 }
03033
03034 ######################################################################
03035 # If we are in "command" mode, deletes the previous character.
03036 proc handle_X {txtt} {
03037
03038 if {[edit::delete_selected $txtt 1]} {
03039 command_mode $txtt
03040 return 1
03041 } else {
03042 set_operator $txtt "delete" {X}
03043 return [do_operation $txtt [list left -num [get_number $txtt]]]
03044 }
03045
03046 return 0
03047
03048 }
03049
03050 ######################################################################
03051 # If we are in "command" mode, add a new line below the current line
03052 # and transition into "edit" mode.
03053 proc handle_o {txtt} {
03054
03055 variable mode
03056 variable operator
03057
03058 if {$mode($txtt) eq "command"} {
03059 switch $operator($txtt) {
03060 "" {
03061 edit::insert_line_below_current $txtt
03062 record_start $txtt "o"
03063 return 1
03064 }
03065 "folding" {
03066 folding::open_fold [get_number $txtt] [winfo parent $txtt] [lindex [split [$txtt index insert] .] 0]
03067 }
03068 }
03069 }
03070
03071 return 0
03072
03073 }
03074
03075 ######################################################################
03076 # If we are in "command" mode, add a new line above the current line
03077 # and transition into "edit" mode.
03078 proc handle_O {txtt} {
03079
03080 variable mode
03081 variable operator
03082
03083 if {$mode($txtt) eq "command"} {
03084 switch $operator($txtt) {
03085 "" {
03086 edit::insert_line_above_current $txtt
03087 record_start $txtt "O"
03088 return 1
03089 }
03090 "folding" {
03091 folding::open_fold 0 [winfo parent $txtt] [lindex [split [$txtt index insert] .] 0]
03092 }
03093 }
03094 }
03095
03096 return 0
03097
03098 }
03099
03100 ######################################################################
03101 # If we are in "command" mode, set the mode to the "quit" mode. If we
03102 # are in "quit" mode, save and exit the current tab.
03103 proc handle_Z {txtt} {
03104
03105 variable mode
03106 variable operator
03107
03108 if {$mode($txtt) eq "command"} {
03109 switch $operator($txtt) {
03110 "" {
03111 set operator($txtt) "quit"
03112 return 1
03113 }
03114 "quit" {
03115 gui::save_current
03116 gui::close_current
03117 }
03118 }
03119 }
03120
03121 return 0
03122
03123 }
03124
03125 ######################################################################
03126 # If we are in start mode and multicursors are enabled, set the Vim mode
03127 # to indicate that any further movement commands should be applied to
03128 # the multicursors instead of the standard cursor.
03129 proc handle_m {txtt} {
03130
03131 variable motion
03132
03133 if {[multicursor::enabled $txtt]} {
03134 multicursor_mode $txtt
03135 } elseif {$motion($txtt) eq "g"} {
03136 return [do_operation $txtt dispmid]
03137 }
03138
03139 return 0
03140
03141 }
03142
03143 ######################################################################
03144 # If we are in "command" mode, finds the next occurrence of the search text.
03145 proc handle_n {txtt} {
03146
03147 variable mode
03148 variable operator
03149 variable search_dir
03150
03151 if {$mode($txtt) eq "command"} {
03152 switch $operator($txtt) {
03153 "" {
03154 set count [get_number $txtt]
03155 if {$search_dir($txtt) eq "next"} {
03156 for {set i 0} {$i < $count} {incr i} {
03157 search::find_next [winfo parent $txtt]
03158 }
03159 } else {
03160 for {set i 0} {$i < $count} {incr i} {
03161 search::find_prev [winfo parent $txtt]
03162 }
03163 }
03164 }
03165 "folding" {
03166 folding::set_vim_foldenable [winfo parent $txtt] 0
03167 }
03168 default {
03169 return [do_operation $txtt [list numberend -adjust "+1c"]]
03170 }
03171 }
03172 }
03173
03174 return 0
03175
03176 }
03177
03178 ######################################################################
03179 # If we are in "command" mode, finds the previous occurrence of the
03180 # search text.
03181 proc handle_N {txtt} {
03182
03183 variable mode
03184 variable operator
03185 variable search_dir
03186
03187 if {$mode($txtt) eq "command"} {
03188 switch $operator($txtt) {
03189 "" {
03190 set count [get_number $txtt]
03191 if {$search_dir($txtt) eq "next"} {
03192 for {set i 0} {$i < $count} {incr i} {
03193 search::find_prev [winfo parent $txtt]
03194 }
03195 } else {
03196 for {set i 0} {$i < $count} {incr i} {
03197 search::find_next [winfo parent $txtt]
03198 }
03199 }
03200 }
03201 "folding" {
03202 folding::set_vim_foldenable [winfo parent $txtt] 1
03203 }
03204 default {
03205 return [do_operation $txtt numberstart]
03206 }
03207 }
03208 }
03209
03210 return 0
03211
03212 }
03213
03214 ######################################################################
03215 # If we are in "command" mode, replaces the current character with the
03216 # next character.
03217 proc handle_r {txtt} {
03218
03219 variable mode
03220
03221 if {$mode($txtt) eq "command"} {
03222 set mode($txtt) "replace"
03223 record_start $txtt "r"
03224 return 1
03225 }
03226
03227 return 0
03228
03229 }
03230
03231 ######################################################################
03232 # If we are in "command" mode, replaces all characters until the escape
03233 # key is hit.
03234 proc handle_R {txtt} {
03235
03236 variable mode
03237 variable operator
03238
03239 if {$mode($txtt) eq "command"} {
03240 switch $operator($txtt) {
03241 "" {
03242 set mode($txtt) "replace_all"
03243 record_start $txtt "R"
03244 return 1
03245 }
03246 "folding" {
03247 folding::open_all_folds [winfo parent $txtt]
03248 }
03249 }
03250 }
03251
03252 return 0
03253
03254 }
03255
03256 ######################################################################
03257 # If we are in "command" mode, puts the mode into "visual char" mode.
03258 proc handle_v {txtt} {
03259
03260 variable mode
03261 variable operator
03262 variable motion
03263
03264 if {$mode($txtt) eq "command"} {
03265 switch $operator($txtt) {
03266 "" {
03267 if {$motion($txtt) eq "g"} {
03268 visual_mode $txtt last
03269 } else {
03270 visual_mode $txtt char
03271 }
03272 }
03273 "folding" {
03274 folding::show_line [winfo parent $txtt] [lindex [split [$txtt index insert] .] 0]
03275 }
03276 default {
03277 set motion($txtt) "v"
03278 return 1
03279 }
03280 }
03281 } elseif {[in_visual_mode $txtt]} {
03282 if {$mode($txtt) eq "visual:char"} {
03283 visual_mode $txtt block
03284 } else {
03285 command_mode $txtt
03286 }
03287 return 1
03288 }
03289
03290 return 0
03291
03292 }
03293
03294 ######################################################################
03295 # If we are in "command" mode, puts the mode into "visual line" mode.
03296 proc handle_V {txtt} {
03297
03298 variable mode
03299 variable operator
03300 variable motion
03301
03302 if {$mode($txtt) eq "command"} {
03303 if {$operator($txtt) eq ""} {
03304 visual_mode $txtt line
03305 } else {
03306 set motion($txtt) "V"
03307 }
03308 return 1
03309 } elseif {[in_visual_mode $txtt]} {
03310 command_mode $txtt
03311 return 1
03312 }
03313
03314 return 0
03315
03316 }
03317
03318 ######################################################################
03319 # If we are in "command" mode, add a cursor.
03320 proc handle_s {txtt} {
03321
03322 variable mode
03323 variable operator
03324 variable motion
03325
03326 switch $motion($txtt) {
03327 "" {
03328 if {$mode($txtt) eq "command"} {
03329 if {$operator($txtt) eq ""} {
03330 multicursor::add_cursor $txtt [$txtt index insert]
03331 } else {
03332 return [do_operation $txtt [list spaceend -adjust "+1c"]]
03333 }
03334 }
03335 }
03336 default {
03337 return [do_object_operation $txtt sentence]
03338 }
03339 }
03340
03341 return 0
03342
03343 }
03344
03345 ######################################################################
03346 # If we are in "command" mode, add cursors between the current anchor
03347 # the current line.
03348 proc handle_S {txtt} {
03349
03350 variable mode
03351 variable operator
03352
03353 if {$mode($txtt) eq "command"} {
03354 if {$operator($txtt) eq ""} {
03355 multicursor::add_cursors $txtt [$txtt index insert]
03356 } else {
03357 return [do_operation $txtt spacestart]
03358 }
03359 }
03360
03361 return 0
03362
03363 }
03364
03365 ######################################################################
03366 # If we are in "command" mode, run the gui::insert_numbers procedure to
03367 # allow the user to potentially insert incrementing numbers into the
03368 # specified text widget.
03369 proc handle_numbersign {txtt} {
03370
03371 variable mode
03372 variable operator
03373
03374 if {$mode($txtt) eq "command"} {
03375 if {$operator($txtt) eq ""} {
03376 gui::insert_numbers $txtt
03377 }
03378 return 1
03379 }
03380
03381 return 0
03382
03383 }
03384
03385 ######################################################################
03386 # Moves the specified bracket one word to the right.
03387 proc move_bracket_right {txtt char} {
03388
03389 if {[set index [$txtt search -forwards -- $char insert]] ne ""} {
03390 $txtt delete $index
03391 $txtt insert "$index wordend" $char
03392 }
03393
03394 }
03395
03396 ######################################################################
03397 # Inserts or moves the specified bracket pair.
03398 proc place_bracket {txtt left {right ""}} {
03399
03400 variable mode
03401
03402 # Get the current selection
03403 if {[llength [set selected [$txtt tag ranges sel]]] > 0} {
03404
03405 foreach {end start} [lreverse $selected] {
03406 $txtt insert $end [expr {($right eq "") ? $left : $right}]
03407 $txtt insert $start $left
03408 }
03409
03410 } else {
03411
03412 # Add the bracket in the appropriate place
03413 if {($left eq "\"") || ($left eq "'")} {
03414 set tag [expr {($left eq "'") ? "_sString" : "_dString"}]
03415 if {[lsearch [$txtt tag names insert] $tag] != -1} {
03416 move_bracket_right $txtt $left
03417 } else {
03418 $txtt insert "insert wordend" $left
03419 $txtt insert "insert wordstart" $left
03420 }
03421 } else {
03422 set re "(\\$left|\\$right)"
03423 if {([set index [$txtt search -backwards -regexp -- $re insert]] ne "") && ([$txtt get $index] eq $left)} {
03424 move_bracket_right $txtt $right
03425 } else {
03426 $txtt insert "insert wordend" $right
03427 $txtt insert "insert wordstart" $left
03428 }
03429 }
03430
03431 }
03432
03433 # Put ourselves back into start mode
03434 command_mode $txtt
03435
03436 }
03437
03438 ######################################################################
03439 # If any text is selected, double quotes are placed around all
03440 # selections. If the insertion cursor is within a completed
03441 # string, the right-most quote of the completed string is moved one
03442 # word to the end; otherwise, the current word is placed within
03443 # double-quotes.
03444 proc handle_quotedbl {txtt} {
03445
03446 return [do_object_operation $txtt double]
03447
03448 }
03449
03450 ######################################################################
03451 # Handles single-quote object selection.
03452 proc handle_quoteright {txtt} {
03453
03454 return [do_object_operation $txtt single]
03455
03456 }
03457
03458 ######################################################################
03459 # Handle a` and i` Vim motions.
03460 proc handle_quoteleft {txtt} {
03461
03462 return [do_object_operation $txtt btick]
03463
03464 }
03465
03466 ######################################################################
03467 # If any text is selected, curly brackets are placed around all
03468 # selections. If the insertion cursor is within a completed
03469 # bracket sequence, the right-most bracket of the sequence is moved one
03470 # word to the end; otherwise, the current word is placed within
03471 # curly brackets.
03472 proc handle_bracketleft {txtt} {
03473
03474 return [do_object_operation $txtt square]
03475
03476 }
03477
03478 ######################################################################
03479 # Handles a] or i] Vim command.
03480 proc handle_bracketright {txtt} {
03481
03482 return [do_object_operation $txtt square]
03483
03484 }
03485
03486 ######################################################################
03487 # If any text is selected, square brackets are placed around all
03488 # selections. If the insertion cursor is within a completed
03489 # bracket sequence, the right-most bracket of the sequence is moved one
03490 # word to the end; otherwise, the current word is placed within
03491 # square brackets.
03492 proc handle_braceleft {txtt} {
03493
03494 variable motion
03495
03496 switch $motion($txtt) {
03497 "" {
03498 return [do_operation $txtt [list paragraph -dir prev -num [get_number $txtt]]]
03499 }
03500 default {
03501 return [do_object_operation $txtt curly]
03502 }
03503 }
03504
03505 return 0
03506
03507 }
03508
03509 ######################################################################
03510 # Handles right curly bracket character.
03511 proc handle_braceright {txtt} {
03512
03513 variable motion
03514
03515 switch $motion($txtt) {
03516 "" {
03517 return [do_operation $txtt [list paragraph -dir next -num [get_number $txtt]]]
03518 }
03519 default {
03520 return [do_object_operation $txtt curly]
03521 }
03522 }
03523
03524 return 0
03525
03526 }
03527
03528 ######################################################################
03529 # If any text is selected, parenthesis are placed around all
03530 # selections. If the insertion cursor is within a completed
03531 # parenthetical sequence, the right-most parenthesis of the sequence
03532 # is moved one word to the end; otherwise, the current word is placed
03533 # within parenthesis.
03534 proc handle_parenleft {txtt} {
03535
03536 variable motion
03537
03538 switch $motion($txtt) {
03539 "" {
03540 return [do_operation $txtt [list sentence -dir prev -num [get_number $txtt]]]
03541 }
03542 default {
03543 return [do_object_operation $txtt paren]
03544 }
03545 }
03546
03547 return 0
03548
03549 }
03550
03551 ######################################################################
03552 # Handles a parenthesis right motion.
03553 proc handle_parenright {txtt} {
03554
03555 variable motion
03556
03557 switch $motion($txtt) {
03558 "" {
03559 return [do_operation $txtt [list sentence -dir next -num [get_number $txtt]]]
03560 }
03561 default {
03562 return [do_object_operation $txtt paren]
03563 }
03564 }
03565
03566 return 0
03567
03568 }
03569
03570 ######################################################################
03571 # If we are in start mode, begin a lshift mode. If we are in
03572 # lshift mode, shift the current line left by one indent. If we
03573 # are in change mode:
03574 #
03575 # If any text is selected, angled brackets are placed around all
03576 # selections. If the insertion cursor is within a completed
03577 # bracket sequence, the right-most bracket of the sequence is moved one
03578 # word to the end; otherwise, the current word is placed within
03579 # angled brackets.
03580 proc handle_less {txtt} {
03581
03582 variable operator
03583 variable motion
03584
03585 switch $operator($txtt) {
03586 "" {
03587 if {$motion($txtt) eq ""} {
03588 if {[edit::unindent_selected $txtt]} {
03589 command_mode $txtt
03590 } else {
03591 set_operator $txtt "lshift" {less}
03592 }
03593 return 1
03594 } else {
03595 return [do_object_operation $txtt angled]
03596 }
03597 }
03598 default {
03599 if {($operator($txtt) eq "lshift") && ($motion($txtt) eq "")} {
03600 return [do_operation $txtt [list lineend -num [get_number $txtt]] linestart]
03601 } else {
03602 return [do_object_operation $txtt angled]
03603 }
03604 }
03605 }
03606
03607 return 0
03608
03609 }
03610
03611 ######################################################################
03612 # If we are in start mode, begin a rshift mode. If we are in
03613 # rshift mode, shift the current line right by one indent.
03614 proc handle_greater {txtt} {
03615
03616 variable operator
03617 variable motion
03618
03619 switch $operator($txtt) {
03620 "" {
03621 if {$motion($txtt) eq ""} {
03622 if {[edit::indent_selected $txtt]} {
03623 command_mode $txtt
03624 } else {
03625 set_operator $txtt "rshift" {greater}
03626 }
03627 return 1
03628 } else {
03629 return [do_object_operation $txtt angled]
03630 }
03631 }
03632 default {
03633 if {($operator($txtt) eq "rshift") && ($motion($txtt) eq "")} {
03634 return [do_operation $txtt [list lineend -num [get_number $txtt]] linestart]
03635 } else {
03636 return [do_object_operation $txtt angled]
03637 }
03638 }
03639 }
03640
03641 return 0
03642
03643 }
03644
03645 ######################################################################
03646 # When in start mode, if any text is selected, the selected code is
03647 # formatted; otherwise, if we are in start mode, we are transitioned to
03648 # format mode. If we are in format mode, we format the currently selected
03649 # line only.
03650 proc handle_equal {txtt} {
03651
03652 variable operator
03653
03654 switch $operator($txtt) {
03655 "" {
03656 if {[llength [set selected [$txtt tag ranges sel]]] > 0} {
03657 foreach {endpos startpos} [lreverse $selected] {
03658 indent::format_text $txtt $startpos $endpos
03659 }
03660 ::tk::TextSetCursor $txtt [edit::get_index $txtt firstchar -startpos $startpos]
03661 command_mode $txtt
03662 } else {
03663 set_operator $txtt "format" {equal}
03664 }
03665 return 1
03666 }
03667 "format" {
03668 return [do_operation $txtt [list lineend -num [get_number $txtt]] linestart]
03669 }
03670 }
03671
03672 return 0
03673
03674 }
03675
03676 ######################################################################
03677 # If we are in "command" or "visual" mode, moves the insertion cursor to the first
03678 # non-whitespace character in the next line.
03679 proc handle_Return {txtt} {
03680
03681 variable motion
03682
03683 if {$motion($txtt) eq ""} {
03684 return [do_operation $txtt [list firstchar -dir next -num [get_number $txtt]]]
03685 }
03686
03687 return 0
03688
03689 }
03690
03691 ######################################################################
03692 # Synonymous with the handle_Return command.
03693 proc handle_plus {txtt} {
03694
03695 variable motion
03696
03697 if {$motion($txtt) eq ""} {
03698 return [do_operation $txtt [list firstchar -dir next -num [get_number $txtt]]]
03699 }
03700
03701 return 0
03702
03703 }
03704
03705 ######################################################################
03706 # If we are in "command" or "visual" mode, moves the insertion cursor to the first
03707 # non-whitespace character in the previous line.
03708 proc handle_minus {txtt} {
03709
03710 variable motion
03711
03712 if {$motion($txtt) eq ""} {
03713 return [do_operation $txtt [list firstchar -dir prev -num [get_number $txtt]]]
03714 }
03715
03716 return 0
03717
03718 }
03719
03720 ######################################################################
03721 # If we are in "command" or "visual" mode, move the cursor to the given
03722 # column of the current line.
03723 proc handle_bar {txtt} {
03724
03725 return [do_operation $txtt [list column -num [get_number $txtt]]]
03726
03727 }
03728
03729 ######################################################################
03730 # If we are in "command" mode, change the case of the current character.
03731 proc handle_asciitilde {txtt} {
03732
03733 variable operator
03734 variable motion
03735
03736 switch $operator($txtt) {
03737 "" {
03738 if {$motion($txtt) eq ""} {
03739 set_operator $txtt "swap" {asciitilde}
03740 return [do_operation $txtt [list char -dir next -num [get_number $txtt]] {} -cursor [list char -dir next -num [get_number $txtt]]]
03741 } elseif {$motion($txtt) eq "g"} {
03742 if {[edit::transform_toggle_case_selected $txtt]} {
03743 command_mode $txtt
03744 } else {
03745 set_operator $txtt "swap" {g asciitilde}
03746 set motion($txtt) ""
03747 }
03748 return 1
03749 }
03750 }
03751 "swap" {
03752 return [do_operation $txtt [list lineend -num [get_number $txtt]] linestart -cursor linestart]
03753 }
03754 }
03755
03756 return 0
03757
03758 }
03759
03760 ######################################################################
03761 # If we are in "command" mode, move the cursor to the start of the middle
03762 # line.
03763 proc handle_M {txtt} {
03764
03765 variable operator
03766
03767 if {$operator($txtt) eq "folding"} {
03768 folding::close_all_folds [winfo parent $txtt]
03769 } else {
03770 return [do_operation $txtt screenmid]
03771 }
03772
03773 return 0
03774
03775 }
03776
03777 ######################################################################
03778 # If we are in "command" mode, search for all occurences of the current
03779 # word.
03780 proc handle_asterisk {txtt} {
03781
03782 variable mode
03783
03784 if {$mode($txtt) eq "command"} {
03785 if {[string trim [set word [$txtt get {*}[edit::get_range $txtt {word 1} {} "i" 0]]]] ne ""} {
03786 array set theme [theme::get_syntax_colors]
03787 catch { $txtt syntax delete classes search search_curr }
03788 $txtt syntax addclass search -fgtheme search_foreground -bgtheme search_background -highpriority 1
03789 $txtt syntax search search $word
03790 search::find_next [winfo parent $txtt]
03791 }
03792 }
03793
03794 return 0
03795
03796 }
03797
03798 ######################################################################
03799 # If we are in "command" mode, sets the current mode to "record" mode.
03800 proc handle_q {txtt} {
03801
03802 variable mode
03803 variable recording
03804
03805 if {$mode($txtt) eq "command"} {
03806 if {[in_recording]} {
03807 record_stop 1
03808 reset_state $txtt 0
03809 } else {
03810 set mode($txtt) "record_reg"
03811 }
03812 return 1
03813 }
03814
03815 return 0
03816
03817 }
03818
03819 ######################################################################
03820 # If we are in start mode, do nothing. If we are in quit mode, close
03821 # the current tab without writing the file (same as :q!).
03822 proc handle_Q {txtt} {
03823
03824 variable mode
03825 variable operator
03826
03827 if {$mode($txtt) eq "command"} {
03828 if {$operator($txtt) eq ""} {
03829 set operator($txtt) "quit"
03830 return 1
03831 } elseif {$operator($txtt) eq "quit"} {
03832 gui::close_current -force 1
03833 }
03834 }
03835
03836 return 0
03837
03838 }
03839
03840 ######################################################################
03841 # If we are in "command" mode, replays the register specified with the
03842 # next character. If we are in "replay_reg" mode, playback the current
03843 # register again.
03844 proc handle_at {txtt} {
03845
03846 variable mode
03847
03848 if {$mode($txtt) eq "command"} {
03849 set mode($txtt) "playback_reg"
03850 return 1
03851 }
03852
03853 return 0
03854
03855 }
03856
03857 ######################################################################
03858 # This is just a synonym for the 'l' command so we'll just call the
03859 # handle_l procedure instead of replicating the code.
03860 proc handle_space {txtt} {
03861
03862 variable mode
03863 variable operator
03864 variable motion
03865
03866 # Move the insertion cursor right one character
03867 set startargs ""
03868 switch [lindex $motion($txtt) end] {
03869 "V" {
03870 set startargs "linestart"
03871 set endargs "lineend"
03872 }
03873 "v" {
03874 if {$operator($txtt) eq ""} {
03875 set endargs [list char -dir next -num [expr [get_number $txtt] + 1]]
03876 } else {
03877 set endargs [list dchar -dir next -num [expr [get_number $txtt] + 1]]
03878 }
03879 }
03880 default {
03881 if {$operator($txtt) eq ""} {
03882 set endargs [list char -dir next -num [get_number $txtt]]
03883 } else {
03884 set endargs [list dchar -dir next -num [get_number $txtt]]
03885 }
03886 }
03887 }
03888
03889 return [do_operation $txtt $endargs $startargs]
03890
03891 }
03892
03893 ######################################################################
03894 # This is just a synonym for the 'h' command so we'll just call the
03895 # handle_h procedure instead of replicating the code.
03896 proc handle_BackSpace {txtt} {
03897
03898 variable operator
03899 variable motion
03900
03901 # Move the insertion cursor left one character
03902 set startargs ""
03903 set cursorargs [list dchar -dir prev -num [get_number $txtt]]
03904 switch [lindex $motion($txtt) end] {
03905 "V" {
03906 set startargs "linestart"
03907 set endargs "lineend"
03908 # set cursorargs "none"
03909 }
03910 "v" {
03911 set startargs "right"
03912 if {$operator($txtt) eq ""} {
03913 set endargs [list char -dir prev -num [get_number $txtt]]
03914 } else {
03915 set endargs [list dchar -dir prev -num [get_number $txtt]]
03916 }
03917 }
03918 default {
03919 if {$operator($txtt) eq ""} {
03920 set endargs [list char -dir prev -num [get_number $txtt]]
03921 } else {
03922 set endargs [list dchar -dir prev -num [get_number $txtt]]
03923 }
03924 }
03925 }
03926
03927 return [do_operation $txtt $endargs $startargs -cursor $cursorargs]
03928
03929 }
03930
03931 ######################################################################
03932 # This is just a synonym for the 'l' command so we'll just call the
03933 # handle_l procedure instead of replicating the code.
03934 proc handle_Right {txtt} {
03935
03936 return [handle_l $txtt]
03937
03938 }
03939
03940 ######################################################################
03941 # This is just a synonym for the 'h' command so we'll just call the
03942 # handle_h procedure instead of replicating the code.
03943 proc handle_Left {txtt} {
03944
03945 return [handle_h $txtt]
03946
03947 }
03948
03949 ######################################################################
03950 # This is just a synonym for the 'k' command so we'll just call the
03951 # handle_k procedure instead of replicating the code.
03952 proc handle_Up {txtt} {
03953
03954 return [handle_k $txtt]
03955
03956 }
03957
03958 ######################################################################
03959 # This is just a synonym for the 'j' command so we'll just call the
03960 # handle_j procedure instead of replicating the code.
03961 proc handle_Down {txtt} {
03962
03963 return [handle_j $txtt]
03964
03965 }
03966
03967 ######################################################################
03968 # If we are in start mode, transition to the folding mode.
03969 proc handle_z {txtt} {
03970
03971 variable operator
03972
03973 if {$operator($txtt) eq ""} {
03974 set operator($txtt) "folding"
03975 return 1
03976 }
03977
03978 return 0
03979
03980 }
03981
03982 ######################################################################
03983 # If we are in goto mode, move the cursor to the last character of the
03984 # line.
03985 proc handle_underscore {txtt} {
03986
03987 variable motion
03988
03989 if {$motion($txtt) eq ""} {
03990 return [do_operation $txtt [list firstchar -dir next -num [expr [get_number $txtt] - 1]]]
03991 } elseif {$motion($txtt) eq "g"} {
03992 return [do_operation $txtt [list lastchar -num [get_number $txtt]]]
03993 }
03994
03995 return 0
03996
03997 }
03998
03999 ######################################################################
04000 # Moves the cursor to the end of the next word.
04001 proc handle_e {txtt} {
04002
04003 variable operator
04004 variable motion
04005
04006 if {$operator($txtt) eq ""} {
04007 if {$motion($txtt) eq ""} {
04008 return [do_operation $txtt [list wordend -dir next -num [get_number $txtt] -exclusive 1]]
04009 } elseif {$motion($txtt) eq "g"} {
04010 return [do_operation $txtt [list wordend -dir prev -num [get_number $txtt] -exclusive 1]]
04011 }
04012 } else {
04013 if {$motion($txtt) eq ""} {
04014 return [do_operation $txtt [list wordend -dir next -num [get_number $txtt] -adjust "+1 display chars" -exclusive 1]]
04015 } elseif {$motion($txtt) eq "g"} {
04016 return [do_operation $txtt [list wordend -dir prev -num [get_number $txtt] -exclusive 1] right]
04017 }
04018 }
04019
04020 return 0
04021
04022 }
04023
04024 ######################################################################
04025 # Move forward/backward to the end of a WORD.
04026 proc handle_E {txtt} {
04027
04028 variable operator
04029 variable motion
04030
04031 switch $operator($txtt) {
04032 "" {
04033 if {$motion($txtt) eq ""} {
04034 return [do_operation $txtt [list WORDend -dir next -num [get_number $txtt] -exclusive 1]]
04035 } elseif {$motion($txtt) eq "g"} {
04036 return [do_operation $txtt [list WORDend -dir prev -num [get_number $txtt] -exclusive 1]]
04037 }
04038 }
04039 "folding" {
04040 folding::delete_all_folds [winfo parent $txtt]
04041 }
04042 default {
04043 if {$motion($txtt) eq ""} {
04044 return [do_operation $txtt [list WORDend -dir next -num [get_number $txtt] -adjust "+1 display chars" -exclusive 1]]
04045 } elseif {$motion($txtt) eq "g"} {
04046 return [do_operation $txtt [list WORDend -dir prev -num [get_number $txtt] -exclusive 1] right]
04047 }
04048 }
04049 }
04050
04051 return 0
04052
04053 }
04054
04055 }