################################################################## # tools.tcl - this file implements the logic of working # with external tools. ################################################################## # svk, 01/2026 ################################################################## namespace eval Tools {} { variable toolsINISections variable toolsVariables } set ::toolsDefault "\[VisualRegexp\] commandString=tkregexp \"%s\" description=A graphical front-end to write/debug regular expression icon= shortCut= \[TkDIFF\] commandString=tkdiff %f %f description=TkDiff is a Tcl/Tk front-end to diff icon= shortCut= " # Создание файла настроек внешних инструментов proc Tools::Create {dir} { set toolsFile [open [file join $dir tools.ini] "w+"] puts $toolsFile $::toolsDefault close $toolsFile } proc Tools::Read {dir} { set ::toolsVariables "" set toolsFile [ini::open [file join $dir tools.ini] "r"] foreach section [ini::sections $toolsFile] { foreach key [ini::keys $toolsFile $section] { lappend ::toolsINIsections($section) $key dict set ::toolsVariables $section $key [ini::value $toolsFile $section $key] DebugPuts "Tools::Read: $toolsFile $section $key = [ini::value $toolsFile $section $key]" } } ini::close $toolsFile } proc Tools::Write {dir} { set toolsFile [ini::open [file join $dir tools.ini] "w"] foreach section [array names ::toolsINIsections] { dict for {key value} [dict get $::toolsVariables $section] { DebugPuts "Tools::write: $section $key = $value" # ini::set $toolsFile $section $key [dict get $::toolsVariables $section $key] ini::set $toolsFile $section $key $value } } ini::commit $toolsFile ini::close $toolsFile } # Добавление перменной в список # если отсутствует нужная секция то она будет добавлена. proc Tools::AddVariable {key value section} { # Проверяем, существует ли уже такая переменная if {[info exists ::toolsVariables($key)]} { DebugPuts "The variable '$key' already exists: " return 0 } # Добавляем в массив переменных # set ::toolsVariables($key) $value dict set ::toolsVariables $section $key $value # Добавляем в список ключей секции if {[dict exists $::toolsVariables $key]} { # Проверяем, нет ли уже такого ключа в секции if {[lsearch -exact $::toolsINIsections($section) $key] == -1} { lappend ::toolsINIsections($section) $key } } else { set ::toolsINIsections($section) [list $key] } DebugPuts "Tools::AddVariable: The variable '$key' has been added to the '$section' array 'toolsVariables' with value '$value'" return 1 } # Проверяем наличие переменных в tools.ini на основе "эталонного" списка # и выставляем значение по умолчанию если в конфиге переменной нет proc Tools::CheckVariables {} { set valList [split $::toolsDefault "\n"] foreach item $valList { if {[regexp -nocase -all -- {\[(\w+)\]} $item -> v1]} { set section $v1 } if {[regexp {^([^=]+)=(.*)$} $item -> key value]} { # puts "$section $key $value >> [dict get $::toolsVariables $section $key]" # if {[dict get $::toolsVariables $section $key] eq ""} if ![dict exists $::toolsVariables $section $key] { DebugPuts "Error in Tools::CheckVariables: variable $section $key not found" Tools::AddVariable "$key" "$value" "$section" # DebugPuts "Tools::CheckVariables: The variable toolsVariables $key setting to default value \"$value\"" } } } foreach id [dict keys $::toolsVariables] { DebugPuts "Tools::CheckVariables: config parameters for $id [dict get $::toolsVariables $id]!" } # DebugPuts "toolsVariables dict keys: [dict keys $::toolsVariables]" } proc Tools::GetMenu {m} { global cfgVariables toolsVariables set count [$m index end] if {$count != "none"} { for {set i $count} {$i >= 0} {incr i -1} { $m delete $i } } foreach toolName [dict keys $toolsVariables] { dict for {key value} [dict get $toolsVariables $toolName] { DebugPuts "GetToolsMenu $key $value" if {$key eq "commandString"} { set cmd "$value" } if {$key eq "shortCut"} { set shortCut "$value" } } if {[info exists cmd] == 1 && $cmd ne ""} { if {[info exists shortCut] == 1 && $shortCut ne ""} { $m add command -label $toolName -accelerator $shortCut -command [list Tools::Execute "$toolName"] bind . <$shortCut> [list Tools::Execute "$toolName"] } else { $m add command -label $toolName -command [list Tools::Execute "$toolName"] } } } $m add separator $m add command -label "[::msgcat::mc "Settings"]" -command Tools::Settings } proc Tools::CommandPathSettings {command} { global tcl_platform if [file exists $command] {return $command} if {$tcl_platform(platform) eq "windows"} { set cmd "where $command)" } else { set cmd "which $command" } DebugPuts [catch {exec {*}$cmd} toolsPath] DebugPuts "executor_path $toolsPath" if {[catch {exec {*}$cmd} toolsPath]} { DebugPuts "Tools::CommandPathSettings: Программа $command не найдена в системе" return "" } set fullPath [string trim $toolsPath] set firstPath [lindex [split $toolsPath "\n"] 0] DebugPuts "Tools::CommandPathSettings: executable path $fullPath" return $fullPath } proc Tools::Execute {toolName} { global cfgVariables toolsVariables tree if ![dict exists $::toolsVariables $toolName commandString] { DebugPuts "Tools::Execute: command for $toolName not found" return } else { set command [dict get $::toolsVariables $toolName commandString] DebugPuts "Tools::Execute: command for $toolName as $command" } # 7. Проверять команды на доступность в системе и подставлять полный путь к команде # если в конфиге не указан полный путь. # Проверем наличие внешгних программ в системе set cmd [lindex [split $command " "] 0] if [file exists $cmd] { set fullCommand $command } else { set fullPathToExec [Tools::CommandPathSettings "$cmd"] set fullCommand [lreplace [split $command " "] 0 0 $fullPathToExec] } if {$fullPathToExec eq ""} { DebugPuts "Tools::Execute: $command not found" return } else { DebugPuts "Tools::Execute: $fullPathToExec, $fullCommand" } # 2. Определять выделен ли текст в открытом редакторе # 5. Заменяем %s на выделенный в редакторе текст set selectedText [Editor::SelectionGet] if {$selectedText ne ""} { regsub -all "%s" $fullCommand "$selectedText" fullCommand DebugPuts "Tools::Execute: selected text \"$selectedText\", command \"$fullCommand\"" } # 1. Определять текущий файл # 3. Опеределять сколько файлов выделено в дереве # 4. Заменяем знак %f на имя текущего файла (файлов) # regsub -all "%f" $command "$filePath" fullCommand set filesList [Tree::GetSelectedItemValues $tree] if {$filesList ne ""} { foreach file $filesList { # Если больше нет %f для замены, выходим из цикла if {![string match "*%f*" $fullCommand]} break set fullCommand [regsub {%f} $fullCommand $file] } } # 6. Заменяем %d на текущий каталог(и), если он выделен в дереве, # и если не выделено то корневой открытый в дереве DebugPuts "Tools::Execute: $fullCommand" set pipe [open "|$fullCommand" "r"] fileevent $pipe readable fconfigure $pipe -buffering none -blocking no } # Правка файла настроек proc Tools::Settings {} { global dir FileOper::Edit [file join $dir(cfg) tools.ini] # Config::read $dir(cfg) }