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 }