home *** CD-ROM | disk | FTP | other *** search
/ Macwelt 1 / Macwelt DVD 1.toast / Web-Publishing / HTML-Editoren / Alpha ƒ / Tcl / Modes / HTML and CSS Modes / htmlIndentation.tcl < prev    next >
Encoding:
Text File  |  2001-01-14  |  13.6 KB  |  407 lines

  1. ## -*-Tcl-*-
  2.  # ###################################################################
  3.  #  HTML mode - tools for editing HTML documents
  4.  # 
  5.  #  FILE: "htmlIndentation.tcl"
  6.  #                                    created: 99-07-17 22.45.03 
  7.  #                                last update: 01-01-14 10.58.51 
  8.  #  Author: Johan Linde
  9.  #  E-mail: <alpha_www_tools@go.to>
  10.  #     www: <http://go.to/alpha_www_tools>
  11.  #  
  12.  # Version: 3.0
  13.  # 
  14.  # Copyright 1996-2001 by Johan Linde
  15.  #  
  16.  # This program is free software; you can redistribute it and/or modify
  17.  # it under the terms of the GNU General Public License as published by
  18.  # the Free Software Foundation; either version 2 of the License, or
  19.  # (at your option) any later version.
  20.  # 
  21.  # This program is distributed in the hope that it will be useful,
  22.  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  23.  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24.  # GNU General Public License for more details.
  25.  # 
  26.  # You should have received a copy of the GNU General Public License
  27.  # along with this program; if not, write to the Free Software
  28.  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  29.  # 
  30.  # ###################################################################
  31.  ##
  32.  
  33. #===============================================================================
  34. # This file contains procs for handling the indentation of HTML elements.
  35. #===============================================================================
  36.  
  37. #===============================================================================
  38. # ◊◊◊◊ Indentation ◊◊◊◊ #
  39. #===============================================================================
  40.   
  41. # Find the indentation the current line should have.
  42. proc html::FindIndent {{pos0 ""}} {
  43.     global HTMLmodeVars
  44.     set indent $HTMLmodeVars(indentElements)
  45.     # Find previous non-blank line.
  46.     if {$pos0 == ""} {set pos0 [getPos]}
  47.     set pos [pos::math [lineStart $pos0] - 1]
  48.     while {[pos::compare $pos >= [minPos]] && [regexp {^[ \t]*$} [getText [lineStart $pos] $pos]]} {
  49.         set pos [pos::math [lineStart $pos] - 1]
  50.     }
  51.     if {[pos::compare $pos < [minPos]]} {set pos [minPos]}
  52.     # Get indentation on that line.
  53.     set previndent [html::GetIndent $pos]
  54.     # Find last tag on or before that line.
  55.     if {[catch {search -s -f 0 -m 0 -r 1 {<([^<>]+)>} $pos} tag] || 
  56.     [pos::compare [lindex $tag 1] < [lineStart $pos]] ||
  57.     ( [pos::compare [lindex $tag 0] < [lineStart $pos0]] && [pos::compare [lindex $tag 1] > [lineStart $pos0]])} {
  58.         set tag ""
  59.     } else {
  60.         set tag [string trim [eval getText $tag] "<>"]
  61.     }
  62.     regexp {[ \t\r\n]*([^ \r\t\n>]+)} $tag "" tag
  63.     # Add to indentation?
  64.     if {[lcontains indent $tag]} {
  65.         set previndent [html::IncreaseIndent $previndent]
  66.     }
  67.     # Find last tag on current line.
  68.     set tag ""
  69.     set lstart [lineStart $pos0]
  70.     if {[pos::compare [set npos [nextLineStart $pos0]] <= $lstart]} {
  71.         set lend $lstart
  72.     } else {
  73.         set lend [pos::math $npos - 1]
  74.     }
  75.     regexp {<([^<>]+)>[^<>]*$} [getText $lstart $lend] dum tag
  76.     regexp {[ \t\r\n]*([^ \r\t\n>]+)} $tag "" tag
  77.     
  78.     # Reduce indentation?
  79.     if {[string index $tag 0] == "/" && [lcontains indent [string range $tag 1 end]]} {
  80.         set previndent [html::ReduceIndent $previndent]
  81.     }
  82.     return $previndent
  83. }
  84.  
  85. # Find the indentation the next line should have.
  86. proc html::FindNextIndent {{pos0 ""}} {
  87.     if {[nextLineStart $pos0] < [maxPos]} {return [html::FindIndent [nextLineStart $pos0]]}
  88.     global HTMLmodeVars
  89.     set indent $HTMLmodeVars(indentElements)
  90.     if {$pos0 == ""} {set pos0 [getPos]}
  91.     set ind [html::FindIndent $pos0]
  92.     # Find last tag before pos0 on current line.
  93.     set tag ""
  94.     set lstart [lineStart $pos0]
  95.     regexp {<([^<>]+)>[^<>]*$} [getText $lstart $pos0] dum tag
  96.     set tag [string toupper [lindex $tag 0]]
  97.     # Add to indentation?
  98.     if {[lcontains indent $tag]} {set ind [html::IncreaseIndent $ind]}
  99.     return $ind
  100. }
  101.  
  102. # get the leading whitespace of the line determined by pos
  103. proc html::GetIndent {pos} {
  104.     return [text::indentOf [string length [text::maxSpaceForm [text::indentString $pos]]]]
  105. }
  106.  
  107. # Adds indentationAmount whitespace.
  108. proc html::IncreaseIndent {indent} {
  109.     global indentationAmount
  110.     return [text::indentBy $indent $indentationAmount]
  111. }
  112.  
  113. # Removes indentationAmount whitespace.
  114. proc html::ReduceIndent {indent} {
  115.     global indentationAmount
  116.     return [text::indentBy $indent [expr {-$indentationAmount}]]
  117. }
  118.  
  119. proc html::IndentShouldBeAdjusted {{pos ""}} {
  120.     if {$pos == ""} {set pos [getPos]}
  121.     return [expr {[string length [text::maxSpaceForm [text::indentString $pos]]] < [string length [text::maxSpaceForm [html::FindNextIndent $pos]]]}]
  122. }
  123.  
  124. #===============================================================================
  125. # ◊◊◊◊ Reformat paragraph/document ◊◊◊◊ #
  126. #===============================================================================
  127.  
  128. proc html::RegisterFormattingStyle {style menutext lineproc {blockproc ::indentRegion}} {
  129.     global html::formattingStyles html::formatBlockProcs html::indentLineProcs
  130.     set html::formattingStyles [lunique [lappend html::formattingStyles $style]]
  131.     set html::formatBlockProcs($style) $blockproc
  132.     set html::indentLineProcs($style) $lineproc
  133.     ;proc html::[join $menutext ""] {} "html::InsertFormatTags $style"
  134.     menu::insert Formatting items end $menutext
  135. }
  136.  
  137. proc html::InsertFormatTags {tag} {
  138.     global htmlCurSel htmlIsSel
  139.     html::GetSel
  140.     if {!$htmlIsSel} {
  141.         elec::Insertion "[html::OpenCR]<!-- [html::SetCase #$tag] -->\r•content•\r<!-- [html::SetCase /#$tag] -->•end•"
  142.     } else {
  143.         replaceText [getPos] [selEnd] "<!-- [html::SetCase #$tag] -->\r$htmlCurSel\r<!-- [html::SetCase /#$tag] -->"
  144.     } 
  145. }
  146.  
  147. proc html::NoFormatting {} {
  148.     html::InsertFormatTags NO-FORMATTING
  149. }
  150.  
  151. proc html::CStyleFormatting {} {
  152.     html::InsertFormatTags C-STYLE-FORMATTING
  153. }
  154. proc html::ReformatParagraph {} {html::TidyUp paragraph}
  155. proc html::ReformatDocument {} {html::TidyUp document}
  156.  
  157. proc html::TidyUp {where} {
  158.     global fillColumn
  159.     set oldFill $fillColumn
  160.     catch {html::TidyUp2 $where}
  161.     set fillColumn $oldFill
  162. }
  163.  
  164. proc html::TidyUp2 {where} {
  165.     global HTMLmodeVars html::ElemLayout fillColumn html::formattingStyles html::formatBlockProcs
  166.     message "Reformatting…"
  167.     set oldfillColumn $fillColumn
  168.     if {$where == "paragraph"} {
  169.         if {[isSelection]} {
  170.             set startPos [getPos]
  171.             set endPos [selEnd]
  172.         } else {
  173.             if {[catch {search -s -f 0 -m 0 -r 1 {^[ \t]*$} [getPos]} sp]} {set sp "[minPos] [minPos]"}
  174.             set startPos [nextLineStart [lindex $sp 1]]
  175.             if {[catch {search -s -f 1 -m 0 -r 1 {^[ \t]*$} [getPos]} sp]} {set sp "[minPos] [maxPos]"}
  176.             if {[pos::compare [lindex $sp 1] < [maxPos]]} {
  177.                 set endPos [pos::math [lindex $sp 1] + 1]
  178.             } else {
  179.                 set endPos [maxPos]
  180.             }
  181.         }
  182.         # Avoid doing something inside STYLE and SCRIPT and PRE.
  183.         foreach stsc {STYLE SCRIPT PRE} {
  184.             if {[html::IsInContainer $stsc $startPos]} {
  185.                 if {[catch {search -s -f 1 -m 0 -r 0 -i 1 "</$stsc>" $startPos} rrr]} {
  186.                     message ""; return
  187.                 } else {
  188.                     set startPos [lindex $rrr 1]
  189.                 }
  190.             }
  191.             if {[html::IsInContainer $stsc $endPos]} {
  192.                 if {[catch {search -s -f 0 -m 0 -r 1 -i 1 "<$stsc\[^<>\]*>" $endPos} rrr]} {
  193.                     message ""; return
  194.                 } else {
  195.                     set endPos [lindex $rrr 0]
  196.                 }
  197.             } 
  198.         }
  199.         set informat 0
  200.         # Avoid doing someting inside formatting tags.
  201.         foreach stsc ${html::formattingStyles} {
  202.             if {[html::IsInCommentContainer $stsc $startPos]} {
  203.                 if {[catch {search -s -f 1 -m 0 -r 1 -i 1 "<!--\[ \t\r\n\]*/#$stsc\[ \t\r\n\]*-->" $startPos} rrr]} {
  204.                     message ""; return
  205.                 } else {
  206.                     set startPos [lindex $rrr 1]
  207.                 }
  208.                 set informat 1
  209.                 set form $stsc
  210.             }
  211.             if {[html::IsInCommentContainer $stsc $endPos]} {
  212.                 if {[catch {search -s -f 0 -m 0 -r 1 -i 1 "<!--\[ \t\r\n\]*#$stsc\[ \t\r\n\]*-->" $endPos} rrr]} {
  213.                     message ""; return
  214.                 } else {
  215.                     set endPos [lindex $rrr 0]
  216.                 }
  217.                 set informat 1
  218.                 set form $stsc
  219.             } 
  220.         }
  221.         set ind [html::FindIndent $startPos]
  222.         set fillColumn [expr {$oldfillColumn - [string length [text::maxSpaceForm $ind]]}]
  223.         set cr 2
  224.     } else {
  225.         set startPos [minPos]
  226.         set endPos [maxPos]
  227.         set ind ""
  228.         set cr 0
  229.     }
  230.     # Indent region if completely inside STYLE, SCRIPT, PRE or formatting tags.
  231.     if {[pos::compare $startPos > $endPos]} {
  232.         if {$informat} {
  233.             if {[set html::formatBlockProcs($form)] != ""} {
  234.                 eval [set html::formatBlockProcs($form)]
  235.             }
  236.         } else {
  237.             ::indentRegion
  238.         }
  239.         message ""
  240.         return
  241.     }
  242.     # Remember position
  243.     if {[pos::compare [getPos] > $startPos]} {
  244.         set pos [getPos]
  245.     } else {
  246.         set pos $startPos
  247.     }
  248.     if {[pos::compare [pos::math $pos - 20] < $startPos]} {
  249.         set srem $startPos
  250.     } else {
  251.         set srem [pos::math $pos - 20]
  252.     }
  253.     set remember_str [quote::Regfind [getText $srem $pos ]]
  254.     regsub -all {\?} $remember_str {\\?} remember_str
  255.     regsub -all "\[ \t\r\n\]+" $remember_str {[ \t\r\n]+} remember_str
  256.     # To handle indentation
  257.     set indList $HTMLmodeVars(indentElements)
  258.     
  259.     # These tags should have a blank line before
  260.     set blBef {}
  261.     # These tags should have a cr before
  262.     set crBef {!--}
  263.     # These tags should have a blank line after
  264.     set blAft {}
  265.     # These tags should have a cr after
  266.     set crAft {!-- !DOCTYPE}
  267.     # Custom elements
  268.     foreach c [array names html::ElemLayout] {
  269.         switch [set html::ElemLayout($c)] {
  270.             open00 -
  271.             nocr {}
  272.             open01 {lappend crAft $c}
  273.             open10 {lappend crBef $c}
  274.             open11 {lappend crBef $c; lappend crAft $c}
  275.             cr0 {lappend crBef $c; lappend crAft /$c}
  276.             cr1 {lappend blBef $c; lappend blAft /$c}
  277.             cr2 {lappend blBef $c; lappend crBef /$c; lappend blAft /$c; lappend crAft $c}
  278.         }
  279.     }
  280.     set all [concat $blBef $blAft $crBef $crAft]
  281.     set bef [concat $blBef $crBef]
  282.     set aft [concat $blAft $crAft]
  283.     set pos $startPos
  284.     set tmp ""
  285.     set text ""
  286.     set crChar [html::CRcharacter]
  287.     while {![catch {search -s -f 1 -m 0 -r 1 {(<!--|<[^<>]+>)} $pos} pos1] && [pos::compare [lindex $pos1 1] <= $endPos]} {
  288.         set wholeTag [string trim [eval getText $pos1] "<>"]
  289.         set tag ""
  290.         regexp {^[ \t\r\n]*([^ \t\r\n]+)} $wholeTag "" tag
  291.         set tag [string toupper $tag]
  292.         if {$tag != "!--"} {
  293.             set w ""
  294.             # To avoid line breaks inside attributes
  295.             while {[regexp -indices {=[ \t\r\n]*(\"[^ \"]* [^\"]*\"|'[^ ']* [^']*')} $wholeTag i]} {
  296.                 append w [string range $wholeTag 0 [expr {[lindex $i 0] - 1}]]
  297.                 regsub -all "\[ \t\r\n\]+" [string range $wholeTag [lindex $i 0] [lindex $i 1]] "" w1
  298.                 append w $w1
  299.                 set wholeTag [string range $wholeTag [expr {[lindex $i 1] + 1}] end]
  300.             }
  301.             set wholeTag $w$wholeTag
  302.         }
  303.         append tmp [getText $pos [lindex $pos1 0]]
  304.         set pos [lindex $pos1 1]            
  305.         if {[lsearch $all $tag] < 0} {
  306.             append tmp <$wholeTag>
  307.             continue
  308.         }
  309.         # cr or blank line before tag
  310.         if {[lsearch $bef $tag] >= 0} {
  311.             regsub -all "\[ \t\]*$crChar\[ \t\]*" [string trim $tmp] " " tmp
  312.             set tmp [string trimright [breakIntoLines $tmp]]
  313.             regsub -all "" $tmp " " tmp
  314.             regsub -all "\r" $tmp "\r$ind" tmp
  315.             if {![is::Whitespace $tmp]} {set cr 0; append text $ind}
  316.             append text $tmp
  317.             set ble [lsearch $blBef $tag]
  318.             if {$cr == 1 && $ble >= 0} {
  319.                 append text $ind
  320.             }
  321.             if {$cr == 0} {
  322.                 append text \r
  323.                 incr cr
  324.                 if {$cr == 1 && $ble >= 0} {append text $ind}
  325.             }
  326.             if {$ble >= 0 && $cr < 2} {append text \r; incr cr}
  327.             set tmp <$wholeTag>
  328.             # Take care of comments separately
  329.             if {$tag == "!--"} {
  330.                 set tmp "<!--"
  331.                 if {[catch {search -s -f 1 -m 0 -r 1 -i 1 -- "-->" $pos} pos2]} {
  332.                     set pos2 "[minPos] $endPos"
  333.                 } elseif {[regexp -nocase "\[ \t\r\n\]*#([join ${html::formattingStyles} |])\[ \t\r\n\]*-->" [getText $pos [lindex $pos2 1]] "" form]} {
  334.                     if {[catch {search -s -f 1 -m 0 -r 1 -i 1 "<!--\[ \t\r\n\]*/#$form\[ \t\r\n\]*-->" $pos2} pos2]} {
  335.                         set pos2 "[minPos] [maxPos]"
  336.                     }
  337.                 }
  338.                 append text $ind$tmp[getText $pos [set pos [lindex $pos2 1]]]
  339.                 set tmp ""
  340.                 set cr 0
  341.             }
  342.             # The contents of these tags should be left untouched
  343.             if {[lsearch {SCRIPT STYLE PRE} $tag] >= 0} {
  344.                 set tag /$tag
  345.                 regsub -all "" $tmp " " tmp
  346.                 if {[catch {search -s -f 1 -m 0 -r 1 -i 1 "<$tag>" $pos} pos2]} {set pos2 "[minPos] $endPos"}
  347.                 append text $ind$tmp[getText $pos [set pos [lindex $pos2 1]]]
  348.                 set tmp ""
  349.                 set cr 0
  350.             }
  351.         } else {
  352.             append tmp <$wholeTag>
  353.         }
  354.         # cr or blank line after tag
  355.         if {[lsearch $aft $tag] >= 0} {
  356.             if {[string index $tag 0] == "/" && [lsearch $indList [string range $tag 1 end]] >= 0} {
  357.                 set ind [html::ReduceIndent $ind]
  358.                 set fillColumn [expr {$oldfillColumn - [string length [text::maxSpaceForm $ind]]}]
  359.             }
  360.             regsub -all "\[ \t\]*$crChar\[ \t\]*" [string trim $tmp] " " tmp
  361.             set tmp [string trimright [breakIntoLines $tmp]]
  362.             regsub -all "" $tmp " " tmp
  363.             regsub -all "\r" $tmp "\r$ind" tmp
  364.             if {![is::Whitespace $tmp]} {set cr 0; append text $ind}
  365.             append text $tmp
  366.             set bla [lsearch $blAft $tag]
  367.             if {[lsearch $indList $tag] >= 0} {
  368.                 set ind [html::IncreaseIndent $ind]
  369.                 set fillColumn [expr {$oldfillColumn - [string length [text::maxSpaceForm $ind]]}]
  370.             }
  371.             if {$cr == 0} {
  372.                 append text \r
  373.                 incr cr
  374.                 if {$cr == 1 && $bla >= 0} {append text $ind}
  375.             }
  376.             if {$bla >= 0 && $cr < 2} {append text \r; incr cr}
  377.             set tmp ""
  378.         }
  379.     }
  380.     # Add what's left
  381.     if {$tmp != "" || [pos::compare $pos < $endPos]} {
  382.         if {[pos::compare $pos < $endPos]} {append tmp [getText $pos $endPos]}
  383.         regsub -all "\[ \t\]*$crChar\[ \t\]*" [string trim $tmp] " " tmp
  384.         set tmp [string trimright [breakIntoLines $tmp]]
  385.         regsub -all "" $tmp " " tmp
  386.         regsub -all "\r" $tmp "\r$ind" tmp
  387.         if {![is::Whitespace $tmp]} {append text $ind}
  388.         append text $tmp
  389.         if {![is::Whitespace $tmp]} {append text \r}
  390.     }
  391.     replaceText $startPos $endPos $text
  392.     # Go back to previous position.
  393.     if { $remember_str != "" } {
  394.         regexp -indices $remember_str [getText $startPos [set end [getPos]]] wholematch
  395.         if {[info exists wholematch]} {
  396.             set p [pos::math $startPos + 1 + [lindex $wholematch 1]]
  397.         } else {
  398.             set p $end
  399.         }
  400.         if {[pos::compare $p >= $end]} {
  401.             goto [pos::math $end - 1]
  402.         } else {
  403.             goto $p
  404.         }
  405.     }
  406. }
  407.