lazarus/components/ideintf/texttools.pas

247 lines
7.8 KiB
ObjectPascal

{
*****************************************************************************
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Author: Mattias Gaertner
Abstract:
Interface to various IDE tools manipulating text.
}
unit TextTools;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, System.UITypes,
// LCL
LCLType;
{ Sorting }
type
TSortDirection = (sdAscending, sdDescending);
TSortDomain = (sdWords, sdLines, sdParagraphs);
TShowSortSelectionDialogFunc = function(const TheText: string;
Highlighter: TObject; var SortedText: string): TModalResult;
TSortTextFunc = function(const TheText: string; Direction: TSortDirection;
Domain: TSortDomain; CaseSensitive, IgnoreSpace: boolean): string;
var
ShowSortSelectionDialogFunc: TShowSortSelectionDialogFunc;
SortTextFunc: TSortTextFunc;
{ Regular expressions
This is a simple interface to regular expressions. The syntax is similar
to Perl regular expressions. An illegal pattern will raise an Exception.
Important: These functions are not thread safe!
REMatches - function to test a regular expression.
REVar - function to read the bracket values, found in the last call
of REMatches.
The ModifierStr sets the default values of r.e.syntax modifiers. Modifiers
in r.e. (?ismx-ismx) will replace this default values.
If you try to set unsupported modifier, an exception is raised
Modifier /i - caseinsensitive, initialized from RegExprModifierI
Modifier /s - '.' works as any char (else as [^\n]),
Modifier /g - Turns all operators to non-greedy. e.g. '*' works as '*?',
all '+' as '+?' and so on.
Modifier /m - Treat string as multiple lines. That is, change `^' and `$'
from matching at only the very start or end of the string to
the start or end of any line anywhere within the string.
Examples:
if REMatches('Lazarus','aza') then ...
if REMatches('Lazarus','a(.)a','i') then
s:=REVar(1); // this will be the 'z'
}
var
REException: ExceptClass; // initialized by the IDE
function REMatches(const TheText, RegExpr: string;
const ModifierStr: string = ''; StartPos: integer = 1): boolean;
function REVar(Index: Integer): string; // 1 is the first
procedure REVarPos(Index: Integer; out MatchStart, MatchLength: integer);
function REVarCount: Integer;
function REReplace(const TheText, FindRegExpr, ReplaceRegExpr: string;
UseSubstutition: boolean;
const ModifierStr: string = ''): string;
function RESplit(const TheText, SeparatorRegExpr: string;
const ModifierStr: string = ''): TStrings;
procedure RESplit(const TheText, SeparatorRegExpr: string; Pieces: TStrings;
const ModifierStr: string = '');
// xml paths
function GetPathElement(const Path: string; StartPos: integer;
Stopper: char): string;
// For searching and filtering items in different lists.
function MultiWordSearch(aFilter, aText: string): boolean;
function KeyToQWERTY(var Key: Word; Shift: TShiftState; out aChar: char; aLowerCase: boolean = false): boolean;
//------------------------------------------------------------------------------
// Internal stuff.
type
TREMatchesFunction = function(const TheText, RegExpr, ModifierStr: string;
StartPos: integer): boolean;
TREVarFunction = function(Index: Integer): string;
TREVarPosProcedure = procedure(Index: Integer;
out MatchStart, MatchLength: integer);
TREVarCountFunction = function: Integer;
TREReplaceProcedure = function(const TheText, FindRegExpr,
ReplaceRegExpr: string; UseSubstutition: boolean;
const ModifierStr: string): string;
TRESplitFunction = procedure(const TheText, SeparatorRegExpr: string;
Pieces: TStrings; const ModifierStr: string);
var
REMatchesFunction: TREMatchesFunction = nil; // initialized by the IDE ...
REVarFunction: TREVarFunction = nil;
REVarPosProcedure: TREVarPosProcedure = nil;
REVarCountFunction: TREVarCountFunction = nil;
REReplaceProcedure: TREReplaceProcedure = nil;
RESplitFunction: TRESplitFunction = nil;
implementation
function REMatches(const TheText, RegExpr: string;
const ModifierStr: string; StartPos: integer): boolean;
begin
Result:=REMatchesFunction(TheText,RegExpr,ModifierStr,StartPos);
end;
function REVar(Index: Integer): string;
begin
Result:=REVarFunction(Index);
end;
procedure REVarPos(Index: Integer; out MatchStart, MatchLength: integer);
begin
REVarPosProcedure(Index,MatchStart,MatchLength);
end;
function REVarCount: Integer;
begin
Result:=REVarCountFunction();
end;
function REReplace(const TheText, FindRegExpr, ReplaceRegExpr: string;
UseSubstutition: boolean; const ModifierStr: string): string;
begin
Result:=REReplaceProcedure(TheText,FindRegExpr,ReplaceRegExpr,UseSubstutition,
ModifierStr);
end;
procedure RESplit(const TheText, SeparatorRegExpr: string; Pieces: TStrings;
const ModifierStr: string);
begin
RESplitFunction(TheText,SeparatorRegExpr,Pieces,ModifierStr);
end;
function RESplit(const TheText, SeparatorRegExpr: string;
const ModifierStr: string): TStrings;
begin
Result:=TStringList.Create;
RESplit(TheText,SeparatorRegExpr,Result,ModifierStr);
end;
function GetPathElement(const Path: string; StartPos: integer;
Stopper: char): string;
var
p: LongInt;
begin
p:=StartPos;
while (p<=length(Path)) and (Path[p]<>Stopper) do inc(p);
Result:=copy(Path,StartPos,p-StartPos);
end;
function MultiWordSearch(aFilter, aText: string): boolean;
var
lExpressions: TStringList;
i: Integer;
function FilterByExpression(AFilter: string): boolean;
var
lConditions: TStringList;
i: Integer;
begin
lConditions := TStringList.Create;
try
lConditions.QuoteChar := #0;
lConditions.AddDelimitedText(AFilter, ' ', true);
for i := 0 to lConditions.Count - 1 do
if lConditions[i] <> '' then
begin
if lConditions[i][1] = '!' then
begin
lConditions[i] := RightStr(lConditions[i], length(lConditions[i]) - 1); // delete "!"
if Pos(lConditions[i], aText) > 0 then
exit(true);
end else begin
if Pos(lConditions[i], aText) <= 0 then
exit(true);
end;
end;
Result := false;
finally
FreeAndNil(lConditions);
end;
end;
begin
if aFilter = '' then exit(true);
aText := '"' + lowercase(aText) + '"';
aFilter := lowercase(aFilter);
lExpressions := TStringList.Create;
try
lExpressions.QuoteChar := #0;
lExpressions.AddDelimitedText(aFilter, ',', true);
for i := 0 to lExpressions.Count - 1 do
if lExpressions[i] <> '' then
if not FilterByExpression(lExpressions[i]) then
exit(true);
result := false;
finally
FreeAndNil(lExpressions);
end;
end;
function KeyToQWERTY(var Key: Word; Shift: TShiftState; out aChar: char; aLowerCase: boolean = false): boolean;
begin
aChar := #0;
if Shift = [] then
case Key of
VK_A..VK_Z: aChar := chr(Key + $20); // VK-codes matches ASCII chars
VK_LCL_COMMA: aChar := ',';
VK_OEM_PERIOD: aChar := '.';
end
else if Shift = [ssShift] then
case Key of
VK_A..VK_Z:
if aLowerCase
then aChar := chr(Key + $20) // VK-codes matches ASCII chars
else aChar := chr(Key);
VK_LCL_MINUS: aChar := '_';
VK_1 : aChar := '!';
VK_LCL_QUOTE: aChar := '"';
end;
result := aChar <> #0;
if result then
Key := 0;
end;
end.