File: texttools.pas

package info (click to toggle)
lazarus 4.0%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 275,760 kB
  • sloc: pascal: 2,341,904; xml: 509,420; makefile: 348,726; cpp: 93,608; sh: 3,387; java: 609; perl: 297; sql: 222; ansic: 137
file content (246 lines) | stat: -rw-r--r-- 7,940 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
{
 *****************************************************************************
  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.