# cc-mode.tcl - mode for editing cc code
# 
# $Id: cc-mode.tcl,v 1.1.1.1 2000/09/19 10:50:13 diam Exp $ 
# 
# Modif :
# diam (13/01/97) : refonte complète 
# diam (28/08/98) : modif de toute chaine "c++" en "cc" complète 
#                   (correction d'un bug d'index dans alias dynamique)
# diam (26/10/98) : modif pattern et nom du menu de navigation 
# 
######################################################################

global stead

proc ccMain {} {
   
   global stead 
   global MODE EDITPREFS FILENAME
   global TE_MODE_SEARCH_STRING TE_MODE_NAVIG_SHOW_TEXT

   # On utilisera de plus en plus stead(mode) a la place de MODE.
   set stead(mode) $MODE 
   
   if [info exists FILENAME] {
       set stead(fileToCompile) $FILENAME
   } else {
       set stead(fileToCompile) ""
   }

   
   # Quelques préférences globales :
   set EDITPREFS(autobreak)     0
   set EDITPREFS(autoindent)    1          ;# Force l'auto indent
   
   # define the prefix for c mode
   set EDITPREFS(prefix) "// "
   set EDITPREFS(suffix) " //"
   
   
   # Définition du menu de navigation
   set TE_MODE_NAVIG_SHOW_TEXT "methods"
   set an \[a-zA-Z0-9_\]  ;# AlphaNum
   set w "\[ \t\]"  ;# a White space
   # Anciennement :
   # set method "\n\[ \t\]*(public|class|private)\[^\n\]+"
   set method "\n$an*$w*($an+::$an+)$w*\\(\[^\n\]+"
   set TE_MODE_SEARCH_STRING $method
   
   ########################################################################
   # cette variable contient une liste de fichiers à éditer dans ce mode
   if ![info exists stead(modecc,sampleFiles)] {
       set stead(modecc,sampleFiles) {
          /insert/here/you/favorite/list/of
          /cc/modeles.cc
       }
   }
      
   ccDefineAlias
   ccBuildMenus
   mode_cc_defineStyles
} ;# endproc ccMain



########################################################################
# procédure de définition des couleurs des couleurs
########################################################################
proc mode_cc_defineStyles {} {
    global stead
    upvar #0 stead(text) w
    
    # "keylist1"  définit une patterne de reconnaissance des mots clé 
    # "important" (i.e. modif de structure et autre...)
    # 
    xtext_style $w define cc keylist1 {
        -type wordListLight
        -config {-foreground red}
        -values {
            inline public protected private friend
            main class typedef 
            import return catch try throw
            namespace
        }
    }
    
    # "keylist2"  définit une patterne de reconnaissance des mots clé 
    # "courants" du cc :
    # 
    xtext_style $w define cc keylist2 {
        -type wordListLight
        -config {-foreground blue}
        -values {
            object Object extends implements synchronized throw
            if else while do for switch static const template
            new
        }
    }
    
    # "keylist3"  définit une patterne de reconnaissance des mots clé 
    # correspondant à des types ou classes couramment prédéfinis :
    # 
    xtext_style $w define cc keylist3 {
        -type wordListLight
        -config {-foreground blue}
        -values {
            byte short int long real bool float double char boolean 
            String Thread Event
            void null true false
            string list vector deque
            iterator
            cout cin clog
            typename 
        }
    }
    
    # "macro"  style pour les macros courantes du préprocesseur :
    # 
    xtext_style $w define cc macro {
        -type wordListLight
        -config {-foreground red}
        -prePat {(?:^|\w)}
        -postPat {(?:\W)}
        -values {
            #include  
            #if     #else   #endif
            #ifdef  #ifndef #define
        }
    }
    
    
    # Traitement des guillemets
    # 
    xtext_style $w define cc {
        quotes {
            -type regexpLight
            -config {-foreground #700}
            -pat {(".*?")}
        }
        singleQuotes {
            -type regexpLine
            -config {-foreground red -font bold}
            -pat {^(?:[^"]*)(")(?:[^"]*)$}
        }
    }

    # Traitement des stream
    # 
    xtext_style $w define cc stream {
        -type regexpLight
        -config {-foreground #700}
        -pat {(<<|>>)}
    }


    set stead(commentConfig) {-foreground #060}
    # set config  "-background green -foreground red"
    # balance stead /*
    set stead(tagSpec,c_comment) [list  \
        -type    regexpPair             \
        -prePat  {/\*}                  \
        -postPat {\*/}                  \
        -config  $stead(commentConfig)   \
    ]
    xtext_style $w define cc c_comment  $stead(tagSpec,c_comment)
    
    xtext_style $w define cc cc_comment [list \
        -type regexpLight  \
        -pat {//.*$}       \
        -config $stead(commentConfig)
    ]
    
    xtext_style $w enable cc
}


###########################################################################
# aide sur le mode cc :
###########################################################################

proc cc_GetHelp {} {
  set help {
    aide du mode cc prototype :
    
    Les electric alias sont des raccourcis qu'on peut lancer en tapant 
    l'abbreviation suivie de <Meta-Space>
    La liste complète de ces abbréviation est définie dans le sous-menu 
    "alias" du menu Template.
    
    Quelques exemples d'alias prédéfinis :
      bb             Begin Bloc :{..} avec indentation
      for            boucle for
      foreach        boucle foreach
      while          boucle while
      do             boucle while ... do
      if             if
      ife            if ...else 
      switch         switch
    
    Le menu "Template" propose quelques macros prédéfinies. Vous pouvez
    en rajouter d'autres dans le fichier de mode utilisateur (voir menu
    "Help/xxx") 
    
    
    Le menu "objects_methods" permet d'accéder rapidement à une procédure 
    du fichier.
    
    raccourci clavier      résultat
    ^^^^^^^^^^^^^^^^^      ^^^^^^^^
    <meta-1>               insertion d'un séparateur "c-----..."
    <Control-c>            refrechit les couleurs
    
  }
  return $help
}

########################################################################
# ccDefineAlias --
# Aliases for cc mode
# 
proc ccDefineAlias {} {

  global stead MODE 
  
  # Pour simplifier l'écriture des electricAlias : on déclare
  # les variables M, B, E et V au niveau local pour 
  # 
  #      M :  Marque temporaire
  #      B :  début de postcommande 
  #      E :  fin de postcommande
  #      V :  Inclusion d'une variable TCL
  # 
  electricAlias-use8bitsCars

  electricAlias-def ${M}cc ${M}cc ${M}cc
  
  electricAlias-var cc bb "\{" ;# Begin Bloc
  electricAlias-var cc eb "\}" ;# End Bloc
  electricAlias-def ${M}cc \{  "$V{bb}\n${B}i${E}$M\n$V{eb}$M"
  electricAlias-def ${M}cc do  "do $V{bb}\n${B}i${E}$M\n$V{eb} while ($M);$M"
  
  
  ######################################################################
  # Seul ces premier alias sont utile : les autres sont des exemples :
  
  ######################################################################
  
  set t1 "${B}askalias {class name} NAME${E}"
  set t2 "class $V{NAME}$M \{\n[i]$M\n\}; // endclass $V{NAME}\n$M"
  electricAlias-def ${M}cc c $t1$t2
  
  # set tmp "public static void main(String args\[ \]) \{\n[i]$M\n\} // main\n$M"
  # electricAlias-def ${M}cc main $tmp
  
  # fonction  main
  set    tmp {int main(int argc, char* argv\[\]) \{\n[i][setStop]\n\} }
  append tmp { // main\n}
  electricAlias-def ${M}cc main $tmp

  electricAlias-def cc bb "\{\n[i]$M\n\}\n"


  electricAlias-def ${M}cc p "public"
  electricAlias-def ${M}cc pr "private"
  electricAlias-def ${M}cc m "main"
  electricAlias-def     cc v "void"
  
  electricAlias-def ${M}cc S "String"
  electricAlias-def ${M}cc sy "synchronized"

  
  
  electricAlias-def ${M}cc tem "template <class Type>\n"
  
  # structure switch
  set    tmp {switch ([setStop]) \{\n}
  append tmp {[i]case : [setStop]\n}
  append tmp {[i]case : [setStop]\n}
  append tmp {[i]       break;\n}
  append tmp {[i]default : [setStop]\n\} }
  append tmp {\n[setStop]}
  electricAlias-def ${M}cc switch $tmp
  electricAlias-def ${M}cc swi    $tmp

 
  # structure if
  set    tmp {if ([setStop]) \{\n[i][setStop]\n\}}
  append tmp {\n[setStop]}
  electricAlias-def ${M}cc if $tmp
  
  # structure if avec else
  set    tmp {if ([setStop]) \{\n[i][setStop]\n\}}
  append tmp { else \{\n}
  append tmp {[i][setStop]\n}
  append tmp {\}\n[setStop]}
  electricAlias-def ${M}cc ife $tmp
  
  
  # boucle while
  set    tmp {while ([setStop]) \{\n[i][setStop]\n\} }
  append tmp {\n[setStop]}
  electricAlias-def ${M}cc while $tmp
  electricAlias-def ${M}cc w     $tmp
  
  # boucle do
  set    tmp {do \{\n[i][setStop]\n}
  append tmp {\} while ([setStop]);\n}
  append tmp {\n[setStop]}
  electricAlias-def ${M}cc do $tmp
  
  # boucle for
  # set    tmp {for ([setStop];[setStop];[setStop]) \{\n}
  set    tmp {for ([setStop]) \{\n}
  append tmp {[i][setStop]\n}
  append tmp {\}\n[setStop]}
  electricAlias-def ${M}cc for $tmp
  
  # alias foreach = fe
  # Iteration sur une collection par un Iterator
  # 
  set    tmp ""
  append tmp {[askVar "local variable name" VAR_NAME]}
  append tmp {[askVar "local variable type" VAR_TYPE]}
  append tmp {[askVar "collection name" LIST_NAME]}
  append tmp {[askVar "collection type" LIST_TYPE]}
  append tmp {[askVar "iterator name" IT_NAME]}
  append tmp {\n}
  append tmp {// for [getVar VAR_NAME] in [getVar LIST_NAME] do...end\n}
  append tmp {[getVar LIST_TYPE]::iterator [getVar IT_NAME] = }
  append tmp {[getVar LIST_NAME]->begin();\n}
  append tmp {while ( [getVar IT_NAME] != [getVar LIST_NAME]->end() ) \{\n}
  append tmp {[i][getVar VAR_TYPE] [getVar VAR_NAME] = *[getVar IT_NAME]; \n}
  append tmp {[i][setStop]\n}
  append tmp {[i][getVar IT_NAME]++;\n}
  append tmp {\}\n}
  append tmp {[setStop]}
  electricAlias-def ${M}cc fe      $tmp
  electricAlias-def ${M}cc foreach $tmp
    

} ;# endproc ccDefineAlias



##################################################################
# definit le libelle des elements de navigation pour le mode cc
##################################################################

proc te:mode:navig_sort_hook {l} {
  return [lsort -command te:mode:sort_command $l]
}
 
#################################################################
# te:tcl:cmd:search_node - recherche la procedure selectionnee   
#################################################################

proc te:tcl:cmd:search_node {{w .main.t}} {

  set sel [xtext_getSel $w]
  if {"x$sel" == "x"} {
    stead:log -warning "c++ : Please select procedure name"
  } else {
    te:tcl:cmd:go_to_node $sel $w
  }
}

#######################################################################
# te:tcl:cmd:go_to_node - selectionne la procedure $p dans le texte $w

proc te:tcl:cmd:go_to_node {p {w .main.t}} {

  global  auto_index
  set methodPat "\n\[ \t\]*(public|class|private)$p\[ \t\]\[^\n\]*"

  stead:log -text "Searching procedure $p" -title "Tcl"
  set text [$w get 1.0 end]
  if {[regexp -indices "$methodPat" $text n]} {
    stead:log -text "procedure $p found." -title "Tcl"
    $w mark set insert "1.0 + [lindex $n 1] chars"
    catch {$w tag remove sel sel.first sel.last}    
    $w tag add sel "1.0 + [lindex $n 1] chars linestart" \
    "1.0 + [lindex $n 1] chars lineend"
    $w see insert
    xtext_updateFocusIn $w
    # A FAIRE :
    # xtext_applyShortHighlight $w \
    #                           "1.0 + [lindex $n 1] chars" \
    #                           "1.0 + [lindex $n 1] chars lineend"

  } else {
    stead:log -text "procedure $p not found" -title "Tcl"
    set file {}
    catch {set file [lindex $auto_index($p) 1]}
    if {"x$file" != "x"} {
      stead:log "Opening file $file"
      exec $stead(exe) $file -command "te:tcl:cmd:go_to_node $p" &
    }
  }
}
# blink
 
######################################################################
# define the TCL menu:
proc ccBuildMenus {} {
global stead MODE 

  set m [te:mode:make_menu $MODE]
  
  $m add command -label {make} \
        -command {te:cmd:make_file}
        
  $m add separator
  $m add command -label {g++ File} \
        -command {te:cmd:gcc_file}
  $m add command -label {g++ -g File} \
        -command {te:cmd:gcc_debug_file}
  $m add command -label {execute File} \
        -command {te:cmd:exe_file}
  $m add command -label {debug File} \
        -command {te:cmd:debug_file}
        
  $m add separator
  $m add command -label {File to Process. . .} \
        -command {te:cmd:fileToCompile}
       


  $m add command -label {search selected method} \
     -command {te:tcl:cmd:search_node }
     
  $m add separator
  $m add command -label {Refresh Colors} \
     -command {refresh_colors} \
     -accelerator {<Control-c>}
  $m add command -label {Cancel Colors} \
     -command {cancel_colors}
     
  $m add separator
  $m add command -label "Aide du mode $MODE"  \
     -command {stead -r 1  -text [cc_GetHelp] }  
  $m add cascade -label {Files sample}  \
     -menu "$m.files"
     
  menu $m.files
  foreach {file} $stead(modecc,sampleFiles) {
     $m.files add command -label $file \
        -command "exec $stead(exe) $file &"
  } ;# endforeach
  
    
  
  ###########################################################################
  # Création et remplissage de menu template
  ###########################################################################
  
  set m [te:mode:make_menu "templates"]
  
  $m add command -label "Insérer un separateur" \
                        -command {separator 1}
  
  $m add command -label {main...} \
                                -command {electricAlias-wrap main}
  
  $m add separator
  
  $m add command -label {boucle for} \
                                -command {electricAlias-wrap for}
  $m add command -label {boucle foreach} \
                                -command {electricAlias-wrap foreach}
  $m add command -label {boucle while} \
                                -command {electricAlias-wrap while}
  $m add command -label {boucle "do...while"} \
                                -command {electricAlias-wrap do}
  $m add command -label {boucle switch} \
                                -command {electricAlias-wrap switch}
                                
  $m add command -label {if} \
                                -command {electricAlias-wrap if}
  $m add command -label {if..else} \
                                -command {electricAlias-wrap ife}
    
  
  $m add separator
  $m add cascade -label Alias -menu $m.alias
  menu $m.alias
  
  foreach alias [electricAlias-softNames] {
     $m.alias add command \
         -label $alias \
         -command "electricAlias-insert $stead(text) [te:no_interp $alias]"
  }

} ;# endproc ccBuildMenus

###########################################################################
# proc separator {number_of_lines textwidget}
# retourne une chaine correspondant au separateur souhaité 
# 
proc separator {{NbLines 1} } {
   
   insert_fill_lines "/" $NbLines 

} ;# endproc separator


########################################################################
########################################################################
# Autre procédure utiles (a deplacer plus tard)


###########################################################################
# te:cmd:fileToCompile - change le fichier a compiler
#
proc te:cmd:fileToCompile {} {

  global stead
  
   set prompt_result [te:prompt -text "Enter Main File" \
   -default $stead(fileToCompile) -title "cc file to compil"]
   
   if { [file isfile $prompt_result] } {
     set stead(fileToCompile) $prompt_result
   } else {
     stead:log -text "invalid File Name" -title "cc"
   }
}

######################################################################
# te:cmd:gcc_file - gcc file
######################################################################

proc te:cmd:gcc_file {} {
  global stead
  
  if {("x$stead(fileToCompile)" == "x") || [xtext_isDirty .main.t]} {
  
    stead:log -text "please save file first" -title "g++"
    
  } else {

    exec xterm -e g++ -o \
       [file rootname $stead(fileToCompile)].out $stead(fileToCompile) 
  }
}

######################################################################
# te:cmd:make_file - make file
######################################################################

proc te:cmd:make_file {} {
  global stead
  if {("x$stead(fileToCompile)" == "x") || [xtext_isDirty .main.t]} {
    stead:log -text "please save file first" -title "g++"
  } else {

    exec xterm -e make& 
  }
}

######################################################################
# te:cmd:gcc_debug_file - gcc file with symbolic info for debugg
######################################################################

proc te:cmd:gcc_debug_file {} {
  global stead
  if {("x$stead(fileToCompile)" == "x") || [xtext_isDirty .main.t]} {
    stead:log -text "please save file first" -title "g++ -g"
  } else {

    exec xterm -e g++ -g -o \
      [file rootname $stead(fileToCompile)].out $stead(fileToCompile) 
  }
}

######################################################################
# te:cmd:debug_file - debug the executable file with xxgdb
######################################################################

proc te:cmd:debug_file {} {
  global stead
  if {("x$stead(fileToCompile)" == "x") || [xtext_isDirty .main.t]} {
    stead:log -text "please save file first" -title "g++"
  } else {
    if [file exists [file rootname $stead(fileToCompile)].out] {
      exec xxgdb [file rootname $stead(fileToCompile)].out &
      stead:log -text "Running Command" \
               -title "xxgdb [file tail [file rootname $stead(fileToCompile)]]"
    } else {
      stead:log -text "No out File" \
               -title "xxgdb [file tail [file rootname $stead(fileToCompile)]]" 
    }
  }
}
######################################################################
# te:cmd:exe_file -  executes the output file 
######################################################################

proc te:cmd:exe_file {} {
  global stead 
  if {("x$stead(fileToCompile)" == "x") || [xtext_isDirty .main.t]} {
    stead:log -text "please save file first" -title "g++"
  } else {
    if [file exists [file rootname $stead(fileToCompile)].out] {
      exec xterm -e [file rootname $stead(fileToCompile)].out &
    } else {
      stead:log -text "No out File" \
               -title "xxgdb [file tail [file rootname $stead(fileToCompile)]]" 
    }
  }
}



######################################################################
# cleanup for cc mode
######################################################################

proc stead:modeCleanup {} {
    global stead
    upvar 0 stead(text) w
    te:mode:destroy_menu $stead(mode)
    te:mode:destroy_menu templates
    xtext_style $w delete $stead(mode)
} 

ccMain


