#!/usr/bin/env ruby -w
# encoding: UTF-8
#
# = KateSyntax.rb -- The TaskJuggler III Project Management Software
#
# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014
#               by Chris Schlaeger <cs@taskjuggler.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#

require 'taskjuggler/SyntaxReference'

class TaskJuggler

  # This class is a generator for Kate (http://kate-editor.org/) TaskJuggler syntax
  # highlighting files.
  class KateSyntax

    # Create a generator object.
    def initialize
      # Create a syntax reference for all current keywords.
      @reference = SyntaxReference.new(nil, true)

      @properties = []
      @attributes = []

      @reference.keywords.each_value do |kw|
        if kw.isProperty?
          @properties << kw
        else
          @attributes << kw
        end
      end

      @file = nil
    end

    # Generate the Kate syntax file into _file_.
    def generate(file)
      @file = File.open(file, 'w')

      header
      keywords
      contexts
      highlights
      footer

      @file.close
    end

    private

    def header
      # Generate the header section. Mostly consists of comments and
      # description attributes
      @file.write <<"EOT"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "language.dtd">
<language
  name="TaskJuggler"
  version="3.0" kateversion="2.1"
  section="Scripts" extensions="*.tjp;*.tji"
  author="TaskJuggler Developers &lt;taskjuggler-devel@googlegroups.com&gt;">
<highlighting>
<!-- This file was automatically generated by KateSyntax.rb -->

EOT
    end

    def footer
      # Generate the footer section. Mostly consists of closing tags.
      @file.write <<"EOT"
</highlighting>
<general>
  <comments>
    <comment name="singleLine" start="#"/>
    <comment name="singleLine" start="//"/>
    <comment name="multiLine" start="/*" end="*/"/>
  </comments>
  <keywords casesensitive="0"/>
</general>
</language>
EOT
    end

    def contexts
      @file.write <<'EOT'
<contexts>
  <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
    <!-- Date(+Time) values -->
    <RegExpr attribute="Date" String="\d{4}-\d{2}-\d{2}\S*"/>
    <!-- Time values -->
    <RegExpr attribute="Time" String="\d{1-2}:\d{2}(:\d{2}|)"/>
    <!-- Duration specifications -->
    <RegExpr attribute="Duration" String="\d(min|h|d|w|m|y)"/>
    <!-- keywords and other builtins -->
    <keyword attribute="Builtin Function" String="builtinfuncs" context="#stay"/>
    <keyword attribute="Keyword" String="keywords" context="#stay"/>
    <keyword attribute="Data Types" String="types" context="#stay"/>
    <!-- Include -->
    <RegExpr attribute="Include" String="include.*$"/>
    <!-- Arguments -->
    <RegExpr attribute="Arguments" String="\{.*}$"/>
    <!-- region (brace) folding -->
    <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace2"/>
    <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace2"/>
    <DetectChar attribute="Symbol" context="#stay" char="[" beginRegion="Brace1"/>
    <DetectChar attribute="Symbol" context="#stay" char="]" endRegion="Brace1"/>
    <!-- number hilighting -->
    <Float attribute="Float" context="#stay">
      <AnyChar String="fF" attribute="Float" context="#stay"/>
    </Float>
    <Int attribute="Decimal" context="#stay">
      <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/>
      <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/>
      <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/>
      <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/>
      <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/>
      <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/>
      <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/>
      <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/>
    </Int>
    <!-- strings -->
    <DetectChar attribute="String" context="String1" char="'"/>
    <DetectChar attribute="String" context="String2" char='"'/>
    <StringDetect attribute="String" context="String3" String="-8&lt;-"/>
    <!-- comments -->
    <DetectChar attribute="Comment" context="Comment1" char="#"/>
    <Detect2Chars attribute="Comment" context="Comment2" char="/" char1="/"/>
    <Detect2Chars attribute="Comment" context="Comment3" char="/" char1="*"/>
  </context>
  <context attribute="String" lineEndContext="#stay" name="String1">
    <DetectChar attribute="String" context="#pop" char="'"/>
  </context>
  <context attribute="String" lineEndContext="#stay" name="String2">
    <DetectChar attribute="String" context="#pop" char='"'/>
  </context>
  <context attribute="String" lineEndContext="#stay" name="String3">
    <StringDetect attribute="String" context="#pop" String="-&gt;8-"/>
  </context>
  <context attribute="Comment" lineEndContext="#pop" name="Comment1">
    <IncludeRules context="##Alerts"/>
  </context>
  <context attribute="Comment" lineEndContext="#pop" name="Comment2">
    <IncludeRules context="##Alerts"/>
  </context>
  <context attribute="Comment" lineEndContext="#stay" name="Comment3">
    <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/"/>
  </context>
</contexts>

EOT
#syn match tjparg contained /\${.*}/
    end

    def keywords
      @file.puts "<list name=\"keywords\">"
      %w( macro project supplement include supplement ).each do |kw|
	@file.puts "<item> #{kw} </item>"
      end
      @file.puts "</list>"

      # Property keywords
      @file.puts "<list name=\"builtinfuncs\">"
      @properties.each do |kw|
        kw.names.each do |name|
          # Ignore the 'supplement' entries. They are not real properties.
          next if name == 'supplement'
          @file.puts "<item> #{name} </item>"
        end
      end
      @file.puts "</list>"

      # Attribute keywords
      @file.puts "<list name=\"types\">"
      @attributes.each do |kw|
        next if %w( resourcereport taskreport textreport ).include?(kw.keyword)
        single = kw.names.length == 1
        kw.names.each do |name|
          break if [ '%', '(', '~', 'include', 'macro', 'project',
                     'supplement' ].include?(name)
          @file.puts "<item> #{name} </item>"
        end
      end
      @file.puts "</list>"
    end

    def highlights
      @file.write <<'EOT'
<itemDatas>
  <itemData name="Normal Text" defStyleNum="dsNormal"/>
  <itemData name="Keyword" defStyleNum="dsKeyword"/>
  <itemData name="Builtin Function" defStyleNum="dsFunction"/>
  <itemData name="Data Types" defStyleNum="dsDataType"/>
  <itemData name="Decimal" defStyleNum="dsDecVal"/>
  <itemData name="Float" defStyleNum="dsDecVal"/>
  <itemData name="Date" defStyleNum="dsBaseN"/>
  <itemData name="Time" defStyleNum="dsBaseN"/>
  <itemData name="String" defStyleNum="dsString"/>
  <itemData name="String Char" defStyleNum="dsChar"/>
  <itemData name="Comment" defStyleNum="dsComment"/>
  <itemData name="Symbol" defStyleNum="dsNormal"/>
</itemDatas>
EOT
    end

  end

end

