(*------------------------------------------------------------------------*)

IMPLEMENTATION MODULE HighSearch;

(*------------------------------------------------------------------------*)

FROM SYSTEM IMPORT BITSPERWORD; (* debug *)

FROM CharCodes IMPORT NewLineCh, CapitalCh;
IMPORT Universe;

FROM Strings IMPORT FindC;

FROM SearchBase IMPORT
   FieldText, PatternMeta, ReplaceMeta,
   FieldType, PatternType, ReplaceType,
   FieldSet, PatternSet,
   Case, Coerce, Direction,
   PatternPtr, ReplacePtr;

IMPORT Buffers, Windows, DisplayPosition, Commands;

(* :*)
IMPORT Debug;
(*: *)

(*------------------------------------------------------------------------*)

VAR
   buffer   : Buffers.BufferP;
   buffPos  : CARDINAL;
   ch       : CHAR;

(* buffer^.Array.Data^ [buffPos] = ch. HighCh highlights ch *)

(*------------------------------------------------------------------------*)

PROCEDURE InitCh (mBuffer: Buffers.BufferP; marker: Buffers.MarkerP);

   BEGIN

(* :*
Debug.WriteS ("InitCh: (");
Debug.WriteC (mBuffer^.Before.Start);
Debug.WriteS ("..");
Debug.WriteC (mBuffer^.Before.End);
Debug.WriteS (", ");
Debug.WriteC (mBuffer^.After.Start);
Debug.WriteS ("..");
Debug.WriteC (mBuffer^.After.End);
Debug.WriteS ("), ");
Debug.WriteC (marker^.Where);
Debug.Writeln;
*: *)

      buffer := mBuffer;
      buffPos := marker^.Where;
      NextCh;
   END InitCh;

(*------------------------------------------------------------------------*)

PROCEDURE NextCh;

   VAR marker: Buffers.MarkerP;

   BEGIN
      IF buffPos >= buffer^.After.End THEN
         ch := 0C;
      ELSE
         INC (buffPos);
         IF buffPos = buffer^.Before.End THEN
            buffPos := buffer^.After.Start
         END (* if *);
         ch := buffer^.Array.Data^ [buffPos];
         IF ch = Universe.MarkerFlagByte THEN
            marker := NIL;
            IF Buffers.MarkerAt (buffer, buffPos, marker) THEN
               IF marker^.tag = Commands.promptMarkerTag THEN
                  buffPos := buffer^.After.End; (* Speed up subsequent (EOF) calls *)
                  ch := 0C;
               ELSE NextCh
               END (* if *);
            ELSE
               ch := CHAR (BITSET (ch) - {7});
               buffer^.Array.Data^ [buffPos] := ch;
            END (* if *);
         ELSE
            ch := CHAR (BITSET (ch) - {7});
            buffer^.Array.Data^ [buffPos] := ch;
         END (* if *);
      END (* if *);

(* :*
Debug.WriteS ("NextCh: ch := ");
Debug.WriteChar (ch);
Debug.Writeln;
*: *)

   END NextCh;

(*------------------------------------------------------------------------*)

PROCEDURE HighCh;

   BEGIN
      buffer^.Array.Data^ [buffPos] := CHAR (BITSET (ch) + {7});

(* :*
Debug.WriteS ("HighCh: ch = ");
Debug.WriteChar (ch);
Debug.Writeln;
*: *)

   END HighCh;

(*------------------------------------------------------------------------*)

PROCEDURE HeatCh;

   BEGIN
      HighCh;
      NextCh;
   END HeatCh;

(*------------------------------------------------------------------------*)

PROCEDURE HighCaseFlip;

   BEGIN
      WHILE ch = '`' DO HeatCh END;
   END HighCaseFlip;

(*------------------------------------------------------------------------*)

PROCEDURE HighPattern (buffer: Buffers.BufferP; marker: Buffers.MarkerP);

   (*------------------------------------------------------------------------*)
   
   PROCEDURE HighPatternAlt;
   
      BEGIN
         WHILE NOT ((ch = ']') OR (ch = 0C)) DO HighPatternElt END;
      END HighPatternAlt;
   
   (*------------------------------------------------------------------------*)
   
   PROCEDURE HighPatternSeq;
   
      BEGIN
(*
         WHILE NOT ((ch = ')') OR (ch = 0C)) DO
*)
         WHILE NOT (ch = 0C) DO HighPatternElt END;
      END HighPatternSeq;
   
   (*------------------------------------------------------------------------*)
   
   PROCEDURE HighPatternElt;
   
      BEGIN
         HighCaseFlip;

         CASE ch OF
           0C:
   
         | '~': HeatCh; HighPatternElt;
(*
         | '%': HeatCh; HighField;
*)
         | '*': HeatCh; HighPatternElt;
         | '^': HeatCh; HighPatternElt;
   
         | '[':
            HeatCh;
            HighPatternAlt;
            IF ch = ']' THEN HeatCh END;
(*
         | '(':
            HeatCh;
            HighPatternSeq;
            IF ch = ')' THEN HeatCh END;
*)
   
         | '.', '@', '#': HeatCh;
   
         ELSE
            HighChar;
            IF ch = '-' THEN
               HeatCh;
               HighChar;
            END (* if *);
            
         END (* case *);
      END HighPatternElt;
   
   (*------------------------------------------------------------------------*)

   BEGIN
      InitCh (buffer, marker);
      HighPatternSeq;
   END HighPattern;

(*------------------------------------------------------------------------*)

PROCEDURE HighChar;

   (*---------------------------------------------------------------------*)
   
   PROCEDURE BasicCh;
   
      BEGIN
         IF ch = '|' THEN
            HeatCh;
            ControlCh;
         ELSIF ch = '\' THEN
            HeatCh;
            NextCh;
         ELSIF ch = '$' THEN HeatCh
         ELSE NextCh
         END (* if *);
      END BasicCh;

   (*---------------------------------------------------------------------*)
   
   PROCEDURE ControlCh;
   
      BEGIN
         ch := CapitalCh (ch);
         IF ch = 0C THEN RETURN (* Error *)
         ELSIF (ch = '@') OR (('A' <= ch) AND (ch <= 'Z')) THEN (* control *)
         ELSIF ch = '?' THEN (* control (delete) *)
         ELSE (* non-control *)
         END (* if *);
         NextCh;
      END ControlCh;

   (*---------------------------------------------------------------------*)
   
   BEGIN
      IF ch = 0C THEN RETURN (* Error *)
      ELSIF ch = '|' THEN
         HeatCh;
         IF ch = '!' THEN
            HeatCh;
            BasicCh;
         ELSE ControlCh
         END (* if *);
      ELSE BasicCh
      END (* if *);
   END HighChar;

(*------------------------------------------------------------------------*)

PROCEDURE HighField;

   VAR
      i        : CARDINAL;
      varBuff  : ARRAY [0..20] OF CHAR;

   BEGIN
      IF ch = '+' THEN HeatCh
      ELSIF ch = '-' THEN HeatCh
      END (* if *);

      IF ch = '&' THEN
         HeatCh;
         RETURN
      END (* if *);

      i := 0;
      varBuff := FieldText;
      IF FindC (varBuff, ch, i) THEN HeatCh END;

      WHILE ('0' <= ch) AND (ch <= '9') DO HeatCh END;
         
   END HighField;

(*------------------------------------------------------------------------*)

PROCEDURE HighReplace (buffer: Buffers.BufferP; marker: Buffers.MarkerP);

   (*------------------------------------------------------------------------*)

   PROCEDURE HighReplaceSeq;
   
      BEGIN
         WHILE NOT (ch = 0C) DO HighReplaceElt END;
      END HighReplaceSeq;
   
   (*------------------------------------------------------------------------*)
   
   PROCEDURE HighReplaceElt;
   
      BEGIN
         CASE ch OF
           0C:
         | '%': HeatCh; HighField;
         | '&': HeatCh;
         ELSE HighChar;
         END (* case *);
      END HighReplaceElt;
   
   (*------------------------------------------------------------------------*)

   BEGIN
      InitCh (buffer, marker);
      HighReplaceSeq;
   END HighReplace;

(*------------------------------------------------------------------------*)

PROCEDURE ValidateSearch (window: Windows.WindowP; vertical: Windows.Vertical);

   (* A reasonable hook to hang search highlighting off! Should make some
   attemp to keep track of modifications to buffer, and avoid continually
   re-highlighting unaltered text (clear ModifiedF when highlighted ?) *)

   VAR marker: Buffers.MarkerP;

   BEGIN

(* :*
Debug.WriteS ("ValidateSearch: entry*N");
*: *)

      IF Buffers.ModifiedF IN window^.Buffer^.Status THEN

(* :*
Debug.WriteS ("ValidateSearch: modified ...*N");
*: *)

         marker := window^.Buffer^.Markers;
         WHILE marker^.tag # Commands.promptMarkerTag DO
            marker := marker^.Next;
         END (* while *);
         HighPattern (window^.Buffer, marker);
         REPEAT
            marker := marker^.Next
         UNTIL (marker = NIL) OR (marker^.tag = Commands.promptMarkerTag);
         IF marker # NIL THEN HighReplace (window^.Buffer, marker) END;

         EXCL (window^.Buffer^.Status, Buffers.ModifiedF);
         
      END (* if *);

      DisplayPosition.ValidateRow (window, vertical);

   END ValidateSearch;

(*------------------------------------------------------------------------*)

END HighSearch.

