#
# Operations for C function/comment manipulation in Text widgets.
#


# Function boundary routines

# Returns 1 if index points to a function's beginning, 0 otherwise.
proc th_fn_check {w index} {
# First char must be legit in a type definition
  if {![regexp {[A-Z_a-z\*\(\)]} [$w get $index]]} {return 0}

# Last char before newline must be brace or whitespace or / (as in */)
  if {![regexp {[/\{\}\ \t\n]} [$w get "$index -2c"]] &&
    !([$w get "$index -2c"] == "\n")} {return 0}

# Must not be in a comment
  set comstart [th_ccomment_begin $w $index]
  set comend [th_ccomment_end $w $index]
  if {($comstart == "") && ($comend != "")} {return 0}
  if {($comstart != "") && ($comend != "") &&
    [$w compare $comend < $comstart]} {return 0}

# Must have paren before brace
  set ob "\{"  ; set cb "\}"
  set op "\("  ; set cp "\)"
  set nextparen [$w search -forward -- $op $index end]
  set nextbrace [$w search -forward -- $ob $index end]
  if {($nextparen == "") || ($nextbrace == "")} {return 0}
  if {[$w compare $nextparen > $nextbrace]} {return 0}

# May not have semicolon before the first paren
  set nextsemi [$w search -forward -- ";" $index $nextparen]
  if {($nextsemi != "") && [$w compare $nextsemi < $nextparen]} {return 0}

  return 1
}

# The following procedures operate on C functions, returning an index or ""
# if unsuccessful.

proc th_fn_begin {w index} {
  scan [$w index $index] "%dx%d" i dummy
  while {$i != 1} {
    if {[th_fn_check $w "$i.0"]} {  return "$i.0"  }
    incr i -1
  }
  return ""
}

proc th_fn_end {w index} {
  if {[set begin [th_fn_begin $w $index]] == ""} {return ""}
  set ob "\{"  ; set cb "\}"
  set body_start [$w search -forward -- $ob $begin end]
  if {[set end [th_Text_right_exp $w $body_start $ob $cb 1]] == ""} {return ""}
  return $end
}

proc th_fn_next {w index} {
  set index "$index linestart +1 lines"
  while {![$w compare $index == end]} {
    if {[th_fn_check $w $index]} {  return $index  }
    set index [$w index "$index +1 lines"]
  }
  return ""
}

proc th_fn_prev {w index} {
  if {[set begin [th_fn_begin $w $index]] == ""} {return ""}
  if {[$w compare [set begin [th_fn_begin $w $index]] != $index]} {
    return $begin
  } else {return [th_fn_begin $w "$index -1c"]
}}


# Comment boundary routines
# Like the function routines above, but these work on C comments.

proc th_ccomment_begin {w index} {
  return [$w search -backward -- "/*" "$index +3c" 1.0]
}

proc th_ccomment_end {w index} {
  set i [$w search -forward -- "*/" "$index -2c" end]
  if {$i == ""} {return ""} else {return "$i +2c"}
}

proc th_ccomment_next {w index} {
  return [$w search -forward -- "/*" "$index +1c" end]
}

proc th_ccomment_prev {w index} {
  if {[$w compare [set begin [th_ccomment_begin $w $index]] != $index]} {
    return $begin 
  } else {return [th_ccomment_begin $w "$index -1c"]
}}


# Adjusts selected region to fit in $length columns, so that no lines wrap
# If unspecified, length defaults to window width.
proc th_ccomment_format {w start end {length ""}} {
  if {[catch {$w index sel.first}]} {
    set selected 0
    if {[set start [th_ccomment_begin $w insert]] == ""} {bell ; return}
    if {[$w compare [set end [th_ccomment_end $w $start]] < insert]} {bell ; return}
  } else {
    set start sel.first ; set end sel.last
    set selected 1
  }
  set chars [$w get $start $end]
  $w mark set th_ccomment_end $end ; $w mark gravity th_ccomment_end right
  if {($length == "")} {set length [$w cget -width]}

  set comstart "[$w get $start [$w search -forward -- " " $start end]] "
  set l [string length $comstart]
  set spaces ""
  for {set i 0} {$i < $l} {incr i} {append spaces " "}
  $w delete $start "$start +$l c"
  for {set i "$start +1l linestart"} {[$w compare $i < th_ccomment_end]} \
	{set i [$w index "$i +1l"]} {
    while {[$w get $i] == " "} {$w delete $i}}
  th_Text_format $w $start th_ccomment_end [expr $length - $l] 0
  th_Text_add_prefix $w "$start +1l" th_ccomment_end $spaces
  $w insert "$start linestart" $comstart
  $w mark set $start "$start linestart"

  if $selected {set end sel.last} else {set end [th_ccomment_end $w $start]}
  set e [$w index th_ccomment_end] ; $w mark unset th_ccomment_end
  th_Text_undoable_replace $w $start $e $chars "Reformat"
}

