소스 검색

* update spellcheck unit with new stuff

git-svn-id: trunk@10274 -
Almindor 17 년 전
부모
커밋
acc4d25f75
1개의 변경된 파일196개의 추가작업 그리고 28개의 파일을 삭제
  1. 196 28
      packages/aspell/src/spellcheck.pp

+ 196 - 28
packages/aspell/src/spellcheck.pp

@@ -8,34 +8,84 @@ unit SpellCheck;
 interface
 
 uses
-  SysUtils, Aspell;
+  SysUtils, Classes, Aspell;
 
 type
   TSuggestionArray = array of string;
   
-  { TSpellCheck }
+  TWordError = record
+    Word: string; // the word itself
+    Pos: LongWord; // word position in line
+    Length: LongWord; // word length
+    Suggestions: TSuggestionArray; // suggestions for the given word
+  end;
+  
+  TLineErrors = array of TWordError;
+  TLineErrorsArray = array of TLineErrors;
+
+  { TSpeller }
+  { Abstract ancestor, don't use directly }
 
-  TSpellCheck = class
+  TSpeller = class // abstract class, basis for all checkers
    protected
-    FSpeller: PAspellSpeller;
     FMode: string;
     FEncoding: string;
     FLanguage: string;
     procedure SetEncoding(const AValue: string);
     procedure SetLanguage(const AValue: string);
     procedure SetMode(const AValue: string);
-    procedure CreateSpeller;
-    procedure FreeSpeller;
+    procedure CreateSpeller; virtual; abstract;
+    procedure FreeSpeller; virtual; abstract;
    public
     constructor Create;
     destructor Destroy; override;
-    function SpellCheck(const Word: string): TSuggestionArray;
    public
     property Mode: string read FMode write SetMode;
     property Encoding: string read FEncoding write SetEncoding;
     property Language: string read FLanguage write SetLanguage;
   end;
 
+  { TWordSpeller }
+  { Basic spelling class for spelling single words without context }
+  
+  TWordSpeller = class(TSpeller) // class for simple per-word checking
+   private
+    FSpeller: PAspellSpeller;
+   protected
+    procedure CreateSpeller; override;
+    procedure FreeSpeller; override;
+   public
+    function SpellCheck(const Word: string): TSuggestionArray; // use to check single words, parsed out by you
+  end;
+  
+  { TDocumentSpeller }
+  { This speller is used to spellcheck lines or even whole documents.
+    It is usefull when different mode (like "tex") is used so you can pass
+    everything to aspell and let it take care of the context }
+
+  TDocumentSpeller = class(TWordSpeller)
+   private
+    FChecker: PAspellDocumentChecker;
+    FLineErrors: TLineErrorsArray;
+    FNameSuggestions: Boolean;
+    function GetLineErrors(i: Integer): TLineErrors;
+    function GetLineErrorsCount: Integer;
+   protected
+    procedure CreateSpeller; override;
+    procedure FreeSpeller; override;
+    procedure DoNameSuggestions(const Word: string; var aWordError: TWordError);
+   public
+    constructor Create;
+    function CheckLine(const aLine: string): TLineErrors;
+    function CheckDocument(const FileName: string): Integer; // returns number of spelling errors found or -1 for error
+    function CheckDocument(aStringList: TStringList): Integer; // returns number of spelling errors found or -1 for error
+    procedure Reset;
+   public
+    property LineErrors[i: Integer]: TLineErrors read GetLineErrors;
+    property LineErrorsCount: Integer read GetLineErrorsCount;
+    property NameSuggestions: Boolean read FNameSuggestions write FNameSuggestions;
+  end;
+
 implementation
 
 const
@@ -50,27 +100,43 @@ begin
     Result := DEFAULT_LANGUAGE;
 end;
 
-{ TSpellCheck }
+{ TSpeller }
 
-procedure TSpellCheck.SetEncoding(const AValue: string);
+procedure TSpeller.SetEncoding(const AValue: string);
 begin
   FEncoding := aValue;
   CreateSpeller;
 end;
 
-procedure TSpellCheck.SetLanguage(const AValue: string);
+procedure TSpeller.SetLanguage(const AValue: string);
 begin
   FLanguage := aValue;
   CreateSpeller;
 end;
 
-procedure TSpellCheck.SetMode(const AValue: string);
+procedure TSpeller.SetMode(const AValue: string);
 begin
   FMode := aValue;
   CreateSpeller;
 end;
 
-procedure TSpellCheck.CreateSpeller;
+constructor TSpeller.Create;
+begin
+  FEncoding := DEFAULT_ENCODING;
+  FLanguage := GetDefaultLanguage;
+  FMode := DEFAULT_MODE;
+
+  CreateSpeller;
+end;
+
+destructor TSpeller.Destroy;
+begin
+  FreeSpeller;
+end;
+
+{ TWordSpeller }
+
+procedure TWordSpeller.CreateSpeller;
 var
   Config: Paspellconfig;
   Error: Paspellcanhaveerror;
@@ -95,7 +161,7 @@ begin
     FSpeller := to_aspell_speller(Error);
 end;
 
-procedure TSpellCheck.FreeSpeller;
+procedure TWordSpeller.FreeSpeller;
 begin
   if Assigned(FSpeller) then begin
     delete_aspell_speller(FSpeller);
@@ -103,21 +169,7 @@ begin
   end;
 end;
 
-constructor TSpellCheck.Create;
-begin
-  FEncoding := DEFAULT_ENCODING;
-  FLanguage := GetDefaultLanguage;
-  FMode := DEFAULT_MODE;
-
-  CreateSpeller;
-end;
-
-destructor TSpellCheck.Destroy;
-begin
-  FreeSpeller;
-end;
-
-function TSpellCheck.SpellCheck(const Word: string): TSuggestionArray;
+function TWordSpeller.SpellCheck(const Word: string): TSuggestionArray;
 var
   sgs: Paspellwordlist;
   elm: Paspellstringenumeration;
@@ -148,5 +200,121 @@ begin
   end;
 end;
 
+{ TDocumentSpeller }
+
+function TDocumentSpeller.GetLineErrors(i: Integer): TLineErrors;
+begin
+  Result := FLineErrors[i];
+end;
+
+function TDocumentSpeller.GetLineErrorsCount: Integer;
+begin
+  Result := Length(FLineErrors);
+end;
+
+procedure TDocumentSpeller.CreateSpeller;
+var
+  Error: PAspellCanHaveError;
+begin
+  inherited CreateSpeller;
+  
+  Error := new_aspell_document_checker(FSpeller);
+
+  if aspell_error_number(Error) <> 0 then
+    raise Exception.Create('Error on checker creation: ' + aspell_error_message(Error))
+  else
+    FChecker := to_aspell_document_checker(Error);
+end;
+
+procedure TDocumentSpeller.FreeSpeller;
+begin
+  if Assigned(FChecker) then begin
+    delete_aspell_document_checker(FChecker);
+    FChecker := nil;
+  end;
+
+  inherited FreeSpeller;
+end;
+
+procedure TDocumentSpeller.DoNameSuggestions(const Word: string;
+  var aWordError: TWordError);
+begin
+  aWordError.Suggestions := SpellCheck(Word);
+end;
+
+constructor TDocumentSpeller.Create;
+begin
+  inherited Create;
+  
+  FNameSuggestions := True;
+end;
+
+function TDocumentSpeller.CheckLine(const aLine: string): TLineErrors;
+const
+  CHUNK_SIZE = 10;
+var
+  i, Count: Integer;
+  Token: AspellToken;
+begin
+  aspell_document_checker_process(FChecker, pChar(aLine), Length(aLine));
+
+  SetLength(Result, CHUNK_SIZE);
+  i := 0;
+  Count := 0;
+  repeat
+    Token := aspell_document_checker_next_misspelling(FChecker);
+
+    if Token.len > 0 then begin
+      if Length(Result) <= i then
+        SetLength(Result, Length(Result) + CHUNK_SIZE);
+
+      Result[i].Word := Copy(aLine, Token.offset + 1, Token.len);
+      Result[i].Pos := Token.offset + 1; // C goes from 0, we go from 1
+      Result[i].Length := Token.len;
+
+      if FNameSuggestions then
+        DoNameSuggestions(Copy(aLine, Token.offset + 1, Token.len), Result[i]);
+        
+      Inc(Count);
+    end;
+
+    Inc(i);
+  until Token.len = 0;
+  
+  SetLength(Result, Count);
+end;
+
+function TDocumentSpeller.CheckDocument(const FileName: string): Integer;
+var
+  s: TStringList;
+begin
+  Result := 0;
+  if FileExists(FileName) then try
+    s := TStringList.Create;
+    s.LoadFromFile(FileName);
+    Result := CheckDocument(s);
+  finally
+    s.Free;
+  end;
+end;
+
+function TDocumentSpeller.CheckDocument(aStringList: TStringList): Integer;
+var
+  i: Integer;
+begin
+  Result := 0;
+  SetLength(FLineErrors, aStringList.Count);
+
+  for i := 0 to aStringList.Count - 1 do begin
+    FLineErrors[i] := CheckLine(aStringList[i]);
+    Inc(Result, Length(FLineErrors[i]));
+  end;
+end;
+
+procedure TDocumentSpeller.Reset;
+begin
+  aspell_document_checker_reset(FChecker);
+end;
+
 end.