Sarnold introduces here a script that provides this feature.
It works either as a command-line tool :
tclsh reformat.tcl ?-indent number? myfile.tcl
and as a Tcl proc :
source reformat.tcl set out [reformat $code]
Sarnold on 2008-04-05 : I fixed a bug that caused duplicating newlines. The -unixnl option was removed because of that bug. I added the -indent option to specify the number of blanks of the indentation.
NEM Could you describe what it does, exactly? Is it a pretty-printer for Tcl source code?
GWM it neatens any text file, correcting and setting indentations. A bug has been fixed in handling lines with multiple braces in them (eg "} else {" or " } } }").
PM on 2009-08-20 : fixed a bug causing commented lines including braces to increase/decrease indent, and added a swap through a temporary file to avoid getting an empty file if something goes wrong.
neatpick on 2011-04-04: the reformatted file should have the same permissions as the original file.
reformat.tcl
proc reformat {tclcode {pad 4}} { set lines [split $tclcode \n] set out "" set continued no set oddquotes 0 set line [lindex $lines 0] set indent [expr {([string length $line]-[string length [string trimleft $line \ \t]])/$pad}] set pad [string repeat " " $pad] foreach orig $lines { set newline [string trim $orig \ \t] set line [string repeat $pad $indent]$newline if {[string index $line end] eq "\\"} { if {!$continued} { incr indent 2 set continued yes } } elseif {$continued} { incr indent -2 set continued no } if { ! [regexp {^[ \t]*\#} $line] } { # oddquotes contains : 0 when quotes are balanced # and 1 when they are not set oddquotes [expr {([count $line \"] + $oddquotes) % 2}] if {! $oddquotes} { set nbbraces [count $line \{] incr nbbraces -[count $line \}] set brace [string equal [string index $newline end] \{] set unbrace [string equal [string index $newline 0] \}] if {$nbbraces>0 || $brace} { incr indent $nbbraces ;# [GWM] 010409 multiple open braces } if {$nbbraces<0 || $unbrace} { incr indent $nbbraces ;# [GWM] 010409 multiple close braces if {$indent<0} { error "unbalanced braces" } ## was: set line [string range $line [string length $pad] end] # 010409 remove multiple brace indentations. Including case # where "} else {" needs to unindent this line but not later lines. set np [expr {$unbrace? [string length $pad]:-$nbbraces*[string length $pad]}] set line [string range $line $np end] } } else { # unbalanced quotes, preserve original indentation set line $orig } } append out $line\n } return $out } proc eol {} { switch -- $::tcl_platform(platform) { windows {return \r\n} unix {return \n} macintosh {return \r} default {error "no such platform: $::tc_platform(platform)"} } } proc count {string char} { set count 0 while {[set idx [string first $char $string]]>=0} { set backslashes 0 set nidx $idx while {[string equal [string index $string [incr nidx -1]] \\]} { incr backslashes } if {$backslashes % 2 == 0} { incr count } set string [string range $string [incr idx] end] } return $count } set usage "reformat.tcl ?-indent number? filename" if {[llength $argv]!=0} { if {[lindex $argv 0] eq "-indent"} { set indent [lindex $argv 1] set argv [lrange $argv 2 end] } else { set indent 4 } if {[llength $argv]>1} { error $usage } set f [open $argv r] set data [read $f] close $f set permissions [file attributes $argv -permissions] set filename "$argv.tmp" set f [open $filename w] puts -nonewline $f [reformat [string map [list [eol] \n] $data] $indent] close $f file copy -force $filename $argv file delete -force $filename file attributes $argv -permissions $permissions }
GWM for interactive users I have created the code:
# basic interface prompt for file and indent it by 2 spaces. # I am sure an interested reader will be able to make the indent adjustable too. set indent 2 set fin [tk_getOpenFile -title "File to be reformatted"] set f [open $fin r] set data [read $f] close $f #console show; puts "Ho look at $fin" ;update idletasks set f [open ${fin}.txt w] puts -nonewline $f [reformat [string map [list [eol] \n] $data] $indent] close $f
ET the "} else {" in the #comment (below ## was:) seems to confuse the tcl interpreter, at least in my 8.6b1 it does; I'm guessing it is treating that as an end of block that happens to have a " at the end of it. I changed the 2 braces to something else and it quit complaining about a missing ".
EP I think that
if {$nbbraces>0 || $brace} { incr indent $nbbraces ;# [GWM] 010409 multiple open braces } if {$nbbraces<0 || $unbrace} {
should become
if {$nbbraces!=0 || $brace || $unbrace}
to handle the following situation:
if { catch [ { .... } } { .... }
srujan - 2013-10-07 06:41:29
hi,
can you please fix the below errors in the reformat.tcl script
tclsh ../../reformat.tcl -indent 4 run-setup_aressim6_orig_indent missing "
while compiling
"" needs to unindent this line but not later lines.
set np [expr {$unbrace? [string length $pad]:-$nbbraces*[string length $pad]}]
..."
("if" else script line 1) while compiling
"if {$nbbraces!=0 || $brace || $unbrace} {
incr indent $nbbraces ;# [GWM] 010409 multiple close braces if {$ind..." ("if" then script line 6) while compiling
"if {! $oddquotes} {
set nbbraces [count $line \{] incr nbbraces -[count $line \}] set brace [strin..." ("if" then script line 6) while compiling
"if { ! regexp {^[ \t*\#} $line] } {
# oddquotes contains : 0 when quotes are balanced # and 1 when they are not ..." ("foreach" body line 14) while compiling
"foreach orig $lines {
set newline [string trim $orig \ \t] set line [string repeat $pad $indent]$newline if {[string index $li..." (compiling body of proc "reformat", line 10) invoked from within
"reformat string map [list [eol \n] $data] $indent"
invoked from within
"if {llength $argv!=0} {
if {[lindex $argv 0] eq "-indent"} { set indent [lindex $argv 1] set argv [lrange $argv 2 end] } els..." (file "../../reformat.tcl" line 81)
srujan - 2013-10-07 06:43:12
please fix the below errors
tclsh ../../reformat.tcl -indent 4 run-setup_aressim6_orig_indent missing " while compiling "" needs to unindent this line but not later lines. set np [expr {$unbrace? [string length $pad]:-$nbbraces*[string length $pad]}] ..." ("if" else script line 1) while compiling "if {$nbbraces!=0 || $brace || $unbrace} { incr indent $nbbraces ;# [GWM] 010409 multiple close braces if {$ind..." ("if" then script line 6) while compiling "if {! $oddquotes} { set nbbraces [count $line \{] incr nbbraces -[count $line \}] set brace [strin..." ("if" then script line 6) while compiling "if { ! [regexp {^[ \t]*\#} $line] } { # oddquotes contains : 0 when quotes are balanced # and 1 when they are not ..." ("foreach" body line 14) while compiling "foreach orig $lines { set newline [string trim $orig \ \t] set line [string repeat $pad $indent]$newline if {[string index $li..." (compiling body of proc "reformat", line 10) invoked from within "reformat [string map [list [eol] \n] $data] $indent" invoked from within "if {[llength $argv]!=0} { if {[lindex $argv 0] eq "-indent"} { set indent [lindex $argv 1] set argv [lrange $argv 2 end] } els..." (file "../../reformat.tcl" line 81)
Durgaram Corrected the code., now it is working fine for me
proc reformat {tclcode {pad 4}} { set lines [split $tclcode \n] set out "" set continued no set oddquotes 0 set line [lindex $lines 0] set indent [expr {([string length $line]-[string length [string trimleft $line \ \t]])/$pad}] set pad [string repeat " " $pad] foreach orig $lines { set newline [string trim $orig \ \t] set line [string repeat $pad $indent]$newline if {[string index $line end] eq "\\"} { if {!$continued} { incr indent 2 set continued yes } } elseif {$continued} { incr indent -2 set continued no } if { ! [regexp {^[ \t]*\#} $line] } { # oddquotes contains : 0 when quotes are balanced # and 1 when they are not set oddquotes [expr {([count $line \"] + $oddquotes) % 2}] if {! $oddquotes} { set nbbraces [count $line \{] incr nbbraces -[count $line \}] set brace [string equal [string index $newline end] \{] set unbrace [string equal [string index $newline 0] \}] if {$nbbraces>0 || $brace} { incr indent $nbbraces ;# [GWM] 010409 multiple open braces } if {$nbbraces<0 || $unbrace} { incr indent $nbbraces ;# [GWM] 010409 multiple close braces if {$indent<0} { error "unbalanced braces" } ## was: set line [string range $line [string length $pad] end] # 010409 remove multiple brace indentations. Including case set np [expr {$unbrace? [string length $pad]:-$nbbraces*[string length $pad]}] set line [string range $line $np end] } } else { # unbalanced quotes, preserve original indentation set line $orig } } append out $line\n } return $out } proc eol {} { switch -- $::tcl_platform(platform) { windows {return \r\n} unix {return \n} macintosh {return \r} default {error "no such platform: $::tc_platform(platform)"} } } proc count {string char} { set count 0 while {[set idx [string first $char $string]]>=0} { set backslashes 0 set nidx $idx while {[string equal [string index $string [incr nidx -1]] \\]} { incr backslashes } if {$backslashes % 2 == 0} { incr count } set string [string range $string [incr idx] end] } return $count } set usage "reformat.tcl ?-indent number? filename" if {[llength $argv]!=0} { if {[lindex $argv 0] eq "-indent"} { set indent [lindex $argv 1] set argv [lrange $argv 2 end] } else { set indent 4 } if {[llength $argv]>1} { error $usage } set f [open $argv r] set data [read $f] close $f set permissions [file attributes $argv -permissions] set filename "$argv.tmp" set f [open $filename w] puts -nonewline $f [reformat [string map [list [eol] \n] $data] $indent] close $f file copy -force $filename $argv file delete -force $filename file attributes $argv -permissions $permissions } set indent 2 set fin [tk_getOpenFile -title "File to be reformatted"] set f [open $fin r] set data [read $f] close $f #console show; puts "Ho look at $fin" ;update idletasks set f [open ${fin}.txt w] puts -nonewline $f [reformat [string map [list [eol] \n] $data] $indent] close $f
i used this code for indentation... working fine :)...
proc align {file} {
set split_line 0 set id [open $file r] set cont [read $id] close $id set id [open $file w+] set i 0 set lines [split $cont \n] foreach line $lines { set line [string trim $line] if {$split_line} { for {set j [expr $i -2]} {$j>=0} {incr j -1} { puts -nonewline $id " " } puts $id " $line" if {[string index $line end] == "\\"} { set split_line 1 } else { set split_line 0 } set opens [regexp -all {\{} $line] set closes [regexp -all {\}} $line] incr i [expr $opens - $closes] continue } if {[string index $line end] == "\\"} { set split_line 1 } else { set split_line 0 } if [regexp {\} else \{} $line] { for {set j [expr $i -2]} {$j>=0} {incr j -1} { puts -nonewline $id " " } puts $id $line continue } set opens [regexp -all {\{} $line] set closes [regexp -all {\}} $line] if {[expr $opens - $closes] == -1} { incr i -1 for {set j [expr $i -1]} {$j>=0} {incr j -1} { puts -nonewline $id " " } puts $id $line continue } for {set j [expr $i -1]} {$j>=0} {incr j -1} { puts -nonewline $id " " } puts $id $line incr i [expr $opens - $closes] } close $id
}
aplsimple - 2018-11-09 14:25:06
Still your last version has some issues that are listed below with code samples to be processed by the reformat procedure.
Issue #1.
The {"} is a legal (though tricky) variant of \". Not seen as legal by the reformat procedure.
set rc [open ./check.tcl r] foreach line [split [read $rc] \n] { if {[expr ([string length $line] - [string length [string map {{"} {}} $line]]) % 2]} { puts " 2 --> Mismatching line: $line" } } close $rc
Issue #2.
Third line (with ::message_box) starts a multi-line string. Processed incorrectly by the reformat procedure.
set ::start0 0 if {$::start0} { ::message_box "Run this with wish test.tcl \"%s\" \[menu\] as a test\n(see readme.md for details)." proc1 }
Issue #3.
Total failure of the reformat procedure. Puts out the error of "unbalanced braces" though its input is a correct Tcl script.
set ::start0 0 if {$::start0=="Run this with wish test.tcl \"%s\" \[menu\] ...line1 ...line2 as a test\n(see readme.md for details)." } { proc2 }
Issue #4.
Strange outputs of continuation lines.
set ::start0 0 if {$::start0=="some" && \ $::start2=="some2" && \ $::start3=="some3" && \ $::start4=="some4" } { ::message_box "Run this with ..." if {somecond3} { set res 3 } } exit
Issue #5.
In the last example supplied for the reformat just before this post, the last "}" after "close $id" is redundant and should cause the error at formatting. The reformat proc doesn't see the error and swallows the last "}". The reformat2 proc (below) raises the error as "Line 54: unbalanced braces!".
Issue #6.
All tests reveal an empty line that is added to the end of input code by the reformat procedure.
#=================================
These issues are resolved in the reformat2 proc below.
The reformat2 proc has the following features:
- it doesn't format the continuation lines leaving this to a coder (compare e.g. Python's PEP 8);
- it doesn't format the multi-line strings: the initial spaces in continuation strings may matter;
- it doesn't format the comment lines considering them being the coder's territory;
- it correctly processes the if and similar commands that are normally ended by "{" in the first line but a coder may not to follow this rule (as in issue #3);
- it sees {"} as a correct variant of \";
- it doesn't add an empty line to a code;
- it shows a line number in the case of error (i.e. in what line the "}" is redundant).
To be honest as for the cost: the reformat2 is twice slower than the reformat.
Please:
proc reformat2 {tclcode {pad 2}} { set lines [split $tclcode \n] set out "" set nquot 0 ;# count of quotes set ncont 0 ;# count of continued strings set line [lindex $lines 0] set indent [expr {([string length $line]-[string length [string trimleft $line \ \t]])/$pad}] set padst [string repeat " " $pad] foreach orig $lines { incr lineindex if {$lineindex>1} {append out \n} set newline [string trimleft $orig] if {$newline==""} continue set is_quoted $nquot set is_continued $ncont if {[string index $orig end] eq "\\"} { incr ncont } else { set ncont 0 } if { [string index $newline 0]=="#" } { set line $orig ;# don't touch comments } else { set npad [expr {$indent * $pad}] set line [string repeat $padst $indent]$newline set i [set ns [set nl [set nr [set body 0]]]] for {set n [string length $newline]} {$i<$n} {incr i} { set ch [string index $newline $i] if {$ch=="\\"} { set ns [expr {[incr ns] % 2}] } elseif {!$ns} { if {$ch=="\""} { set nquot [expr {[incr nquot] % 2}] } elseif {!$nquot} { switch $ch { "\{" { if {[string range $newline $i $i+2]=="\{\"\}"} { incr i 2 ;# quote in braces - correct (though tricky) } else { incr nl set body -1 } } "\}" { incr nr set body 0 } } } } else { set ns 0 } } set nbbraces [expr {$nl - $nr}] incr totalbraces $nbbraces if {$totalbraces<0} { error "Line $lineindex: unbalanced braces!" } incr indent $nbbraces if {$nbbraces==0} { set nbbraces $body } if {$is_quoted || $is_continued} { set line $orig ;# don't touch quoted and continued strings } else { set np [expr {- $nbbraces * $pad}] if {$np>$npad} { ;# for safety too set np $npad } set line [string range $line $np end] } } append out $line } return $out }