anchor.diff:    ESearchSyntaxError = class( Exception )
     end;
   
  -  TSearchTermCombineMethod = ( cmOr, cmAnd, cmNot );
  -
  -  TSearchTerm = class
  +  TSearchTermCombineMethod =
  +  (
  +    cmOptional,
  +    cmRequired,
  +    cmExcluded
  +  );
  +
  +  TSearchTerm = class(TObject)
  +  public
       Text: string;
  +    Parts: TStringList;
       CombineMethod: TSearchTermCombineMethod;
  -  end;
  -
  -  TTextSearchQuery = class
  -  private
  -    procedure Cleanup;
  +    constructor Create( const TheText: string;
  +                        const TheCombineMethod: TSearchTermCombineMethod );
  +    destructor Destroy; override;
  +  end;
  +
  +
  +  TTextSearchQuery = class(TObject)
     protected
       Terms: TList;
       function GetTerm( Index: longint ): TSearchTerm;
       function GetTermCount: longint;
     public
  -    constructor Create( SearchString: string );
  -    destructor Destroy; override;
  -
  -    property Term[ Index: longint ]: TSearchTerm read GetTerm;
  -    property TermCount: longint read GetTermCount;
  -  end;
  -
  -Implementation
  +    constructor Create( const SearchString: string );
  +    destructor  Destroy; override;
  +    property    Term[ Index: longint ]: TSearchTerm read GetTerm;
  +    property    TermCount: longint read GetTermCount;
  +  end;
  +
  +
  +implementation
   
   uses
  -  nvUtilities;
  -//  ACLStringUtility, ACLUtility, Dialogs;
  -
  -constructor TTextSearchQuery.Create( SearchString: string );
  +  nvUtilities
  +  ,ACLStringUtility
  +  ;
  +
  +
  +const
  +  QueryErrorMissingWord1 = 'No search word given after';
  +  QueryErrorMissingWord2 = ' before ';
  +
  +
  +constructor TTextSearchQuery.Create( const SearchString: string );
   var
  -  SearchWord: string;
  -  RemainingSearchString: string;
  +  TermText: string;
     CombineMethod: TSearchTermCombineMethod;
     lTerm: TSearchTerm;
  +  tmpTerms : TStringList;
  +  i : integer;
   begin
     inherited Create;
     Terms := TList.Create;
     try
  -    RemainingSearchString := Uppercase( SearchString );
  -    while RemainingSearchString <> '' do
  -    begin
  -      SearchWord := ExtractNextValue( RemainingSearchString, ' ' );
  -
  -      // Check for modifiers + (word must be matched)
  -      // and - (word must not be matched)
  -      case SearchWord[ 1 ] of
  +    tmpTerms := TStringList.Create;
  +    StrExtractStringsQuoted(tmpTerms, SearchString);
  +
  +    for i := 0 to tmpTerms.count-1 do
  +    begin
  +      TermText := tmpTerms[i];
  +
  +      // Check for modifiers:
  +      //  + word must be matched
  +      //  - word must not be matched
  +      case TermText[ 1 ] of
          '+':
  -         CombineMethod := cmAnd;
  +         CombineMethod := cmRequired;
          '-':
  -         CombineMethod := cmNot;
  +         CombineMethod := cmExcluded;
          else
  -         CombineMethod := cmOr;
  -      end;
  -      if CombineMethod <> cmOr then
  +         CombineMethod := cmOptional;
  +      end;
  +
  +      if CombineMethod <> cmOptional then
         begin
           // delete + or -
  -        if Length( SearchWord ) = 1 then
  -          raise ESearchSyntaxError.Create( 'No search word given after "'
  -                                           + SearchWord + '" before "'
  -                                           + RemainingSearchString
  -                                           + '"' );
  -        Delete( SearchWord, 1, 1 );
  -      end;
  -
  -      lTerm := TSearchTerm.Create;
  -      lTerm.Text := SearchWord;
  -      lTerm.CombineMethod := CombineMethod;
  +        if Length( TermText ) = 1 then
  +          if (i < tmpTerms.count-1) then
  +            raise ESearchSyntaxError.Create( QueryErrorMissingWord1
  +                                             + StrInDoubleQuotes(TermText)
  +                                             + QueryErrorMissingWord2
  +                                             + StrInDoubleQuotes(tmpTerms[i+1]) )
  +          else
  +            raise ESearchSyntaxError.Create( QueryErrorMissingWord1
  +                                             + StrInDoubleQuotes(TermText));
  +        Delete( TermText, 1, 1 );
  +      end;
  +
  +      lTerm := TSearchTerm.Create( TermText,
  +                                  CombineMethod );
         Terms.Add( lTerm );
       end;
  +    tmpTerms.Free;
     except
         while Terms.Count > 0 do
         begin
  
positive.code: {$mode objfpc}{$H+}
  
  // NewView - a new OS/2 Help Viewer
  // Copyright 2001 Aaron Lawrence (aaronl at consultant dot com)
  // This software is released under the Gnu Public License - see readme.txt
  
  Interface
  
negative.code: 
  destructor TTextSearchQuery.Destroy;
  begin
    DestroyListObjects( Terms );
    Terms.Free;
    inherited Destroy;
  end;
  
  function TTextSearchQuery.GetTerm( index: longint ): TSearchTerm;
  begin
    Result := TSearchTerm(Terms[ Index ]);
  end;
  
  function TTextSearchQuery.GetTermCount: longint;
  begin
    Result := Terms.Count;
  end;
  
  constructor TSearchTerm.Create( const TheText: string;
                                  const TheCombineMethod: TSearchTermCombineMethod );
  var
    TermParseIndex: longint;
    TermChar: char;
    TermPart: string;
  begin
    Parts := TStringList.Create;
  
    Text := TheText;
    CombineMethod := TheCombineMethod;
  
    // Break out each part of the term as IPF does:
    // consecutive alphanumeric chars become a "word"
    // but each symbol is a separate word, and symbols break
    // up alphanumerics into multiple words. e.g.
    // CAKE_SAUSAGE becomes three words in IPF,
    // one each for "CAKE" "_" and "SAUSAGE"
  
    TermParseIndex := 1;
    while TermParseIndex <= Length( Text ) do
    begin
      // collect alphanumeric chars
      TermPart := '';
      while TermParseIndex <= Length( Text ) do
      begin
        TermChar := Text[ TermParseIndex ];
        if  (    IsAlpha( TermChar )
              or IsDigit( TermChar ) ) then
        begin
          // alpha numeric, collect it
          TermPart := TermPart + TermChar;
          inc( TermParseIndex );
        end
        else
        begin
          // not alpha numeric, so stop
          break;
        end;
      end;
      if Length( TermPart ) > 0 then
      begin
        Parts.Add( TermPart ); // add collected alphanumeric part
      end;
  
      if TermParseIndex <= Length( Text ) then
      begin
        // must be a symbol,
        // each symbol (excluding space) is an individual item
        if Text[ TermParseIndex ] <> ' ' then
          Parts.Add( Text[ TermParseIndex ] );
        inc( TermParseIndex );
      end;
  
    end;
  
  end;
  
  destructor TSearchTerm.Destroy;
  begin
    Parts.Free;
    inherited Destroy;
  end;
  
  
  end.
  